đŸ¶
Terraform

Terraform assume_role_policy vs IAM Policy: Key Differences Explained

By Filip on 10/08/2024

Learn the subtle differences between Terraform's assume_role_policy and standard IAM policies, ensuring your infrastructure deployments are secure and efficient.

Terraform assume_role_policy vs IAM Policy: Key Differences Explained

Table of Contents

Introduction

When defining IAM Assume Role Policies in Terraform, it's crucial to understand that they differ from regular IAM policies. Instead of using the aws_iam_policy resource, you define Assume Role Policies directly within the aws_iam_role resource using the assume_role_policy argument. This argument requires a JSON string representing the policy, which you can either write directly in your Terraform code or load from a separate file using the file() function. The JSON structure for an Assume Role Policy must include a "Statement" array, with each statement specifying the "Principal" allowed to assume the role and the conditions under which they can do so. For instance, to allow an EC2 instance to assume the role, you would specify "ec2.amazonaws.com" as the "Service" within the "Principal". Remember to keep your assume_role_policy idempotent to prevent unintended updates. When granting cross-account access, include the external account ID in the "Principal" section. After implementing any changes, always test thoroughly to ensure the intended entities can successfully assume the role.

Step-by-Step Guide

  1. Understand the Difference: An IAM Assume Role Policy is not the same as a regular IAM policy. While they look similar, you cannot use the aws_iam_policy resource for Assume Role Policies in Terraform.

  2. Use the Right Resource: In Terraform, define Assume Role Policies directly within the aws_iam_role resource using the assume_role_policy argument.

  3. JSON Format: The assume_role_policy argument expects a JSON string. You can either write the JSON directly within your Terraform code or store it in a separate file and load it using the file() function.

  4. Structure: The JSON structure for an Assume Role Policy is specific. It should contain a "Statement" array, where each statement defines who (the "Principal") can assume this role and under what conditions.

  5. Example: Here's a basic example of an assume_role_policy allowing an EC2 instance to assume the role:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
  1. External File: For better organization, store the JSON in a separate file (e.g., trust-policy.json) and load it in your Terraform code:
resource "aws_iam_role" "example" {
  name = "example-role"
  assume_role_policy = file("trust-policy.json")
  # ... other role configurations
}
  1. Idempotency: Ensure your assume_role_policy is idempotent. Avoid using placeholders or dynamically generated values that might change on each Terraform run, leading to unintended updates.

  2. Cross-Account Access: To allow an IAM entity from a different AWS account to assume the role, specify the account ID within the "Principal" section of your Assume Role Policy.

  3. Testing: After creating or modifying an IAM role and its Assume Role Policy, thoroughly test that the intended entities can successfully assume the role.

Code Example

This code defines an AWS IAM role using Terraform. It sets up an Assume Role Policy that allows the root user of a specific AWS account (123456789012) to assume the role. The policy includes a condition requiring an external ID match for security. The Terraform code defines the role, loads the policy from a JSON file, and applies the configuration to create the IAM role in AWS.

This example demonstrates how to create an IAM role with an Assume Role Policy that allows EC2 instances in a specific account to assume the role.

1. Create trust-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:root" 
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "987654321012" 
        }
      }
    }
  ]
}

Explanation:

  • Version: Specifies the version of the policy language.
  • Statement: Contains an array of policy statements.
  • Effect: Defines whether the statement allows or denies access ("Allow" in this case).
  • Principal: Specifies who can assume the role. Here, it's the root user of account 123456789012.
  • Action: Defines the allowed action, which is "sts:AssumeRole" for assuming a role.
  • Condition: (Optional) Adds conditions for assuming the role. Here, it requires an external ID match for added security.

2. Create main.tf:

resource "aws_iam_role" "example_role" {
  name = "example-role"
  assume_role_policy = file("trust-policy.json")

  # ... other role configurations like inline policies or managed policy attachments
}

Explanation:

  • aws_iam_role: Defines an IAM role resource in Terraform.
  • name: Sets the name of the role.
  • assume_role_policy: Loads the Assume Role Policy from the trust-policy.json file using the file() function.

3. Apply the Terraform code:

terraform init
terraform apply

This will create the IAM role with the specified Assume Role Policy, allowing only the specified principal to assume the role under the defined conditions.

Important Considerations:

  • Security: Carefully manage the Principal and Condition sections to control access to your role.
  • External IDs: Using external IDs significantly enhances security when granting cross-account access.
  • Testing: Always test your Assume Role Policy to ensure it functions as expected.

Additional Notes

Policy Syntax and Validation:

  • JSON Formatting: While Terraform uses HCL, the assume_role_policy requires strict JSON. Ensure proper quoting, commas, and data types within the JSON string. Online JSON validators can be helpful.
  • AWS Policy Syntax: The structure of the policy document itself follows AWS's IAM policy syntax. Familiarize yourself with allowed keys like Version, Statement, Effect, Principal, Action, Resource, and Condition.
  • Terraform Validation: Terraform performs basic validation on the JSON structure but doesn't check the validity of the policy logic itself. AWS provides policy simulators and linters to help catch errors before deployment.

Best Practices and Security:

  • Principle of Least Privilege: Grant only the necessary permissions in your Assume Role Policy. Avoid using wildcards (*) whenever possible.
  • External IDs for Cross-Account Access: External IDs act as shared secrets, adding an extra layer of security when allowing entities from other AWS accounts to assume the role.
  • Rotation of Credentials: If you're using temporary credentials to assume a role, ensure you have mechanisms in place to rotate those credentials regularly.
  • Policy Versioning: While not mandatory, consider versioning your Assume Role Policies, especially for complex scenarios, to track changes and roll back if needed.

Troubleshooting:

  • Terraform Errors: Pay close attention to Terraform error messages related to the assume_role_policy. They often provide clues about syntax issues or incorrect key usage.
  • AWS Console: Use the IAM section of the AWS Management Console to inspect the created role and its associated Assume Role Policy. This can help identify discrepancies between your Terraform code and the actual AWS resource.
  • CloudTrail Logs: Enable CloudTrail logging for IAM events to track role assumptions and troubleshoot access issues.

Additional Resources:

Summary

This table summarizes key points about managing IAM Assume Role Policies in Terraform:

Feature Description
Resource Type Use the assume_role_policy argument within the aws_iam_role resource. Do not use the aws_iam_policy resource.
Format The assume_role_policy argument requires a JSON string.
JSON Structure Must contain a "Statement" array. Each statement defines a "Principal" (who can assume the role) and the allowed "Action" (sts:AssumeRole).
Example json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
External Files Store the JSON in a separate file for better organization and load it using the file() function.
Idempotency Ensure the assume_role_policy remains the same across Terraform runs to avoid unintended updates.
Cross-Account Access Specify the external account ID within the "Principal" section to grant access from another AWS account.
Testing Always test that intended entities can successfully assume the role after any changes.

Conclusion

By adhering to these principles and utilizing the provided examples, you can effectively manage IAM Assume Role Policies in Terraform, ensuring secure and controlled access to your AWS resources. Remember that proper IAM configuration is paramount for maintaining a secure and well-architected AWS environment.

References

Were You Able to Follow the Instructions?

😍Love it!
😊Yes
😐Meh-gical
😞No
đŸ€źClickbait