This folder contains several helper scripts for automatically building deployable, versioned artifacts of your apps:
build-docker-image: This script is meant to be run from a CI job to automatically build a Docker image. It builds
the Docker image, tags the image with the sha1 of the most recent Git commit (or a custom tag, if specified), and then
pushes the new image to a Docker registry (unless the same image/tag already exists in the registry!).
build-packer-artifact: This script can be used to automatically build an artifact, such as an AMI, defined in a
Packer template. It runs a Packer build, runs an optional test command to verify the new artifact, extracts the
artifact ID from the build, and writes the ID to a properties file. This script is meant to be run from a CI job and
the properties file is a convenient way to pass information about the new artifact to another CI job.
After a successful build, the build helper scripts write the versions of the artifacts they produce to a properties
file. The idea is that this properties file is easy to use in other scripts or other CI jobs to deploy those artifacts.
The build-docker-image script adds an entry to the properties file of the form:
DOCKER_IMAGE_TAG=<IMAGE_TAG>
The build-packer-artifact script adds an entry to the properties file of the form:
ARTIFACT_ID=<ARTIFACT_ID>
Examples
The examples below should give you an idea of how to use the scripts. Run the scripts above with the --help flag to
see full documentation.
Imagine you have a Packer template templates/build.json that specifies how to build an AMI for your app. You could
set up automatic deployment for this app using two Jenkins CI jobs: build-app and deploy-app.
The build-app CI job would first use the build-packer-artifact script to automatically build your AMI:
Next, the build-app CI job would then pass the contents of artifacts.properties directly to the deploy-app CI
job using the Jenkins Parameterized Trigger
Plugin. The deploy-app CI job, in turn,
would be a parameterized build that takes as input
a parameter called ARTIFACT_ID (the same parameter name that's in the artifacts.properties file) and use it, along
with the scripts in the terraform-helpers module to automatically deploy the new AMI:
cd templates
terraform-update-variable --name rails_app_version --value $ARTIFACT_ID
terragrunt apply
Remote packer templates
build-packer-artifact also supports building Packer templates that are stored in a git repository. When a path of the
format git::GIT_REPO_URL//RELATIVE_PATH_TO_PACKER_TEMPLATE?ref=GIT_REF is passed in as the Packer template path,
build-packer-artifact will clone the repository GIT_REPO_URL at the ref GIT_REF to a temporary directory and build
the template referenced at RELATIVE_PATH_TO_PACKER_TEMPLATE in the git repository.
For example, to build the example jenkins Packer template in this repo on the release tag v0.19.0:
build-packer-artifact supports a way to load authentication credentials to access private remote git repositories via
SSH or HTTPS. Both mechanisms assume the authentication credentials are stored in AWS Secrets
Manager.
For SSH, build-packer-artifact can load a password-less SSH private key from AWS Secrets Manager:
Create a new AWS Secrets Manager entry containing the full private SSH key. You can create it using the awscli:
Record the ARN of the Secrets Manager entry you created in step 2.
Pass the ARN of the Secrets Manager entry to build-packer-artifact using the input option
--github-token-secrets-manager-arn, --gitlab-token-secrets-manager-arn, or
--bitbucket-token-secrets-manager-arn, depending on which VCS platform you are using. Note that for BitBucket, you
must also provide --bitbucket-username, which corresponds to the username of the BitBucket account the personal
access token is for.
Idempotent packer templates
For AWS AMI builds that use tags, build-packer-artifact supports idempotent builds by AMI tags. When you pass in
--idempotent true to the script, build-packer-artifact will only build a new AMI if an existing AMI with the same
tags and similar name does not already exist in your account. A name is similar if everything matches except for unique
build time identifiers (e.g., that returned by isotime, uuid, or timestamp).
Note that we include the name in the idempotency check to ensure that you can preserve AMIs in a multiaccount scenario,
where AMI tags are not visible in the target account.
For example, consider the following packer template:
{
"variables": {
"aws_region": "us-east-1",
"tag": ""
},
"builders": [{
"ami_name": "gruntwork-test-{{user `tag`}}-packer-build-{{uuid | clean_resource_name}}",
"ami_description": "An AMI created as part of testing the build-packer-artifact script.",
"instance_type": "t2.micro",
"region": "{{user `aws_region`}}",
"type": "amazon-ebs",
"source_ami": "ami-fce3c696",
"ssh_username": "ubuntu",
"tags": {
"tag": "{{user `tag`}}"
}
}],
"provisioners": [{
"type": "shell",
"inline": [
"echo 'Hello, World'"
]
}]
}
If you pass in the --idempotent true and --var tag=v1, then this will only build the AMI if it does not find any AMI
with the tag tag=v1 and name that begins with gruntwork-test-v1-packer-build- in the us-east-1 region.
Under the hood the tags, names, and regions are computed at runtime using packer console with the provided vars.
Note that the following conditions must be true in order to use this feature:
Packer version is at least v1.4.2 (version that introduced packer console).
Templates are in json format (you can not use HCL based templates at this time).
Build is for an AMI (builder type amazon-ebs).
Builder is configured to tag the AMIs (tags is set).
Questions? Ask away.
We're here to talk about our services, answer any questions, give advice, or just to chat.
{"treedata":{"name":"root","toggled":true,"children":[{"name":".circleci","children":[{"name":"config.yml","path":".circleci/config.yml","sha":"0ebc3336efb41b6ce069e48f39a8cbaed6beaa3b"},{"name":"post-upgrade-test-results.sh","path":".circleci/post-upgrade-test-results.sh","sha":"a4867e8fbdc334b7a90259568ee41ea577fbe764"},{"name":"set-upgrade-test-vars.sh","path":".circleci/set-upgrade-test-vars.sh","sha":"8d961461f09584ccd42432b5d56d64db43da1a6e"}]},{"name":".github","children":[{"name":"ISSUE_TEMPLATE","children":[{"name":"bug_report.md","path":".github/ISSUE_TEMPLATE/bug_report.md","sha":"d2e87e27c601e423865ed660ec697082470ca60f"},{"name":"feature_request.md","path":".github/ISSUE_TEMPLATE/feature_request.md","sha":"023a33099be2336476930c96e17ff1ba5dc55348"}]},{"name":"pull_request_template.md","path":".github/pull_request_template.md","sha":"6b100e40e323b5b07f40ed30616277c51c9f4b9e"}]},{"name":".gitignore","path":".gitignore","sha":"a05d4940aa35b39d0d4e0aa438d2072335a22162"},{"name":".pre-commit-config.yaml","path":".pre-commit-config.yaml","sha":"124016cb71af4e0c43dfa761466f7e739c6fbea1"},{"name":"CODEOWNERS","path":"CODEOWNERS","sha":"821a961d9a4c9b551211b85f2e4978171a5b1ac3"},{"name":"LICENSE.txt","path":"LICENSE.txt","sha":"f4e3d9bd4717a044ed31ad847a300eee74371a78"},{"name":"README-CircleCI.adoc","path":"README-CircleCI.adoc","sha":"1c9f44dd12bd8f6d56705855d5ca30ddf2280af4"},{"name":"README-Jenkins.adoc","path":"README-Jenkins.adoc","sha":"2ce110c0ecfbb76d5920662f63f54c7b05589c06"},{"name":"README-Terraform-Terragrunt-Pipeline.adoc","path":"README-Terraform-Terragrunt-Pipeline.adoc","sha":"239358d991bab66d24ef0f74ddec359a1fab4d9a"},{"name":"README.adoc","path":"README.adoc","sha":"8af248131430f9a9704190703fbd3bc8e9c01674"},{"name":"_ci","children":[{"name":"output-debug-values.sh","path":"_ci/output-debug-values.sh","sha":"fa613638c76c2031b1427c2daa1d66f71fedad68"}]},{"name":"_docs","children":[{"name":"circleci-cicd-architecture.png","path":"_docs/circleci-cicd-architecture.png","sha":"06f8a55b7c123b6e589333a1ff1c3d90c43222d6"},{"name":"circleci-icon.png","path":"_docs/circleci-icon.png","sha":"d4e8df17858e6f230ff9e8d90ea388b3ff340b79"},{"name":"jenkins-architecture.png","path":"_docs/jenkins-architecture.png","sha":"a35a534eb7f13547e232635262d6b1c1506e9230"},{"name":"jenkins-icon.png","path":"_docs/jenkins-icon.png","sha":"cfb474486acb167b655c22a400fe5cc999959164"},{"name":"terraform-icon.png","path":"_docs/terraform-icon.png","sha":"85602f11c76fd989788112ba40c08d979ddb1164"},{"name":"tftg-pipeline-architecture.png","path":"_docs/tftg-pipeline-architecture.png","sha":"c016f9263be935e933db832e6e5047d6d656339c"},{"name":"travisci-cicd-architecture.png","path":"_docs/travisci-cicd-architecture.png","sha":"c7da609e901de1baba84918893af9016c3da78ed"},{"name":"travisci-icon.png","path":"_docs/travisci-icon.png","sha":"57116e900f797c8f35399929fb3a24b2cf0e7181"}]},{"name":"examples","children":[{"name":"ecs-deploy-runner","children":[{"name":"README.md","path":"examples/ecs-deploy-runner/README.md","sha":"278a6974ddf0e9a95f28b62269250ffffa54665b"},{"name":"cross-account-ecr-repo","children":[{"name":"main.tf","path":"examples/ecs-deploy-runner/cross-account-ecr-repo/main.tf","sha":"05bdcaece513603609a402b5447c305bf5bea736"},{"name":"outputs.tf","path":"examples/ecs-deploy-runner/cross-account-ecr-repo/outputs.tf","sha":"7ceaa57f462257bc0aa953b08b8fee6fd0370f83"},{"name":"variables.tf","path":"examples/ecs-deploy-runner/cross-account-ecr-repo/variables.tf","sha":"27a1d304429a8284815697b0cfb880e4e14963af"}]},{"name":"main.tf","path":"examples/ecs-deploy-runner/main.tf","sha":"b5fcfb3fe805a9775f650e63afb255232203de86"},{"name":"outputs.tf","path":"examples/ecs-deploy-runner/outputs.tf","sha":"0d39cc204673e2e92f0b6e32464ac8080fda59c2"},{"name":"variables.tf","path":"examples/ecs-deploy-runner/variables.tf","sha":"561bf2ff7fa8e8f56daefe0d22743e9d56a10463"}]},{"name":"iam-policies","children":[{"name":"README.md","path":"examples/iam-policies/README.md","sha":"4eaf42cd7b4bc254ac9aabfaa8d6c1b4b8cc4281"},{"name":"main.tf","path":"examples/iam-policies/main.tf","sha":"5283a4908432753a7923d70a1b6c72cc0ac41a04"},{"name":"vars.tf","path":"examples/iam-policies/vars.tf","sha":"aa595b933deb3717ff196517ecb6c2714b2d9afe"}]},{"name":"jenkins","children":[{"name":"README.md","path":"examples/jenkins/README.md","sha":"87fae26e2ab6ba6afc1d7245ee7c5ed6797eb0ff"},{"name":"docker-compose.yml","path":"examples/jenkins/docker-compose.yml","sha":"49ea640e3997dbd71e240d88f598b8a7832edffa"},{"name":"main.tf","path":"examples/jenkins/main.tf","sha":"5fc984b3301aaf47570a7cdcd4d75466cd397ee6"},{"name":"mock","children":[{"name":"mock-user-data.sh","path":"examples/jenkins/mock/mock-user-data.sh","sha":"610ba8091622d0bbec4301093b63c4263b5939ae"},{"name":"mount-ebs-volume","path":"examples/jenkins/mock/mount-ebs-volume","sha":"faa2394ad8a7a35657fb7f34b2014c7f05224e56"},{"name":"systemctl","path":"examples/jenkins/mock/systemctl","sha":"c656c65a7fd2b411548adb7865bc9c065333ad2e"}]},{"name":"outputs.tf","path":"examples/jenkins/outputs.tf","sha":"7283c3f6348eaaa5c0ed4c331f051b1bd436dfc6"},{"name":"packer","children":[{"name":"jenkins.json","path":"examples/jenkins/packer/jenkins.json","sha":"7b25f6dc251a3f39bc7123c3735df1f8ced796fe"}]},{"name":"user-data","children":[{"name":"user-data.sh","path":"examples/jenkins/user-data/user-data.sh","sha":"19a4ccc9a1f8cb264d6995473ece0c2b02a0091a"}]},{"name":"vars.tf","path":"examples/jenkins/vars.tf","sha":"0c50a16eb8179ae3e7c6703502376ea298ec7a20"}]}]},{"name":"modules","children":[{"name":"aws-helpers","children":[{"name":"README.md","path":"modules/aws-helpers/README.md","sha":"7ef5360df466b4aad373b73fdb5161aae67a1d61"},{"name":"bin","children":[{"name":"publish-ami","path":"modules/aws-helpers/bin/publish-ami","sha":"1d5697d411cbeed05734fb356ffbf598f7e4eeeb"}]},{"name":"install.sh","path":"modules/aws-helpers/install.sh","sha":"2700711d6a80c6f6c218e4b3d5b1b0cfe4d7609b"}]},{"name":"build-helpers","children":[{"name":"README.md","path":"modules/build-helpers/README.md","sha":"0a91a778414c82bd3d4058df343bb5c2a0d66606","toggled":true},{"name":"bin","children":[{"name":"build-docker-image","path":"modules/build-helpers/bin/build-docker-image","sha":"0fde55c7e1985925fe00d2d399b190ae6b2edd26"},{"name":"build-packer-artifact","path":"modules/build-helpers/bin/build-packer-artifact","sha":"4d5847eb854bdf2040478e1cdaebefc3d97fd63f"}]},{"name":"install.sh","path":"modules/build-helpers/install.sh","sha":"a132488127b88ef8268399a0b3852aa5e0967a20"}],"toggled":true},{"name":"check-url","children":[{"name":"README.md","path":"modules/check-url/README.md","sha":"1c3219738597ddec48ea7f7f25ed480a96df3426"},{"name":"bin","children":[{"name":"check-url","path":"modules/check-url/bin/check-url","sha":"4e6c57dd70e0a385fb23814619dc917b7c527e57"}]},{"name":"install.sh","path":"modules/check-url/install.sh","sha":"488fad728f75e4ffb6d7156b7cc9ee9682d60183"}]},{"name":"circleci-helpers","children":[{"name":"README.md","path":"modules/circleci-helpers/README.md","sha":"2420e2ca38c96ea153dd3e80576138deed3e10b4"},{"name":"bin","children":[{"name":"install-go-version","path":"modules/circleci-helpers/bin/install-go-version","sha":"8a0121205f24358a00af129e729795013ebf7edb"},{"name":"place-repo-in-gopath","path":"modules/circleci-helpers/bin/place-repo-in-gopath","sha":"ac7085bb304a2004050e676bea658f039493fdcc"}]},{"name":"install.sh","path":"modules/circleci-helpers/install.sh","sha":"313288f93c55678a883f53106471a21f8ebbb2fa"}]},{"name":"ec2-backup","children":[{"name":"README.md","path":"modules/ec2-backup/README.md","sha":"1ecd6984bd3014c303db7e4b26db2c7227a8d6ac"},{"name":"backup-lambda-function","children":[{"name":"ec2-snapper_linux_amd64-v0.5.2","path":"modules/ec2-backup/backup-lambda-function/ec2-snapper_linux_amd64-v0.5.2","sha":"11f872d3dde35b44c160eccf3f0adf49cc5bd72f"},{"name":"index.js","path":"modules/ec2-backup/backup-lambda-function/index.js","sha":"555ea9666346d7d57588452a5a9642ff73bcf25e"}]},{"name":"main.tf","path":"modules/ec2-backup/main.tf","sha":"50d24660356bb358ef0a6d56860450332ce32e82"},{"name":"outputs.tf","path":"modules/ec2-backup/outputs.tf","sha":"770744637fcd7cafd98991397b26a82d01524192"},{"name":"vars.tf","path":"modules/ec2-backup/vars.tf","sha":"338b354b5360c49aa52048889e780b5a4000973f"}]},{"name":"ecs-deploy-runner-invoke-iam-policy","children":[{"name":"README.md","path":"modules/ecs-deploy-runner-invoke-iam-policy/README.md","sha":"66152227a29ea201358ced76a359bb5f2cfd7c9d"},{"name":"main.tf","path":"modules/ecs-deploy-runner-invoke-iam-policy/main.tf","sha":"0dd253f2a087fe6c6c97a3da75b6d294b451cd74"},{"name":"outputs.tf","path":"modules/ecs-deploy-runner-invoke-iam-policy/outputs.tf","sha":"8703d962acdf4584375558aa59831c1c873fe606"},{"name":"variables.tf","path":"modules/ecs-deploy-runner-invoke-iam-policy/variables.tf","sha":"566d414f7ef8621e8c7613847d92b77c3e08cbb6"}]},{"name":"ecs-deploy-runner-standard-configuration","children":[{"name":"README.md","path":"modules/ecs-deploy-runner-standard-configuration/README.md","sha":"8d326578110a022e139f19077991009209ff8167"},{"name":"main.tf","path":"modules/ecs-deploy-runner-standard-configuration/main.tf","sha":"affc331d624dfd99309f5a2aa5907ceb78409f97"},{"name":"outputs.tf","path":"modules/ecs-deploy-runner-standard-configuration/outputs.tf","sha":"8a49694a66a0914299b1fb73e4b9576d32ec001e"},{"name":"variables.tf","path":"modules/ecs-deploy-runner-standard-configuration/variables.tf","sha":"bafc91135373552222a57ec07461e0b9c16ba31e"}]},{"name":"ecs-deploy-runner","children":[{"name":"README.adoc","path":"modules/ecs-deploy-runner/README.adoc","sha":"4fb9fc3ea474666f60770457754b95810087c68d"},{"name":"_docs","children":[{"name":"images","children":[{"name":"sequence-diagram.png","path":"modules/ecs-deploy-runner/_docs/images/sequence-diagram.png","sha":"15c9ea45ff27d20fdc23cf918f8c9ba64c0601a3"}]}]},{"name":"core-concepts.md","path":"modules/ecs-deploy-runner/core-concepts.md","sha":"7aac14ed7deec143377c908007b07b29dea2c555"},{"name":"docker","children":[{"name":"deploy-runner","children":[{"name":"Dockerfile","path":"modules/ecs-deploy-runner/docker/deploy-runner/Dockerfile","sha":"f7936306c92e948ae8b64d9f3c9f547d1aadb2bc"},{"name":"install_additional_terraform_versions.sh","path":"modules/ecs-deploy-runner/docker/deploy-runner/install_additional_terraform_versions.sh","sha":"caaa6a422f35a27e4c2335bd3f02d26f9afb915f"},{"name":"known_hosts","path":"modules/ecs-deploy-runner/docker/deploy-runner/known_hosts","sha":"0dd0a16cd905887f363c0ba4effb5e88a8c4afac"}]},{"name":"kaniko","children":[{"name":"Dockerfile","path":"modules/ecs-deploy-runner/docker/kaniko/Dockerfile","sha":"8b1a72762fa3dbaa0cd73e4c979ff0f1d4c3b13d"},{"name":"build_docker_image.go","path":"modules/ecs-deploy-runner/docker/kaniko/build_docker_image.go","sha":"d1f1b94c5c6f63b8d5fc9db627ff25daa32af010"},{"name":"config.json","path":"modules/ecs-deploy-runner/docker/kaniko/config.json","sha":"34798d807bda1825a3285dd3ac4436eab0905d9d"},{"name":"go.mod","path":"modules/ecs-deploy-runner/docker/kaniko/go.mod","sha":"623d5e6d3c4f31113afa5b3f447248462dec275f"},{"name":"go.sum","path":"modules/ecs-deploy-runner/docker/kaniko/go.sum","sha":"913bb7b5e22e485fde74356a79beb0eca32c479d"}]}]},{"name":"entrypoint","children":[{"name":"deploy_runner_entrypoint.go","path":"modules/ecs-deploy-runner/entrypoint/deploy_runner_entrypoint.go","sha":"1642d8d8991a98dcbb3013408a96fdda7fa165da"},{"name":"go.mod","path":"modules/ecs-deploy-runner/entrypoint/go.mod","sha":"bc99dd9708ad25422cf8c242947a0754a7708273"},{"name":"go.sum","path":"modules/ecs-deploy-runner/entrypoint/go.sum","sha":"00c65fdc0736f9cfcbd08b0d6fb228729720f897"},{"name":"install.sh","path":"modules/ecs-deploy-runner/entrypoint/install.sh","sha":"6c051ab106ae9a19fe56099ae997e9ad68e0bdc6"}]},{"name":"invoker-lambda","children":[{"name":"dev_requirements.txt","path":"modules/ecs-deploy-runner/invoker-lambda/dev_requirements.txt","sha":"930bdb1c56ece3d0a28721bad49c2d9617da104e"},{"name":"invoker","children":[{"name":"__init__.py","path":"modules/ecs-deploy-runner/invoker-lambda/invoker/__init__.py","sha":"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"},{"name":"assertions.py","path":"modules/ecs-deploy-runner/invoker-lambda/invoker/assertions.py","sha":"137be569a1ac38b91f3e5cff5c1545c5f7473a03"},{"name":"exceptions.py","path":"modules/ecs-deploy-runner/invoker-lambda/invoker/exceptions.py","sha":"79c636a4387a02c505213f89348275d58aedd876"},{"name":"index.py","path":"modules/ecs-deploy-runner/invoker-lambda/invoker/index.py","sha":"ee1ad652dd7f4024e56e9c2d21cc669be616be8e"},{"name":"project_logging.py","path":"modules/ecs-deploy-runner/invoker-lambda/invoker/project_logging.py","sha":"b8c1f628fcaebaca7864bb57f6dbfda0375e13c9"},{"name":"types.py","path":"modules/ecs-deploy-runner/invoker-lambda/invoker/types.py","sha":"b812525d7a49aec663224160f0ecc29c15f026af"}]},{"name":"test","children":[{"name":"test_assertions.py","path":"modules/ecs-deploy-runner/invoker-lambda/test/test_assertions.py","sha":"b7cf3ad155f49fe5e631b768b45228a2d513d205"},{"name":"test_invoker.py","path":"modules/ecs-deploy-runner/invoker-lambda/test/test_invoker.py","sha":"c4aafa4a5bcaeafc100f98404236060f5d985e1b"}]}]},{"name":"main.tf","path":"modules/ecs-deploy-runner/main.tf","sha":"9c11c1c5a630df4638309ab77b92ff0fab7d2826"},{"name":"main_ecs.tf","path":"modules/ecs-deploy-runner/main_ecs.tf","sha":"a4058042dae2578615563187b0552f6c918286e2"},{"name":"main_lambda.tf","path":"modules/ecs-deploy-runner/main_lambda.tf","sha":"a83a1013e3f9664d58df5137b0a03b65e7bd75b0"},{"name":"outputs.tf","path":"modules/ecs-deploy-runner/outputs.tf","sha":"d2d2a93a57ee8a130ed1a7c6c8b1e3781812291f"},{"name":"variables.tf","path":"modules/ecs-deploy-runner/variables.tf","sha":"fcb603359cfeddd0b070ba49e0908dda8f2a73a3"}]},{"name":"git-helpers","children":[{"name":"README.md","path":"modules/git-helpers/README.md","sha":"8749b21dba677a1e2a1d2fd56af962e5ce8a8fc5"},{"name":"bin","children":[{"name":"git-add-commit-push","path":"modules/git-helpers/bin/git-add-commit-push","sha":"f270bc4da9ee46bc7754c0ce8bf8f5a6c6c38c0a"},{"name":"git-rebase","path":"modules/git-helpers/bin/git-rebase","sha":"887dd90ba36adc5c0362cbadca79707756535b24"}]},{"name":"install.sh","path":"modules/git-helpers/install.sh","sha":"f22ede2f2f085af0a065dd43ed1279c69dd5a00b"}]},{"name":"gruntwork-module-circleci-helpers","children":[{"name":"README.md","path":"modules/gruntwork-module-circleci-helpers/README.md","sha":"58f715757b3ff6ce5cdc56167de773db71cbaeb1"},{"name":"bin","children":[{"name":"build-go-binaries","path":"modules/gruntwork-module-circleci-helpers/bin/build-go-binaries","sha":"56c7085e0b63eb293d170ac64377df0ba403d23a"},{"name":"configure-environment-for-gruntwork-module","path":"modules/gruntwork-module-circleci-helpers/bin/configure-environment-for-gruntwork-module","sha":"c268a8eac4f13e83b968d7b4abf5e30726232041"},{"name":"run-go-tests","path":"modules/gruntwork-module-circleci-helpers/bin/run-go-tests","sha":"865947418d3dbcac18d644c5566a63d0ed02c3ca"},{"name":"upload-github-release-assets","path":"modules/gruntwork-module-circleci-helpers/bin/upload-github-release-assets","sha":"5091c9118c1b931079ac3c112e2b54577b52194a"}]},{"name":"install.sh","path":"modules/gruntwork-module-circleci-helpers/install.sh","sha":"a9cc16c05a73cf6c40097608838758a8de64daab"}]},{"name":"iam-policies","children":[{"name":"README.md","path":"modules/iam-policies/README.md","sha":"da28056c8749bc2c46dc99ed16286e14b7c55511"},{"name":"ecr-docker-push","children":[{"name":"README.md","path":"modules/iam-policies/ecr-docker-push/README.md","sha":"6a0a84157fe573f2c7ac1e0bd6e2169b2814f1c0"},{"name":"main.tf","path":"modules/iam-policies/ecr-docker-push/main.tf","sha":"dfb81637389b3b0df7c3606f08c0f9f2412d9564"},{"name":"outputs.tf","path":"modules/iam-policies/ecr-docker-push/outputs.tf","sha":"ba3177631efce4651f499e457cc9aa0b1afb338d"},{"name":"vars.tf","path":"modules/iam-policies/ecr-docker-push/vars.tf","sha":"d8aaeae6205750961e3deaba741ee68180a32ca7"}]},{"name":"ecs-service-deployment","children":[{"name":"README.md","path":"modules/iam-policies/ecs-service-deployment/README.md","sha":"77bb1df6db0d7cd886ee665f1d6566944521b00c"},{"name":"main.tf","path":"modules/iam-policies/ecs-service-deployment/main.tf","sha":"f0b19d1e6144de1d9e42a272e58c632270494282"},{"name":"outputs.tf","path":"modules/iam-policies/ecs-service-deployment/outputs.tf","sha":"4bf7dcb16326113889318ee8da63dadfc1ce35e9"},{"name":"vars.tf","path":"modules/iam-policies/ecs-service-deployment/vars.tf","sha":"78c2705c906b853f0e4b18b6c8f2c05e164109bf"}]},{"name":"terraform-remote-state-s3","children":[{"name":"README.md","path":"modules/iam-policies/terraform-remote-state-s3/README.md","sha":"b7892fd6dabecb66909706dbb8941e63e2c56954"},{"name":"main.tf","path":"modules/iam-policies/terraform-remote-state-s3/main.tf","sha":"68a33aa21793e3d955a2023c9192d3db68c0ad8c"},{"name":"outputs.tf","path":"modules/iam-policies/terraform-remote-state-s3/outputs.tf","sha":"07cabd2ad56a78ea31e2bf59f1cb938fdf3b9664"},{"name":"vars.tf","path":"modules/iam-policies/terraform-remote-state-s3/vars.tf","sha":"720084fa0f9cf944b19ba30f59e78efc2e7b96b9"}]},{"name":"terragrunt","children":[{"name":"README.md","path":"modules/iam-policies/terragrunt/README.md","sha":"eccacc553810fa838b23d24718ffb8f70a9a39e5"},{"name":"main.tf","path":"modules/iam-policies/terragrunt/main.tf","sha":"cf3fc37b73963231551d075e5c4db8f2717d8d2a"},{"name":"outputs.tf","path":"modules/iam-policies/terragrunt/outputs.tf","sha":"2b948005b8a1322f408e999226ac7b66629759cd"},{"name":"vars.tf","path":"modules/iam-policies/terragrunt/vars.tf","sha":"52f0480b69a57d0ff43e76ae26cb17d77dab5069"}]}]},{"name":"infrastructure-deploy-script","children":[{"name":"README.adoc","path":"modules/infrastructure-deploy-script/README.adoc","sha":"38c5f953a2f97251277650d9d7f44a5e2de5a26c"},{"name":"core-concepts.md","path":"modules/infrastructure-deploy-script/core-concepts.md","sha":"e617d3dd528507993c8162811cb6de16328e9ed3"},{"name":"dev_requirements.txt","path":"modules/infrastructure-deploy-script/dev_requirements.txt","sha":"d4f836eb792be30da6183eaba4bb57f15ffb2f54"},{"name":"infrastructure_deploy_script","children":[{"name":"__init__.py","path":"modules/infrastructure-deploy-script/infrastructure_deploy_script/__init__.py","sha":"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"},{"name":"deploy.py","path":"modules/infrastructure-deploy-script/infrastructure_deploy_script/deploy.py","sha":"e57442678159d362329d76f58a7c7772cf0bee61"},{"name":"exceptions.py","path":"modules/infrastructure-deploy-script/infrastructure_deploy_script/exceptions.py","sha":"dd014c3f9240871277b5a47a72ee3b4baf538e6f"},{"name":"git.py","path":"modules/infrastructure-deploy-script/infrastructure_deploy_script/git.py","sha":"f32161d4b04482cbe659fb18b3a1437426ef7bff"},{"name":"project_logging.py","path":"modules/infrastructure-deploy-script/infrastructure_deploy_script/project_logging.py","sha":"a72c2a19962838f3375390380dcf444b73e1fcef"},{"name":"py.typed","path":"modules/infrastructure-deploy-script/infrastructure_deploy_script/py.typed","sha":"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"},{"name":"shell.py","path":"modules/infrastructure-deploy-script/infrastructure_deploy_script/shell.py","sha":"6365772d88619273883ae02a047c6e98d8955232"},{"name":"ssh.py","path":"modules/infrastructure-deploy-script/infrastructure_deploy_script/ssh.py","sha":"fa487859e84a655cf69e8646b8d44b29b2e10886"},{"name":"terra.py","path":"modules/infrastructure-deploy-script/infrastructure_deploy_script/terra.py","sha":"ee87678613921c3f11326c385b6edf8dbd216728"}]},{"name":"install.sh","path":"modules/infrastructure-deploy-script/install.sh","sha":"5dedba5c0d9c29dee1a5f625d502dccc15c5dc36"},{"name":"requirements.txt","path":"modules/infrastructure-deploy-script/requirements.txt","sha":"6b0824aebda5ba488c0e8bb4691ff343c0a2fcfc"},{"name":"scripts","children":[{"name":"infrastructure-deploy-script","path":"modules/infrastructure-deploy-script/scripts/infrastructure-deploy-script","sha":"9adf313562163a979dbe428afd13965622ffb361"}]},{"name":"setup.py","path":"modules/infrastructure-deploy-script/setup.py","sha":"b9399339fe7f52763c705ac83252ab7f3a9fc047"},{"name":"test","children":[{"name":"conftest.py","path":"modules/infrastructure-deploy-script/test/conftest.py","sha":"ebae81d85a6ce4997c9d6f4a3d833c617822a0a0"},{"name":"fixtures","children":[{"name":"terraform-with-inputs","children":[{"name":"input.tfvars","path":"modules/infrastructure-deploy-script/test/fixtures/terraform-with-inputs/input.tfvars","sha":"68468ad2c7a8319c2674d572eec058660cddfdfb"},{"name":"main.tf","path":"modules/infrastructure-deploy-script/test/fixtures/terraform-with-inputs/main.tf","sha":"0a16a02487d5bf436440bff9b0fb1f6372a284e2"},{"name":"terragrunt.hcl","path":"modules/infrastructure-deploy-script/test/fixtures/terraform-with-inputs/terragrunt.hcl","sha":"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"}]},{"name":"terraform","children":[{"name":"main.tf","path":"modules/infrastructure-deploy-script/test/fixtures/terraform/main.tf","sha":"e370f64097c82bca28d07e7b12befda38aa3df04"}]},{"name":"terragrunt","children":[{"name":"terragrunt.hcl","path":"modules/infrastructure-deploy-script/test/fixtures/terragrunt/terragrunt.hcl","sha":"2359b15bc6a698e481a8e99fdb352e174203b925"}]}]},{"name":"test_deploy.py","path":"modules/infrastructure-deploy-script/test/test_deploy.py","sha":"45b981d9c2b84f9aa9cdcdbd28f6316a6a785bab"},{"name":"test_git.py","path":"modules/infrastructure-deploy-script/test/test_git.py","sha":"ea5b0e3d0243ffb270d3802368a493587ce5db9f"},{"name":"test_shell.py","path":"modules/infrastructure-deploy-script/test/test_shell.py","sha":"fa0f984c4ce6218303d7f9c38c4c20d018411318"}]}]},{"name":"infrastructure-deployer","children":[{"name":"README.adoc","path":"modules/infrastructure-deployer/README.adoc","sha":"330baa82f653a9112314f097dab665cbba94c7c9"},{"name":"core-concepts.md","path":"modules/infrastructure-deployer/core-concepts.md","sha":"917f3e952686f26ba67c758216404860aabd60fc"},{"name":"deploy","children":[{"name":"aws.go","path":"modules/infrastructure-deployer/deploy/aws.go","sha":"ed547b9e816adac716b3731f0b66a428d5920fd8"},{"name":"aws_ecs.go","path":"modules/infrastructure-deployer/deploy/aws_ecs.go","sha":"c5572b0f84d7fb372b9fc9562585e18c6d8642ab"},{"name":"deploy.go","path":"modules/infrastructure-deployer/deploy/deploy.go","sha":"a4dcbc57ecaf83a0936062bbd458ec621bef3c18"},{"name":"errors.go","path":"modules/infrastructure-deployer/deploy/errors.go","sha":"21cc411d97b50e4a0e4aaa2bea42f9db591ad1a7"}]},{"name":"go.mod","path":"modules/infrastructure-deployer/go.mod","sha":"72455cd3d9e872ba3c9892ac48749a295b30924e"},{"name":"go.sum","path":"modules/infrastructure-deployer/go.sum","sha":"1fc1ee88c241493241b0612bef7cb74dba4dc7b6"},{"name":"logging","children":[{"name":"logging.go","path":"modules/infrastructure-deployer/logging/logging.go","sha":"9a7a2aa73ff0dc233bdb5b48ddfdf1d14423f011"}]},{"name":"main.go","path":"modules/infrastructure-deployer/main.go","sha":"3060e42a51789c078b1ba5249097c40cd7bf17ac"},{"name":"revshlex","children":[{"name":"revshlex.go","path":"modules/infrastructure-deployer/revshlex/revshlex.go","sha":"965c2960b0d9a0ba56e7b1868def546556167c3b"},{"name":"revshlex_test.go","path":"modules/infrastructure-deployer/revshlex/revshlex_test.go","sha":"f25d5a84e38fb4fb44930477ad999431f323f5d7"}]}]},{"name":"install-jenkins","children":[{"name":"README.md","path":"modules/install-jenkins/README.md","sha":"6d2b4a045192ce1ea32764520b6e0c6cf2627109"},{"name":"install.sh","path":"modules/install-jenkins/install.sh","sha":"d65a63c8a1c487957eb0ecc84413eced499b8732"},{"name":"run-jenkins","path":"modules/install-jenkins/run-jenkins","sha":"491b104ddedf7406e288504417bfe5f26378a961"}]},{"name":"jenkins-server","children":[{"name":"README.md","path":"modules/jenkins-server/README.md","sha":"6c209c9b27bc55215147413d6419c88243d1ebdc"},{"name":"main.tf","path":"modules/jenkins-server/main.tf","sha":"5b3823f5e8c7c6bce7fac91a0662b1eb54c35af7"},{"name":"outputs.tf","path":"modules/jenkins-server/outputs.tf","sha":"5670bd47368b97e5eeedcca805a28a337230e9e2"},{"name":"vars.tf","path":"modules/jenkins-server/vars.tf","sha":"dd471a5eb74afa3bac20b8655e8b7162c4cd0b34"}]},{"name":"kubernetes-circleci-helpers","children":[{"name":"README.md","path":"modules/kubernetes-circleci-helpers/README.md","sha":"b2f09495ef47eae1ef33db52a7d8d4ca5c712393"},{"name":"bin","children":[{"name":"setup-minikube","path":"modules/kubernetes-circleci-helpers/bin/setup-minikube","sha":"74aa11d9e0a667d29deeb1121eabb2c3889414d3"}]},{"name":"install.sh","path":"modules/kubernetes-circleci-helpers/install.sh","sha":"2ae8670872407ceb2c434dc5b117ae14073af9a2"}]},{"name":"monorepo-helpers","children":[{"name":"README.adoc","path":"modules/monorepo-helpers/README.adoc","sha":"256693033af8ecfbd050f4bd9df26a276690f74a"},{"name":"core-concepts.md","path":"modules/monorepo-helpers/core-concepts.md","sha":"72e71c8f84ec1cdd0f902c158f8ec9a3c57a5cb0"},{"name":"dev_requirements.txt","path":"modules/monorepo-helpers/dev_requirements.txt","sha":"16bf6a0c4c62e5dc99b4bf19d24cfbc608c00986"},{"name":"find_tf_monorepo_tests","children":[{"name":"__init__.py","path":"modules/monorepo-helpers/find_tf_monorepo_tests/__init__.py","sha":"9c0fa90a19fd599d0fe6668b8f3b75d6c368c500"},{"name":"config.py","path":"modules/monorepo-helpers/find_tf_monorepo_tests/config.py","sha":"ef562e400aaf6ee7c7c8081f81256e53c15fc646"},{"name":"constants.py","path":"modules/monorepo-helpers/find_tf_monorepo_tests/constants.py","sha":"b0dc5f3563e282b55a0cc544430fb0bdac5f0683"},{"name":"exceptions.py","path":"modules/monorepo-helpers/find_tf_monorepo_tests/exceptions.py","sha":"925b112424dd2d891cd1045f43901c1820ef042a"},{"name":"external_proc.py","path":"modules/monorepo-helpers/find_tf_monorepo_tests/external_proc.py","sha":"143c14cdac0b13d3b18cfd252376cc067bfc5b8e"},{"name":"string_helpers.py","path":"modules/monorepo-helpers/find_tf_monorepo_tests/string_helpers.py","sha":"01b5782d82cedd8e6ef59677c65b5fbb4c9c10b5"},{"name":"test_search.py","path":"modules/monorepo-helpers/find_tf_monorepo_tests/test_search.py","sha":"f0d9e8d34fb771fc6c57425258ad6a7c502b1b78"},{"name":"types.py","path":"modules/monorepo-helpers/find_tf_monorepo_tests/types.py","sha":"4965ca64f73effea4b3513b19178ba1c4d17617a"}]},{"name":"install.sh","path":"modules/monorepo-helpers/install.sh","sha":"137a9f21edef3d27f297b79bb0fd29b616e0668f"},{"name":"requirements.txt","path":"modules/monorepo-helpers/requirements.txt","sha":"4c2278c3d405ace58f9d18c0712e9599ac6728f4"},{"name":"scripts","children":[{"name":"find-tf-monorepo-tests","path":"modules/monorepo-helpers/scripts/find-tf-monorepo-tests","sha":"32b2e574048ca7b696bbb09bdd455928ac42c702"},{"name":"validate-monorepo-test-mappings","path":"modules/monorepo-helpers/scripts/validate-monorepo-test-mappings","sha":"4659a35f387c526edc65a3ac6a5c37cc53c5a605"}]},{"name":"setup.py","path":"modules/monorepo-helpers/setup.py","sha":"4bb9f6c04574e4ad735e856859ea20a455a2a64d"},{"name":"test","children":[{"name":"test_search.py","path":"modules/monorepo-helpers/test/test_search.py","sha":"b1b8ddcd197636fd2c596af0f215e94816aac5c0"},{"name":"test_string_helpers.py","path":"modules/monorepo-helpers/test/test_string_helpers.py","sha":"a6be235dd2a75c69056bf5a269060d50947e9049"}]}]},{"name":"sign-binary-helpers","children":[{"name":"README.md","path":"modules/sign-binary-helpers/README.md","sha":"8d9923652c25a08e7c6545e312f06c9623b3814a"},{"name":"bin","children":[{"name":"sign-binary","path":"modules/sign-binary-helpers/bin/sign-binary","sha":"ae2a67b880cb6d616d96481f1d315b94d0102d2c"}]},{"name":"install.sh","path":"modules/sign-binary-helpers/install.sh","sha":"935c76c7b9891d683ce77954aa7615990c1d1fa6"}]},{"name":"terraform-helpers","children":[{"name":"README.md","path":"modules/terraform-helpers/README.md","sha":"3436693398bf5e77a117313d0646c594441a80aa"},{"name":"bin","children":[{"name":"git-updated-files","path":"modules/terraform-helpers/bin/git-updated-files","sha":"2790734b850e0fb7792b2f4b133f9b7815164f89"},{"name":"git-updated-folders","path":"modules/terraform-helpers/bin/git-updated-folders","sha":"e3f61e873f16b47ee02e691bce65232f7299879e"},{"name":"terraform-update-variable","path":"modules/terraform-helpers/bin/terraform-update-variable","sha":"43fb7c517d086ca8f27b9846241cdd5e34de013c"}]},{"name":"install.sh","path":"modules/terraform-helpers/install.sh","sha":"3dd11c583323c347e39fc20fb696a742decc033d"}]}],"toggled":true},{"name":"setup.cfg","path":"setup.cfg","sha":"fbb2d6b0a5cc69116979f432a6c390e2110c8277"},{"name":"terraform-cloud-enterprise-private-module-registry-placeholder.tf","path":"terraform-cloud-enterprise-private-module-registry-placeholder.tf","sha":"ae586c0fe830819580e1009d41a9074f16e65bed"},{"name":"test","children":[{"name":"README.md","path":"test/README.md","sha":"0baa161831e9c98eb68f3bd6e3c6d73992859a27"},{"name":"build_docker_image_test.go","path":"test/build_docker_image_test.go","sha":"1c3c70c8e7ae97c05acb50fdfda9c14a2762392e"},{"name":"build_helpers.go","path":"test/build_helpers.go","sha":"0e77ed869d6406431209fdbf405745dce7a7cc26"},{"name":"build_packer_artifact_test.go","path":"test/build_packer_artifact_test.go","sha":"fa5e450e71a3fea7e8b3454cf6f83ae2cf0845f5"},{"name":"build_packer_artifact_unit_test.go","path":"test/build_packer_artifact_unit_test.go","sha":"4bd517a7dca9d2bb6ccce741059e69fd43003cf2"},{"name":"check_url_test.go","path":"test/check_url_test.go","sha":"bd3419bcb07624923777c14619eba17c7edc8c64"},{"name":"destroy_test.go","path":"test/destroy_test.go","sha":"639fa96d47dbbe3431d112b12125c0e03642a09d"},{"name":"destroy_test_helpers.go","path":"test/destroy_test_helpers.go","sha":"ce1a5f2111b0d7162c677d52092c8dba859cc9e9"},{"name":"edr-tests","children":[{"name":"ecs_deploy_runner_docker_test.go","path":"test/edr-tests/ecs_deploy_runner_docker_test.go","sha":"9e8f7d1d86c2a544cb7f3d568ac4f48bfd8ae105"},{"name":"ecs_deploy_runner_ec2_test.go","path":"test/edr-tests/ecs_deploy_runner_ec2_test.go","sha":"99b17f4a1c69cdc25165b18e29497f04afde237d"},{"name":"ecs_deploy_runner_https_test.go","path":"test/edr-tests/ecs_deploy_runner_https_test.go","sha":"757903b53d25d21630675f11d0a8705872d506d9"},{"name":"ecs_deploy_runner_kaniko_test.go","path":"test/edr-tests/ecs_deploy_runner_kaniko_test.go","sha":"5830a89979df9beb08afb93adcecd5a3a8566c23"},{"name":"ecs_deploy_runner_security_test.go","path":"test/edr-tests/ecs_deploy_runner_security_test.go","sha":"f7b7380b113026876c7e74e0b9dfc982c2b1cd30"},{"name":"ecs_deploy_runner_standard_configuration_test.go","path":"test/edr-tests/ecs_deploy_runner_standard_configuration_test.go","sha":"754bba676c7f25653900a8cd9ca3ea231f63a4d9"},{"name":"ecs_deploy_runner_test.go","path":"test/edr-tests/ecs_deploy_runner_test.go","sha":"8eaa21984f797ae3b8308b5895593fc8d709dd26"},{"name":"ecs_deploy_runner_test_helpers.go","path":"test/edr-tests/ecs_deploy_runner_test_helpers.go","sha":"51565e077c0a34d02bc9576ec75154e2fd87ba4c"},{"name":"ecs_deploy_runner_workflow_test.go","path":"test/edr-tests/ecs_deploy_runner_workflow_test.go","sha":"1b347e500078a1fb7e45168a1276bdd9841f0004"},{"name":"infrastructure_deploy_script_test.go","path":"test/edr-tests/infrastructure_deploy_script_test.go","sha":"6e412f9722088350dbe87d5ac95a2d97870c727c"}]},{"name":"edrhelpers","children":[{"name":"edrhelpers.go","path":"test/edrhelpers/edrhelpers.go","sha":"278016e56aa0c886eeb155fb03acc99d8f0ab24f"},{"name":"edrhelpers_test.go","path":"test/edrhelpers/edrhelpers_test.go","sha":"5b34ed2f7d5ea1f8290b354b045554db143069f9"},{"name":"fixtures","children":[{"name":"docker","children":[{"name":"Dockerfile","path":"test/edrhelpers/fixtures/docker/Dockerfile","sha":"09bd120286de9d94d1340b658e66088f6f8ae28b"}]}]},{"name":"go.mod","path":"test/edrhelpers/go.mod","sha":"c31e30950721c4f785b6d30f3804ca3b4982063b"},{"name":"go.sum","path":"test/edrhelpers/go.sum","sha":"218718c98f6110c949157daf970a9346f59b4a02"}]},{"name":"fixtures","children":[{"name":"build-packer-image-unit","children":[{"name":"ami-name","children":[{"name":"clean-resource-name.json","path":"test/fixtures/build-packer-image-unit/ami-name/clean-resource-name.json","sha":"2601caa0afd10705ba4c023f31dd03c1b2c10417"},{"name":"formatdate.pkr.hcl","path":"test/fixtures/build-packer-image-unit/ami-name/formatdate.pkr.hcl","sha":"3b5d6a6980d571292d6f0f925550578c53a47630"},{"name":"isotime-ami-name.json","path":"test/fixtures/build-packer-image-unit/ami-name/isotime-ami-name.json","sha":"29fa01b7f32d374cca8549b2bda8b18b69953373"},{"name":"multiple.json","path":"test/fixtures/build-packer-image-unit/ami-name/multiple.json","sha":"436b13dd54866f8926ce24c7766a043ff6d1e223"},{"name":"multiple.pkr.hcl","path":"test/fixtures/build-packer-image-unit/ami-name/multiple.pkr.hcl","sha":"bceb2c26063c414ac0718e7478a921680c1009e8"},{"name":"timestamp-ami-name.json","path":"test/fixtures/build-packer-image-unit/ami-name/timestamp-ami-name.json","sha":"17bb05e841d46f41900e3f647acaec28c58a34cc"},{"name":"uuid-ami-name.json","path":"test/fixtures/build-packer-image-unit/ami-name/uuid-ami-name.json","sha":"834762f57e84d3a3b62ae2afb04ac56aa145ad00"},{"name":"variables.json","path":"test/fixtures/build-packer-image-unit/ami-name/variables.json","sha":"5743df9f17cf8056f9c553196dc49afac1dbb59d"},{"name":"variables.pkr.hcl","path":"test/fixtures/build-packer-image-unit/ami-name/variables.pkr.hcl","sha":"c0a06f9957934682fccc31d794ba8a4ccf44e02f"},{"name":"whitespace.json","path":"test/fixtures/build-packer-image-unit/ami-name/whitespace.json","sha":"35cfd5200bbd124689868f115cb1b5498a8dfbbe"},{"name":"whitespace.pkr.hcl","path":"test/fixtures/build-packer-image-unit/ami-name/whitespace.pkr.hcl","sha":"a74b0503720df65dd9b6c13e1b65af99fabd3d96"}]},{"name":"ami-region","children":[{"name":"variable.json","path":"test/fixtures/build-packer-image-unit/ami-region/variable.json","sha":"2601caa0afd10705ba4c023f31dd03c1b2c10417"},{"name":"variable.pkr.hcl","path":"test/fixtures/build-packer-image-unit/ami-region/variable.pkr.hcl","sha":"3b5d6a6980d571292d6f0f925550578c53a47630"}]},{"name":"ami-tags","children":[{"name":"multiple-tags-variable.json","path":"test/fixtures/build-packer-image-unit/ami-tags/multiple-tags-variable.json","sha":"a546cb645b151da0156251a2e40c0599945f605b"},{"name":"multiple-tags-variable.pkr.hcl","path":"test/fixtures/build-packer-image-unit/ami-tags/multiple-tags-variable.pkr.hcl","sha":"baa093ab43bd9c5a42a5ee6a1bce39e70d520089"},{"name":"no-tags.json","path":"test/fixtures/build-packer-image-unit/ami-tags/no-tags.json","sha":"d160046152b90e840fe08650cb9fb4ae8273ab40"},{"name":"no-tags.pkr.hcl","path":"test/fixtures/build-packer-image-unit/ami-tags/no-tags.pkr.hcl","sha":"3b5d6a6980d571292d6f0f925550578c53a47630"},{"name":"single-tag.json","path":"test/fixtures/build-packer-image-unit/ami-tags/single-tag.json","sha":"c2770e7139805962dfa1eab83e5d02dde42a7b95"},{"name":"single-tag.pkr.hcl","path":"test/fixtures/build-packer-image-unit/ami-tags/single-tag.pkr.hcl","sha":"b4205dc5f59762daf4bc4af09ad084f5369f7eab"}]},{"name":"assert-build-amazon","children":[{"name":"amazon-ebs.json","path":"test/fixtures/build-packer-image-unit/assert-build-amazon/amazon-ebs.json","sha":"d160046152b90e840fe08650cb9fb4ae8273ab40"},{"name":"ambiguous.json","path":"test/fixtures/build-packer-image-unit/assert-build-amazon/ambiguous.json","sha":"b516d4fd64b4fae25af1582d2a9236266740333c"},{"name":"docker.json","path":"test/fixtures/build-packer-image-unit/assert-build-amazon/docker.json","sha":"65adc353ebf9bda1f6821d42ec0b2f6f8d5fc125"}]},{"name":"scripts","children":[{"name":"ami-name-test.sh","path":"test/fixtures/build-packer-image-unit/scripts/ami-name-test.sh","sha":"af00f6ad4a819f7d3a52b927a672645115f038e7"},{"name":"ami-region-test.sh","path":"test/fixtures/build-packer-image-unit/scripts/ami-region-test.sh","sha":"eb065fe4c4f55e87f190885f3402263ef8e90e06"},{"name":"ami-tags-test.sh","path":"test/fixtures/build-packer-image-unit/scripts/ami-tags-test.sh","sha":"8018361dbbe35c8654da9a84f1cb8682c7e0e214"},{"name":"assert-builder-amazon-test.sh","path":"test/fixtures/build-packer-image-unit/scripts/assert-builder-amazon-test.sh","sha":"fdd17b5cfc834f152de4c87f11663e68729b279e"}]}]},{"name":"git-add-commit-push","children":[{"name":"auto-committed.txt","path":"test/fixtures/git-add-commit-push/auto-committed.txt","sha":"5a1b0487943a6bb6645e502dc2c550c3c043f52a"}]},{"name":"hello-world-go-app","children":[{"name":"main.go","path":"test/fixtures/hello-world-go-app/main.go","sha":"3e0c7643f51386747f1b85656a0c797f282aed04"}]},{"name":"infra-pipeline-workflow","children":[{"name":"deploy-ami","children":[{"name":"main.tf","path":"test/fixtures/infra-pipeline-workflow/deploy-ami/main.tf","sha":"ce71c0fc94842687489f9423d9983cd831c79751"},{"name":"terragrunt.hcl","path":"test/fixtures/infra-pipeline-workflow/deploy-ami/terragrunt.hcl","sha":"726d218ced39781cf10699baf05283fbfcfb22df"}]},{"name":"deploy-docker","children":[{"name":"main.tf","path":"test/fixtures/infra-pipeline-workflow/deploy-docker/main.tf","sha":"4c5b2f8e767ab37cdedc43910ee91c6e181feb6b"},{"name":"terragrunt.hcl","path":"test/fixtures/infra-pipeline-workflow/deploy-docker/terragrunt.hcl","sha":"6d6be2748d15d39b736ebdf7b9621ed001b1da09"}]}]},{"name":"null_resource_module","children":[{"name":"main.tf","path":"test/fixtures/null_resource_module/main.tf","sha":"b9ddb64d3a71c26511281e336383ae3995954c6c"}]},{"name":"null_resource_terragrunt","children":[{"name":"terragrunt.hcl","path":"test/fixtures/null_resource_terragrunt/terragrunt.hcl","sha":"91585298f1066168e7a5a379c347fe0616f38856"}]},{"name":"test-docker-image","children":[{"name":"Dockerfile","path":"test/fixtures/test-docker-image/Dockerfile","sha":"5de35bbecce145045ae22fbc5fb97c133568a1ff"},{"name":"test.sh","path":"test/fixtures/test-docker-image/test.sh","sha":"ef3083cf7f3436ac7d01acfa33e4dab143fbcafd"}]},{"name":"test-go-test-files","children":[{"name":"simple_test.go","path":"test/fixtures/test-go-test-files/simple_test.go","sha":"53007ca88996e540ddfc503ce42982bdd5e785d2"},{"name":"test.sh","path":"test/fixtures/test-go-test-files/test.sh","sha":"25b8ef156d589c8d088d026b422e3b9c751c4b53"}]},{"name":"test-packer-image","children":[{"name":"hello-world-multiple-builders.json","path":"test/fixtures/test-packer-image/hello-world-multiple-builders.json","sha":"1c3e101f8965041b7aef0f5c4cddb14e1011d520"},{"name":"hello-world-no-tags-builder.json","path":"test/fixtures/test-packer-image/hello-world-no-tags-builder.json","sha":"d160046152b90e840fe08650cb9fb4ae8273ab40"},{"name":"hello-world-one-builder.json","path":"test/fixtures/test-packer-image/hello-world-one-builder.json","sha":"44a88c43721e45e9f5d921d8c04d08fa62c26748"},{"name":"hello-world-one-builder.pkr.hcl","path":"test/fixtures/test-packer-image/hello-world-one-builder.pkr.hcl","sha":"08fa9db48f63742d94f8b0c007cfc222d8d6fdc6"}]},{"name":"test-tfvars-files","children":[{"name":"multiple-similar-variables.tfvars","path":"test/fixtures/test-tfvars-files/multiple-similar-variables.tfvars","sha":"8147977dee3d9a196177c575cd664354c601e68f"},{"name":"multiple-variables-and-comments.tfvars","path":"test/fixtures/test-tfvars-files/multiple-variables-and-comments.tfvars","sha":"8b757cfa5ed1d5049c478ed9180b9e9a5743335e"},{"name":"one-variable-extra-whitespace.tfvars","path":"test/fixtures/test-tfvars-files/one-variable-extra-whitespace.tfvars","sha":"743b02f7015bd51232f9c40564c654f424cbb523"},{"name":"one-variable-no-whitespace.tfvars","path":"test/fixtures/test-tfvars-files/one-variable-no-whitespace.tfvars","sha":"39ddb49aa78f683e3e45384d2440a904d0ad7ec9"},{"name":"one-variable.tfvars","path":"test/fixtures/test-tfvars-files/one-variable.tfvars","sha":"82a0cea8ac06d9534dd2549c73ae70afd47336bb"}]},{"name":"test-tghcl-files","children":[{"name":"multiple-similar-variables.hcl","path":"test/fixtures/test-tghcl-files/multiple-similar-variables.hcl","sha":"8224297de9667b6887136c897b473977f7013fc0"},{"name":"multiple-variables-and-comments.hcl","path":"test/fixtures/test-tghcl-files/multiple-variables-and-comments.hcl","sha":"1905719b9208e53d78a9bced9792194f6955f928"},{"name":"one-variable-extra-whitespace.hcl","path":"test/fixtures/test-tghcl-files/one-variable-extra-whitespace.hcl","sha":"aa09bc5beacd10b66660e71173d16f3b093e9415"},{"name":"one-variable-no-whitespace.hcl","path":"test/fixtures/test-tghcl-files/one-variable-no-whitespace.hcl","sha":"8e77e4baf099fdb7f1e1d1571725c835ac92093a"},{"name":"one-variable.hcl","path":"test/fixtures/test-tghcl-files/one-variable.hcl","sha":"0afe11b72b3635feb75d71d7aff4cc28806a7f67"}]},{"name":"tfpipeline","children":[{"name":"failure","children":[{"name":"terraform","children":[{"name":"main.tf","path":"test/fixtures/tfpipeline/failure/terraform/main.tf","sha":"5488bde3fbd03a58398aeeb904cfef74d7200aae"}]},{"name":"terragrunt","children":[{"name":"terragrunt.hcl","path":"test/fixtures/tfpipeline/failure/terragrunt/terragrunt.hcl","sha":"2359b15bc6a698e481a8e99fdb352e174203b925"}]}]},{"name":"nested","children":[{"name":"terraform","children":[{"name":"main.tf","path":"test/fixtures/tfpipeline/nested/terraform/main.tf","sha":"5aab075e062b66046b8a381029859b59a35c0117"}]},{"name":"terragrunt","children":[{"name":"terragrunt.hcl","path":"test/fixtures/tfpipeline/nested/terragrunt/terragrunt.hcl","sha":"2359b15bc6a698e481a8e99fdb352e174203b925"}]}]},{"name":"root","children":[{"name":"terraform","children":[{"name":"main.tf","path":"test/fixtures/tfpipeline/root/terraform/main.tf","sha":"4c38ad94d4d10a46cb1acd012f9b3e5513757ac2"}]},{"name":"terragrunt","children":[{"name":"terragrunt.hcl","path":"test/fixtures/tfpipeline/root/terragrunt/terragrunt.hcl","sha":"2359b15bc6a698e481a8e99fdb352e174203b925"}]}]}]}]},{"name":"git_updated_folders_test.go","path":"test/git_updated_folders_test.go","sha":"9ac9c0aca0de5fe4f04ee65f6db8e627ad04a701"},{"name":"github_helpers.go","path":"test/github_helpers.go","sha":"56e54040790227aab12ae1b2f43991b9f00f87f6"},{"name":"go.mod","path":"test/go.mod","sha":"5a7425a6b9348af7a0827b213208788d933a2054"},{"name":"go.sum","path":"test/go.sum","sha":"90d910e47c97b33ca445f7d4eab965056d57798d"},{"name":"gruntwork_module_circleci_helpers_integration_test.go","path":"test/gruntwork_module_circleci_helpers_integration_test.go","sha":"cf307b6ed15585a660e190c287708c188bbaeda6"},{"name":"iam_policies_test.go","path":"test/iam_policies_test.go","sha":"eac6b5cdfb473c1e15f3715d2b7a3149bc5a3534"},{"name":"jenkins_test.go","path":"test/jenkins_test.go","sha":"435ce6943986b4ea452d17dd2ab9a2ee02abe733"},{"name":"kubernetes_circleci_helpers_test.go","path":"test/kubernetes_circleci_helpers_test.go","sha":"76a87d2854c2c7cf0a57c56582796b9cdb533c1b"},{"name":"monorepo_helpers_integration_test.go","path":"test/monorepo_helpers_integration_test.go","sha":"ba3a50643d0682aed75e51b53b133468e7c21675"},{"name":"publish_ami_test.go","path":"test/publish_ami_test.go","sha":"04f7667c39445fe5d6a336641c110e2c6c1744a3"},{"name":"sign_binary_helpers_integration_test.go","path":"test/sign_binary_helpers_integration_test.go","sha":"58a2773b3b7e7ea036fdb4bd37687e0f5a95db7e"},{"name":"terraform_update_variable_unit_test.go","path":"test/terraform_update_variable_unit_test.go","sha":"c40cec18a46204ffc9fb1f29bc1b9bb1f3901327"},{"name":"terragrunt_update_variable_unit_test.go","path":"test/terragrunt_update_variable_unit_test.go","sha":"69aee2044f92db0a42acde9d9f8ee5217a840796"},{"name":"test_helpers.go","path":"test/test_helpers.go","sha":"15939ff54435659854f406fc61df250c79bc23be"},{"name":"upgrades","children":[{"name":"constants.go","path":"test/upgrades/constants.go","sha":"779b38158ec68005c5540238016bfc2aa5d3f291"},{"name":"go.mod","path":"test/upgrades/go.mod","sha":"f27a1993b36b4523faccc41fbb064fb6ca4fa284"},{"name":"go.sum","path":"test/upgrades/go.sum","sha":"81b480ef2bec4b4801cf0d9e67f4699340f94b9c"},{"name":"test_helpers_for_upgrade_tests.go","path":"test/upgrades/test_helpers_for_upgrade_tests.go","sha":"fe49c25f1bf9d7c70f8799f2600fd47102e4aa3b"},{"name":"upgrade_module.go","path":"test/upgrades/upgrade_module.go","sha":"54b1cf0dc13be3a3f15680b82ea7704ad7a61978"},{"name":"upgrade_module_iam_policies_test.go","path":"test/upgrades/upgrade_module_iam_policies_test.go","sha":"52306bff01e5d6d982a2c3b26cc4a57a7312632b"}]},{"name":"validation","children":[{"name":"validate_all_modules_and_examples_test.go","path":"test/validation/validate_all_modules_and_examples_test.go","sha":"74c928d0cbc2914e5cd708277bd857cb2375b660"}]}]}]},"detailsContent":"<h1 class=\"preview__body--title\" id=\"build-helpers\">Build Helpers</h1><div class=\"preview__body--border\"></div><p>This folder contains several helper scripts for automatically building deployable, versioned artifacts of your apps:</p>\n<ul>\n<li>\n<p><code>build-docker-image</code>: This script is meant to be run from a CI job to automatically build a Docker image. It builds\nthe Docker image, tags the image with the sha1 of the most recent Git commit (or a custom tag, if specified), and then\npushes the new image to a Docker registry (unless the same image/tag already exists in the registry!).</p>\n</li>\n<li>\n<p><code>build-packer-artifact</code>: This script can be used to automatically build an artifact, such as an AMI, defined in a\nPacker template. It runs a Packer build, runs an optional test command to verify the new artifact, extracts the\nartifact ID from the build, and writes the ID to a properties file. This script is meant to be run from a CI job and\nthe properties file is a convenient way to pass information about the new artifact to another CI job.</p>\n</li>\n</ul>\n<h2 class=\"preview__body--subtitle\" id=\"using-the-helper-scripts-in-your-code\">Using the helper scripts in your code</h2>\n<p>You can install these scripts using the <a href=\"/repos/gruntwork-installer\" class=\"preview__body--description--blue\">Gruntwork Installer</a>:</p>\n<pre><span class=\"hljs-string\">gruntwork-install </span><span class=\"hljs-built_in\">--module-name</span> <span class=\"hljs-string\">\"build-helpers\"</span> <span class=\"hljs-built_in\">--repo</span> <span class=\"hljs-string\">\"https://github.com/gruntwork-io/terraform-aws-ci\"</span> <span class=\"hljs-built_in\">--tag</span> <span class=\"hljs-string\">\"v0.29.1\"</span>\n</pre>\n<h2 class=\"preview__body--subtitle\" id=\"properties-files\">Properties files</h2>\n<p>After a successful build, the build helper scripts write the versions of the artifacts they produce to a properties\nfile. The idea is that this properties file is easy to use in other scripts or other CI jobs to deploy those artifacts.</p>\n<p>The <code>build-docker-image</code> script adds an entry to the properties file of the form:</p>\n<pre><span class=\"hljs-attr\">DOCKER_IMAGE_TAG</span>=<IMAGE_TAG>\n</pre>\n<p>The <code>build-packer-artifact</code> script adds an entry to the properties file of the form:</p>\n<pre><span class=\"hljs-attr\">ARTIFACT_ID</span>=<ARTIFACT_ID>\n</pre>\n<h2 class=\"preview__body--subtitle\" id=\"examples\">Examples</h2>\n<p>The examples below should give you an idea of how to use the scripts. Run the scripts above with the <code>--help</code> flag to\nsee full documentation.</p>\n<p>Imagine you have a Packer template <code>templates/build.json</code> that specifies how to build an AMI for your app. You could\nset up automatic deployment for this app using two Jenkins CI jobs: <code>build-app</code> and <code>deploy-app</code>.</p>\n<p>The <code>build-app</code> CI job would first use the <code>build-packer-artifact</code> script to automatically build your AMI:</p>\n<pre><span class=\"hljs-keyword\">build</span>-packer-artifact --packer-template-<span class=\"hljs-keyword\">path</span> templates/<span class=\"hljs-keyword\">build</span>.json --output-properties-file artifacts.properties\n</pre>\n<p>Next, the <code>build-app</code> CI job would then pass the contents of <code>artifacts.properties</code> directly to the <code>deploy-app</code> CI\njob using the <a href=\"https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Trigger+Plugin\" class=\"preview__body--description--blue\" target=\"_blank\">Jenkins Parameterized Trigger\nPlugin</a>. The <code>deploy-app</code> CI job, in turn,\nwould be a <a href=\"https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Build\" class=\"preview__body--description--blue\" target=\"_blank\">parameterized build</a> that takes as input\na parameter called <code>ARTIFACT_ID</code> (the same parameter name that's in the <code>artifacts.properties</code> file) and use it, along\nwith the scripts in the <a href=\"/repos/v0.37.5/module-ci/modules/terraform-helpers\" class=\"preview__body--description--blue\">terraform-helpers module</a> to automatically deploy the new AMI:</p>\n<pre>cd templates\n<span class=\"hljs-keyword\">terraform</span>-update-<span class=\"hljs-keyword\">variable</span> --name rails_app_version --value $ARTIFACT_ID\nterragrunt apply\n</pre>\n<h2 class=\"preview__body--subtitle\" id=\"remote-packer-templates\">Remote packer templates</h2>\n<p><code>build-packer-artifact</code> also supports building Packer templates that are stored in a git repository. When a path of the\nformat <code>git::GIT_REPO_URL//RELATIVE_PATH_TO_PACKER_TEMPLATE?ref=GIT_REF</code> is passed in as the Packer template path,\n<code>build-packer-artifact</code> will clone the repository <code>GIT_REPO_URL</code> at the ref <code>GIT_REF</code> to a temporary directory and build\nthe template referenced at <code>RELATIVE_PATH_TO_PACKER_TEMPLATE</code> in the git repository.</p>\n<p>For example, to build the example jenkins Packer template in this repo on the release tag <code>v0.19.0</code>:</p>\n<pre>build-packer-artifact \\\n --packer-template-path git::git@github.com:gruntwork-io/<span class=\"hljs-keyword\">terraform</span>-aws-ci.git//examples/jenkins/packer/jenkins.json?ref=v0.<span class=\"hljs-number\">19.0</span>\n</pre>\n<h3 class=\"preview__body--subtitle\" id=\"accessing-private-repositories\">Accessing private repositories</h3>\n<p><code>build-packer-artifact</code> supports a way to load authentication credentials to access private remote git repositories via\nSSH or HTTPS. Both mechanisms assume the authentication credentials are stored in <a href=\"https://aws.amazon.com/secrets-manager/\" class=\"preview__body--description--blue\" target=\"_blank\">AWS Secrets\nManager</a>.</p>\n<p>For SSH, <code>build-packer-artifact</code> can load a password-less SSH private key from AWS Secrets Manager:</p>\n<ol>\n<li>\n<p>Create a new AWS Secrets Manager entry containing the full private SSH key. You can create it using the <code>awscli</code>:</p>\n<pre><code> aws secretsmanager create-secret --name GitSSHPrivateKey --secret-string file://path/to/ssh/private/key.pem\n</code></pre>\n</li>\n<li>\n<p>Record the ARN of the Secrets Manager entry you created in step 1.</p>\n</li>\n<li>\n<p>Pass the ARN of the Secrets Manager entry to <code>build-packer-artifact</code> using the input option\n<code>--ssh-key-secrets-manager-arn</code>.</p>\n</li>\n</ol>\n<p>For HTTPS, <code>build-packer-artifact</code> can load personal access tokens to authenticate to remote repositories for GitHub,\nGitLab, and BitBucket.</p>\n<ol>\n<li>\n<p>For each platform, create a Personal Access Token for accessing private repositories over HTTPS.</p>\n</li>\n<li>\n<p>Load the personal access token for your VCS platform in to AWS Secrets Manager. You can create it using the <code>awscli</code>:</p>\n<pre><code> aws secretsmanager create-secret --name GitPersonalAccessToken --secret-string "$GIT_PERSONAL_ACCESS_TOKEN"\n</code></pre>\n</li>\n<li>\n<p>Record the ARN of the Secrets Manager entry you created in step 2.</p>\n</li>\n<li>\n<p>Pass the ARN of the Secrets Manager entry to <code>build-packer-artifact</code> using the input option\n<code>--github-token-secrets-manager-arn</code>, <code>--gitlab-token-secrets-manager-arn</code>, or\n<code>--bitbucket-token-secrets-manager-arn</code>, depending on which VCS platform you are using. Note that for BitBucket, you\nmust also provide <code>--bitbucket-username</code>, which corresponds to the username of the BitBucket account the personal\naccess token is for.</p>\n</li>\n</ol>\n<h2 class=\"preview__body--subtitle\" id=\"idempotent-packer-templates\">Idempotent packer templates</h2>\n<p>For AWS AMI builds that use <code>tags</code>, <code>build-packer-artifact</code> supports idempotent builds by AMI tags. When you pass in\n<code>--idempotent true</code> to the script, <code>build-packer-artifact</code> will only build a new AMI if an existing AMI with the same\ntags and similar name does not already exist in your account. A name is similar if everything matches except for unique\nbuild time identifiers (e.g., that returned by <code>isotime</code>, <code>uuid</code>, or <code>timestamp</code>).</p>\n<p>Note that we include the name in the idempotency check to ensure that you can preserve AMIs in a multiaccount scenario,\nwhere AMI tags are not visible in the target account.</p>\n<p>For example, consider the following packer template:</p>\n<pre>{\n <span class=\"hljs-attr\">\"variables\"</span>: {\n <span class=\"hljs-attr\">\"aws_region\"</span>: <span class=\"hljs-string\">\"us-east-1\"</span>,\n <span class=\"hljs-attr\">\"tag\"</span>: <span class=\"hljs-string\">\"\"</span>\n },\n <span class=\"hljs-attr\">\"builders\"</span>: [{\n <span class=\"hljs-attr\">\"ami_name\"</span>: <span class=\"hljs-string\">\"gruntwork-test-{{user `tag`}}-packer-build-{{uuid | clean_resource_name}}\"</span>,\n <span class=\"hljs-attr\">\"ami_description\"</span>: <span class=\"hljs-string\">\"An AMI created as part of testing the build-packer-artifact script.\"</span>,\n <span class=\"hljs-attr\">\"instance_type\"</span>: <span class=\"hljs-string\">\"t2.micro\"</span>,\n <span class=\"hljs-attr\">\"region\"</span>: <span class=\"hljs-string\">\"{{user `aws_region`}}\"</span>,\n <span class=\"hljs-attr\">\"type\"</span>: <span class=\"hljs-string\">\"amazon-ebs\"</span>,\n <span class=\"hljs-attr\">\"source_ami\"</span>: <span class=\"hljs-string\">\"ami-fce3c696\"</span>,\n <span class=\"hljs-attr\">\"ssh_username\"</span>: <span class=\"hljs-string\">\"ubuntu\"</span>,\n <span class=\"hljs-attr\">\"tags\"</span>: {\n <span class=\"hljs-attr\">\"tag\"</span>: <span class=\"hljs-string\">\"{{user `tag`}}\"</span>\n }\n }],\n <span class=\"hljs-attr\">\"provisioners\"</span>: [{\n <span class=\"hljs-attr\">\"type\"</span>: <span class=\"hljs-string\">\"shell\"</span>,\n <span class=\"hljs-attr\">\"inline\"</span>: [\n <span class=\"hljs-string\">\"echo 'Hello, World'\"</span>\n ]\n }]\n}\n</pre>\n<p>If you pass in the <code>--idempotent true</code> and <code>--var tag=v1</code>, then this will only build the AMI if it does not find any AMI\nwith the tag <code>tag=v1</code> and name that begins with <code>gruntwork-test-v1-packer-build-</code> in the <code>us-east-1</code> region.</p>\n<p>Under the hood the tags, names, and regions are computed at runtime using <code>packer console</code> with the provided vars.</p>\n<p>Note that the following conditions must be true in order to use this feature:</p>\n<ul>\n<li>Packer version is at least <code>v1.4.2</code> (version that introduced <code>packer console</code>).</li>\n<li>Templates are in <code>json</code> format (you can not use HCL based templates at this time).</li>\n<li>Build is for an AMI (builder type <code>amazon-ebs</code>).</li>\n<li>Builder is configured to tag the AMIs (<code>tags</code> is set).</li>\n</ul>\n","repoName":"module-ci","repoRef":"v0.49.1","serviceDescriptor":{"serviceName":"EC2 backup","serviceRepoName":"module-ci","serviceRepoOrg":"gruntwork-io","serviceMainReadmePath":"/modules/ec2-backup","cloudProviders":["aws"],"description":"Snapshot your EC2 instances on a scheduled basis.","imageUrl":"grunt.png","licenseType":"subscriber","technologies":["Terraform","JavaScript","Lambda"],"compliance":[],"tags":[""]},"serviceCategoryName":"Backup & recovery","fileName":"README.md","filePath":"/modules/build-helpers","title":"Repo Browser: EC2 backup","description":"Browse the repos in the Gruntwork Infrastructure as Code Library."}