-
-
Save dmattia/0d17696bad1dffd90ec7c899e0343955 to your computer and use it in GitHub Desktop.
/** | |
* Wrapper around terragrunt to display output succinctly on Atlantis. | |
* | |
* Terragrunt is notoriously verbose, which can cause Atlantis to output | |
* hundreds of comments on single PRs, which can be annoying. | |
* | |
* This script will output just the final plan for resources to update on | |
* successful terragrunt runs, but will output all terragrunt output on | |
* errors. | |
*/ | |
const shell = require('shelljs'); | |
const path = require('path'); | |
const { PLANFILE } = process.env; | |
const logger = console; | |
/** | |
* A map of terraform field names to mask the output of to a Github | |
* Issue explaining why that field is masked | |
*/ | |
const maskMap = { | |
// Sensitive values from aws_cognito_identity_provider | |
client_id: | |
'https://github.com/terraform-providers/terraform-provider-aws/issues/9934', | |
client_secret: | |
'https://github.com/terraform-providers/terraform-provider-aws/issues/9934', | |
}; | |
/** | |
* A map of patterns for terragrunt modules that we would expect to fail to a Github | |
* Issue explaining why those modules are expected to fail. | |
* | |
* When these modules fail, we will display a message clearly stating that | |
* this is an expected behavior | |
*/ | |
const expectedFailingModules = { | |
'some/terragrunt/module': | |
'https://github.com/some-org/some-repo/issues/1234', | |
}; | |
/** | |
* Masks any blocklisted field names in the terraform output. | |
* | |
* Ideally, PRs would be sent to mark these fields as sensitive in | |
* the terraform provider itself, but this works as a temporary measure | |
* while fields those PRs are in review | |
* | |
* @param output - The original plan output | |
* @returns the plan output with sensitive values removed | |
*/ | |
function maskSensitiveValues(output) { | |
return Object.keys(maskMap).reduce( | |
(out, fieldName) => | |
out.replace( | |
new RegExp(`("${fieldName}" *=) ".*"`, 'g'), | |
(_, match) => | |
`${match} This field is sensitive and cannot be shown in PRs`, | |
), | |
output, | |
); | |
} | |
/** | |
* Promisifies shelljs.exec | |
* | |
* @param {string} command - Command to execute in the local shell | |
* @returns The resolved command | |
*/ | |
async function run(command) { | |
return new Promise((resolve) => { | |
shell.exec(command, { silent: true }, (code, stdout, stderr) => { | |
resolve({ code, stdout, stderr }); | |
}); | |
}); | |
} | |
/** | |
* Runs a plan via terragrunt. Output is only shown on error | |
*/ | |
async function runPlan() { | |
const wasExpectedToFail = Object.keys( | |
expectedFailingModules, | |
).some((pattern) => new RegExp(pattern).test(shell.pwd())); | |
if (wasExpectedToFail) { | |
logger.log( | |
`Atlantis does not currently support the module in ${shell.pwd()}. Please run this module locally`, | |
); | |
shell.touch(PLANFILE); | |
return; | |
} | |
const { code, stderr } = await run( | |
`terragrunt plan -no-color -out=${PLANFILE}`, | |
); | |
if (code !== 0) { | |
logger.log(stderr); | |
throw Error(`Failed to run plan in ${shell.pwd()}`); | |
} | |
} | |
/** | |
* Prints a representation of the terraform plan output to the console | |
*/ | |
async function printPlanFile() { | |
const { dir, base } = path.parse(PLANFILE); | |
shell.cd(dir); | |
const { stdout } = await run(`terragrunt show -no-color ${base}`); | |
logger.log(maskSensitiveValues(stdout)); | |
} | |
/** | |
* Runs an apply via terragrunt. Output is only shown on error | |
*/ | |
async function runAndPrintApply() { | |
const { code, stdout, stderr } = await run( | |
`terragrunt apply -no-color ${PLANFILE}`, | |
); | |
if (code !== 0) { | |
logger.log(stderr); | |
throw Error(`Failed to run apply in ${shell.pwd()}`); | |
} else { | |
logger.log(stdout); | |
} | |
} | |
/** | |
* Main function | |
*/ | |
async function main() { | |
const args = process.argv.slice(2); | |
const command = args[0]; | |
if (command.toString().trim() === 'apply') { | |
await runAndPrintApply(); | |
} else { | |
await runPlan(); | |
await printPlanFile(); | |
} | |
} | |
/** | |
* Run the program, exiting with a status code of 1 on any error | |
*/ | |
main().catch((err) => { | |
logger.error(err); | |
process.exit(1); | |
}); |
It was a little tricky at first to get this up and running, especially since the comments above with suggestions are a bit old and the script seem to have changed since then (most notably for example switched name from terragroan.js to terragrunt_light.js) and I started off trying without thinking too much.. =)
As a help for people trying to use this, we had to do the following to get this to work:
(Setup: Having terragrunt-atlantis-config
running in a docker container and used by github actions)
In the repos.yaml
(github action for atlantis)
Under plan:
run: terragrunt plan -no-color -out=$PLANFILE
-> run: /atlantis/config/terragrunt_light.js plan
Under apply:
run: terragrunt apply -no-color $PLANFILE
-> run: /atlantis/config/terragrunt_light.js apply
InDockfile
:
Added:
RUN apk add --no-cache nodejs
RUN apk add --no-cache npm
RUN npm install shelljs --global
ENV NODE_PATH=/usr/lib/node_modules
COPY terragrunt_light.js /atlantis/config/
Now it runs beautifully and we are happy =)
It is a great script/help for when using terragrunt in atlantis!
@dmattia I feel this should be part of the big (and also great) https://github.com/transcend-io/terragrunt-atlantis-config or at least referenced from there.. =)
@dmattia thanks for sharing this small wrapper around Terragunt + Atlantis. I use it in combination with your terragrunt-atlantis-config
to generate and validate atlantis.yaml
files :) - I was wondering whether you're familiar with some tool to help visualise better the actual colours in the Atlantis output when using Terragrunt plan? I know the reasons why we use the no-colour
CLI flag but not sure if another parsing to the Atlantis output could be performed?
Yes, that's true.
I ended up with my own implementation of such wrapper, but the whole idea kept the same. For
tg plan-all
I use grep to remove from its output excessive information because I couldn't find a way how to use PLANFILE withtg show
for several resources.