Cannot assume role by code pipeline on code pipeline action AWS CDK - amazon-web-services

I have been playing with AWS CDK and was working on building a code pipeline stack on my AWS educate account. The user that I am using has enough permission to access and use the code pipeline. My problem is, AWS CDK generates a role for the code pipeline action whose Principle is ARN of the root account. So it doesn't have the permission to perform assume the role on the root account.
Action code:
{
stageName: "Build",
actions: [
new codepipelineActions.CodeBuildAction(
{
actionName: "Build",
input: sourceOutput,
project: builder
}
)
]
}
Cloudformation Template Output:
"devPipelineBuildCodePipelineActionRole8696D056": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":iam::",
{
"Ref": "AWS::AccountId"
},
":root"
]
]
}
}
}
],
"Version": "2012-10-17"
}
},
"Metadata": {
"aws:cdk:path": "PipeLineStack/dev-Pipeline/Build/Build/CodePipelineActionRole/Resource"
}
}
...
{
"Actions": [
{
"ActionTypeId": {
"Category": "Build",
"Owner": "AWS",
"Provider": "CodeBuild",
"Version": "1"
},
"Configuration": {
"ProjectName": {
"Ref": "BuildAndTestB9A2F419"
}
},
"InputArtifacts": [
{
"Name": "SourceOutput"
}
],
"Name": "Build",
"RoleArn": {
"Fn::GetAtt": [
"devPipelineBuildCodePipelineActionRole8696D056",
"Arn"
]
},
"RunOrder": 1
}
],
"Name": "Build"
}
This will throw the error:
arn:aws:iam::acount_id:role/PipeLineStack-devPipelineRole5B29FEBC-1JK24J0K5N1UG is not authorized to perform AssumeRole on role arn:aws:iam::acount_id:
role/PipeLineStack-devPipelineBuildCodePipelineActionRo-17ETJU1KZCCNQ (Service: AWSCodePipeline; Status Code: 400; Error Code: InvalidStructureException; Req
uest ID: c8c8af89-2409-4cc1-aad8-4de553a1764f; Proxy: null)
If I remove the RoleArn from the Action and execute the template it works.
My question is, How do I prevent CDK to prevent adding default role with Principle using the root account or a work around to it?

It looks like actions are not allowed to assume any role in AWS Educate currently. So to have a workaround and remove the manual overhead, use CDK L1 Constructs to modify the generated cloud formation.
The pipeline can be created like:
// Custom role to pass in to pipeline
const pipeLineRole = new iam.Role(this, "CodePipeLineRole", {
assumedBy: new iam.ServicePrincipal("codepipeline.amazonaws.com"),
});
pipeLineRole.addToPolicy(
// Required policy for each aciton to run
)
const pipeline = new codepipeline.Pipeline(this, "Pipeline", {
role: pipeLineRole,
stages: [
// ...
{
actions: [action1, action2],
},
// ...
],
});
// Altering cloudformation to remove role arn from actions
const pipelineCfn = pipeline.node.defaultChild as cdk.CfnResource;
// addDeletionOverride removes the property from the cloudformation itself
// Delete action arn for every stage and action created
pipelineCfn.addDeletionOverride("Properties.Stages.1.Actions.0.RoleArn");
pipelineCfn.addDeletionOverride("Properties.Stages.2.Actions.0.RoleArn");
pipelineCfn.addDeletionOverride("Properties.Stages.3.Actions.0.RoleArn");
This is a workaround, it works, but there are still unwanted and dangling policies and roles created that have not been assigned to any service which had been created for individual actions.

The following code in pipeline configuration:
"RoleArn": {
"Fn::GetAtt": [
"devPipelineBuildCodePipelineActionRole8696D056",
"Arn"
]
},
... means when CodePipeline service will invoke the "Build" action, it will "assume" the role "devPipelineBuildCodePipelineActionRole8696D056" but this role does not have a trust policy with "codepipeline.amazonaws.com" service hence the error.
The 'RoleArn' property under the action is useful when you have a cross account action (CodeBuild project is in another account) so unless that is the case, it is better to drop this property.
We will need to see the cdk code to answer your question:
How do I prevent CDK to prevent adding default role with Principle using the root account or a work around to it?

Subesh's code works in removing RoleArn. But in my AWS env, RoleArn is still required. I am trying to replaced it with an existing role, but it still only removes RoleArn. What is wrong with my code?
pipelineCfn.addDeletionOverride("Properties.Stages.1.Actions.0.RoleArn");
pipelineCfn.addDeletionOverride("Properties.Stages.2.Actions.0.RoleArn");
pipelineCfn.addPropertyOverride(
"Properties.Stages.1.Actions.0.RoleArn",
pipeline_role_arn
);
pipelineCfn.addPropertyOverride(
"Properties.Stages.2.Actions.0.RoleArn",
pipeline_role_arn
);

Related

CDK Unable to Add CodeStarNotification to CodePipeline

I use CDK to deploy a codepipeline. It works fine until I try to add notification for codepipeline success/fail events. It gives CREATE_FAILED error with message Resource handler returned message: "Invalid request provided: AWS::CodeStarNotifications::NotificationRule" (RequestToken: bb566fd0-1ac9-5d61-03fe-f9c27b4196fa, HandlerErrorCode: InvalidRequest). What could be the reason? Thanks.
import * as codepipeline from "#aws-cdk/aws-codepipeline";
import * as codepipeline_actions from "#aws-cdk/aws-codepipeline-actions";
import * as codestar_noti from "#aws-cdk/aws-codestarnotifications";
import * as sns from "#aws-cdk/aws-sns";
const pipeline = new codepipeline.Pipeline(...);
const topicArn = props.sns_arn_for_developer;
const targetTopic = sns.Topic.fromTopicArn(
this,
"sns-notification-topic",
topicArn
);
new codestar_noti.NotificationRule(this, "Notification", {
detailType: codestar_noti.DetailType.BASIC,
events: [
"codepipeline-pipeline-pipeline-execution-started",
"codepipeline-pipeline-pipeline-execution-failed",
"codepipeline-pipeline-pipeline-execution-succeeded",
"codepipeline-pipeline-pipeline-execution-canceled",
],
source: pipeline,
targets: [targetTopic],
});
Here is the snippet of generated cloudformation tempalte.
"Notification2267453E": {
"Type": "AWS::CodeStarNotifications::NotificationRule",
"Properties": {
"DetailType": "BASIC",
"EventTypeIds": [
"codepipeline-pipeline-pipeline-execution-started",
"codepipeline-pipeline-pipeline-execution-failed",
"codepipeline-pipeline-pipeline-execution-succeeded",
"codepipeline-pipeline-pipeline-execution-canceled"
],
"Name": "sagemakerbringyourownNotification36194CEC",
"Resource": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":codepipeline:ap-southeast-1:305326993135:",
{
"Ref": "sagemakerbringyourownpipeline0A8C43B1"
}
]
]
},
"Targets": [
{
"TargetAddress": "arn:aws:sns:ap-southeast-1:305326993135:whitespace_alerts",
"TargetType": "SNS"
}
]
},
"Metadata": {
"aws:cdk:path": "sagemaker-bring-your-own/Notification/Resource"
}
},
FWIW, I got the exact same error "Invalid request provided: AWS::CodeStarNotifications::NotificationRule" from a CDK app where the Topic was created (not imported). It turned out to be a transient issue, because it succeeded the second time without any changes. I suspect it was due to a very large ECR image which was build the first time as part of the deploy and which took quite some time. My speculation is that the Topic timed out and got into some kind of weird state waiting for the NotificationRule to be created.
This is because imported resources cannot be modified. As you pointed out in the comments, setting up the notification involves modifying the Topic resource, specifically its access policy.
Reference: https://docs.aws.amazon.com/cdk/v2/guide/resources.html#resources_importing
I was able to solve this by doing the following in that order:
First removing the below statement from the resource policy of the SNS topic.
Then deploying the stack(which interestingly doesn't add anything to the resource policy)
Once the stack deployment finishes, update the resource policy manually to add the below statement.
{
"Sid": "AWSCodeStarNotifications_publish",
"Effect": "Allow",
"Principal": {
"Service": "codestar-notifications.amazonaws.com"
},
"Action": "SNS:Publish",
"Resource": "arn:aws:sns:ap-south-1:xxxxxxxxx:test"
}

In CloudFormation, does "A DependsOn B" ensure that A is deleted before B?

We are using CloudFormation to set up a role and a policy for it. The policy is set to depend on the role using the "DependsOn" property like so:
Role definition:
"LambdaExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
[...]
Policy definition:
"lambdaexecutionpolicy": {
"DependsOn": [
"LambdaExecutionRole"
],
"Roles": [
{
"Ref": "LambdaExecutionRole"
}
],
[...]
From the official documentation, I understand that this DependsOn relation between the two entities should ensure that the policy is always deleted before the role.
Resource A is deleted before resource B.
However, we encounter an error where it appears that the system tries to delete the role before the policy:
Resource Name: [...] (AWS::IAM::Role)
Event Type: delete
Reason: Cannot delete entity, must delete policies first. (Service: AmazonIdentityManagement; Status Code: 409; Error Code: DeleteConflict; Request ID: [...]; Proxy: null)
I'm not sure how that's even possible, as I would have considered the "A DependsOn B" to ensure that the system never tries to delete B before deleting A. Is my understanding wrong here? Can there be a situation where the system tries to delete B before A?
And yes, I understand that in this case the obvious solution is to use an inline policy, as the policy is only used for this specific role. But as this behavior seems to conflict with my intuitive understanding of the official documentation, I want to properly understand what the "DependsOn" property actually means.
TL;DR Unable to replicate the error. DependsOn does not seem to be the culprit.
I used the CDK to create two versions of a minimum test stack with only two resources, AWS::IAM::Role and AWS::IAM::ManagedPolicy. V1 had no explicit policy dependency set on the role. V2, like the OP, did. The difference made no difference. Both versions deployed and were destroyed without error.
Version 1: CDK-Generated Default: no 'depends on' in the template
Version 2 (as in OP): has explicit dependency - Policy depends on the Role. The CDK added one line to the template: "DependsOn": [ "TestRole6C9272DF" ] under "TestPolicyCC05E598"
The two versions differed only by that single DependsOn. Both versions deployed and destroyed as expected without error.
// resource section of CDK-generated Cloud Formation Template
"Resources": {
"TestRole6C9272DF": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
},
"Metadata": {
"aws:cdk:path": "TsCdkPlaygroundIamDependencyStack/TestRole/Resource"
}
},
"TestPolicyCC05E598": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"Description": "",
"Path": "/",
"Roles": [
{
"Ref": "TestRole6C9272DF"
}
]
},
"DependsOn": [
"TestRole6C9272DF" // <-- The difference that makes no difference
],
"Metadata": {
"aws:cdk:path": "TsCdkPlaygroundIamDependencyStack/TestPolicy/Resource"
}
},

How can I recreate lambda function via cloudformation?

I am using serverless to manage IaC which uses cloudformation internally. There are a number of lambdas and roles defined in serverless.yml. It works very well until I tried to sls remove all resources and sls deploy again. After doing that, I get an error when run lambdas: The role defined for the function cannot be assumed by Lambda. (Service: AWSLambda; Status Code: 403; Error Code: AccessDeniedException; Request ID: 0879c203-bec7-480b-81c6-4c7a61e2cb15.
The error says lambda doesn't have permission however, it works if I change the lambda role to something else and change it back. It seems that lambda's role still references to the deprecated one.
I wonder what the proper way to do remove followed by deploy.
The role is:
{
"Role": {
"Path": "/",
"RoleName": "getSiteHandlerRole",
"RoleId": "xxxxx",
"Arn": "arn:aws:iam::115136697128:role/getSiteHandlerRole",
"CreateDate": "2020-07-27T03:37:18Z",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Description": "",
"MaxSessionDuration": 3600,
"Tags": [
{
"Key": "STAGE",
"Value": "user"
}
]
}
}

Amazon EventBridge Policies for AWS Services as targets using CF/SAM

I'm using AWS CloudFormation to setup an EventBridge Bus + Rules + Targets (say SNS). For SNS as a target, per the doc at https://docs.aws.amazon.com/eventbridge/latest/userguide/resource-based-policies-eventbridge.html#sns-permissions, I need to apply resource policies outside of CloudFormation and I don't think CF supports this yet?
For CW Logs Group as a target, Im using the aws logs put-resource-policy to set this up in a script. Is there a better way to automate this?
The link you've provided refers to setting up permissions for SNS topic. Setting such permissions is supported by the CloudFormation by means of AWS::SNS::TopicPolicy.
However, you also state that you want to set resource-based policies on the CloudWatch Logs (aws logs put-resource-policy). If this is the case, then you are correct and it is not supported in CloudFormation.
You would have to use custom resource based on a lambda function to add such functionality to your templates.
Here is a snippet from my SAM:
{
"MyDevQueue": {
"Properties": {
"QueueName": "my-dev-queue",
"ReceiveMessageWaitTimeSeconds": 20,
"Tags": [
{
"Key": "env",
"Value": "dev"
}
],
"VisibilityTimeout": 300
},
"Type": "AWS::SQS::Queue"
},
"MyDevQueuePolicy": {
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": [
"SQS:SendMessage"
],
"Condition": {
"ArnEquals": {
"aws:SourceArn": "arn:aws:events:<region>:<AccountID>:rule/my-dev-queue/my-dev-queue"
}
},
"Effect": "Allow",
"Principal": {
"Service": [
"events.amazonaws.com"
]
},
"Resource": [
{
"Fn::GetAtt": [
"MyDevQueue",
"Arn"
]
}
]
}
]
},
"Queues": [
"MyDevQueue"
]
},
"Type": "AWS::SQS::QueuePolicy"
}
}

Grant permissions between AWS resources with CloudFormation

I would like to have a CloudFormation template create an EC2 instance and give that instance access to a S3 bucket.
One way is to have the template create an IAM user with proper permissions and use its access key to grant access.
But what if I don't want to give that user access to the IAM service?
Is there a way to have that user deploy this template without IAM?
UPDATE:
I want to be able to just share that template, so I am wondering if it is possible to not have a dependency on pre-existing IAM resources (roles, policies, etc)
The common method to grant permissions for an instance is Instance Profiles. You create a role with all the required permissions, assign that role to an instance profile and then assign the profile to any instance you need.
You can do this with CloudFormation:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"myEC2Instance": {
"Type": "AWS::EC2::Instance",
"Version": "2009-05-15",
"Properties": {
"ImageId": "ami-205fba49",
"InstanceType": "t2.micro",
"IamInstanceProfile": {
"Ref": "RootInstanceProfile"
}
}
},
"MyRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version" : "2012-10-17",
"Statement": [ {
"Effect": "Allow",
"Principal": {
"Service": [ "ec2.amazonaws.com" ]
},
"Action": [ "sts:AssumeRole" ]
} ]
},
"Path": "/"
}
},
"RolePolicies": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "s3",
"PolicyDocument": {
"Version" : "2012-10-17",
"Statement": [ {
"Effect": "Allow",
"Action":["s3:PutObject","s3:PutObjectAcl"],
"Resource":["arn:aws:s3:::examplebucket/*"],
} ]
},
"Roles": [ { "Ref": "MyRole" } ]
}
},
"RootInstanceProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Path": "/",
"Roles": [ { "Ref": "MyRole" } ]
}
}
}
}
If you want to avoid giving the user deploying this template IAM access, you can create the instance profile before deploying the template and specify the already existing instance profile in the template. I haven't tried that yet, but it seems that should only require ec2:AssociateIamInstanceProfile and you should be able to constrain that just to that one specific profile.
Depends on what you mean by IAM service.
You can create IAM User Access Keys that give permissions to specific AWS services and no others. Access Keys do not allow IAM Console Access (this requires login credentials or federation).
For your use case your user will need at a minimum:
Permission to use CloudFormation to execute your template.
Permission to create the EC2 instance.
These permissions are defined in a policy that you add to the IAM user in the AWS Management Console. You can create users that cannot log into the console. Then you generate the Access Keys that the user will use in their application, AWS CLI, etc.
Overview of IAM Policies