python-3.xamazon-web-servicesaws-cloudformationtroposphere

How to attach an AWS managed policy to a role in cloudformation and troposphere


In my troposphere code i basically want to create an sns topic and a lambda execution role to which i can attach a few managed policy from aws. But the issue is i cannot find a way to just reference the arn name of the managed policy. Below is my code but here i am copying and pasting the managed policy json document.

Is there a better way out ?

from troposphere import FindInMap, GetAtt, Join, Output, Template, Ref, ImportValue
from troposphere.sns import Topic
from troposphere.iam import Role, Policy


t = Template()

t.set_version("2010-09-09")

sns_topic = Topic(TopicName='IngestStateTopic', title='IngestStateTopic')

t.add_resource(sns_topic)

LambdaExecutionRole = t.add_resource(
    Role(
        "LambdaExecutionRole",
        Path="/",
        Policies=[
            Policy(PolicyName="CloudWatchLogsFullAccess",
                   PolicyDocument={
                       "Version":
                       "2012-10-17",
                       "Statement": [{
                           "Action": ["logs:*"],
                           "Effect": "Allow",
                           "Resource": "*"
                       }]
                   }),
            Policy(PolicyName="SnsReadOnlyAccess",
                   PolicyDocument={
                       "Version":
                       "2012-10-17",
                       "Statement": [{
                           "Effect":
                           "Allow",
                           "Action": ["sns:GetTopicAttributes", "sns:List*"],
                           "Resource":
                           "*"
                       }]
                   }),
            Policy(PolicyName="LambdaBasicExecutionRole-Test",
                   PolicyDocument={
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:eu-west-1:498129003450:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:eu-west-1:498129003450:log-group:/aws/lambda/lambda_layers_test:*"
            ]
        }
    ]
})
        ],
        AssumeRolePolicyDocument={
            "Version":
            "2012-10-17",
            "Statement": [{
                "Action": ["sts:AssumeRole"],
                "Effect": "Allow",
                "Principal": {
                    "Service": ["lambda.amazonaws.com"]
                }
            }]
        },
    ))

t.add_output(
    Output(
    "IngestServiceArn",
    Description="ARN of the sns topic",
    Value=Ref(sns_topic),
))

t.add_output(
    Output(
    "LambdaExcecutionRole",
    Description="ARN of the lambda plocy document",
    Value=GetAtt(LambdaExecutionRole, "Arn"),
))

with open('sns_lambda_role.yaml', 'w') as s:
    s.write(t.to_yaml())

And below is my cloud formation yaml file name:

AWSTemplateFormatVersion: '2010-09-09'
Outputs:
  IngestServiceArn:
    Description: ARN of the sns topic
    Value: !Ref 'IngestStateTopic'
  LambdaExcecutionRole:
    Description: ARN of the lambda plocy document
    Value: !GetAtt 'LambdaExecutionRole.Arn'
Resources:
  IngestStateTopic:
    Properties:
      TopicName: IngestStateTopic
    Type: AWS::SNS::Topic
  LambdaExecutionRole:
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
        Version: '2012-10-17'
      Path: /
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - logs:*
                Effect: Allow
                Resource: '*'
            Version: '2012-10-17'
          PolicyName: CloudWatchLogsFullAccess
        - PolicyDocument:
            Statement:
              - Action:
                  - sns:GetTopicAttributes
                  - sns:List*
                Effect: Allow
                Resource: '*'
            Version: '2012-10-17'
          PolicyName: SnsReadOnlyAccess
        - PolicyDocument:
            Statement:
              - Action: logs:CreateLogGroup
                Effect: Allow
                Resource: arn:aws:logs:eu-west-1:498129003450:*
              - Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Effect: Allow
                Resource:
                  - arn:aws:logs:eu-west-1:498129003450:log-group:/aws/lambda/lambda_layers_test:*
            Version: '2012-10-17'
          PolicyName: LambdaBasicExecutionRole-Test
    Type: AWS::IAM::Role

Solution

  • You can do this by specifying a list of ManagedPolicyArns for Role cloudformation resource, but not the Policies - Documentation:

    {
      "Type" : "AWS::IAM::Role",
      "Properties" : {
          "AssumeRolePolicyDocument" : Json,
          "ManagedPolicyArns" : [ String, ... ],
          "MaxSessionDuration" : Integer,
          "Path" : String,
          "PermissionsBoundary" : String,
          "Policies" : [ Policy, ... ],
          "RoleName" : String
        }
    }
    

    For ManagedPolicy CloudFormation has separate resource type - AWS::IAM::ManagedPolicy:

    SampleManagedPolicy:
        Type: AWS::IAM::ManagedPolicy
        Properties:
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Sid: AllowAllUsersToListAccounts
                Effect: Allow
                Action:
                  - iam:ListAccountAliases
                  - iam:ListUsers
                  - iam:GetAccountSummary
                Resource: "*
    

    Examle:

    RootRole:
        Type: 'AWS::IAM::Role'
        Properties:
          AssumeRolePolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Principal:
                  Service:
                    - ec2.amazonaws.com
                Action:
                  - 'sts:AssumeRole'
          Path: /
          ManagedPolicyArns:
            - !Ref awsExampleManagedPolicyParameterOne            
            - !Ref awsExampleManagedPolicyParameterTwo
    

    So, if we are talking about the tropopshere - it also has separate class for ManagedPolicy:

    class ManagedPolicy(AWSObject):
        resource_type = "AWS::IAM::ManagedPolicy"
    
        props = {
            'Description': (basestring, False),
            'Groups': ([basestring], False),
            'ManagedPolicyName': (basestring, False),
            'Path': (iam_path, False),
            'PolicyDocument': (policytypes, True),
            'Roles': ([basestring], False),
            'Users': ([basestring], False),
        }
    

    And you refer to it using Ref function.