Set global parameters in aws cloudformation - amazon-web-services

I'm building a complex application in AWS using Cloudformation.
My setup is the following: I'm going to use yaml files to define the stacks and corresponding json files which contain the stack parameters. Anyway there are parameters which are the same in multiple json files and I'd like to define them globally in one file/stack instead of having to update them in multiple files everytime they change.
What is the recommended way to set such global parameters using cloudformation?
Help would be highly appreciated.

You could possibly create one stack with command parameters, end export their values from this stack. Then, in other stack, the parameter values would be accessed using Fn::ImportValue.
An alternative could be to store common parameters in SSM Parameter Store, and then use dynamic references in your template to access them.

Related

Add new environment variables to Lambda using Cloud Formation Template

I have a nested Cloud Formation Template (multiple templates within a root template )to create a complete web application.
Lambda is created in the first template and few environment variables are added to it.
The later part of the templates also produces some values that has to be added as environment variables.
Is there a way to attach these environment variables to the existing lambda function?
I don't think so, but there are a few options. If you could change the stack dependency order, you could build the stack creating the values depended upon first. If you cannot, you can store your environment variables in SSM Parameter Store as mentioned in this knowledge center article.
So you set the environment variable to a path where the value can be expected, then when creating the stack that knows the value, you store it at that path. When the lambda runs, you just do get parameter.

AWS Lambda - One function with multiple parameter sets or multiple functions?

I have a lambda function which is pretty general. It queries an Athena Database table, and writes the output to an S3 Bucket. The function is currently set up to accept environment variables for the Database name, table name, output filename and output bucket.
Currently, I call this function with 4 different sets of environment variables, so I have 4 different lambdas, whose code is the same, but whose environment variables are different. Do I need to have 4 different lambdas, or is there a way of having one lambda with 4 environment variable 'sets'?
Thanks!
Here's one option: To handle 4 sets of configuration with a single lambda, send a variable (e.g. type: Foo) part of the lambda invocation1. As #Marcin suggests, the lambda uses the type value to fetch the config variables from the SSM Parameter Store at runtime with the GetParametersByPath API. Parameters support hierarchies, so you can store your config using nested variable names like: /Foo/db, /Foo/table, /Bar/table etc.
(1) For example, send type in the event detail if event-triggered, or in the SDK Invoke command payload.

I need a strategy for handling optional SSM Parameter Store parameters in CDK

In my stack definition I pull in a number of parameters from SSM Parameter Store...
const p1 = ssm.StringParameter.fromStringParameterAttributes( ... )
const p2 = ssm.StringParameter.fromStringParameterAttributes( ... )
I then pass them along to the relevant lambdas as environment vars...
environment: {
PARAM_ONE: p1.stringValue
PARAM_TWO: p2.stringValue
}
However I don't want all of those parameters to be mandatory. I would like the ones that exist to be passed in as env vars, and the ones that don't to just remain undefined as my app has defaults for them anyway. However, trying to inspect the value of p1.stringValue just gives me a Token, not a value, so I can't do any logic based on it's presence or absence: https://docs.aws.amazon.com/cdk/latest/guide/tokens.html
If I ask for the parameter and it is not defined in SSM Parameter Store I then get an error that I can't catch or ignore when it tries to build the changeset and the deployment fails...
MyApp: creating CloudFormation changeset...
❌ MyAppStack failed: Error [ValidationError]: Unable to fetch parameters [/myapp/param1,/myapp/param2] from parameter store for this account.
So how can I deal with SSM parameters which may or may not exist at deploy time?
I assume you are only grabbing the manager in your import, not the actual values inside your secrets. If this is the case, then your best bet is to leverage the SDK to do this for you - a simple call using the SDK (which will be run during the synth stage of a cdk deploy or cdk synth) to see if said SMM fields/groups exist. If they do, go ahead and import them.
I do something very similar with Layers - the from methods for layers require the version number - that may change at any time. So i have a small function that gets the latest version number of a given layer using the SDK and i can then use that to import the layer definition into my stack.
If you are trying to get the actual secret inside the secret manager parameter ... that is better suited to outside the CDK for most scenarios - done in the exact location you need the secrets so you dont end up with secret value in plain text somewhere.

Global environment variables for AWS CloudFormation

Is there a way to have global environment variables in a AWS CloudFormation yaml file for Lambdas?
Currently we are using the SSM Parameter Store for global variables, but we don't want to use that anymore.
I looking forward to have something like this:
Environment:
Variables:
variable1: xxx // local variables
variable2: xxx
...
${file(./globalvariables.yml)} // global variables
Or even better: every lambda is including the global environment variables as default without explicitly calling it.
Is this possible? Or what approach would you suggest? Thanks in advance!
Sadly I'm unaware of having predefined defaults for environment variables to be set through CloudFormation for Lambdas however - One possible option is instead of using env variables in CloudFormation add a lambda layer with all the config and pull the values from there.
Benefits of this are that if a value changes you only have to update your layer once then update lambdas to use new layer which could be a single parameter instead of manually updating every single one.
Docs here: https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html
Another option would be to use AWS Secrets Manager Or SSM Parameter Store as ServerMonkey suggested.

Can I parameterize AWS lambda functions differently for staging and release resources?

I have a Lambda function invoked by S3 put events, which in turn needs to process the objects and write to a database on RDS. I want to test things out in my staging stack, which means I have a separate bucket, different database endpoint on RDS, and separate IAM roles.
I know how to configure the lambda function's event source and IAM stuff manually (in the Console), and I've read about lambda aliases and versions, but I don't see any support for providing operational parameters (like the name of the destination database) on a per-alias basis. So when I make a change to the function, right now it looks like I need a separate copy of the function for staging and production, and I would have to keep them in sync manually. All of the logic in the code would be the same, and while I get the source bucket and key as a parameter to the function when it's invoked, I don't currently have a way to pass in the destination stuff.
For the destination DB information, I could have a switch statement in the function body that checks the originating S3 bucket and makes a decision, but I hate making every function have to keep that mapping internally. That wouldn't work for the DB credentials or IAM policies, though.
I suppose I could automate all or most of this with the SDK. Has anyone set something like this up for a continuous integration-style deployment with Lambda, or is there a simpler way to do it that I've missed?
I found a workaround using Lambda function aliases. Given the context object, I can get the invoked_function_arn property, which has the alias (if any) at the end.
arn_string = context.invoked_function_arn
alias = arn_string.split(':')[-1]
Then I just use the alias as an index into a dict in my config.py module, and I'm good to go.
config[alias].host
config[alias].database
One thing I'm not crazy about is that I have to invoke my function from an alias every time, and now I can't use aliases for any other purpose without affecting this scheme. It would be nice to have explicit support for user parameters in the context object.