Features | Important | Examples | Usage | Requirements | Inputs | Outputs | Related projects | Authors | License
This Terraform module manages AWS IAM to its full extend.
It is only required to have a single module invocation per AWS account, as this module allows the creation of unlimited resources and you will therefore have an auditable single source of truth for IAM.
- Completely configurable via
terraform.tfvars
only - Arbitrary number of IAM policies, groups, users and roles
- Policies can be defined via JSON or templatable JSON files
- Policies can be defined via
aws_iam_policy_document
(Example here) - Groups, users and roles can be attached to an arbitrary number of custom policies, inline policies and existing policy ARN's
- Users can be added to an arbitrary number of groups
- Users support AWS access/secret key rotation
- Roles support trusted entities
- Arbitrary number of identity providers (SAML and OIDC)
- Account settings: account alias and password policy
When creating an IAM user with an Inactive
access key, it is initially created with access key set to Active
. You will have to run it a second time in order to deactivate the access key.
This is either an issue with the terraform resource aws_iam_access_key
or with the AWS api itself.
This module is very flexible and might look a bit complicated at first glance. To show off a few features which are possible, have a look at the following examples.
📄 Also see each example README.md file for more detailed explanations on each of the covered resources. They serve as a documentation purpose as well.
Example | Description |
---|---|
POLICIES | |
JSON policies | Define JSON policies with variable templating |
Policies with custom data sources | Use terraform's aws_iam_policy_document data source to create policies and attach them to defined roles. |
GROUPS / USERS | |
Groups | Defines groups |
Users | Defines users |
Groups, users and policies | Defines groups, users and policies |
Access key rotation | Shows how to safely rotate AWS access keys for IAM users |
ROLES | |
Roles | Define roles (cross-account assumable) |
ADVANCED | |
SAML Login | Login into AWS via SAML identity provider and assume cross-account roles. Also read about best-practices for separating login roles and permission roles. |
You can simply clone this repository and add your terraform.tfvars
file into the root of this directory.
terraform.tfvars
# --------------------------------------------------------------------------------
# Account Management
# --------------------------------------------------------------------------------
account_alias = "prod-account"
account_pass_policy = {
manage = true
allow_users_to_change_password = true
hard_expiry = false
max_password_age = 365
minimum_password_length = 8
password_reuse_prevention = 5
require_lowercase_characters = true
require_numbers = true
require_symbols = true
require_uppercase_characters = true
}
# --------------------------------------------------------------------------------
# Account Identity provider
# --------------------------------------------------------------------------------
# Add a SAML provider for login
providers_saml = [
{
name = "AzureAD"
file = "path/to/azure/meta.xml"
},
{
name = "ADFS"
file = "path/to/adfs/meta.xml"
}
]
# --------------------------------------------------------------------------------
# Policies, Groups, Users and Roles
# --------------------------------------------------------------------------------
# List of policies to create
# Policies defined here can be used by name in groups, users and roles list
policies = [
{
name = "ro-billing"
path = "/assume/human/"
desc = "Provides read-only access to billing"
file = "policies/ro-billing.json"
vars = {}
},
]
# List of groups to manage
# Groups defined here can be used in users list
groups = [
{
name = "admin-group"
path = null
policies = []
policy_arns = [
"arn:aws:iam::aws:policy/AdministratorAccess",
]
inline_policies = []
},
]
# List of users to manage
users = [
{
name = "admin"
path = null
groups = ["admin-group"]
access_keys = []
permissions_boundary = null
policies = []
policy_arns = []
inline_policies = []
},
]
# List of roles to manage
roles = [
{
name = "ROLE-ADMIN"
instance_profile = null
path = ""
desc = ""
trust_policy_file = "trust-policies/admin.json"
permissions_boundary = null
policies = []
policy_arns = [
"arn:aws:iam::aws:policy/AdministratorAccess",
]
inline_policies = []
},
{
name = "ROLE-DEV"
instance_profile = null
path = ""
desc = ""
trust_policy_file = "trust-policies/dev.json"
permissions_boundary = "arn:aws:iam::aws:policy/PowerUserAccess"
policies = [
"ro-billing",
]
policy_arns = [
"arn:aws:iam::aws:policy/PowerUserAccess",
]
inline_policies = []
},
]
# --------------------------------------------------------------------------------
# Defaults
# --------------------------------------------------------------------------------
policy_path = "/"
policy_desc = "Managed by Terraform"
group_path = "/"
user_path = "/"
role_path = "/"
role_desc = "Managed by Terraform"
role_max_session_duration = 3600
role_force_detach_policies = true
tags = {
env = "prod"
owner = "terraform"
}
Create your own module by sourcing this module.
module "iam_roles" {
source = "github.com/Flaconi/terraform-aws-iam-roles?ref=v6.1.0"
# --------------------------------------------------------------------------------
# Account Management
# --------------------------------------------------------------------------------
account_alias = "prod-account"
account_pass_policy = {
manage = true
allow_users_to_change_password = true
hard_expiry = false
max_password_age = 365
minimum_password_length = 8
password_reuse_prevention = 5
require_lowercase_characters = true
require_numbers = true
require_symbols = true
require_uppercase_characters = true
}
# --------------------------------------------------------------------------------
# Account Identity provider
# --------------------------------------------------------------------------------
# Add a SAML provider for login
providers_saml = [
{
name = "AzureAD"
file = "path/to/azure/meta.xml"
},
{
name = "ADFS"
file = "path/to/adfs/meta.xml"
}
]
# --------------------------------------------------------------------------------
# Policies, Groups, Users and Roles
# --------------------------------------------------------------------------------
# List of policies to create
# Policies defined here can be used by name in groups, users and roles list
policies = [
{
name = "ro-billing"
path = "/assume/human/"
desc = "Provides read-only access to billing"
file = "policies/ro-billing.json"
vars = {}
},
]
# List of groups to manage
# Groups defined here can be used in users list
groups = [
{
name = "admin-group"
path = null
policies = []
policy_arns = [
"arn:aws:iam::aws:policy/AdministratorAccess",
]
inline_policies = []
},
]
# List of users to manage
users = [
{
name = "admin"
path = null
groups = ["admin-group"]
access_keys = []
permissions_boundary = null
policies = []
policy_arns = []
inline_policies = []
},
]
# List of roles to manage
roles = [
{
name = "ROLE-ADMIN"
instance_profile = null
path = ""
desc = ""
trust_policy_file = "trust-policies/admin.json"
permissions_boundary = null
policies = []
policy_arns = [
"arn:aws:iam::aws:policy/AdministratorAccess",
]
inline_policies = []
},
{
name = "ROLE-DEV"
instance_profile = null
path = ""
desc = ""
trust_policy_file = "trust-policies/dev.json"
permissions_boundary = "arn:aws:iam::aws:policy/PowerUserAccess"
policies = [
"ro-billing",
]
policy_arns = [
"arn:aws:iam::aws:policy/PowerUserAccess",
]
inline_policies = []
},
]
# --------------------------------------------------------------------------------
# Defaults
# --------------------------------------------------------------------------------
policy_path = "/"
policy_desc = "Managed by Terraform"
group_path = "/"
user_path = "/"
role_path = "/"
role_desc = "Managed by Terraform"
role_max_session_duration = 3600
role_force_detach_policies = true
tags = {
env = "prod"
owner = "terraform"
}
}
Wrap this module into Terragrunt
terraform {
source = "github.com/Flaconi/terraform-aws-iam-roles?ref=v6.1.0"
}
inputs = {
# --------------------------------------------------------------------------------
# Account Management
# --------------------------------------------------------------------------------
account_alias = "prod-account"
account_pass_policy = {
manage = true
allow_users_to_change_password = true
hard_expiry = false
max_password_age = 365
minimum_password_length = 8
password_reuse_prevention = 5
require_lowercase_characters = true
require_numbers = true
require_symbols = true
require_uppercase_characters = true
}
# --------------------------------------------------------------------------------
# Account Identity provider
# --------------------------------------------------------------------------------
# Add a SAML providers for login
providers_saml = [
{
name = "AzureAD"
file = "path/to/azure/meta.xml"
},
{
name = "ADFS"
file = "path/to/adfs/meta.xml"
}
]
# --------------------------------------------------------------------------------
# Policies, Groups, Users and Roles
# --------------------------------------------------------------------------------
# List of policies to create
# Policies defined here can be used by name in groups, users and roles list
policies = [
{
name = "ro-billing"
path = "/assume/human/"
desc = "Provides read-only access to billing"
file = "policies/ro-billing.json"
vars = {}
},
]
# List of groups to manage
# Groups defined here can be used in users list
groups = [
{
name = "admin-group"
path = null
policies = []
policy_arns = [
"arn:aws:iam::aws:policy/AdministratorAccess",
]
inline_policies = []
},
]
# List of users to manage
users = [
{
name = "admin"
path = null
groups = ["admin-group"]
access_keys = []
permissions_boundary = null
policies = []
policy_arns = []
inline_policies = []
},
]
# List of roles to manage
roles = [
{
name = "ROLE-ADMIN"
instance_profile = null
path = ""
desc = ""
trust_policy_file = "trust-policies/admin.json"
permissions_boundary = null
policies = []
policy_arns = [
"arn:aws:iam::aws:policy/AdministratorAccess",
]
inline_policies = []
},
{
name = "ROLE-DEV"
instance_profile = null
path = ""
desc = ""
trust_policy_file = "trust-policies/dev.json"
permissions_boundary = "arn:aws:iam::aws:policy/PowerUserAccess"
policies = [
"ro-billing",
]
policy_arns = [
"arn:aws:iam::aws:policy/PowerUserAccess",
]
inline_policies = []
},
]
# --------------------------------------------------------------------------------
# Defaults
# --------------------------------------------------------------------------------
policy_path = "/"
policy_desc = "Managed by Terraform"
group_path = "/"
user_path = "/"
role_path = "/"
role_desc = "Managed by Terraform"
role_max_session_duration = 3600
role_force_detach_policies = true
tags = {
env = "prod"
owner = "terraform"
}
}
Name | Version |
---|---|
aws | >= 5 |
Name | Version |
---|---|
terraform | >= 1.3 |
aws | >= 5 |
No required inputs.
The following input variables are optional (have default values):
Description: Assign the account alias for the AWS Account. Unmanaged by default. Resource will be created if the string is non-empty.
Type: string
Default: ""
Description: Manages Password Policy for the AWS Account. Unmanaged by default. Resource will be created if 'manage' is set to true.
Type:
object({
manage = optional(bool, false) # Set to true, to manage the AWS account password policy
allow_users_to_change_password = optional(bool) # Allow users to change their own password?
hard_expiry = optional(bool) # Users are prevented from setting a new password after their password has expired?
max_password_age = optional(number) # Number of days that an user password is valid
minimum_password_length = optional(number) # Minimum length to require for user passwords
password_reuse_prevention = optional(number) # The number of previous passwords that users are prevented from reusing
require_lowercase_characters = optional(bool) # Require lowercase characters for user passwords?
require_numbers = optional(bool) # Require numbers for user passwords?
require_symbols = optional(bool) # Require symbols for user passwords?
require_uppercase_characters = optional(bool) # Require uppercase characters for user passwords?
})
Default: {}
Description: A list of dictionaries defining saml providers.
Type:
list(object({
name = string # The name of the provider to create
file = string # Path to XML generated by identity provider that supports SAML 2.0
}))
Default: []
Description: A list of dictionaries defining openid connect providers.
Type:
list(object({
url = string # URL of the identity provider. Corresponds to the iss claim
client_id_list = list(string) # List of client IDs (also known as audiences)
thumbprint_list = list(string) # List of server certificate thumbprints.
}))
Default: []
Description: A list of dictionaries defining all policies.
Type:
list(object({
name = string # Name of the policy
path = optional(string) # Defaults to 'var.policy_path' if variable is set to null
desc = optional(string) # Defaults to 'var.policy_desc' if variable is set to null
file = string # Path to json or json.tftpl file of policy
vars = optional(map(string), {}) # Policy template variables {key = val, ...}
}))
Default: []
Description: A list of dictionaries defining all groups.
Type:
list(object({
name = string # Name of the group
path = optional(string) # Defaults to 'var.group_path' if variable is set to null
policies = optional(list(string), []) # List of names of policies (must be defined in var.policies)
policy_arns = optional(list(string), []) # List of existing policy ARN's
inline_policies = optional(list(object({
name = string # Name of the inline policy
file = string # Path to json or json.tftpl file of policy
vars = optional(map(string), {}) # Policy template variables {key = val, ...}
})), [])
}))
Default: []
Description: A list of dictionaries defining all users.
Type:
list(object({
name = string # Name of the user
path = optional(string) # Defaults to 'var.user_path' if variable is set to null
groups = optional(list(string), []) # List of group names to add this user to
access_keys = optional(list(object({
name = string # IaC identifier for first or second IAM access key (not used on AWS)
pgp_key = optional(string) # Leave empty for non or provide a b64-enc pubkey or keybase username
status = optional(string, "Active") # 'Active' or 'Inactive'
})), [])
permissions_boundary = optional(string) # ARN to a policy used as permissions boundary (or null/empty)
policies = optional(list(string), []) # List of names of policies (must be defined in var.policies)
policy_arns = optional(list(string), []) # List of existing policy ARN's
inline_policies = optional(list(object({
name = string # Name of the inline policy
file = string # Path to json or json.tftpl file of policy
vars = optional(map(string), {}) # Policy template variables {key = val, ...}
})), [])
}))
Default: []
Description: A list of dictionaries defining all roles.
Type:
list(object({
name = string # Name of the role
instance_profile = optional(string) # Name of the instance profile
path = optional(string) # Defaults to 'var.role_path' if variable is set to null
desc = optional(string) # Defaults to 'var.role_desc' if variable is set to null
trust_policy_file = string # Path to file of trust/assume policy. Will be templated if vars are passed.
trust_policy_vars = optional(map(string), {}) # Policy template variables {key = val, ...}
permissions_boundary = optional(string) # ARN to a policy used as permissions boundary (or null/empty)
permission_boundary_policies = optional(list(object({
file = string # Path to json or json.tftpl file of policy
vars = optional(map(string), {}) # Policy template variables {key = val, ...}
})), [])
policies = optional(list(string), []) # List of names of policies (must be defined in var.policies)
policy_arns = optional(list(string), []) # List of existing policy ARN's
inline_policies = optional(list(object({
name = string # Name of the inline policy
file = string # Path to json or json.tftpl file of policy
vars = optional(map(string), {}) # Policy template variables {key = val, ...}
})), [])
}))
Default: []
Description: The default path under which to create the policy if not specified in the policies list. You can use a single path, or nest multiple paths as if they were a folder structure. For example, you could use the nested path /division_abc/subdivision_xyz/product_1234/engineering/ to match your company's organizational structure.
Type: string
Default: "/"
Description: The default description of the policy.
Type: string
Default: "Managed by Terraform"
Description: The path under which to create the group. You can use a single path, or nest multiple paths as if they were a folder structure. For example, you could use the nested path /division_abc/subdivision_xyz/product_1234/engineering/ to match your company's organizational structure.
Type: string
Default: "/"
Description: The path under which to create the user. You can use a single path, or nest multiple paths as if they were a folder structure. For example, you could use the nested path /division_abc/subdivision_xyz/product_1234/engineering/ to match your company's organizational structure.
Type: string
Default: "/"
Description: The path under which to create the role. You can use a single path, or nest multiple paths as if they were a folder structure. For example, you could use the nested path /division_abc/subdivision_xyz/product_1234/engineering/ to match your company's organizational structure.
Type: string
Default: "/"
Description: The path under which to create the permission boundary. You can use a single path, or nest multiple paths as if they were a folder structure. For example, you could use the nested path /division_abc/subdivision_xyz/product_1234/engineering/ to match your company's organizational structure.
Type: string
Default: "/"
Description: The description of the role.
Type: string
Default: "Managed by Terraform"
Description: The maximum session duration (in seconds) that you want to set for the specified role. This setting can have a value from 1 hour to 12 hours specified in seconds.
Type: number
Default: 3600
Description: Specifies to force detaching any policies the role has before destroying it.
Type: bool
Default: true
Description: Key-value mapping of tags for the IAM role or user.
Type: map(string)
Default: {}
Name | Description |
---|---|
account_alias | Created Account alias. |
account_pass_policy | Created Account password policy. |
group_inline_policy_attachments | Attached group inline IAM policies |
group_policy_arn_attachments | Attached group IAM policy arns |
group_policy_attachments | Attached group customer managed IAM policies |
groups | Created IAM groups |
policies | Created customer managed IAM policies |
providers_oidc | Created OpenID Connect providers. |
providers_saml | Created SAML providers. |
role_inline_policy_attachments | Attached role inline IAM policies |
role_policy_arn_attachments | Attached role IAM policy arns |
role_policy_attachments | Attached role customer managed IAM policies |
roles | Created IAM roles |
user_access_keys | Created access keys |
user_group_memberships | Assigned user/group memberships |
user_inline_policy_attachments | Attached user inline IAM policies |
user_policy_arn_attachments | Attached user IAM policy arns |
user_policy_attachments | Attached user customer managed IAM policies |
users | Created IAM users |
GitHub | Module Registry | Description |
---|---|---|
aws-iam | aws-iam | Manages AWS IAM to its full extend |
aws-iam-roles | aws-iam-roles | Deprecated by aws-iam |
aws-iam-cross_account | aws-iam-cross-account | Deprecated by aws-iam |
aws-route53 | aws-route53 | Manages creation of multiple Route53 zones including attachment to new or existing delegation set |
aws-elb | aws-elb | Manages ELB with optionally a public and/or private Route53 DNS record attached to it |
aws-rds | aws-rds | Manages RDS resources on AWS |
Forked from cytopia.
Copyright (c) 2023 Flaconi GmbH