Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

codepipeline: CloudFormationCreateUpdateStackAction for cross-account deployment #27484

Closed
soleyman-devops opened this issue Oct 10, 2023 · 12 comments
Labels
@aws-cdk/aws-iam Related to AWS Identity and Access Management bug This issue is a bug. closed-for-staleness This issue was automatically closed because it hadn't received any attention in a while. effort/medium Medium work item – several days of effort p2 response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days.

Comments

@soleyman-devops
Copy link

Describe the bug

Resource handler returned message: "The role with name CrossAccountDevDeploy cannot be found"

I'm creating cross account cicd, i've deployed the CrossAccountDevDeploy role in my Dev Account, and created a trusted entity in my CICD account to assume this role, and passed the CICD Account ID in my Dev Role.

However when I use the role in my dev deploy stage with codepipeline it cannot find the CrossAccountDevDeploy

CICD Pipeline

export class CicdPipeline extends cdk.Stack {
    constructor(scope: Construct, id: string, props?: cdk.StackProps) {
      super(scope, id, props);
  
      // Pipeline IAM
      const pipelineRole = new iam.Role(this, 'CustomPipelineRole', {
        assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'),
      })
  
      // Pipeline to Secrets Manager Github Token
      pipelineRole.addToPolicy(new iam.PolicyStatement({
        actions: ['secretsmanager:GetSecretValue', 'secretsmanager:DescribeSecret', 'sts:AssumeRole'],
        resources: ['arn:aws:secretsmanager:eu-west-1:xxxxxxxxxxxx:secret:xxxxxxxxxxxx', 'arn:aws:iam::xxxxxxxxxxxx:role/CrossAccountDevDeploy'],
      }))
 
  
      // Output Artifacts
      const sourceOutput = new codepipeline.Artifact();
      const cdkOutputs = new codepipeline.Artifact('CDKOutputs')
  
      // CDK Build Stage
      const cdkBuild = new codebuild.Project(this, 'CDKBuild', {
        buildSpec: codebuild.BuildSpec.fromObject({
          version: '0.2',
          phases: {
            install: {
              commands: ['npm install -g aws-cdk', 'npm install']
            },
            build: {
              commands: ['cdk synth']
            },
          },
          artifacts: {
            'base-directory': 'cdk.out',
            files: ['*']
          }
        }),
  
        // Runtime env for CodeBuild
        environment: {
          buildImage: codebuild.LinuxBuildImage.STANDARD_5_0
        }
      })
  
      // Pipeline itself
      new codepipeline.Pipeline(this, "Pipeline", {
        pipelineName: 'Foundational-Pipeline',
        role: pipelineRole,
        stages: [
          {
            stageName: 'Source',
            actions: [
              new codepipelineActions.GitHubSourceAction({
                actionName: 'Github',
                repo: 'foundation',
                oauthToken: cdk.SecretValue.secretsManager('xxxxxxxxxxxx'),
                output: sourceOutput,
                owner: 'pipeline',
                trigger: codepipelineActions.GitHubTrigger.WEBHOOK
              })
            ]
          },
          // Build CDK into CloudFormation
          {
            stageName: 'Build',
            actions: [
              new codepipelineActions.CodeBuildAction({
                actionName: 'CDK_Build',
                project: cdkBuild,
                input: sourceOutput,
                outputs: [new codepipeline.Artifact('CDKOutputs')]
              })
            ]
  
          },
          // Deploy to Dev Stage
          {
            stageName: 'DeployDev',
            actions: [
              new codepipelineActions.CloudFormationCreateUpdateStackAction({
                actionName: 'Deploy',
                stackName: 'DevFoundationalNetworking',
                templatePath: cdkOutputs.atPath('NetworkingDev.template.json') ,
                adminPermissions: false,
                role: iam.Role.fromRoleArn(this, 'CrossAccountRole', 'arn:aws:iam::xxxxxxxxxxxx:role/CrossAccountDevDeploy')
              })
            ]
          }

dev cross account role

          export class DevAccount extends cdk.Stack {
            constructor(scope: Construct, id: string, props?: cdk.StackProps) {
              super(scope, id, props);
          
              const cicdAccountId = 'xxxxxxxxxx'
          
              const devDeployRole = new iam.Role(this, 'DevDeployRole', {
                assumedBy: new iam.AccountPrincipal(cicdAccountId),
                roleName: 'CrossAccountDevDeploy',
                description: 'Allows CICD account to deploy CloudFormation in dev'
              })
          
              devDeployRole.addToPolicy(new iam.PolicyStatement ({
                actions: ['*'],
                resources: ['*']
              }))
            }
          }
          
``` ### 

### Expected Behavior

To assume the CrossAccountDevDeploy role in Dev Account from CICD Account and enable the pipeline to deploy

### Current Behavior

The above bug Failure when deploying the update dev deploy stage. 

### Reproduction Steps

I deploy the pipeline using 'cdk deploy --profile xxx' it connects to cicd account and starts the deployment but errors . 

### Possible Solution

_No response_

### Additional Information/Context

Ive removed any sensestive data from the code.

### CDK CLI Version

2.96.2

### Framework Version

_No response_

### Node.js Version

v20.6.1

### OS

Mac

### Language

TypeScript

### Language Version

_No response_

### Other information

ive dug through the internet and they all say make sure the ARN and account ID are correct in dev account which they are. theres something not right in my cicd account reading the CrossAccountDevDeploy
@soleyman-devops soleyman-devops added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Oct 10, 2023
@github-actions github-actions bot added the @aws-cdk/aws-iam Related to AWS Identity and Access Management label Oct 10, 2023
@peterwoodworth
Copy link
Contributor

Could you more clearly describe how the DevAccount stack is getting deployed? When you bootstrapped did you include the --trust or --trust-for-lookup options when bootstrapping to allow for lookups cross account?

@peterwoodworth peterwoodworth added response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. and removed needs-triage This issue or PR still needs to be triaged. labels Oct 10, 2023
@soleyman-devops
Copy link
Author

@peterwoodworth DevAccount stack is deployed and it was CDK bootstrapped, however I did not add the --trust or --trust-for-lookup options. I will do this now and get back to you

Screenshot 2023-10-11 at 09 06 01

@soleyman-devops
Copy link
Author

Screenshot 2023-10-11 at 09 25 52

@peterwoodworth - still having the same error, after adding the --trust to the bootstrap for the DevAccount.

Screenshot 2023-10-11 at 09 28 06

cdk bootstrap --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess --trust xxxxxxx aws://xxxxxxxxx/eu-west-1

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Oct 11, 2023
@pahud pahud assigned pahud and unassigned khushail Nov 30, 2023
@pahud
Copy link
Contributor

pahud commented Nov 30, 2023

This could be a little bit confusing but I think the general idea is to clarify the 3 roles

  1. pipeline role - the main role the pipeline run with
  2. action role - the role that pipeline role assumes when executing this action. In this case, CloudFormationCreateUpdateStackAction and is defailed as role property.
  3. cloudformation deploy role - the action role would use this role to deploy cloudformation which is defined as deploymentRole.

I guess you should specify CrossAccountDevDeploy as deployment role instead.

Now, if you look at the synthesized template as below, your CrossAccountDevDeploy should be at 1 rather than 2.

image

Let me know if it works for you. It's a little bit confusing to be honest.

@pahud pahud added p2 effort/medium Medium work item – several days of effort labels Nov 30, 2023
@pahud pahud removed their assignment Nov 30, 2023
@pahud pahud added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Nov 30, 2023
@pahud pahud self-assigned this Nov 30, 2023
@pahud pahud changed the title AWS CDK IAM: Cannot create IAM role from central CICD account codepipeline: IAM role not found in the CloudFormationCreateUpdateStackAction Nov 30, 2023
Copy link

github-actions bot commented Dec 2, 2023

This issue has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.

@github-actions github-actions bot added the closing-soon This issue will automatically close in 4 days unless further comments are made. label Dec 2, 2023
@soleyman-devops
Copy link
Author

Hey @pahud @peterwoodworth I've gone through the code and have done the required steps, ie validated permissions from my dev deployment role allows my pipeline role in cicd source account to be assumed.

So pipeline role has permissions to assume dev deployment role from target account and vice versa.

However, I am still getting the same error.

Ive bootstrapped too as a extra step but still no luck.

export class AwsCicdStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Pipeline IAM
    const pipelineRole = new iam.Role(this, 'CodePipelineRole', {
      roleName: 'FoundationCodePipelineRole',
      assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'),
    })

    // // Pipeline Assume Dev Role
    pipelineRole.addToPolicy(new iam.PolicyStatement({
      actions: ['sts:AssumeRole'],
      resources: [ 
        'arn:aws:iam::devaccountid:role/Dev-Deployment-Role'
      ],
    }))

    // Output Artifacts
    const sourceOutput = new codepipeline.Artifact('SourceArtifact');
    const cdkOutputs = new codepipeline.Artifact('CDKOutputs')

    // CDK Build Stage
    const cdkBuild = new codebuild.PipelineProject(this, 'CDKBuild', {
      buildSpec: codebuild.BuildSpec.fromObject({
        version: '0.2',
        phases: {
          install: {
            commands: ['npm install -g aws-cdk', 'npm install']
          },
          build: {
            commands: ['npm run cdk synth']
          },
        },
        artifacts: {
          'base-directory': 'cdk.out',
          files: [`*.template.json`],
        }
      }),
      // Runtime env for CodeBuild
      environment: {
        buildImage: codebuild.LinuxBuildImage.STANDARD_5_0
      },
      // encryptionKey: key
    })
const pipeline = new codepipeline.Pipeline(this, "Pipeline", {
      pipelineName: 'Foundational-Pipeline',
      crossAccountKeys: true,
      role: pipelineRole,
      stages: [
        {
          stageName: 'Source',
          actions: [
            new codepipelineActions.GitHubSourceAction({
              actionName: 'Github',
              repo: 'ops-aws-foundation',
              branch: 'main',
              oauthToken: cdk.SecretValue.secretsManager('xxxx'),
              output: sourceOutput,
              owner: 'xxxx',
              trigger: codepipelineActions.GitHubTrigger.WEBHOOK
            })
          ]
        },
        // Build CDK into CloudFormation
        {
          stageName: 'Build',
          actions: [
            new codepipelineActions.CodeBuildAction({
              actionName: 'CDK_Build',
              project: cdkBuild,
              input: sourceOutput,
              outputs: [new codepipeline.Artifact('CDKOutputs')],
              runOrder: 1
            })
          ]
        },
        {
          stageName: 'DeployDev',
          actions: [
            new codepipelineActions.CloudFormationCreateUpdateStackAction({
              actionName: 'DeployNetworkingStack',
              stackName: 'FoundationalNetworking',
              templatePath: cdkOutputs.atPath('AwsFoundationStack.template.json'),
              adminPermissions: true,
              deploymentRole: iam.Role.fromRoleArn(this, 'role', 'arn:aws:iam::devaccountid:role/Dev-Deployment-Role')
          })
          ]
        },
      ]
    });

    pipeline.addToRolePolicy(new iam.PolicyStatement({
      actions: ['sts:AssumeRole'],
      resources: [
        `arn:aws:iam::devaccountid:role/Dev-Deployment-Role`
      ]
    }))
  }
}

`

Any help would be appreciated

@github-actions github-actions bot removed closing-soon This issue will automatically close in 4 days unless further comments are made. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. labels Dec 7, 2023
@pahud
Copy link
Contributor

pahud commented Dec 11, 2023

I will try to provide a working sample but before that, can you share the synthesized cloudformation template of the "Actions" as the screenshot above?

@pahud
Copy link
Contributor

pahud commented Dec 11, 2023

Hi @soleyman-devops I will try to write a small sample for this. Hopefully to clarify some details.

@pahud
Copy link
Contributor

pahud commented Dec 12, 2023

Please check the full sample below:

Assuming the pipeline account 111111111111 and the target deployment account 222222222222. Both the pipeline and deployment stack are in us-east-1.

  1. Make sure you cdk bootstrap 222222222222 with --trust for 111111111111
# in account 222222222222
cdk bootstrap aws://222222222222/us-east-1 --trust 111111111111 --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess

check the iam policies of cdk-hnb659fds-deploy-role-222222222222-us-east-1 iam role, make sure the trust policy is correctly configured as:

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::222222222222:root"
            },
            "Action": "sts:AssumeRole"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:root"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
  1. Now let's create the following CDK stack for the pipeline
export class Demo extends DemoStack {
	constructor(scope: Construct, id: string, props: StackProps) {
		super(scope, id, props);

		const sourceOutput = new codepipeline.Artifact();

		new codepipeline.Pipeline(this, 'pipeline', {
			stages: [
				// source stage
				{
					stageName: 'Source',
					actions: [
						new codepipeline_actions.CodeStarConnectionsSourceAction({
							actionName: 'GithubSource',
							connectionArn,
							owner: 'pahud',
							repo: 'test-pipeline',
							branch: 'main',
							output: sourceOutput,
						})
					]
				},
				// deploy stage
				{
					stageName: 'cfn-deploy',
					actions: [
						new codepipeline_actions.CloudFormationCreateUpdateStackAction({
							actionName: 'CloudFormationCreateUpdate',
							stackName: 'MyStackName',
							adminPermissions: true,
							templatePath: sourceOutput.atPath('template.yaml'),
							// the pipeline role will assume this role to run this deploy action, which passes
							// the `deploymentRole` to cloudformation to assume.
							role: iam.Role.fromRoleArn(this, 'actionRole', 
								'arn:aws:iam::222222222222:role/cdk-hnb659fds-deploy-role-222222222222-us-east-1'),
							// cloudformation will deploy the stack using this role
							deploymentRole: iam.Role.fromRoleArn(this, 'deployRole', 
							'arn:aws:iam::222222222222:role/cdk-hnb659fds-cfn-exec-role-222222222222-us-east-1'),
						}),
					]
				}

			]
		});
	
	}	
}
  1. Now cdk diff or cdk synth, check the json template in the cdk.out. You should see the Actions like
   "Actions": [
       {
        "ActionTypeId": {
         "Category": "Deploy",
         "Owner": "AWS",
         "Provider": "CloudFormation",
         "Version": "1"
        },
        "Configuration": {
         "StackName": "MyStackName",
         "Capabilities": "CAPABILITY_NAMED_IAM",
         "RoleArn": "arn:aws:iam::222222222222:role/cdk-hnb659fds-cfn-exec-role-222222222222-us-east-1",
         "ActionMode": "CREATE_UPDATE",
         "TemplatePath": "Artifact_Source_GithubSource::template.yaml"
        },
        "InputArtifacts": [
         {
          "Name": "Artifact_Source_GithubSource"
         }
        ],
        "Name": "CloudFormationCreateUpdate",
        "RoleArn": "arn:aws:iam::222222222222:role/cdk-hnb659fds-deploy-role-222222222222-us-east-1",
        "RunOrder": 1
       }
      ],
      "Name": "cfn-deploy"
     }
    ]

This means the pipeline role will assume the "action role" to run the cloudformation action by passing the "deploymentRole" to the cloudformation service. The "action role" and "deploymentRole" have to be both in the target account.

In this case:

  • the pipeline role would be auto generated by CDK
  • action role is cdk-hnb659fds-deploy-role-222222222222-us-east-1, the pipeline role will assume this role
  • deployment role is cdk-hnb659fds-cfn-exec-role-222222222222-us-east-1, the action role will pass this role to cloudformation service.
  1. On deploy completed, the pipeline should be triggered and you should see this from the codepipeline console:
image
  1. Now, check the cloudformation console in account 222222222222, the stack should be successfully deployed.
image

Let me know if it works for you.

@pahud pahud added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Dec 12, 2023
@pahud pahud removed their assignment Dec 12, 2023
@pahud pahud changed the title codepipeline: IAM role not found in the CloudFormationCreateUpdateStackAction codepipeline: CloudFormationCreateUpdateStackAction for cross-account deployment Dec 12, 2023
@soleyman-devops
Copy link
Author

soleyman-devops commented Dec 12, 2023

Screenshot 2023-12-12 at 10 30 22

Hi Paul, thanks for taking time out to send me the example. I followed the steps, but getting this action role cannot be found.

Everything matches what you shared above until deployment of the CodePipeline stack.
I double checked and its definitely present in the target account. Seems strange that it cannot find it even after bootstrapping.

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Dec 12, 2023
@pahud
Copy link
Contributor

pahud commented Dec 12, 2023

@soleyman-devops Interesting. If it says this role cannot be found then it technically does not exist.

Are you able to get-role with AWS CLI like this in your target account?

 aws iam get-role --role-name cdk-hnb659fds-deploy-role-<your_target_account_id>-us-east-1

Are you able to reproduce from my provided code snippets above and see if it works for you?

@pahud pahud added the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Dec 12, 2023
Copy link

This issue has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.

@github-actions github-actions bot added closing-soon This issue will automatically close in 4 days unless further comments are made. closed-for-staleness This issue was automatically closed because it hadn't received any attention in a while. and removed closing-soon This issue will automatically close in 4 days unless further comments are made. labels Dec 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-iam Related to AWS Identity and Access Management bug This issue is a bug. closed-for-staleness This issue was automatically closed because it hadn't received any attention in a while. effort/medium Medium work item – several days of effort p2 response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days.
Projects
None yet
Development

No branches or pull requests

4 participants