Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I force CloudFormation to resolve values from Secrets Manager?

In the following (abbreviated CloudFormation template), I am trying to configure an AWS Lambda function to get a value from AWS Secrets Manager injected into its environment:

Resources:
  Function:
    Type: AWS::Serverless::Function
    Properties:
      Environment:
        Variables:
          SECRET_KEY: !Sub '{{resolve:secretsmanager:${Secret}:SecretString:KEY}}'

  Secret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: 'very-secret-thing'
      SecretString: '{"KEY":"dummy"}'

When creating a stack using this template, everything comes up as expected. I then go and change the value of the secret outside of CloudFormation, as I would not really want the secret checked into source control. This is totally possible, and the documentation implies, that the secret's value will not be touched subsequent CloudFormation stack updates, as long as I avoid changing the dummy value for SecretString in the template.

So, after setting the actual secret in the AWS Console, I need to trigger a redeploy of the Lambda function, so that the new secret value will be resolved by CloudFormation and set in the function's environment. How do I do that?

Executing aws cloudformation deploy fails with the message: No changes to deploy.

I suspect CloudFormation is comparing the "raw" version of the template with what was deployed last, without first resolving the references to Secrets Manager. Is that the case? And is there some trick to force earlier dereferencing?

Footnote: I am well aware that using Secrets Manager this way will cause the secret value to be visible in the AWS Lambda Console, and that getting the value from Secrets Manager at runtime would be the more secure approach. That just happens to be out-of-scope for what I am hoping to do.

like image 586
Jørn Schou-Rode Avatar asked Jan 09 '20 10:01

Jørn Schou-Rode


1 Answers

You can artificially change something else on the AWS::Serverless::Function resource to force it to update when you do your deployment.

Say, for example, a timestamp:

Parameters:
  DeployTimestamp: { Type: String }

Resources:
  Function:
    Type: AWS::Serverless::Function
    Properties:
      Environment:
        Variables:
          SECRET_KEY: !Sub '{{resolve:secretsmanager:${Secret}:SecretString:KEY}}'
          SECRET_KEY_UPDATED: !Ref DeployTimestamp

Assuming you do your deployment from a script, then you'd do something like aws cloudformation deploy --parameter-overrides "DeployTimestamp=$(date)" to change the value each time.

The downside to this, of course, is that the function will update every deployment, even if the secret hasn't updated. If that bothers you, you could get fancier and inject aws secretsmanager describe-secret --query LastChangedDate as a parameter instead of the current time.

like image 73
Dave Shifflett Avatar answered Nov 07 '22 00:11

Dave Shifflett