Create IAM policies
You must assign explicit permissions to IAM identities (users, groups, or roles) to enable their access AWS resources. The associated IAM policy determines the privileges available to an IAM identity. Policies are JSON documents that define explicit allow/deny privileges to specific resources or resource groups.
There are advantages to managing IAM policies in Terraform rather than manually in AWS. With Terraform, you can reuse your policy templates and ensure the principle of least privilege with resource interpolation.
In this tutorial, you will create an IAM user and an S3 bucket. Then, you will map permissions for that bucket with an IAM policy. Finally, you will attach that policy to the new user and learn how to iterate on more complex policies.
Prerequisites
You can complete this tutorial using the same workflow with either Terraform Community Edition or HCP Terraform. HCP Terraform is a platform that you can use to manage and execute your Terraform projects. It includes features like remote state and execution, structured plan output, workspace resource summaries, and more.
Select the Terraform Community Edition tab to complete this tutorial using Terraform Community Edition.
This tutorial assumes that you are familiar with the Terraform and HCP Terraform workflows. If you are new to Terraform, complete the Get Started collection first. If you are new to HCP Terraform, complete the HCP Terraform Get Started tutorials first.
For this tutorial, you will need:
- Terraform v1.2+ installed locally.
- An HCP Terraform account and organization.
- HCP Terraform locally authenticated.
- The AWS CLI.
- An AWS account with IAM administrative permissions.
- An HCP Terraform variable set configured with your AWS credentials.
Clone the example repository
Clone the Create IAM policies with Terraform repository.
$ git clone https://github.com/hashicorp-education/learn-terraform-iam-policy
Change into the repository directory.
$ cd learn-terraform-iam-policy
Review the IAM policy resource
The IAM policy resource is the starting point for creating an IAM policy in Terraform.
The main.tf
file contains an IAM policy resource, an S3 bucket, and a new IAM user. Open the main.tf
file in your code editor and review the IAM policy resource. The name
in your policy is a random_pet
string to avoid duplicate policy names.
main.tf
resource "aws_iam_policy" "policy" { name = "${random_pet.pet_name.id}-policy" description = "My test policy" policy = <<EOT{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:ListAllMyBuckets" ], "Effect": "Allow", "Resource": "*" }, { "Action": [ "s3:*" ], "Effect": "Allow", "Resource": "${aws_s3_bucket.bucket.arn}" } ] }EOT}
The resource's policy
attribute uses a multi-line heredoc string. For simple policies or one-off configurations, this approach is acceptable. However, as your policies grow more complex and you begin to reuse them throughout your environment, it can be difficult to parse policies using heredoc strings.
Refactor your policy
The aws_iam_policy_document
data source uses HCL to generate a JSON representation of an IAM policy document. Writing the policy as a Terraform configuration has several advantages over defining your policy inline in the aws_iam_policy
resource.
- Terraform data sources makes applying policies to your AWS resources more flexible. You can overwrite, append, or update policies with this resource by using the
source_policy_documents
andoverride_policy_documents
arguments. - Terraform data sources make it easier to reuse policies throughout your environment.
- Terraform error checking automatically formats your policy document into correct JSON when you run your apply.
Copy the aws_iam_policy_document
configuration below into your main.tf
file. This data source uses HCL syntax to define the same IAM privileges as the policy in the heredoc string.
main.tf
data "aws_iam_policy_document" "example" { statement { actions = ["s3:ListAllMyBuckets"] resources = ["arn:aws:s3:::*"] effect = "Allow" } statement { actions = ["s3:*"] resources = [aws_s3_bucket.bucket.arn] effect = "Allow" }}
Both statements in this policy apply to any user, group, or role with this policy attached. The first policy statement allows the user to list every S3 bucket in the AWS account. The second policy statement allows the user to perform any action on the bucket you create in this configuration, but not on other buckets in the account.
Update your iam_policy
resource policy
attribute to use the IAM policy document and save your changes.
main.tf
resource "aws_iam_policy" "policy" { name = "${random_pet.pet_name.id}-policy" description = "My test policy"+ policy = data.aws_iam_policy_document.example.json- policy = <<EOT- {- "Version": "2012-10-17",- "Statement": [- {- "Action": [- "s3:ListAllMyBuckets"- ],- "Effect": "Allow",- "Resource": "*"- },- {- "Action": [- "s3:*"- ],- "Effect": "Allow",- "Resource": "${aws_s3_bucket.bucket.arn}"- }- ]- }- EOT }
Create a policy attachment
The iam_policy
resource and iam_policy_document
data source used together will create a policy, but this configuration does not apply this policy to any users or roles. You must create a policy attachment for your policy to apply to your users.
In your main.tf
file, add a new policy attachment resource to apply your policy to the user created in this configuration.
main.tf
resource "aws_iam_user_policy_attachment" "attachment" { user = aws_iam_user.new_user.name policy_arn = aws_iam_policy.policy.arn}
The policy attachment resource has two required attributes: the user
and the policy_arn
. Terraform interpolates your policy Amazon Resource Name (ARN) from your previously defined iam_policy
resource when you apply this configuration. Each AWS resource has an ARN, and Terraform passes that unique identifier to the AWS API.
This resource assigns your policy to a specific user. If you are creating a group or a role, you can use the group or role attachment resources instead.
Add an output for your JSON-rendered policy to the end of your outputs.tf
file.
outputs.tf
output "rendered_policy" { value = data.aws_iam_policy_document.example.json}
Create your user, bucket, and policy
Now that you have created and attached a policy in your configuration, apply your changes.
Set the TF_CLOUD_ORGANIZATION
environment variable to your HCP Terraform
organization name. This will configure your HCP Terraform integration.
$ export TF_CLOUD_ORGANIZATION=
In your terminal, initialize your Terraform configuration. Terraform will automatically create the learn-terraform-aws-iam-policy
workspace in your HCP Terraform organization.
$ terraform initInitializing HCP Terraform...Initializing provider plugins...- Reusing previous version of hashicorp/aws from the dependency lock file- Installing hashicorp/aws v4.4.0...- Installed hashicorp/aws v4.4.0 (signed by HashiCorp)HCP Terraform has been successfully initialized!You may now begin working with HCP Terraform. Try running "terraform plan" tosee any changes that are required for your infrastructure.If you ever set or change modules or Terraform Settings, run "terraform init"again to reinitialize your working directory.
Note
This tutorial assumes that you are using a tutorial-specific HCP Terraform organization with a global variable set of your AWS credentials. Review the Create a Credential Variable Set for detailed guidance. If you are using a scoped variable set, assign it to your new workspace now.
Apply your configuration. Enter yes
when prompted to accept your changes.
$ terraform apply## ...Apply complete! Resources: 5 added, 0 changed, 0 destroyed. Outputs: rendered_policy = <<EOT{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Action": "s3:ListAllMyBuckets", "Resource": "arn:aws:s3:::*" }, { "Sid": "", "Effect": "Allow", "Action": "s3:*", "Resource": "arn:aws:s3:::primarily-liberal-spider-bucket" } ]}EOT
The output should contain a rendered policy with the complete ARN of your newly created bucket.
Test the policy
Test this policy in the AWS Policy Simulator. This tool lets you test an IAM policy by simulating whether a user would be allowed to run AWS operations.
Use the Policy Simulator to test if your user can delete any objects or buckets in the S3 service.
- First, select
new_user
, then your policy name from the left sidebar. Your policy name should start with your random animal and-policy-
. - In the "Select service" drop-down, select "S3".
- In the "Select actions" drop-down, choose "DeleteObject" and "DeleteBucket".
- Click "Run Simulation" and verify the simulator denies both actions as intended.
Next, test if your user can delete objects in the test bucket you created or delete the bucket itself.
- Copy the ARN of your test bucket. Your ARN should start with
arn:aws:s3:::<RANDOM_ANIMAL>-bucket-
. - Expand the simulation result drop-down for each action and add the bucket ARN, replacing the existing
*
character. - Select "Run Simulation" and verify the simulator allows both actions as intended.
Clean up your infrastructure
Before moving on, destroy the infrastructure you created in this tutorial.
$ terraform destroy
Respond to the confirmation prompt with yes.
If you used HCP Terraform for this tutorial, after destroying your resources, delete the learn-terraform-aws-iam-policy
workspace from your HCP Terraform organization.
Next Steps
In this tutorial, you created and refactored an AWS IAM policy with Terraform. To learn more about creating policies with Terraform, consider the resources below.
- S3 bucket policies differ from IAM policies. To learn more about S3 bucket policy resources, review the S3 bucket policy resource.
- The
templatefile()
function allows you to create templatized policies for use in your configuration. - For an alternate to heredoc formatting, use the
jsonencode()
function. - Learn how to create default tags for AWS resources.
- Learn how to use AssumeRole to Provision AWS Resources.