Accessing CDK Stack Resources - amazon-web-services

When creating a basic s3 resource with the following code
export class Test1Stack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new s3.Bucket(this, 'sampleBucket', {
versioned: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true
});
}
}
Cdk creates the following resources:
AWS::S3::Bucket
AWS::S3::BucketPolicy
Custom::S3AutoDeleteObjects
AWS::IAM::Role
AWS::Lambda::Function
AWS::CDK::Metadata
I can access AWS::S3::Bucket AWS::S3::BucketPolicy properties by using
const s3 = new s3.Bucket(.......)
What object would give me access to the rest of the resources, for example if I'd like to overwrite the logical id for AWS::IAM::Role.
I imagine that I could create my own AWS::IAM::Role, AWS::Lambda::Function, AWS::CDK::Metadata and in this way I could use the same mechanism I am using for s3 to manipulate & override properties, but it is not what I am looking for.
I just want to be able to access the other resources at run time.
I have tried the properties from the constructor with no success.
I also understand that CDK does not recommend overriding resources.

You can access the bucket policy, assuming it got auto-created, and change its logical id like so:
const policy = bucket.policy!;
(policy.node.defaultChild as CfnBucketPolicy).overrideLogicalId("MyBucketPolicy")
As for the IAM Role related to autoDeleteObjects it is also possible but a little bit more brittle. There's a custom resource provider mini-framework used. This means that the labmda is shared between all Buckets in a given stack that use autoDeleteObjects.
Still, it is possible to get a hold of the lambda itself like so:
const provider = this.node.findChild('Custom::S3AutoDeleteObjectsCustomResourceProvider') as CustomResourceProvider
const lambda = provider.node.findChild('Handler') as CfnResource

Related

Cannot add ManagedPolicy to the lambda that is created in the same stack

I'm new to AWS CDK and I'm trying to set up lambda with few AWS managed policies.
Lambda configuration,
this.lambdaFunction = new Function(this, 'LambdaName', {
functionName: 'LambdaName',
description: `Timestamp: ${new Date().toISOString()} `,
code: ...,
handler: '...',
memorySize: 512,
timeout: Duration.seconds(30),
vpc: ...,
runtime: Runtime.PYTHON_3_8,
});
I want to add AmazonRedshiftDataFullAccess ManagedPolicy to lambda role but couldn't find out a way to do it as addToRolePolicy supports only the PolicyStatement and not ManagedPolicy.
Tried something as following, it errored out saying role may be undefined.
this.lambdaFunction.role
.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonRedshiftDataFullAccess"));
Could anyone help me understand what is the right way to add a ManagedPolicy to the default role that gets created with the lambda function?
okay I have made a couple of mistakes,
It is AmazonRedshiftDataFullAccess, not service-role/AmazonRedshiftDataFullAccess
As the role is optional here, I should have done Optional Chaining (?.)
The following worked for me,
this.lambdaFunction.role
?.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("AmazonRedshiftDataFullAccess"));
Its a 3 step process :-
You need to first create role for lambda.
create lambda and attach role to lambda.
add aws managed( make sure its correct name ) policy to lambda.
example
const myRole = new iam.Role(this, 'My Role', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
});
const fn = new lambda.Function(this, 'MyFunction', {
runtime: lambda.Runtime.NODEJS_16_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')),
role: myRole, // user-provided role
});
myRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonRedshiftDataFullAccess"));

Cannot grant permission to EventBridge bus

I am creating a custom bus in AWS EventBridge via CDK:
export class EventbridgeStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const targetCoreBus = new events.EventBus(this, 'TargetCoreBus', {
eventBusName: 'TargetCoreBus',
});
targetCoreBus.grantPutEventsTo(new iam.AccountPrincipal('1234567890'));
}
}
The bus is created fine, but I assumed the line
targetCoreBus.grantPutEventsTo(new iam.AccountPrincipal('1234567890'));
Would add policy to the bus that would allow specified account to put events into it. But it doesn't seem to do anything, nothing new is synthesized in the stack, no policy is added to the bus. Is it expected, am I doing something wrong?
grantPutEventsTo adds an inline, identity-based policy to the Grantee. For instance, targetCoreBus.grantPutEventsTo(MyLambda) would add a AWS::IAM::Policy to the Lambda's execution role.
You want to add the account principal to the Bus' resource-based policy. The CfnEventBusPolicy construct will do just that:
new events.CfnEventBusPolicy(this, 'CustomBusResoucePolicy', {
statementId: 'Cross-Account-Bus-20220509',
action: 'events:PutEvents',
principal: '123456789012',
eventBusName: targetCoreBus.eventBusName,
condition: {...},
});

AWS CDK: Lambda resource based policy for a function with an alias

I am using CDK to create a lambda, a new version of the lambda, and point the "live" alias to the newest version of the lambda like so
const func = new lambda.Function(this, 'lambdaName', {
// Other properties
description: `Generated on: ${new Date().toISOString()}`,
});
const version = func.addVersion(new Date().toISOString());
const alias = new lambda.Alias(this, 'lambdaName-alias', {
aliasName: 'live',
version: version,
});
Now, I want to add resource based permission to the alias live and not just the main lambda function.
I assumed that this would work but it doesn't create any resource based permissions at all:
alias.addPermission('CrossAccountAccessId', {
action: 'lambda:InvokeFunction',
principal: new ArnPrincipal('ACOUNT_NUMBER_XXX')
});
How to add permissions to an alias using AWS CDK?
I referred to this for resource based alias permission-ing: https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html#versioning-permissions-alias
For the CDK docs, I read this: https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Alias.html#addwbrpermissionid-permission
I think it might be related to a bug in AWS CDK where adding resource based policy on a lambda version fails as mentioned here: AWS CDK: Resource Policies are not being granted for Lambda Version but I'm not sure if aliases also have the same problem as that of versions.
I tried the other answers however, I did not see the Alias permission being created under the template tab of the CFN stack.
I ended up using the CFN L1 primitive in the CDK code like this:
const cfnPermission = new lambda.CfnPermission(this, 'LambdaInvokeAccessRemote', {
action: 'lambda:InvokeFunction',
functionName: alias.functionName,
principal: "ACCOUNT_NUMBER"
});
After that, I was able to see the alias based permission under the template tab of the CFN stack:
LambdaInvokeAccessRemote:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName:
Fn::Join:
- ""
- - Fn::Select:
- 6
- Fn::Split:
- ":"
- Ref: LambdaAlias273D1F4C
- :live
Principal: "ACCOUNT_NUMBER"
The following generates the expected cross-account lambda permission for the alias. I use CDK v2, although the v1 will work, too.
The addVersion method is deprecated and is removed completely in v2. "Instead, use this.currentVersion to obtain a reference to a version resource that gets automatically recreated when the function configuration (or code) changes".
const alias = func.currentVersion.addAlias('live');
// `iam.ArnPrincipal` will work (cdk does not validate the format), but, `iam.AccountPrincipal` is semantically correct in your case.
const principal = new iam.AccountPrincipal('123456789012');
These 3 permission methods are equivalent:
this.alias.grantInvoke(principal);
this.alias.addPermission('CrossAccountPermission', { principal });
this.alias.addPermission('CrossAccountPermission', {
action: 'lambda:InvokeFunction', // this is the default value
principal,
});
The permission is created as expected. This test passes with any of the above 3 methods:
// MyStack.test.ts
const cfnAlias = stack.alias.node.defaultChild as lambda.CfnAlias;
template.hasResourceProperties('AWS::Lambda::Permission', {
Action: 'lambda:InvokeFunction',
FunctionName: { Ref: stack.resolve(cfnAlias.logicalId) },
Principal: '123456789012',
});

AWS CDK destroy fails to delete secret

I have a CDK script that creates an S3 bucket, VPC, and an RDS instance. Deploy is working, but the destroy fails with an error that my user is not authorized to secretsmanager:DeleteSecret.
I used the IAM policy testing tool to check and it passes. I am able to delete the secret via the UI. The CDK destroy command continues to fail though. Any thoughts?
CDK script:
class AcmeCdkStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// create a general purpose bucket for use with the app
new s3.Bucket(this, 'app-bucket', {
versioned: true
});
// create a vpc for our application
const vpc = new ec2.Vpc(this, 'app-vpc, {
cidr: "10.0.0.0/16",
});
// create a database instance
const db = new rds.DatabaseInstance(this, `app-db`, {
engine: rds.DatabaseInstanceEngine.POSTGRES,
instanceClass: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
vpc,
masterUsername: `dbadmin`,
deleteAutomatedBackups: false,
deletionProtection: false,
// https://github.com/aws/aws-cdk/issues/4036
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
}
}
const app = new cdk.App();
new AcmeCdkStack(app, 'app-stack;);
Error:
User: arn:aws:iam::0000000000:user/user#acme.com is not authorized to perform: secretsmanager:DeleteSecret on resource: arn:aws:secretsmanager:us-east-1:0000000000:secret:appdbdemoSecret0261-mjgIXOsp5rLL-HxFng1 (Service: AWSSecretsManager; Status Code: 400; Error Code: AccessDeniedException; Request ID: 000000000)
Based on the comments, the problem was that the CDK was using different credentials than expected. The solution was to use correct AWS_PROFILE.

How to add S3 BucketPolicy with AWS CDK?

I wanna translate this CloudFormation piece into CDK:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: S3BucketImageUploadBuffer
PolicyDocument:
Version: "2012-10-17"
Statement:
Action:
- s3:PutObject
- s3:PutObjectAcl
Effect: Allow
Resource:
- ...
Looking at the documentation here, I don't see a way to provide the policy document itself.
This is an example from a working CDK-Stack:
artifactBucket.addToResourcePolicy(
new PolicyStatement({
resources: [
this.pipeline.artifactBucket.arnForObjects("*"),
this.pipeline.artifactBucket.bucketArn],
],
actions: ["s3:List*", "s3:Get*"],
principals: [new ArnPrincipal(this.deploymentRole.roleArn)]
})
);
Building on #Thomas Wagner's answer, this is how I did this. I was trying to limit the bucket to a given IP range:
import * as cdk from '#aws-cdk/core';
import * as s3 from '#aws-cdk/aws-s3';
import * as s3Deployment from '#aws-cdk/aws-s3-deployment';
import * as iam from '#aws-cdk/aws-iam';
export class StaticSiteStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Bucket where frontend site goes.
const mySiteBucket = new s3.Bucket(this, 'mySiteBucket', {
websiteIndexDocument: "index.html"
});
let ipLimitPolicy = new iam.PolicyStatement({
actions: ['s3:Get*', 's3:List*'],
resources: [mySiteBucket.arnForObjects('*')],
principals: [new iam.AnyPrincipal()]
});
ipLimitPolicy.addCondition('IpAddress', {
"aws:SourceIp": ['1.2.3.4/22']
});
// Allow connections from my CIDR
mySiteBucket.addToResourcePolicy(ipLimitPolicy);
// Deploy assets
const mySiteDeploy = new s3Deployment.BucketDeployment(this, 'deployAdminSite', {
sources: [s3Deployment.Source.asset("./mysite")],
destinationBucket: mySiteBucket
});
}
}
I was able to use the s3.arnForObjects() and iam.AnyPrincipal() helper functions rather than specifying ARNs or Principals directly.
The assets I want to deploy to the bucket are kept in the root of my project directory in a directory called mysite, and then referenced via a call to s3Deployment.BucketDeployment. This can be any directory your build process has access to, of course.
The CDK does this a little differently. I believe you are supposed to use bucket.addToResourcePolicy, as documented here.
As per the original question, then the answer from #thomas-wagner is the way to go.
If anyone comes here looking for how to create the bucket policy for a CloudFront Distribution without creating a dependency on a bucket then you need to use the L1 construct CfnBucketPolicy (rough C# example below):
IOriginAccessIdentity originAccessIdentity = new OriginAccessIdentity(this, "origin-access-identity", new OriginAccessIdentityProps
{
Comment = "Origin Access Identity",
});
PolicyStatement bucketAccessPolicy = new PolicyStatement(new PolicyStatementProps
{
Effect = Effect.ALLOW,
Principals = new[]
{
originAccessIdentity.GrantPrincipal
},
Actions = new[]
{
"s3:GetObject",
},
Resources = new[]
{
Props.OriginBucket.ArnForObjects("*"),
}
});
_ = new CfnBucketPolicy(this, $"bucket-policy", new CfnBucketPolicyProps
{
Bucket = Props.OriginBucket.BucketName,
PolicyDocument = new PolicyDocument(new PolicyDocumentProps
{
Statements = new[]
{
bucketAccessPolicy,
},
}),
});
Where Props.OriginBucket is an instance of IBucket (just a bucket).