Skip to content

Latest commit

 

History

History
837 lines (655 loc) · 31 KB

README.md

File metadata and controls

837 lines (655 loc) · 31 KB

Terraform module: AWS IAM

Features | Important | Examples | Usage | Requirements | Inputs | Outputs | Related projects | Authors | License

lint test Tag 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.

⭐ Features

  • 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

❗ Important

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.

💡 Examples

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.

💻 Usage

  1. Use terraform.tfvars only
  2. Use Module
  3. Use Terragrunt

Use terraform.tfvars only

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"
}

Use Module

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"
  }
}

Use Terragrunt

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"
  }
}

Providers

Name Version
aws >= 5

Requirements

Name Version
terraform >= 1.3
aws >= 5

Required Inputs

No required inputs.

Optional 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: {}

Outputs

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

Related projects

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

Authors

Forked from cytopia.

License

MIT License

Copyright (c) 2023 Flaconi GmbH