A GitHub Action to automatically cleanup development/preview stacks in a Pulumi project.
Create a workflow file to run on a schedule. The example below defines a policy called cleanup-dev-stacks
which cleans up all preview environments where:
- The stack has been marked with the tag
environment
, where the value of the tag isdevelopment
. - It has been at least 24 hours since the the stack has been updated.
name: cleanup-stacks
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: jbrunton/cleanup-pulumi-stacks@v2
with:
working-directory: pulumi
# uncomment to disable previews when you're confident in your policies
# preview: false
config: |
policies:
cleanup-dev-stacks:
match:
tags:
environment: development
ttl:
hours: 24
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
working-directory
- Relative path in the repo to the Pulumi project (i.e. the project with yourPulumi.yaml
file). Defaults to the current working directory.config
- Yaml config for policies (see below).config_file
- Can be used to specify a path to a config file in place of theconfig
input.preview
- Whentrue
, logs a preview of what the action would do without making changes to any stacks. Defaults totrue
.verbose
- Whentrue
, provides detailed output on all the policy checks applied to each stack. Defaults tofalse
.
You can specify the config in a couple of different ways:
- In the workflow, with the
config
key:
- uses: jbrunton/cleanup-pulumi-stacks@v2
with:
config: |
policies:
cleanup-dev-stacks:
match:
name: dev-*
- In a file in source control, with the
config_file
key:
- uses: jbrunton/cleanup-pulumi-stacks@v2
with:
config_file: cleanup-config.yml
- By default, if no config or file are specified, the action will default to looking for a config file at
<working-directory>/cleanup-config.yml
.
# uses config file at pulumi/cleanup-config.yml
- uses: jbrunton/cleanup-pulumi-stacks@v2
with:
working-directory: pulumi
Cleanup policies are listed under the policies
key in the config. They must be given a ttl
(time-to-live), and additional match parameters based on stack names or tags.
The time-to-live policy can be specified in either days, hours or minutes:
policies:
# cleanup any stacks not updated in the last 3 days
cleanup-previous-day:
ttl:
days: 3
# cleanup any stacks not updated in the last 6 hours
cleanup-previous-hour:
ttl:
hours: 6
The stack name policy can be given any glob pattern supported by micromatch.
policies:
# match literal (the stack named `test`)
cleanup-test:
match:
name: test
ttl:
hours: 24
# match glob (any stacks starting with `dev-`)
cleanup-dev:
match:
name: 'dev-*'
ttl:
hours: 24
# match logical 'or' of patterns (any stack named `test` or starting with `dev-`)
cleanup-all:
match:
name: '(test|dev-*)'
ttl:
hours: 24
# match negations
cleanup-all:
match:
name: '!production'
ttl:
hours: 24
The stack name policy can define patterns for arbitrary tags.
policies:
# match literal (any stack where the `env` tag is `test`)
cleanup-test:
match:
tags:
env: test
ttl:
hours: 24
# match glob (any stacks where the `env` tag starts with `dev-`)
cleanup-dev:
match:
tags:
env: 'dev-*'
ttl:
hours: 24
# match logical 'or' of patterns
cleanup-all:
match:
tags:
env: '(test|dev-*)'
ttl:
hours: 24
It goes without saying that you should be careful using any automated tool which will destroy your stacks. Here are a few ways this tool is designed to protect you from accidental deletes, and a few steps you can take.
- Keep the action in preview mode until you're confident your policies work as expected.
- You can also use Pulumi's protect resource option on production resources. This will prevent the action from accidentally destroying anything it shouldn't.
- Consider using separate projects/accounts for production services.
- By default the action runs in preview mode, and won't destroy anything. You can view its runs and decide when to disable preview mode.
- You have to specify at least one
match
policy, so you can't accidentally add a "remove all" policy. You must also define at least onestack
policy to match on. - Test coverage of key application logic is intentionally high, and checked using a mutation testing tool.