How to make an AWS SDK call from a CDK project using the interface AwsSdkCall?

3 minute read
2

I want to make AWS SDK calls from my AWS Cloud Development Kit (AWS CDK) project using the AwsSdkCall interface.

Short description

You can use AWS Software Development Kit (AWS SDK) calls for stack deployment scenarios such as:

  • Retrieve configurations while a stack is being created and updated dynamically.
  • Retrieve attributes of resources that aren't supported in return values of the resources in AWS CloudFormation.
  • Make small patches or configuration changes that aren't supported by AWS CloudFormation.

You can trigger AWS SDK calls on different CloudFormation stack operation events such as Create, Delete, or Update. You don't need to set up access and tools for your deployment pipeline to make AWS SDK calls. The AWS SDK runtime is set up completely by AWS Cloud Development Kit (AWS CDK). Also, you can customize and limit the access for the AWS Lambda function behind the custom resource.

Resolution

Note: The following examples in this resolution use Python. Make sure to use commands specific to the programming language that you're using.

To make an AWS SDK call using AwsSdkCall, you must declare three basic parameters:

  • Service - This is the AWS service that you intend to call. Values are case-sensitive.
  • Action - This is the API/action call that you intend to make. This usually follows a camel-case pattern. Values are case-sensitive.
  • Parameters - These are the optional parameters that you pass while making the API/action call. You can declare parameters as a variable and then pass them to the interface resembling a JSON object for API payload. For more information, see AWS SDK for JavaScript.

1.    Retrieve an Amazon Machine Image ID from AWS Systems Manager Agent:

Note: In the following example import custom_resources as cr and aws_cdk as cdk.

get_ami_id = cr.AwsCustomResource(self, "GetAMIId",
                                                on_create=cr.AwsSdkCall(
                                                    service="SSM",
                                                    action="getParameter",
                                                    parameters={
                                                        "Name": "/aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2"
                                                    },
                                                    physical_resource_id=cr.PhysicalResourceId.of('get-ami-id')),
                                                policy=cr.AwsCustomResourcePolicy.from_sdk_calls(
                                                    resources=cr.AwsCustomResourcePolicy.ANY_RESOURCE
                                                ))
cdk.CfnOutput(self, 'ImageId', value=get_ami_id.get_response_field('Parameter.Value'))

Note: You can reference the response of the API call with the get_response_field parameter.

2.    Encrypt the Lambda log group using a custom KMS key:

Note: In the following example, from aws_cdk import aws_lambda as lambda_, custom_resources as cr, and aws_kms as kms.

encryption_key = kms.Key(self, 'Key')
encryption_key.grant_encrypt_decrypt(
            iam.ServicePrincipal('logs.amazonaws.com'))

fn = lambda_.Function(self, "MyFunction",
                              runtime=lambda_.Runtime.NODEJS_16_X,
                              handler="index.handler",
                              code=lambda_.Code.from_inline("hello world"))

associate_kms_key = cr.AwsCustomResource(self, "AssociateKmsKey",
                                                 on_create=cr.AwsSdkCall(
                                                     service="CloudWatchLogs",
                                                     action="associateKmsKey",
                                                     parameters={
                                                       "kmsKeyId": encryption_key.key_arn,
                                                       "logGroupName": fn.log_group.log_group_name
                                                     },
                                                     physical_resource_id=cr.PhysicalResourceId.of("associate-kms-key")),
                                                 policy=cr.AwsCustomResourcePolicy.from_sdk_calls(
                                                     resources=cr.AwsCustomResourcePolicy.ANY_RESOURCE
                                                 )
                                                 )

Note: You can use the property policy to grant permissions to the Lambda function behind the custom resource to make API calls. If you use from_sdk_calls(), then each API call is translated into the corresponding AWS Identity and Access Management (IAM) permissions. If the correct permissions aren't added after synthesis, then you can manually add the permissions using from_statements().

Example:

policy=cr.AwsCustomResourcePolicy.from_statements(
                                                     statements=[iam.PolicyStatement(actions=[
                                                         'dynamodb:DescribeTable', 'dynamodb:ListTables'],
                                                         resources=['*'],
                                                     )]
                                                 )
AWS OFFICIAL
AWS OFFICIALUpdated 10 months ago
1 Comment

Hey there!

Concerning the AwsSdkCall-parameters, the article mentions:

Service - This is the AWS service that you intend to call. Values are case-sensitive.

The CDK docs go into more detail under interface AwsSdkCall :

This is the name of an AWS service, in one of the following forms:

  • An AWS SDK for JavaScript v3 package name (@aws-sdk/client-api-gateway)
  • An AWS SDK for JavaScript v3 client name (api-gateway)
  • An AWS SDK for JavaScript v2 constructor name (APIGateway)
  • A lowercase AWS SDK for JavaScript v2 constructor name (apigateway)

However, I am currently experiencing odd behavior when using this to call service 'lambda' across different deploys from different pipelines. The AwsSdkCall is identical, but one pipeline works using the lowercase v2 constructor name (lambda), while another other pipeline leads to status FAILED for this custom resource, and will only work correctly with the regular v2 constructor name (Lambda).

Would anyone have an idea what the issue could be?

replied 2 months ago