How to Reference Existing IAM Policies When Creating New Roles in AWS CloudFormation


4 views

When working with AWS CloudFormation, you'll often encounter situations where you need to create new IAM roles while incorporating existing policies. The standard approach of copying policy documents leads to maintenance headaches and violates the DRY (Don't Repeat Yourself) principle.

While CloudFormation doesn't support directly referencing policy documents from existing roles in the Role resource definition, you can achieve the same result using AWS::IAM::Policy or AWS::IAM::ManagedPolicy resources. Here's the most effective pattern:

Resources:
  NewRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      Path: "/"
  
  AttachExistingPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: AttachExistingPolicy
      Roles:
        - !Ref NewRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action: iam:GetPolicy
            Resource: arn:aws:iam::123456789012:policy/ExistingPolicyName

For AWS managed policies or customer-managed policies, you can attach them directly:

Resources:
  NewRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
        - arn:aws:iam::123456789012:policy/MyExistingPolicy

1. Centralize common policies: Create shared managed policies in your AWS account
2. Use parameter store: Store policy ARNs in SSM Parameter Store for easy reference
3. Version control: Track policy changes in your infrastructure as code repository
4. Least privilege: Only attach necessary policies to each role

If you encounter permission errors when attaching policies:
- Verify the CloudFormation execution role has iam:GetPolicy permissions
- Check for policy ARN typos (they're case-sensitive)
- Ensure the policy exists in the same region (IAM is global, but some services have regional policies)


When managing AWS infrastructure through CloudFormation, you'll often encounter situations where you need to create new IAM roles that incorporate existing policies. The specific challenge arises when you want to avoid policy duplication while maintaining clean, maintainable templates.

CloudFormation doesn't directly support referencing standalone IAM policies (those not attached to any role) in a Role resource definition. The Policies property expects inline policy documents, not references to existing managed policies.


// This WON'T work in CloudFormation:
"Policies": [
    {
        "PolicyName": "ExistingPolicy",
        "Ref": "arn:aws:iam::123456789012:policy/MyExistingPolicy"
    }
]

Here are three effective approaches to solve this problem:

1. Using ManagedPolicyArns Property

The proper way to attach existing managed policies to a new role:


"AppTierS3AccessRole": {
    "Type": "AWS::IAM::Role",
    "Properties": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [{
                "Effect": "Allow",
                "Principal": { "Service": ["ec2.amazonaws.com"] },
                "Action": ["sts:AssumeRole"]
            }]
        },
        "ManagedPolicyArns": [
            "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess",
            {"Fn::Sub": "arn:aws:iam::${AWS::AccountId}:policy/MyExistingPolicy"}
        ]
    }
}

2. Export/Import Technique for Cross-Stack Policies

When the policy exists in another CloudFormation stack:


// In the stack that creates the policy:
"Outputs": {
    "SharedPolicyArn": {
        "Value": {"Ref": "MyExistingPolicy"},
        "Export": {"Name": "SharedPolicyArn"}
    }
}

// In your new stack:
"ManagedPolicyArns": [
    {"Fn::ImportValue": "SharedPolicyArn"}
]

3. Policy Document Reuse with Parameters

For complex scenarios where you need to maintain a single source of truth:


"Parameters": {
    "BasePolicyDocument": {
        "Type": "String",
        "Default": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"s3:Get*\",\"s3:List*\"],\"Resource\":\"*\"}]}"
    }
},

"Resources": {
    "AppRole": {
        "Type": "AWS::IAM::Role",
        "Properties": {
            "Policies": [{
                "PolicyName": "CombinedAccess",
                "PolicyDocument": {
                    "Fn::Sub": "${BasePolicyDocument}"
                }
            }]
        }
    }
}
  • Always prefer AWS managed policies when they meet your requirements
  • Use consistent naming conventions for customer managed policies
  • Document policy ARNs in a central location for team reference
  • Consider using AWS SSO or Permission Sets for complex scenarios