AWS CDK Secrets Manager Tutorial & Best Practices
This article demonstrates how you can leverage Secrets Manager with AWS Cloud Development Kit (CDK), an open-source software development framework, to model and provision your AWS infrastructure as code. We will detail how to use AWS CDK to provision and reference secrets in Secrets Manager. We will also review other AWS CDK secrets manager options, like Parameter Store, and how integration with third-party tools can help improve secret AWS CDK secrets manager workflows.
Summary of AWS CDK Secrets Manager concepts
The table below summarizes the key concepts that will be covered in this article.
Common secrets and parameters used in AWS CDK projects
AWS CDK provides construct libraries for handling secrets and parameters using Secrets Manager and Parameter Store. Let’s take a quick look at the common use cases for secrets and parameters in a CDK project.
Common secrets
Some of the commonly used secrets in CDK projects are:
Common parameters
Some of the common parameters used along with CDK are:
Managing Secrets in AWS CDK with Secrets Manager and Parameter Store
Now that we’ve gone through the common secrets and parameters used in CDK, let’s learn how we can use CDK to provision and retrieve these secrets in your project.
We will use the TypeScript CDK construct libraries to provision Secret Manager and SSM Parameter Store resources.
Secret Manager integration
To provision Secret Manager using AWS CDK, you must include the aws-cdk-lib/aws-secretsmanager module in your CDK project.
The following example demonstrates how to create a Secret Manager secret for database credentials.
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
export class SecretManagerCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Secret Manager Credentials
const secret = new secretsmanager.Secret(this, 'SampleSecret', {
secretName: '/stage/credentials',
generateSecretString: {
secretStringTemplate: JSON.stringify({ username: 'admin' }),
generateStringKey: 'password',
},
});
}
}
To create a Secret Manager store with a custom KMS encryption key, use the following code:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
import * as kms from 'aws-cdk-lib/aws-kms';
export class SecretManagerCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// KMS Key
const key = kms.Key.fromKeyArn(this, 'secretManagerKey', 'arn:aws:kms:ap-south-1:126345658785:key/8cd94c7e-ef37-4423-9bf7-2b0642fab0ef');
// Secret Manager Credentials
const secret = new secretsmanager.Secret(this, 'SampleSecret', {
secretName: '/stage/credentials',
generateSecretString: {
secretStringTemplate: JSON.stringify({ username: 'admin' }),
generateStringKey: 'password',
},
encryptionKey: key,
});
}
}
To create a Secret Manager store for use cases such as API keys, use the following code:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
export class SecretManagerCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Secret Manager API Key
const apiKey = new secretsmanager.Secret(this, 'SampleApiKey', {
secretName: '/stage/api-key',
generateSecretString: {
secretStringTemplate: JSON.stringify({ apiKey: 'asdasdasd' }),
generateStringKey: 'apiKey',
},
});
}
}
For the purpose of this article, let’s look at how we can inject the new Secret Manager value into a Lambda function:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
export class SecretManagerCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Secret Manager API Key
const apiKey = new secretsmanager.Secret(this, 'SampleApiKey', {
secretName: '/stage/api-key',
generateSecretString: {
secretStringTemplate: JSON.stringify({ apiKey: 'asdasdasd' }),
generateStringKey: 'apiKey',
},
});
// Associate Secret Manager with Lambda
const sampleLambda = new lambda.Function(this, 'SampleLambda', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromInline(`exports.handler = async (event) => { console.log("Hello World"); return { statusCode: 200, body: JSON.stringify('Hello from Lambda!') }; };`),
environment: {
API_KEY_ID: apiKey.secretValue.unsafeUnwrap().toString(), // This is not recommended
},
});
}
}
Note that for security purposes, it is recommended to retrieve the secret at Lambda runtime logic to avoid exposing the secret in the CloudFormation template. For this, pass the secret ARN as an environment variable to the lambda function instead of passing the secret value directly.
领英推荐
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
export class SecretManagerCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Secret Manager API Key
const apiKey = new secretsmanager.Secret(this, 'SampleApiKey', {
secretName: '/stage/api-key',
generateSecretString: {
secretStringTemplate: JSON.stringify({ apiKey: 'asdasdasd' }),
generateStringKey: 'apiKey',
},
});
// Associate Secret Manager with Lambda
const sampleLambda = new lambda.Function(this, 'SampleLambda', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromInline(`exports.handler = async (event) => { console.log("Hello World"); return { statusCode: 200, body: JSON.stringify('Hello from Lambda!') }; };`),
environment: {
API_KEY_ID: apiKey.secretArn // Note that we’re passing the secret ARN here
},
});
}
}
To retrieve an existing Secret Manager secret in your CDK project using the Secret Manager ARN, use the following code:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class SecretManagerCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Secret Manager secretKey
const secretKey = secretsmanager.Secret.fromSecretCompleteArn(this, 'secretKey', 'arn:aws:secretsmanager:ap-south-1:1234567895:secret:prod/secretKey-2pUG9Y');
// Associate Secret Manager with Lambda
const sampleLambda = new lambda.Function(this, 'SampleLambda', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromInline(`exports.handler = async (event) => { console.log("Hello World"); return { statusCode: 200, body: JSON.stringify('Hello from Lambda!') }; };`),
environment: {
API_KEY_ID: secretKey.secretValue.unsafeUnwrap().toString(), // This is not recommended
},
});
}
}
SSM Parameter Store integration
To provision Parameter Store with the AWS CDK, include the aws-cdk-lib/aws-ssm module in your CDK project.
The following example demonstrates how you can create an SSM Parameter Store:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ssm from 'aws-cdk-lib/aws-ssm';
export class SecretManagerCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Parameter Store
const sampleStore = new ssm.StringParameter(this, 'SecretKey', {
parameterName: '/sample/secretvalue',
stringValue: 'asdasadas',
description: 'Sample secret value',
tier: ssm.ParameterTier.STANDARD,
});
}
}
To inject a Parameter Store value into a Lambda function, use the following code:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ssm from 'aws-cdk-lib/aws-ssm';
export class SecretManagerCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Parameter Store
const sampleStore = new ssm.StringParameter(this, 'SecretKey', {
parameterName: '/sample/secretvalue',
stringValue: 'asdasadas',
description: 'Sample secret value',
tier: ssm.ParameterTier.STANDARD,
});
// Associate Secret Manager with Lambda
const sampleLambda = new lambda.Function(this, 'SampleLambda', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromInline(`exports.handler = async (event) => { console.log("Hello World"); return { statusCode: 200, body: JSON.stringify('Hello from Lambda!') }; };`),
environment: {
SECRET_VALUE: sampleStore.stringValue, // This is not recommended
},
});
}
}
Similar to the Secrets Manager example mentioned above, using this method is not recommended for security reasons as it can expose the secret value. Instead, pass the secret ARN as an environment variable to the lambda function so that you can fetch the secret during runtime.
Secret Manager vs. Parameter Store
To learn more about which secret management solution suits your use case and to understand the difference between them, follow this detailed AWS guide by Doppler.
Six limitations of AWS native tools
AWS CDK faces the following limitations when it comes to secret management using Secrets Manager and Parameter Store in CDK projects:
An alternative solution natively integrated with AWS
Doppler is a third-party secrets management tool that can integrate with AWS CDK and address the limitations mentioned above. Doppler provides a user-friendly platform for managing secrets, configurations, and credentials. It can enhance the CDK development experience in the following ways:
Conclusion
While AWS CDK can lay the foundation for infrastructure as code in your projects, it’s essential to acknowledge the limitations within CDK when it comes to handling secrets effectively. Challenges such as dependency management between CDK projects, external secret rotation, limited support for secret types, and the absence of built-in auditing and logging capabilities can introduce complexities and hinder a seamless development experience. Integrating Doppler with AWS CDK can significantly enhance the secrets management experience in your projects. Developers can mitigate these limitations by leveraging Doppler’s more efficient, secure, and agile solution for managing secrets and configurations in their AWS environments.
This article originally appeared at https://www.doppler.com/guides/aws-guides/aws-cdk-secrets-manager