Stelligent

Create a Cross-Account Pipeline in AWS CloudFormation

When creating pipelines in AWS CodePipeline, you may want to make these pipelines available to other AWS accounts. When doing this, you’ll likely want to make these pipelines read only. A typical use case is that you want consistency for certain types of pipelines across an enterprise by providing a view of these pipelines to other accounts. Doing this involves the use of CodePipeline and AWS Identity and Access Management (IAM). Moreover, if you’re like us, you’ll want to automate the provisioning of this in AWS CloudFormation.
In this post, I describe how to automate the provisioning of cross-account access to pipelines in AWS CodePipeline using IAM. All of the examples in this post are coded in one CloudFormation template: codepipeline-cross-account-pipeline.json. Instructions for running this template are provided at the end of this post.

Create a Simple CodePipeline Stack

The first code fragment you see here is the beginning of the CodePipeline definition using the AWS::CodePipeline::Pipeline type. To expedite the example, I’m using the Lambda-based pipeline that we described in the Mocking AWS CodePipeline pipelines with Lambda post. For simplicity, I’m defining two stages: Source and Commit.

    "CodePipelineStack":{
      "Type":"AWS::CodePipeline::Pipeline",
      "DependsOn":[
        "CodePipelineLambdaDummy"
      ],
      "Properties":{
        "RoleArn":{
          "Fn::Join":[
            "",
            [
              "arn:aws:iam::",
              {
                "Ref":"AWS::AccountId"
              },
              ":role/AWS-CodePipeline-Service"
            ]
          ]
        },
        "Stages":[
          {
            "Name":"Source",
            "Actions":[
              {
                "InputArtifacts":[
                ],
                "Name":"Source",
                "ActionTypeId":{
                  "Category":"Source",
                  "Owner":"AWS",
                  "Version":"1",
                  "Provider":"S3"
                },
                "OutputArtifacts":[
                  {
                    "Name":"MyApp"
                  }
                ],
                "Configuration":{
                  "S3Bucket":{
                    "Ref":"S3Bucket"
                  },
                  "S3ObjectKey":{
                    "Ref":"S3Key"
                  }
                },
                "RunOrder":1
              }
            ]
          },
...

Create an IAM Role

Here, I’m creating an IAM Role that references a user-based parameter containing the AWS account id of the account for which we want to share access to the pipeline(s) we’re defining in this AWS account. This way the other AWS account can assume the role defined in the account that’s defining the IAM role and for which this CloudFormation stack is being launched.

    "CrossAccountPipelineViewersRole":{
      "Type":"AWS::IAM::Role",
      "Properties":{
        "AssumeRolePolicyDocument":{
          "Version":"2012-10-17",
          "Statement":[
            {
              "Effect":"Allow",
              "Principal":{
                "AWS":{
                  "Fn::Join":[
                    "",
                    [
                      "arn:aws:iam::",
                      {
                        "Ref":"PipelineAWSAccountId"
                      },
                      ":root"
                    ]
                  ]
                }
              },
              "Action":"sts:AssumeRole"
            }
          ]
        },
        "Path":"/"
      }
    },
...

Create an IAM Policy

When defining the IAM policy, I set dependencies on the CodePipeline stack and the IAM role using the DependsOn attribute. By defining these dependencies, I can reference the role and stack in the IAM Policy.
You can see the limits I’m defining in the Allow Actions of the PolicyDocument of this definition. This is based on the example from Configure Cross-Account Access to a Pipeline. The Resource attribute defines the limits to the specific pipelines in CodePipeline for which we’ll provide access.

    "CrossAccountPipelineViewersPolicy":{
      "Type":"AWS::IAM::Policy",
      "DependsOn":[
        "CodePipelineStack",
        "CrossAccountPipelineViewersRole"
      ],
      "Properties":{
        "PolicyName":"CrossAccountPipelineViewersPolicy",
        "Roles":[
          {
            "Ref":"CrossAccountPipelineViewersRole"
          }
        ],
        "PolicyDocument":{
          "Version":"2012-10-17",
          "Statement":[
            {
              "Effect":"Allow",
              "Action":[
                "codepipeline:GetPipeline",
                "codepipeline:GetPipelineState",
                "codepipeline:ListActionTypes",
                "codepipeline:ListPipelines",
                "iam:ListRoles",
                "s3:GetBucketPolicy",
                "s3:GetObject",
                "s3:ListAllMyBuckets",
                "s3:ListBucket",
                "codedeploy:GetApplication",
                "codedeploy:GetDeploymentGroup",
                "codedeploy:ListApplications",
                "codedeploy:ListDeploymentGroups",
                "elasticbeanstalk:DescribeApplications",
                "elasticbeanstalk:DescribeEnvironments",
                "lambda:GetFunctionConfiguration",
                "lambda:ListFunctions"
              ],
              "Resource":[
                "*"
              ]
            }
          ]
        }
      }
    },
...

Create a CodePipeline URL Output in CloudFormation

While not directly related to limiting access permissions, I’ve found the code fragment below to be useful when defining my CloudFormation stacks for CodePipeline. It’s a CloudFormation Output that defines the URL for CodePipeline. It’s useful as a way to quickly jump to the pipeline in CodePipeline once the CloudFormation stack is complete.

    "CodePipelineURL":{
      "Value":{
        "Fn::Join":[
          "",
          [
            "https://console.aws.amazon.com/codepipeline/home?region=",
            {
              "Ref":"AWS::Region"
            },
            "#/view/",
            {
              "Ref":"CodePipelineStack"
            }
          ]
        ]
      }
    },

Launch the CloudFormation Stack

To launch the CloudFormation stack, click on the Launch Stack button below and enter the AWS account id for which you want to provide access through the AssumeRole functionality. It’ll take a few minutes to launch.


To run the same from the command line, here’s a command-line example. Likewise, you’ll need to enter a parameter values for the PipelineAWSAccountId parameter key.

aws cloudformation create-stack --stack-name CrossAccountPipeline
--template-url https://s3.amazonaws.com/stelligent-public/cloudformation-templates/github/cross-account-pipeline/codepipeline-cross-account-pipeline.json
 --region us-east-1 --disable-rollback --capabilities="CAPABILITY_IAM"
--parameters ParameterKey=PipelineAWSAccountId,ParameterValue=YOURAWSACCTID

Access Pipeline Resources from other AWS Account

Once your CloudFormation has launched, go to the IAM role that was generated as part of the stack. It’ll be called something CrossAccountPipelineViewersUniqueID where the UniqueId is a generated value. For easier access, just click on the CrossAcccountIAMRole Output link in the CloudFormation stack.
From here, copy the link provided and login to your other AWS account for which you have access with the copied link.

Then go to CodePipeline. You should see any pipelines for which you have access in the other account.

 

Limiting cross-account access to specific pipelines

Ideally, you’ll also want to limit access to specific pipelines. To do this, you should be able to configure the ARN(s) in the Resource attribute associated with the IAM Policy. So far, I haven’t been able to get this to work, so if anyone finds a solution to this problem, let me know. The way I attempted to get this to work was to define the resource as you see in the code fragment below (it does not work).

"Resource":[
  {
    "Fn::Join":[
      "",
      [
        "arn:aws:codepipeline:",
        {
          "Ref":"AWS::Region"
        },
          ":",
        {
          "Ref":"AWS::AccountId"
        },
          ":",
        {
          "Ref":"CodePipelineStack"
        }
      ]
    ]
  }
]

 

Resources

Stelligent Amazon Pollycast