amazon-web-servicesaws-cloudformationamazon-iamaws-codepipeline

When do I need to have CAPABILITY_NAMED_IAM


I was editing my CloudFormation templates and suddenly AWS tells me I need CAPABILITY_NAMED_IAM. I am curious as to which change triggers this?

What is a named IAM resource?

Before I already "name" my resources like

RoleName: !Sub '${PipelineName}-codebuild'

I am not asked to add this capability, I think until I add

Parameters:
  AppName:
    Type: String
    Description: Prefix for resources

Resources:
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref AppName

To my SAM application template. But arent they the "same" except one uses !Ref? Or maybe some other change triggered this?

For reference, my CodePipeline stack

AWSTemplateFormatVersion : '2010-09-09'
Description: 'Skynet stack for CodePipeline'

Parameters:
  PipelineName:
    Type: String
    Description: Pipeline Name (Lower case only, since S3 bucket names can only have lowercase)
    Default: skynet-pipeline
  GitHubOwner:
    Type: String
    Description: GitHub Owner
    Default: 2359media
  GitHubRepo:
    Type: String
    Description: GitHub Repo
    Default: 'skynet'
  GitHubBranch:
    Type: String
    Description: GitHub Branch
    Default: master
  GitHubToken:
    Type: String
    Description: GitHub Token
    NoEcho: true

Resources:
  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Ref PipelineName
      RoleArn: !GetAtt [PipelineRole, Arn]
      ArtifactStore:
        Location: !Ref PipelineArtifactStore
        Type: S3
      DisableInboundStageTransitions: []
      Stages:
        - Name: GitHubSource
          Actions:
          - Name: Source
            ActionTypeId:
              Category: Source
              Owner: ThirdParty
              Version: 1
              Provider: GitHub
            Configuration:
              Owner: !Ref GitHubOwner
              Repo: !Ref GitHubRepo
              Branch: !Ref GitHubBranch
              OAuthToken: !Ref GitHubToken
            OutputArtifacts:
              - Name: SourceCode
        - Name: Build
          Actions:
          - Name: Lambda
            InputArtifacts:
              - Name: SourceCode
            OutputArtifacts:
              - Name: LambdaPackage
            ActionTypeId:
              Category: Build
              Owner: AWS
              Version: 1
              Provider: CodeBuild
            Configuration:
              ProjectName: !Ref CodeBuildLambda
        - Name: CreateChangeSet
          Actions:
          - Name: Lambda
            InputArtifacts:
              - Name: LambdaPackage
            OutputArtifacts:
              - Name: LambdaDeployment
            ActionTypeId:
              Category: Deploy
              Owner: AWS
              Version: 1
              Provider: CloudFormation
            Configuration:
              ActionMode: CHANGE_SET_REPLACE
              ChangeSetName: !Sub
                - '${PipelineName}-lambda'
                - {PipelineName: !Ref PipelineName}
              RoleArn: !GetAtt [CloudFormationRole, Arn]
              StackName: !Sub
                - '${PipelineName}-lambda'
                - {PipelineName: !Ref PipelineName}
              TemplatePath: 'LambdaPackage::SkynetLambdaPackaged.yml'
              Capabilities: CAPABILITY_NAMED_IAM
              ParameterOverrides: !Sub '{"AppName": "${PipelineName}-lambda"}'
        - Name: ExecuteChangeSet
          Actions:
          - Name: Lambda
            ActionTypeId:
              Category: Deploy
              Owner: AWS
              Version: 1
              Provider: CloudFormation
            Configuration:
              ActionMode: CHANGE_SET_EXECUTE
              ChangeSetName: !Sub
                - '${PipelineName}-lambda'
                - {PipelineName: !Ref PipelineName}
              StackName: !Sub
                - '${PipelineName}-lambda'
                - {PipelineName: !Ref PipelineName}

  CodeBuildLambda:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Sub '${PipelineName}-lambda'
      Artifacts:
        Type: CODEPIPELINE
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/nodejs:7.0.0
        Type: LINUX_CONTAINER
        EnvironmentVariables:
          - Name: S3_BUCKET
            Value: !Ref PipelineArtifactStore
      ServiceRole: !Ref CodeBuildRole
      Source:
        BuildSpec: 'lambda/buildspec.yml'
        Type: CODEPIPELINE

  PipelineArtifactStore:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${PipelineName}-artifacts'
      VersioningConfiguration:
        Status: Enabled

  CodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${PipelineName}-codebuild'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          Effect: Allow
          Principal:
            Service: codebuild.amazonaws.com
          Action: sts:AssumeRole
      Policies:
        - PolicyName: !Sub '${PipelineName}-codebuild'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Resource: 'arn:aws:logs:*:*:*'
                Action:
                - 'logs:CreateLogGroup'
                - 'logs:CreateLogStream'
                - 'logs:PutLogEvents'
              - Effect: Allow
                Resource:
                  - !Sub 'arn:aws:s3:::codepipeline-${AWS::Region}-*/*'
                  - !Sub
                    - '${PipelineArtifactStoreArn}/*'
                    - {PipelineArtifactStoreArn: !GetAtt [PipelineArtifactStore, Arn]}
                Action:
                  - 's3:GetObject'
                  - 's3:GetObjectVersion'
                  - 's3:PutObject'

  CloudFormationRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${PipelineName}-cloudformation'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service: cloudformation.amazonaws.com
          Action:
          - sts:AssumeRole
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/AWSLambdaExecute'
      Policies:
        - PolicyName: !Sub '${PipelineName}-cloudformation'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Resource: '*'
                Action:
                - 's3:GetObject'
                - 's3:GetObjectVersion'
                - 's3:GetBucketVersioning'
              - Effect: Allow
                Resource: 'arn:aws:s3:::codepipeline*'
                Action:
                - 's3:PutObject'
              - Effect: Allow
                Resource: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:*'
                Action:
                - 'lambda:*'
              - Effect: Allow
                Resource: !Sub 'arn:aws:apigateway:${AWS::Region}::*'
                Action:
                - 'apigateway:*'
              - Effect: Allow
                Resource: '*'
                Action:
                - 'lambda:CreateEventSourceMapping'
                - 'lambda:DeleteEventSourceMapping'
                - 'lambda:GetEventSourceMapping'
              - Effect: Allow
                Resource: '*'
                Action:
                - 'iam:GetRole'
                - 'iam:CreateRole'
                - 'iam:DeleteRole'
                - 'iam:PassRole'
                - 'iam:AttachRolePolicy'
                - 'iam:DetachRolePolicy'
                - 'iam:DeleteRolePolicy'
                - 'iam:PutRolePolicy'
              - Effect: Allow
                Resource: '*'
                Action:
                - 'iam:PassRole'
              - Effect: Allow
                Resource: !Sub 'arn:aws:cloudformation:${AWS::Region}:aws:transform/Serverless-2016-10-31'
                Action:
                - 'cloudformation:CreateChangeSet'

  PipelineRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${PipelineName}-pipeline'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Action: ['sts:AssumeRole']
          Effect: Allow
          Principal:
            Service: [codepipeline.amazonaws.com]
      Path: /
      Policies:
        - PolicyName: SkynetPipeline
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                - 's3:GetObject'
                - 's3:GetObjectVersion'
                - 's3:GetBucketVersioning'
                Effect: 'Allow'
                Resource: '*'
              - Action:
                - 's3:PutObject'
                Effect: 'Allow'
                Resource:
                - !GetAtt [PipelineArtifactStore, Arn]
              - Action:
                - 'codecommit:CancelUploadArchive'
                - 'codecommit:GetBranch'
                - 'codecommit:GetCommit'
                - 'codecommit:GetUploadArchiveStatus'
                - 'codecommit:UploadArchive'
                Effect: 'Allow'
                Resource: '*'
              - Action:
                - 'codedeploy:CreateDeployment'
                - 'codedeploy:GetApplicationRevision'
                - 'codedeploy:GetDeployment'
                - 'codedeploy:GetDeploymentConfig'
                - 'codedeploy:RegisterApplicationRevision'
                Effect: 'Allow'
                Resource: '*'
              - Action:
                - 'elasticbeanstalk:*'
                - 'ec2:*'
                - 'elasticloadbalancing:*'
                - 'autoscaling:*'
                - 'cloudwatch:*'
                - 's3:*'
                - 'sns:*'
                - 'cloudformation:*'
                - 'rds:*'
                - 'sqs:*'
                - 'ecs:*'
                - 'iam:PassRole'
                Effect: 'Allow'
                Resource: '*'
              - Action:
                - 'lambda:InvokeFunction'
                - 'lambda:ListFunctions'
                Effect: 'Allow'
                Resource: '*'
              - Action:
                - 'opsworks:CreateDeployment'
                - 'opsworks:DescribeApps'
                - 'opsworks:DescribeCommands'
                - 'opsworks:DescribeDeployments'
                - 'opsworks:DescribeInstances'
                - 'opsworks:DescribeStacks'
                - 'opsworks:UpdateApp'
                - 'opsworks:UpdateStack'
                Effect: 'Allow'
                Resource: '*'
              - Action:
                - 'cloudformation:CreateStack'
                - 'cloudformation:DeleteStack'
                - 'cloudformation:DescribeStacks'
                - 'cloudformation:UpdateStack'
                - 'cloudformation:CreateChangeSet'
                - 'cloudformation:DeleteChangeSet'
                - 'cloudformation:DescribeChangeSet'
                - 'cloudformation:ExecuteChangeSet'
                - 'cloudformation:SetStackPolicy'
                - 'cloudformation:ValidateTemplate'
                - 'iam:PassRole'
                Effect: 'Allow'
                Resource: '*'
              - Action:
                - 'codebuild:BatchGetBuilds'
                - 'codebuild:StartBuild'
                Effect: 'Allow'
                Resource: '*'

The part of SAM stack (sam.yml) changed recently

AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: 'Skynet. AWS Management Assistant'
Parameters:
  AppName:
    Type: String
    Description: Prefix for resources

Resources:
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref AppName
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
    Statement:
    - Effect: Allow
      Principal:
        Service:
          - lambda.amazonaws.com
          - apigateway.amazonaws.com
      Action:
      - sts:AssumeRole
  ManagedPolicyArns:
    - 'arn:aws:iam::aws:policy/AmazonEC2FullAccess'
    - 'arn:aws:iam::aws:policy/AWSLambdaFullAccess'
    - 'arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess'
    - 'arn:aws:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess'
    - 'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess'

Solution

  • When are CAPABILITY_IAM/CAPABILITY_NAMED_IAM Required

    According to CloudFormation CreateStack Parameters, one of these is required when your Template includes any of the following resource types:

    AWS::IAM::AccessKey
    AWS::IAM::Group
    AWS::IAM::InstanceProfile
    AWS::IAM::Policy
    AWS::IAM::Role
    AWS::IAM::User
    AWS::IAM::UserToGroupAddition 
    

    When to use CAPABILITY_NAMED_IAM instead of CAPABILITY_IAM

    When any of your IAM resources have a custom name, such as a RoleName then CAPABILITY_NAMED_IAM is required.

    Why are these required?

    The Capabilities are there to ensure you realize that you're creating IAM resources, that these will modify the permissions on your account, and that you have reviewed these resources and their permissions as necessary.