-- Leo's gemini proxy
-- Connecting to capsule.adrianhesketh.com:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/gemini; charset=utf-8
Prior to the first release of a project, alongside internal security testing, and security tooling carried out as part of CI/CD, it's common to have a pentest carried out by a 3rd party security team to check over everything.
Security teams often request the CloudFormation templates used to create infrastructure, since having good documentation gives them the best chance to find issues.
In my latest project, there are 21 CDK projects that make up the overall solution, so when I was asked, my first thought was to check out each of the projects, and run the `cdk synth` to export the raw CloudFormation, but that would take a relatively long time, so I picked up the AWS CLI instead.
aws cloudformation list-stacks --output=json | jq --raw-output '.StackSummaries | map(.StackName) | .[]' | xargs -L 1 ./get.sh
Create a `get.sh` file with the following contents.
aws cloudformation get-template --output=json --stack-name=$1 | jq --raw-output ".TemplateBody | ." > $1.yaml
Log in to your AWS account on the command line, and run `./run.sh`.
The AWS CLI allows us to get a single stack contents but doesn't output the raw template contents, so the output needs to be tidied up.
aws cloudformation get-template --output=json --stack-name=myteststack`
Outputting JSON enables the output to be piped into the `jq` command line tool [0] which can execute filter and transform operations on JSON to extract just the required information.
I have my CLI set to output JSON by default [1] to enable scripting, but you can add the `--output=json` flag to any AWS CLI command to force it.
`jq` can filter the result to only output the contents of the `TemplateBody` field and produce plain text output (using the `--raw-output` parameter).
The built-in shell operator `>` can write the output to a file.
Adding the command to a `get.sh` file, provides a resuable script that takes a positional parameter (`$1`) for the stack name, e.g. `./get.sh mytesttemplate`:
aws cloudformation get-template --output=json --stack-name=$1 | jq --raw-output ".TemplateBody | ." > $1.yaml
Listing the stacks is straightforward.
aws cloudformation list-stacks --output=json
But as per the docs [1], the output looks something like this:
{ "StackSummaries": [ { "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/myteststack/466df9e0-0dff-08e3-8e2f-5088487c4896", "TemplateDescription": "AWS CloudFormation Sample Template S3_Bucket: Sample template showing how to create a publicly accessible S3 bucket. **WARNING** This template creates an S3 bucket. You will be billed for the AWS resources used if you create a stack from this template.", "StackStatusReason": null, "CreationTime": "2013-08-26T03:27:10.190Z", "StackName": "myteststack", "StackStatus": "CREATE_COMPLETE" } ] }
Since only the stack name is required, `jq` can be used to filter the output and output all the stack names, one on each line.
aws cloudformation list-stacks --output json | jq --raw-output '.StackSummaries | map(.StackName) | .[]'
Breaking down the `jq` parameters:
`'.StackSummaries | map(.StackName)'` gets the contents of the `StackSummaries` field, and then extracts the value of each `StackName` field into an output array.
`.[]` outputs the array.
`--raw-output` means that `jq` outputs plain text instead of JSON.
The output looks something like this:
myteststack stack2
To execute the `./get.sh` script for every stack, the output of the `list-stacks` command gets piped into the `xargs` command.
The `-L 1` parameter of `xargs` tells `xargs` that each line of the input should be used to execute the `./get.sh` script with the contents of the input line as a parameter.
aws cloudformation list-stacks| jq --raw-output '.StackSummaries | map(.StackName) | .[]' | xargs -L 1 ./get.sh
-- Response ended
-- Page fetched on Sat Apr 27 19:13:30 2024