Possibly the most dangerous thing I’ve ever written.
This is python3.6 code that is intended to run inside of AWS Lambda. The Lambda Function is intended to be used as a CloudFormation Custom Resource. This Custom Resource takes properties that describe a set of commands to run. It allows you to look up data from the event source, response objects returned by other commands, modify lookups to cast to int
or str
, and also interpolate random strings.
GitHub: https://github.com/dejonghe/cfn-boto-interface
I often find myself writing custom resources for things that either are not yet supported by CloudFormation, or for things that seem like they will never be supported, like a lookup. Often these custom resources are just a few boto3 calls that create, update, and delete a given resource. I has started to look at spot fleets for running ECS clusters after the pricing model changed in March 2018. The EC2 Launch Template was not supported at that time and neither was the ability to use EC2 Launch Templates with a Spot Fleet built by CloudFormation. With that challenge I figured I had a couple custom resources to build and I thought about how I could abstract that as much as possible, and I think I found it. I decided to build a direct interface to boto3 through CloudFormation.
Overall, I wrote this to aid in things such as quick lookups or utilizing new features and services before they get CloudFormation Support. It’s already working out for me. I tested it on Secrets Manager with in a week of the service being available and I’m able to retrieve secrets.
The GitHub Repo has more of a full rundown that includes building the zip, creating the Lambda Resource in CloudFormation, etc. You can use the pre built release package directly, as it’s a Zip file containing the code, cfn-response, and boto3==1.7.4. Here I’d just like to add an example and the high level documentation of what this object is made up of.
Note: Use GetAtt for secrets so that it doesn’t show up in CFN console as PhysicalResourceId
SecretString:
Type: Custom::FetchSecret
Properties:
# Reference to the Lambda Function that gets called
ServiceToken: !GetAtt 'BotoInterface.Arn'
# When a create event type is send to the lambda use this object
Create:
PhysicalResourceId: '!Create[0].VersionId' # Lookup and return secret when Ref'd
ResponseData: # Key,Value pairs for GetAtt to use
Secret: '!Create[0].SecretString'
Commands:
- Client: secretsmanager # Boto3 client to use
Method: get_secret_value # method to call on the boto3.client('secretsmanager') object
Arguments: # Keyword Arguments to pass to the method
SecretId: !Ref 'SecretId'
Update:
Replace: 'True' # If an update is called just run create again,
# CFN will send a delete on cleanup
Lookups are denoted with a !
prefix. The lookups traverse dict objects by use of .
notation
!event
: Looks up a value in the event passed to the lambda from CloudFormation!Create[]
: (Create ActionObject Only) - Looks up a value from the return of the command at that index ran in the Create ActionObject!Update[]
: (Update ActionObject Only) - Looks up a value from the return of the command at that index ran in the Update ActionObject!Delete[]
: (Delete ActionObject Only) - Looks up a value from the return of the command at that index ran in the Delete ActionObjectIf a lookup returns a value in a type that you need to cast you can use the modifiers after the lookup notation.
!int
: Cast lookup to int!str
: Cast lookup to str!random
: Interpolates a random 4 Alpha Numeric stringI was serious when I said this was dangerous, but then again it’s kind of cool. I hope others find use in it. If you have issues comment here or make an issue in GitHub. I’ll try to help out when I can.