Guide: Managing IAM Resources
Relevant source files
The following files were used as context for generating this wiki page:This guide provides a comprehensive overview of managing AWS Identity and Access Management (IAM) resources using the Terraform AWS provider. It covers the creation of IAM roles, the definition of IAM policies using both inline JSON and the aws_iam_policy_document data source, and the various methods for attaching these policies to IAM entities such as roles, users, and groups.
The primary resources and data sources involved in this process are aws_iam_role, aws_iam_policy, aws_iam_role_policy, aws_iam_policy_attachment, and the aws_iam_policy_document data source. Understanding how these components interact is fundamental to securely managing access to AWS resources through infrastructure as code.
Composing IAM Policies with aws_iam_policy_document
The aws_iam_policy_document data source is a powerful tool for dynamically generating IAM policy JSON documents within your Terraform configuration. This avoids embedding large, static JSON strings and allows for the use of variables and other resource attributes.
Core Concepts
The data source constructs a policy from a series of statement blocks. It also supports merging multiple source policies, providing a flexible way to build complex permission sets.
The following diagram illustrates the data flow for generating a final policy document by merging source and override documents.
This diagram shows how aws_iam_policy_document merges source_policy_documents, its own statement blocks, and override_policy_documents to produce a single JSON output.
Sources: internal/service/iam/policy_document_data_source.go:121-198
Statement Block Schema
Each statement block defines a single permission statement within the policy. The key arguments are detailed below.
| Attribute | Type | Description |
|---|---|---|
sid | String | An optional statement identifier. Must be unique within the policy. |
effect | String | The effect of the statement, either Allow or Deny. Defaults to Allow. |
actions | Set of Strings | A list of action identifiers (e.g., s3:GetObject). |
not_actions | Set of Strings | A list of actions to which the policy does not apply. |
principals | Set of Blocks | A block defining the entity to which the policy applies. Used in trust policies. |
not_principals | Set of Blocks | A block defining entities to which the policy does not apply. |
resources | Set of Strings | A list of ARNs for the resources to which the policy applies. |
not_resources | Set of Strings | A list of resource ARNs to which the policy does not apply. |
condition | Set of Blocks | A block that specifies conditions for when the policy is in effect. |
Sources: internal/service/iam/policy_document_data_source.go:48-118
A principals block has the following structure:
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
Sources: internal/service/iam/policy_document_data_source.go:297-316
A condition block has the following structure:
condition {
test = "StringEquals"
variable = "sts:ExternalId"
values = ["12345"]
}
Sources: internal/service/iam/policy_document_data_source.go:62-80, examples/cognito-user-pool/main.tf:52-56
IAM Roles and Policies
IAM Roles are a central part of managing permissions. They are defined by a trust policy (assume_role_policy) and have permissions policies attached to them.
Creating an aws_iam_role
An aws_iam_role requires an assume_role_policy, which is a JSON policy document that grants an entity permission to assume the role. This is a perfect use case for the aws_iam_policy_document data source.
resource "aws_iam_role" "ecs_service" {
name = "tf_example_ecs_role"
assume_role_policy = <<EOF
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
This example shows an IAM role that can be assumed by the AWS ECS service.
Sources: examples/ecs-alb/main.tf:145-162
Attaching Policies to IAM Entities
There are several ways to attach permissions policies to IAM roles, users, and groups. The method you choose depends on whether the policy is managed by AWS, managed by you as a standalone resource, or specific to a single entity (inline).
The following diagram shows the relationships between IAM entities, policies, and the different attachment resources.
This ER diagram illustrates how different attachment resources connect managed policies (aws_iam_policy) to users, groups, and roles, and how an inline policy (aws_iam_role_policy) is embedded directly within a role.
Inline Policies (aws_iam_role_policy)
An inline policy is a policy that is embedded in a single IAM role, group, or user. It is managed as part of that entity. Use the aws_iam_role_policy resource to create an inline policy for a role.
resource "aws_iam_role_policy" "main" {
name = "terraform-example-cognito-idp"
role = aws_iam_role.cidp.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sns:publish"
],
"Resource": [
"*"
]
}
]
}
EOF
}
This code creates an inline policy and attaches it directly to the aws_iam_role.cidp role.
Sources: examples/cognito-user-pool/main.tf:63-79
Managed Policies (aws_iam_policy and Attachments)
Managed policies are standalone IAM resources that can be attached to multiple users, roles, and groups. This promotes reusability and easier management of permissions.
First, define the policy using aws_iam_policy:
resource "aws_iam_policy" "DynamoDBCrudPolicy" {
name = "DynamoDBCrudPolicy"
policy = <<EOT
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:DeleteItem",
"dynamodb:PutItem",
"dynamodb:Scan",
"dynamodb:Query"
],
"Resource": [
"${aws_dynamodb_table.ConnectionsTable.arn}"
]
}]
}
EOT
}
Sources: examples/api-gateway-websocket-chat-app/main.tf:38-59
Once the policy is created, you can attach it using one of the attachment resources.
aws_iam_role_policy_attachment
This resource attaches a single managed policy to a single IAM role. It is a one-to-one mapping.
resource "aws_iam_role_policy_attachment" "OnConnectRoleDynamoDBCrudPolicyAttachment" {
role = aws_iam_role.OnConnectRole.name
policy_arn = aws_iam_policy.DynamoDBCrudPolicy.arn
}
This example attaches the previously defined DynamoDBCrudPolicy to the OnConnectRole.
Sources: examples/api-gateway-websocket-chat-app/main.tf:173-176
aws_iam_policy_attachment
This resource is more flexible and can attach a single managed policy to multiple users, roles, and/or groups simultaneously.
resource "aws_iam_policy_attachment" "test-attach" {
name = "tf-acc-attachment-pa-basic-..."
users = [aws_iam_user.user.name]
roles = [aws_iam_role.role.name]
groups = [aws_iam_group.group.name]
policy_arn = aws_iam_policy.policy.arn
}
This example from a test file demonstrates attaching a single policy to a user, a role, and a group all with one resource.
Sources: internal/service/iam/policy_attachment_test.go:276-282
The provider validates these attachments by calling the AWS API to list entities for the policy and ensuring the count and names match the configuration.
This sequence diagram shows how Terraform interacts with the AWS IAM API to create and verify policy attachments.
Sources: internal/service/iam/policy_attachment_test.go:191-221
aws_iam_group_policy_attachment
This resource is similar to aws_iam_role_policy_attachment but is used for attaching a managed policy to an IAM group.
resource "aws_iam_group_policy_attachment" "test-attach" {
group = aws_iam_group.group.name
policy_arn = aws_iam_policy.policy.arn
}
This example shows a direct attachment of a policy to a group.
Sources: internal/service/iam/group_policy_attachment_test.go:148-151
Conclusion
The Terraform AWS provider offers a comprehensive and declarative suite of resources for managing IAM. By combining the aws_iam_policy_document data source with aws_iam_role, aws_iam_policy, and the various attachment resources, developers can create, manage, and version control their AWS permissions with precision and clarity. This infrastructure-as-code approach is essential for maintaining a secure and auditable cloud environment.