Skip to content

Latest commit

 

History

History

policies-with-custom-data-sources

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

Policies with custom data sources

This example creates a policy via terraforms aws_iam_policy_document and then adds it to a role's policy_arns list.

Table of Contents

  1. Overview
  2. Example
  3. Usage
  4. Requirements
  5. Providers
  6. Inputs
  7. Outputs

Overview

By using aws_iam_policy_document you have the advantage to use terraform's variables inside the policy definition and be able to first gather already existing resources and add them to that policy. This makes the process of creating policies as dynamic and flexible as it can get.

Example

The following defines two roles, both without any policy attached.

Variable definition

terraform.tfvars

roles = [
  {
    name                 = "ROLE-ADMIN"
    path                 = null
    desc                 = null
    trust_policy_file    = "data/trust-policy-file.json"
    permissions_boundary = null
    policies             = []
    inline_policies      = []
    policy_arns          = []
  },
  {
    name                 = "ROLE-DEV"
    path                 = null
    desc                 = null
    trust_policy_file    = "data/trust-policy-file.json"
    permissions_boundary = null
    policies             = []
    inline_policies      = []
    policy_arns          = []
  },
]

Use data source to fetch a dynamic value

Now we're going to dynamically fetch the current AWS account id (just for the sake of this example to have something dynamic).

main.tf

data "aws_caller_identity" "current" {}

Build our policy

We can then use this account id and include it in our policy document.

main.tf

data "aws_iam_policy_document" "s3" {
  statement {
    sid = "1"

    actions = [
      "s3:ListAllMyBuckets",
    ]

    resources = [
      "arn:aws:s3::${data.aws_caller_identity.current.account_id}:*",
    ]
  }
}

Based on the created policy document, we can define our policy.

main.tf

resource "aws_iam_policy" "s3" {
  name        = "s3-policy"
  path        = "/custom/"
  description = "Custom S3 policy"
  policy      = data.aws_iam_policy_document.s3.json

  lifecycle {
    create_before_destroy = true
  }
}

Enrich roles list with created policy

We can now enrich the roles list and add this policy to ROLE-ADMIN. We do this by creating a local with the exact same structure and use a condition to attach it to the specific role.

main.tf

locals {
  # Roles in this list will have the custom policy added to its policy_arns list
  roles_enriched = [
    for role in var.roles : {
      name                 = role.name
      path                 = role.path
      desc                 = role.desc
      trust_policy_file    = role.trust_policy_file
      permissions_boundary = role.permissions_boundary
      policies             = role.policies
      inline_policies      = role.inline_policies
      policy_arns          = concat(role.policy_arns, [aws_iam_policy.s3.arn])
    } if role["name"] == "ROLE-ADMIN"
  ]

  # Roles in this list will be left as they were (condition reversed)
  roles_default = [
    for role in var.roles : {
      name                 = role.name
      path                 = role.path
      desc                 = role.desc
      trust_policy_file    = role.trust_policy_file
      permissions_boundary = role.permissions_boundary
      policies             = role.policies
      inline_policies      = role.inline_policies
      policy_arns          = role.policy_arns
    } if role["name"] != "ROLE-ADMIN"
  ]

  # Let's merge both created lists
  roles = concat(local.roles_enriched, local.roles_default)
}

Define the iam module

Now we add everything together and use the iam module

main.tf

module "aws_iam" {
  source = "github.com/cytopia/terraform-aws-iam?ref=v5.0.4"

  # Note: we're using the local here as input instead
  roles = local.roles
}

Usage

To run this example you need to execute:

$ terraform init
$ terraform plan
$ terraform apply

Note that this example may create resources which cost money. Run terraform destroy when you don't need these resources.

Requirements

No requirements.

Providers

Name Version
aws n/a

Inputs

Name Description Type Default Required
account_alias Assign the account alias for the AWS Account. Unmanaged by default. Resource will be created if the string is non-empty. string "" no
account_pass_policy Manages Password Policy for the AWS Account. Unmanaged by default. Resource will be created if 'manage' is set to true.
object({
manage = bool # Set to true, to manage the AWS account password policy
allow_users_to_change_password = bool # Allow users to change their own password?
hard_expiry = bool # Users are prevented from setting a new password after their password has expired?
max_password_age = number # Number of days that an user password is valid
minimum_password_length = number # Minimum length to require for user passwords
password_reuse_prevention = number # The number of previous passwords that users are prevented from reusing
require_lowercase_characters = bool # Require lowercase characters for user passwords?
require_numbers = bool # Require numbers for user passwords?
require_symbols = bool # Require symbols for user passwords?
require_uppercase_characters = bool # Require uppercase characters for user passwords?
})
{
"allow_users_to_change_password": null,
"hard_expiry": null,
"manage": false,
"max_password_age": null,
"minimum_password_length": null,
"password_reuse_prevention": null,
"require_lowercase_characters": null,
"require_numbers": null,
"require_symbols": null,
"require_uppercase_characters": null
}
no
providers_saml A list of dictionaries defining saml providers.
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
}))
[] no
providers_oidc A list of dictionaries defining openid connect providers.
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.
}))
[] no
policies A list of dictionaries defining all policies.
list(object({
name = string # Name of the policy
path = string # Defaults to 'var.policy_path' if variable is set to null
desc = string # Defaults to 'var.policy_desc' if variable is set to null
file = string # Path to json or json.tmpl file of policy
vars = map(string) # Policy template variables {key: val, ...}
}))
[] no
groups A list of dictionaries defining all groups.
list(object({
name = string # Name of the group
path = string # Defaults to 'var.group_path' if variable is set to null
policies = list(string) # List of names of policies (must be defined in var.policies)
policy_arns = list(string) # List of existing policy ARN's
inline_policies = list(object({
name = string # Name of the inline policy
file = string # Path to json or json.tmpl file of policy
vars = map(string) # Policy template variables {key = val, ...}
}))
}))
[] no
users A list of dictionaries defining all users.
list(object({
name = string # Name of the user
path = string # Defaults to 'var.user_path' if variable is set to null
groups = list(string) # List of group names to add this user to
access_keys = list(object({
name = string # IaC identifier for first or second IAM access key (not used on AWS)
pgp_key = string # Leave empty for non or provide a b64-enc pubkey or keybase username
status = string # 'Active' or 'Inactive'
}))
permissions_boundary = string # ARN to a policy used as permissions boundary (or null/empty)
policies = list(string) # List of names of policies (must be defined in var.policies)
policy_arns = list(string) # List of existing policy ARN's
inline_policies = list(object({
name = string # Name of the inline policy
file = string # Path to json or json.tmpl file of policy
vars = map(string) # Policy template variables {key = val, ...}
}))
}))
[] no
roles A list of dictionaries defining all roles.
list(object({
name = string # Name of the role
path = string # Defaults to 'var.role_path' if variable is set to null
desc = string # Defaults to 'var.role_desc' if variable is set to null
trust_policy_file = string # Path to file of trust/assume policy
permissions_boundary = string # ARN to a policy used as permissions boundary (or null/empty)
policies = list(string) # List of names of policies (must be defined in var.policies)
policy_arns = list(string) # List of existing policy ARN's
inline_policies = list(object({
name = string # Name of the inline policy
file = string # Path to json or json.tmpl file of policy
vars = map(string) # Policy template variables {key = val, ...}
}))
}))
[] no
policy_path 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. string "/" no
policy_desc The default description of the policy. string "Managed by Terraform" no
group_path 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. string "/" no
user_path 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. string "/" no
role_path 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. string "/" no
role_desc The description of the role. string "Managed by Terraform" no
role_max_session_duration 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. string "3600" no
role_force_detach_policies Specifies to force detaching any policies the role has before destroying it. bool true no
tags Key-value mapping of tags for the IAM role or user. map(any) {} no

Outputs

Name Description
roles Created roles