Reputation: 3553
I am having some trouble getting a specific Secrets Manager Secret key value to pass it to my lambda through CDK.
After some time I finally realized that my SecretValue
is only resolved when I actually deploy this to lambda, and not while running local through SAM CLI.
By doing
cdk.SecretValue.secretsManager(secretId).toString()
I get something like "{\"apiKey\":\"sdfsdf-sdfsdf-sddsf\"}"
, but I want to rather have the apiKey directly.
Unfortunately, in my CDK code, I cannot JSON:parse(...secretsManager(..).toString())
as this will only be resolved once deployed. Before, the value is simply:
{{resolve:secretsmanager:apiKey:SecretString:::}}
(which seems to be a Token: https://docs.aws.amazon.com/cdk/latest/guide/tokens.html)
So I guess I would need some way to tell CDK how to use the rendered value, maybe by passing a callback that transforms the rendered result - is that possible? Are there any other tools I can use in my CDK setup that allow me to receive a specific key from a secret so that I can pass it to lambda directly?
I hope the problem is understandable. Thanks in advance for your help.
Upvotes: 5
Views: 14608
Reputation: 695
python version
environment_variables = secretsmanager.Secret.from_secret_complete_arn(
self,
"environment_variables",
"arn:aws:secretsmanager:us-east-1:1234:secret:my-keys",
)
lambda = _lambda.Function(
self, "MyLambda",
function_name="my-function",
runtime=_lambda.Runtime.PYTHON_3_9,
handler="lambda_function.lambda_handler",
timeout=cdk.Duration.seconds(3),
architecture=_lambda.Architecture.ARM_64,
code=_lambda.Code.from_asset(os.path.join(dirname, '../app_lambdas/my_lambda')),
role=my_lambda_role,
environment={
"API_KEY": environment_variables.secret_value_from_json('api-key').unsafe_unwrap()
},
)
Upvotes: 1
Reputation: 8887
You need to use Secret. You can use any of the static from
methods to get the secret. From there you can use the secretValueFromJson method to get the value.
Example (secret for Postgres db):
import * as secretsmanager from '@aws-cdk/aws-secretsmanager';
const dbSecret = secretsmanager.Secret.fromSecretNameV2(this, 'db-secret', 'db-secret-name');
const dbUser = dbSecret.secretValueFromJson('username').toString();
const dbPass = dbSecret.secretValueFromJson('password').toString();
const dbName = dbSecret.secretValueFromJson('dbname').toString();
Upvotes: 9
Reputation: 2411
Using .toString()
on Secrets seems no longer accepted without calling .unsafeUnwrap()
first.
As far as the documentation goes:
If you don't call this method, using the secret value directly in a string context or as a property value somewhere will produce an error.
For me, it produced the error on cdk deploy
:
Resolution error: Synthing a secret value to Resources(...) Using a SecretValue here risks exposing your secret. Only pass SecretValues to constructs that accept a SecretValue property, or call AWS Secrets Manager directly in your runtime code. Call 'secretValue.unsafeUnwrap()' if you understand and accept the risks..
as the message suggests try to use frameworks, which accept a SecretValue, or if you don't have the chance (as in my case I am trying to use Prisma, and it doesn't accept it yet)
use .unsafeUnwrap()
. Like:
const getValueFromSecret = (secret: ISecret, key: string): string => {
return secret.secretValueFromJson(key).unsafeUnwrap()
}
// usage (e.g. in Lambda stack)
const password = getValueFromSecret(secret, 'password')
Upvotes: 15
Reputation: 12402
I solved this for Parameter Store with AWS SDK, here's an extract from my stack.ts:
import { SSM } from 'aws-sdk'
const ssmSDK = new SSM()
async function fetchParam(name: string): Promise<string> {
try {
const param = await ssmSDK
.getParameter({
Name: name,
WithDecryption: true,
})
.promise()
if (!param.Parameter || !param.Parameter.Value) {
throw new Error(`${name} parameter not found!`)
}
return param.Parameter.Value
} catch (err) {
throw new Error(`failed to fetch ${name} parameter!`)
}
}
export async function buildSearchServiceStack(
scope: cdk.App,
id: string,
props?: cdk.StackProps
): Promise<cdk.Stack> {
const stack = new cdk.Stack(scope, id, props)
const [jwtSecret, elasticPassword] = await Promise.all([
fetchParam(`/shared/jwt/SECRET`),
fetchParam('/shared/elasticsearch/URL')])
// ..
Upvotes: -1