AWS CodePipeline error: Cross-account pass role is not allowed - amazon-web-services

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?

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

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

Related

AWS Cloudformation:Template validation error Role and policy

I am new to cloudformation and trying to create a template that can create a execution role and associated policies for my lambda function.
AWSTemplateFormatVersion: 2010-09-09
Description: AWS CloudFormation Template for creating iam role for SSM lambda
Parameters:
rolename:
Type: String
Description: The name of the iam role for SSM Lambda
Default: SSM_lambda_role
policyname:
Type: String
Description: pcluster lambda iam policy for SSM Lambda
Default: SSM_lambda_policy
Resources:
ssmlambdarole:
Type: 'AWS::IAM::Role'
Properties:
RoleName: !Sub '${rolename}'
Description: iam role for ssm lambda role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
ManagedPolicyArns:
- !Sub 'arn:aws:iam::${AWS::AccountId}:policy/${policyname}'
ssmlambdapolicy:
Type: 'AWS::IAM::ManagedPolicy'
Properties:
ManagedPolicyName: !Sub '${policyname}'
Description: The name of the iam role for SSM Lambda
Path: '/'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- logs:CreateLogGroup
Resource: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*
Effect: Allow
Sid: CloudWatchLogsPolicy
- Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${policyname}:*
Effect: Allow
Sid: CloudWatchLogsPolicy
- Action:
- ssm:Describe*
- ssm:Get*
- ssm:List*
Resource: "*"
Effect: Allow
If I define a role first in the above template, I get an error during stack creation mentioning that the policy is not found and if I create policy first in the above order, I keep getting a validation error. can someone tell me where am I getting wrong.
There is an attribute that can help to achieve that: DependsOn,
but the better way is to use - !Ref ssmlambdapolicy instead of - !Sub 'arn:aws:iam::${AWS::AccountId}:policy/${policyname}'.
In each case, it will establish a dependency between resources. Thanks to that AWS will be able to recognize resource creation orders - you didn't use any of them, so AWS 1stly tries to create a role (or policy, depending on the order in the template), and attach a policy that doesn't exist yet.
The validation error is due to that you missed !sub in the policy statements.
Btw, I strongly recommend looking for help in CFN documentation - sometimes there is a section with use-case examples.

Event trigger using serverless framwork on an existing S3 bucket

I'm trying to set a lambda to trigger when an object is created in an S3 bucket.
My serverless.yml includes this:
handleNewRawObjectInS3:
handler: lambdas/handleNewRawObjectInS3/handleNewRawObjectInS3.handleS3Event
events:
- s3:
bucket: ${file(../evn.${opt:stage, 'dev'}.json):RAW_IMAGE_BUCKET}
event: s3:ObjectCreated:*
This results in an error:
Error:
CREATE_FAILED: S3Bucketxxxxxxxxxrawimages (AWS::S3::Bucket)
xxxxxxxxx-raw-images already exists in stack arn:aws:cloudformation:us-east-1:xxxx:stack/s3-xxxxxxxxx-raw-images/310e7010-xxx-xxx-xxxx-12f066874c93
I already have the bucket created -- created via uploading a cloudformation template directly (our corporate version of AWS doesn't allow us to use serverless framework to create a bucket). How to add S3 trigger event on AWS Lambda function using Serverless framework? indicates this was not possible with older versions of serverless, but following the rabbit hole, you can see a feature request ... and later an answer that shows you need to add 'existing: true'.
So I add that to my serverless framework setup:
service: my-service-events
provider:
name: aws
runtime: nodejs14.x
region: ${file(../evn.${opt:stage, 'dev'}.json):REGION}
stage: ${opt:stage, 'dev'}
deploymentBucket: #must name manually-created bucket for deployment because enterprise doesn't allow automated bucket creation
name: ${file(../evn.${opt:stage, 'dev'}.json):DEPLOYMENT_BUCKET}
iam: #must name a role because enterprise doesn't allow automated role creation
role: myServerlessRole # This is a reference to the resource name from the role created in the resources -> iam roles section
deploymentRole: ${file(../evn.${opt:stage, 'dev'}.json):DEPLOYMENT_ROLE}
resources:
- ${file(../iam-roles.${opt:stage, 'dev'}.yml)}
functions:
handleNewRawObjectInS3:
handler: lambdas/handleNewRawObjectInS3/handleNewRawObjectInS3.handleS3Event
events:
- s3:
bucket: ${file(../evn.${opt:stage, 'dev'}.json):RAW_IMAGE_BUCKET}
event: s3:ObjectCreated:*
existing: true
The IAM file/role referenced above looks like this:
Resources:
myServerlessRole:
Type: AWS::IAM::Role
Properties:
PermissionsBoundary: arn:aws:iam::xxx:policy/csr-Developer-Permissions-Boundary
Path: /my/default/path/
RoleName: myServerlessRole-${self:service}
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
Policies:
- PolicyName: myPolicyName
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- 'Fn::Join':
- ':'
-
- 'arn:aws:logs'
- Ref: 'AWS::Region'
- Ref: 'AWS::AccountId'
- 'log-group:/aws/lambda/*:*:*'
- Effect: "Allow"
Action:
- "s3:*"
Resource: "arn:aws:s3:::*" #
- Effect: "Allow"
Action:
- "lambda:*"
Resource: "*"
- Effect: "Allow"
Action:
- cloudfront:CreateDistribution
- cloudfront:GetDistribution
- cloudfront:UpdateDistribution
- cloudfront:DeleteDistribution
- cloudfront:TagResource
Resource: "arn:aws:cloudfront:::*"
Trying to deploy this gets me the error:
Error:
CREATE_FAILED: CustomDashresourceDashexistingDashs3LambdaFunction (AWS::Lambda::Function)
Resource handler returned message: "The role defined for the function cannot be assumed by Lambda. (Service: Lambda, Status Code: 400, Request ID: 812c5384-1c26-42c9-bdef-1ce4a59f2be4)" (RequestToken: 9cdbb5af-3bc7-d6bf-384b-5126d1048ccd, HandlerErrorCode: InvalidRequest)
How can I deploy this lambda triggered by an s3 event?
The issue comes from the fact that CustomDashresourceDashexistingDashs3LambdaFunction lambda runs as the deployementRole and not under the defined role (which is the default role that lambdas run under). Given the deployment role doesn't normally need to assumeRole my deployment role did not have the assumeRole permission.
The fix for this is to ensure that the sts:assumeRole trust relationship has been applied to the deploymentRole like so:
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}

AWS event bridge rule role not attaching to event bridge when creating via CloudFormation

I have a Cloudformation template to create an event bridge rule with target to a central event bus running in another account. When i run the below code , both IAM role and event bridge is getting created but the IAM role is not getting attached to eventbridge rule. Below is the yaml template i am using.
Please see the attached screenshot also.
AWSTemplateFormatVersion: 2010-09-09
Resources:
EventRuleRegion1:
Type: AWS::Events::Rule
Properties:
Description: Event rule to send events to monitoring account event bus
EventBusName: default
EventPattern:
source:
- aws.ec2
Name: ec2-lifecycle-events2
RoleArn: !GetAtt
- EventBridgeIAMrole
- Arn
State: ENABLED
Targets:
- Arn: >-
arn:aws:events:ap-southeast-2:123456789123:event-bus/central-eventbus-sydney
Id: 'central-eventbus-sydney'
EventBridgeIAMrole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: !Sub events.amazonaws.com
Action: 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: PutEventsDestinationBus
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'events:PutEvents'
Resource:
- >-
arn:aws:events:ap-southeast-2:123456789123:event-bus/central-eventbus-sydney
Manually Created Event rule which shows the role created and attached.
Policy in the role
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"events:PutEvents"
],
"Resource": [
"arn:aws:events:ap-southeast-2:123456789123:event-bus/central-eventbus-sydney"
]
}
]
}
Cross-account permissions are not set using role, but EventBus resource permissions. From [docs][1]:
The permissions for an event bus are granted or denied using a resource-based policy attached to the event bus.
To do this in CloudFormation, you have to develop your own custom resource.
Update
You haven't specified RoleArn for your target. This is different RoleArn that you have now.

Possible to create a policy and share the policy to role across multiple accounts

I am trying to create a policy in one AWS account and need to share that policy to a role in multiple accounts (Prod, Dev, Sandbox).
And I can add the AWS accounts number manually and assign AWS Managed Policy to roles and needs to create multiple roles as well.
How can we achieve this?
Here is the code I wrote
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Create a role that authorizes access to users in another account'
Metadata:
Version: 0.7
Parameters:
RoleName:
Type: String
Default: R_EC2-Describe-Instance
MainAccountId:
Type: String
Description: >-
Include the Managed Services Account ID(the account ID where the Main VPC is registered)
Default: 111111111111
MaxLength: 12
MinLength: 12
Resources:
AssumeRole:
Type: AWS::IAM::Policy
Properties:
RoleName: !Ref RoleName
Policies:
-
PolicyName: "CoreSVC-Describe-EC2"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- 'sts:AssumeRole'
Resource: !Join [ "", [ "arn:aws:iam::", !Ref MainAccountId, ":role/R_EC2-Describe-Instance" ] ]
AssumeRolePolicyDocument:
Version: '2012-10-17'
ManagedPolicyName:
- "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAcess"
Statement:
- Effect: Allow
Principal:
"AWS": !Join [ "", [ "arn:aws:iam::", !Ref MainAccountId, ":root" ] ]
Action:
- sts:AssumeRole
Condition: {}
You can't share an IAM Policy with other accounts, as it doesn't have a resource policy to allow it.
The code sample you've shared is sharing the IAM Role with multiple accounts, which is possible via the role's resource/trust policy.
If you want to share the same policy across multiple accounts, then you should probably use CloudFormation StackSets (as mentioned by #ervin-szilagyi), or some other infrastructure-as-code approach.
If you want to share the role with other accounts, then you've already done it. All that's missing is role in those accounts with access to the sts:AssumeRole action so that it can assume the role you've shared in your code.

Cross-Region access to CodeCommit repository from CodePipeline

I am building a CodePipeline using CloudFormation. The pipeline is deployed to the eu-west-1 region however it must get the source code from the CodeCommit repository that is located in the us-east-1 region.
The pipeline definition looks like this:
CodePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Ref PipelineName
RoleArn: !GetAtt CodePipelineServiceRole.Arn
Stages:
- Name: Source
Actions:
- Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
OutputArtifacts:
- Name: SourceCodeOutputArtifact
Configuration:
RepositoryName: !Ref MyRepository
BranchName: !Ref DeploymentBranch
RunOrder: 1
Region: us-east-1
The attached role and policy looks like this:
CodePipelineServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Principal:
Service:
- codepipeline.amazonaws.com
CodePipelineServicePolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: CodePipelineServicePolicy
Roles:
- !Ref CodePipelineServiceRole
PolicyDocument:
Version: 2012-10-17
Statement:
......
- Effect: Allow
Action:
- codecommit:BatchGet*
- codecommit:BatchDescribe*
- codecommit:Get*
- codecommit:Describe*
- codecommit:List*
- codecommit:GitPull
Resource: !Sub "arn:aws:codecommit:us-east-1:${AWS::AccountId}:MyRepository*"
Alas, the code pipeline still don't have access to my repository in the us-east-1, or it is looking for it in the incorrect region, and as a result I'm getting the following error
The service role or action role doesn’t have the permissions required to access the AWS CodeCommit repository named MyRepository. Update the IAM role permissions, and then try again. Error: User: arn:aws:sts::111111111111:assumed-role/MyPipeline-CodePipelineServiceRole-N996SC3JYINW/1564844186052 is not authorized to perform: codecommit:GetBranch on resource: arn:aws:codecommit:eu-west-1:111111111111:MyRepository
Any help would be greatly appreciated.
Edit:
I know that I can replicate the repo to another region but I am wondering is there any way to do what I want to do without replicating the repo to another region?
You need to replicate the codecommit to the other region. You can do it by using a CI Job, a scheduled container in ECS or Fargate.