CloudFormation中CloudWatch事件Input引用Secrets Manager密钥失败求助
The issue here is that CloudFormation dynamic references (like {{resolve:secretsmanager:...}}) only work for top-level resource properties, not nested string values like the Input field inside the Targets array. When you use the resolve expression in the Name field (a top-level property), CloudFormation parses it during stack creation/update. But for nested string properties like Input, CloudFormation treats the entire string as literal text, so the resolve expression doesn't get evaluated.
Solutions to Pass Secrets to CloudWatch Event Body
1. Use an AWS Lambda as a Middle Layer (Recommended)
This is the most secure and reliable approach. Instead of directly triggering your target resource from the CloudWatch Events Rule, have the rule invoke a Lambda function. The Lambda can fetch the secret from Secrets Manager, construct the required input payload, and then call your target service.
Here's a simplified adjustment to your CloudFormation template:
"dareMeXDevCloudwatchMissingPayoutsJob": { "Type": "AWS::Events::Rule", "DependsOn": [ "xxx" ], "Properties": { "Description": "xxxxx", "RoleArn": { "Fn::GetAtt": [ "xxxxx", "Arn" ] }, "Name": "xxxxx", "ScheduleExpression": "cron(0 8 ? * 6 *)", "State": "ENABLED", "Targets": [ { "Arn": { "Fn::GetAtt": [ "YourLambdaFunction", "Arn" ] }, "Id": "LambdaTarget", "Input": "{\"val1\":\"val1\"}" } ] } }
And a sample Node.js Lambda implementation to handle the secret fetching:
const AWS = require('aws-sdk'); const secretsManager = new AWS.SecretsManager(); const lambda = new AWS.Lambda(); exports.handler = async (event) => { // Fetch secret from Secrets Manager const secretResponse = await secretsManager.getSecretValue({ SecretId: 'your-secret-arn-or-name' }).promise(); const secretVal = JSON.parse(secretResponse.SecretString).yourSecretKey; // Build payload with the resolved secret const payload = { val1: event.val1, secretVal: secretVal }; // Invoke your original target service await lambda.invoke({ FunctionName: 'your-original-target-function', Payload: JSON.stringify(payload) }).promise(); return { statusCode: 200 }; };
2. Use Fn::Sub with Dynamic Reference (Less Secure)
If you must pass the secret directly in the event input (note: this exposes the secret in CloudWatch Event logs), you can use Fn::Sub to embed the resolved secret value into the Input string. CloudFormation will evaluate the dynamic reference when processing the Fn::Sub function.
Adjust your Targets section like this:
"Targets": [ { "Arn": { "Fn::GetAtt": [ "xxxxxxx", "Arn" ] }, "Id": "xxxx", "Input": { "Fn::Sub": "{\"val1\":\"val1\",\"secretVal\":\"${resolve:secretsmanager:arn-of-your-secret:SecretString}\"}" } } ]
Critical Security Warning: The resolved secret will be visible in the CloudWatch Events console, event history, and any logs that capture the event payload. This is not recommended for sensitive secrets.
Key Takeaway
The top-level vs nested property limitation is a CloudFormation constraint. For sensitive data, always use a Lambda middle layer to avoid exposing secrets in event logs or stack outputs.
内容的提问来源于stack exchange,提问作者Piekarski D

