Can CloudFormation add existing policy to existing role?

0

With a CloudFormatin template, I am wanting to add an existing managed policy to an existing iam role?

I can find details on

  1. adding new or existing policy to new iam role
  2. adding new policy to existing iam role
  3. adding inline policy to existing iam role

But can't find details on adding an existing managed policy to an existing iam role.

Am I missing something obvious, or is this a limitation of cloudformation?

AWS
MODERATOR
asked 4 years ago6449 views
2 Answers
2
Accepted Answer

There is no native mechanism to take existing role and existing policy and attach one to the other in CloudFormation. Since CFT is about creating resources, you need to be creating at least one of the two resources. You've probably seen this support question that covers the supported scenarios: How can I attach an IAM managed policy to an IAM role in AWS CloudFormation?

To accomplish what you want, you can create a lambda-backed custom resource passing in the ARN for the existing policy and role name with the function calling the IAM AttachRolePolicy API to do this.

Here is a working example I threw together. On stack delete, it will detach the policy that was attached. On stack update, it will detach the old policy from the old role and then attach the new policy to the new role allowing you to change the existing policy, existing role or both

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  ExistingPolicyArn:
    Type: String
    Default: arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess
  ExistingRoleName:
    Type: String
    Default: ExistingRole
Resources:
  IamXtachPolicyRoleLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action: sts:AssumeRole
          Condition: {}
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - iam:DetachRolePolicy
                  - iam:AttachRolePolicy
                Effect: Allow
                Resource:
                  - !Sub "arn:aws:iam::${AWS::AccountId}:role/*"
            Version: 2012-10-17
          PolicyName: aws-app-iam-xtach-policy

  IamXtachPolicyRoleLambda:
    DependsOn: IamXtachPolicyRoleLambdaRole
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.lambda_handler
      Timeout: 30
      Role: !GetAtt IamXtachPolicyRoleLambdaRole.Arn
      Runtime: python3.8
      Code:
        ZipFile: |
          import logging
          import boto3
          import cfnresponse

          logger = logging.getLogger()
          logger.setLevel(logging.INFO)

          def lambda_handler(event, context):
            
            def attach_policy(policyArn, roleName):
              return iam.attach_role_policy(
                PolicyArn=policyArn,
                RoleName=roleName,
              ) 
            
            def detach_policy(policyArn, roleName):
              return iam.detach_role_policy(
                PolicyArn=policyArn,
                RoleName=roleName,
              ) 

            try:

              iam = boto3.client('iam')
            
              existingPolicyArn = event['ResourceProperties'].get('ExistingPolicyArn')
              existingRoleName = event['ResourceProperties'].get('ExistingRoleName')
            
              if event['RequestType'] == 'Create':
                response = attach_policy(existingPolicyArn, existingRoleName)
              elif event['RequestType'] == 'Delete':
                response = detach_policy(existingPolicyArn, existingRoleName)
              elif event['RequestType'] == 'Update':
                oldExistingPolicyArn = event['OldResourceProperties'].get('ExistingPolicyArn')
                oldExistingRoleName = event['OldResourceProperties'].get('ExistingRoleName')
                response = detach_policy(oldExistingPolicyArn, oldExistingRoleName)
                response = attach_policy(existingPolicyArn, existingRoleName)

              result = cfnresponse.SUCCESS
              
            except Exception as error:
              logger.exception(error)
              response = {
                'status': 500,
                'error': {
                  'type': type(error).__name__,
                  'description': str(error),
                },
              }

              result = cfnresponse.FAILED 

            finally:
              cfnresponse.send(event, context, result, response, {})
              return
 
  AttachExistingPolicyToExistingRole:
    DependsOn: IamXtachPolicyRoleLambda
    Type: Custom::IamXtachPolicyRoleLambda
    Properties:
      ServiceToken: !GetAtt IamXtachPolicyRoleLambda.Arn
      ExistingPolicyArn: !Ref ExistingPolicyArn
      ExistingRoleName: !Ref ExistingRoleName
AWS
Scott_K
answered 4 years ago
profile picture
EXPERT
reviewed 4 months ago
  • Furthermore, you can ask for an ARN value via a parameter so that it can be changed at installation time. This technique is also useful for specifying the PermissionBoundary value, i.e.: PermissionsBoundary: !Ref PermissionBoundaryPolicyArn. See my detailed answer below.

0

Here is another way that also shows how you can get a value from the user and then use that string to specify a policy and a permissions boundary (in my case I use the same value).

AWSTemplateFormatVersion: "2010-09-09"
Description: Example of adding a policy using a parameter
Parameters:
  PermissionBoundaryPolicyArn:
    Type: String
    Description: (Optional) A policy arn for the account Permission Boundary policy, similar to arn:aws:iam::12345679012:policy/ProjAdminPolicy
Conditions:
  AssociatePermissionBoundary:
    !Not [!Equals [!Ref PermissionBoundaryPolicyArn, ""]]

# Define Resources that will be launched via this template
Resources:
  # Example EC2 Server Instance Role Profile
  UnixIamProfile:
    Type: "AWS::IAM::InstanceProfile"
    Properties:
      Path: /
      Roles:
        - !Ref UnixInstanceRole

  # EC2 Server Instance Role that permits SSM Session Manager, S3 access, and has a permission boundary
  UnixInstanceRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ssm.amazonaws.com
                - ec2.amazonaws.com
            Action: "sts:AssumeRole"
      ManagedPolicyArns:
        - !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore
        - !Sub arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess
        - !If [
            AssociatePermissionBoundary,
            !Ref PermissionBoundaryPolicyArn,
            !Ref "AWS::NoValue",
          ]
      PermissionsBoundary:
        !If [
          AssociatePermissionBoundary,
          !Ref PermissionBoundaryPolicyArn,
          !Ref "AWS::NoValue",
        ]
      Policies:
        - PolicyName: CodeCommitReadOnlyAdditional
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - codecommit:BatchDescribe*
                  - codecommit:EvaluatePullRequestApprovalRules
                Resource:
                  - !Sub arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:*
      Tags:
        - Key: Purpose
          Value: Shows how existing policies can be added to a role
answered 9 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions