AWS CodePipeline error: Cross-account pass role is

2020-06-20 18:51发布

问题:

I am trying to create an AWS CodePipeline that deploys the production code to a separate account. The code consists of a lambda function which is setup using a sam template and cloudformation. I have it currently deploying to the same account without error. I added another stage that has a manual approval action and after approval it should deploy to the other account. It fails with the following error:

Cross-account pass role is not allowed (Service: AmazonCloudFormation; Status Code: 403; Error Code: AccessDenied; Request ID: d880bdd7-fe3f-11e7-8a8c-7dcffeae19ae)

I have a role in the production account that has a trust relationship back to the dev account that has the pipeline. I gave the pipeline role and the production role administrator policies just to make sure it was not a policy issue. I edited the pipeline using the technique in this walkthrough. I am following the walkthrough loosely since they are setting their scenario up just slightly different from what I am doing.

The deploy section in my pipeline looks like:

{
   "name": "my-stack",
   "actionTypeId": {
       "category": "Deploy",
       "owner": "AWS",
       "provider": "CloudFormation",
       "version": "1"
   },
   "runOrder": 2,
   "configuration": {
       "ActionMode": "CHANGE_SET_REPLACE",
           "Capabilities": "CAPABILITY_IAM",
       "ChangeSetName": "ProductionChangeSet",
       "RoleArn": "arn:aws:iam::000000000000:role/role-to-assume",
       "StackName": "MyProductionStack",
       "TemplatePath": "BuildArtifact::NewSamTemplate.yaml"
   },
   "outputArtifacts": [],
   "inputArtifacts": [
       {
           "name": "BuildArtifact"
      }
   ]
}

I am able to assume into the role in the production account using the console. I am not sure how passrole is different but from everything I have read it requires the same assume role trust relationship.

How can I configure IAM for cross account pipelines?

回答1:

Generally, if you want to do anything across multiple accounts you have to allow this on the both sides. This is done via role-assuming.

The pipeline distributed parts communicate via pipeline artifacts which are saved in a S3 bucket and de/encrypted with a KMS encryption key. This key must be accesible from all the accounts where the pipeline is distributed in.

key in the CI account

KMSKey:
  Type: AWS::KMS::Key
  Properties:
    EnableKeyRotation: true
    KeyPolicy:
      Version: "2012-10-17"
      Id: pipeline-kms-key
      Statement:
        - Sid: Allows admin of the key
          Effect: Allow
          Principal:
            AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
          Action: ["kms:*"]
          Resource: "*"
        - Sid: Allow use of the key from the other accounts
          Effect: Allow
          Principal:
            AWS:
              - !Sub "arn:aws:iam::${DevAccountId}:root"
              - !GetAtt CodePipelineRole.Arn
          Action:
            - kms:Encrypt
            - kms:Decrypt
            - kms:ReEncrypt*
            - kms:GenerateDataKey*
            - kms:DescribeKey
          Resource: "*"
KMSAlias:
  Type: AWS::KMS::Alias
  Properties:
    AliasName: !Sub alias/codepipeline-crossaccounts
    TargetKeyId: !Ref KMSKey

The S3 bucket must allow the access from different accounts via a policy:

pipeline stack in the CI account

S3ArtifactBucketPolicy:
  Type: AWS::S3::BucketPolicy
  Properties:
    Bucket: !Ref S3ArtifactBucket
    PolicyDocument:
      Statement:
      - Action: ["s3:*"]
        Effect: Allow
        Resource:
        - !Sub "arn:aws:s3:::${S3ArtifactBucket}"
        - !Sub "arn:aws:s3:::${S3ArtifactBucket}/*"
        Principal:
          AWS:
          - !GetAtt CodePipelineRole.Arn
          - !Sub "arn:aws:iam::${DevAccountId}:role/cross-account-role"
          - !Sub "arn:aws:iam::${DevAccountId}:role/cloudformation-role"

CodePipeline:
  Type: AWS::CodePipeline::Pipeline
  Properties:
    ArtifactStore:
      Type: S3
      Location: !Ref S3ArtifactBucket
      EncryptionKey:
        Id: !Ref KMSKey
        Type: KMS
    ...

The pipeline (CI account) has to have a permission to assume a role in the other (DEV) account:

pipeline stack in the CI account

CodePipelinePolicy:
  Type: AWS::IAM::Policy
  Properties:
     PolicyDocument:
        Statement:
          - Action: ["sts:AssumeRole"]
            Resource: !Sub "arn:aws:iam::${DevAccountId}:role/cross-account-role
            Effect: Allow
          ...

And that role has to allow to be assumed to the pipeline:

pipeline stack in the DEV account

CrossAccountRole:
  Type: AWS::IAM::Role
  Properties:
    RoleName: cross-account-role
    Path: /
    AssumeRolePolicyDocument:
      Version: 2012-10-17
      Statement:
        - Effect: Allow
          Principal:
            AWS: !Sub "arn:aws:iam::${CIAccountId}:root"
          Action: sts:AssumeRole

CrossAccountPolicy:
  Type: AWS::IAM::Policy
  Properties:
    PolicyName: CrossAccountPolicy
    PolicyDocument:
      Version: 2012-10-17
      Statement:
        - Effect: Allow
          Action:
            - cloudformation:*
            - codebuild:*
            - s3:*
            - iam:PassRole
          Resource: "*"
        - Effect: Allow
          Action: ["kms:Decrypt", "kms:Encrypt"]
          Resource: !Ref KMSKey
    Roles: [!Ref CrossAccountRole]

The pipeline (managed and executed from the CI account) must assume a role from the other account to execute the action from within the account:

pipeline stack in the CI account

CodePipeline:
  Type: AWS::CodePipeline::Pipeline
  Properties:
    Name: pipeline
    RoleArn: !GetAtt CodePipelineRole.Arn
    Stages:
      ...
      - Name: StagingDev
      Actions:
      - Name: create-changeset
        InputArtifacts:
        - Name: BuildArtifact
        OutputArtifacts: []
        ActionTypeId:
          Category: Deploy
          Owner: AWS
          Version: "1"
          Provider: CloudFormation
        Configuration:
          StackName: app-stack-dev
          ActionMode: CHANGE_SET_REPLACE
          ChangeSetName: app-changeset-dev
          Capabilities: CAPABILITY_NAMED_IAM
          TemplatePath: "BuildArtifact::template.yml"
          RoleArn: !Sub "arn:aws:iam::${DevAccountId}:role/cloudformation-role"  # the action will be executed with this role
        RoleArn: !Sub "arn:aws:iam::${DevAccountId}:role/cross-account-role" # the pipeline assume this role to execute this action
  ...

The code above shows how to execute a CloudFormation action in a different account, the approach is the same for different actions like CodeBuild or CodeDeploy.

There is a nice sample https://github.com/awslabs/aws-refarch-cross-account-pipeline from AWS team.

Another example is here https://github.com/adcreare/cloudformation/tree/master/code-pipeline-cross-account

Or you can take a look at my whole working code here https://github.com/ttulka/aws-samples/tree/master/cross-account-pipeline



回答2:

I think the issue is that your CloudFormation role is in the other account but your action role is not. Only the pipeline role is allowed to assume an action role in a different account.

The action role is the one located directly under the ActionDeclaration.

Basically your roles should be configured as follows:

  • Pipeline role: Account A
  • Action role: Account B
  • CloudFormation role: Account B

There's some information on setting up cross-account actions here: https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-create-cross-account.html

Here's where the action role is defined: https://docs.aws.amazon.com/codepipeline/latest/APIReference/API_ActionDeclaration.html