Guardian is a Terraform collaboration and automation tool. Using the features of GitHub, Guardian enables teams to enforce a propose, review, and acutate process for secure modification of resources.
[!IMPORTANT] The usage of Guardian does not increase the security posture of your cloud resources if poorly configured.
This is not an official Google product.
For support related items, please open a GitHub Issue.
Guardian release announcements are done via GitHub and can be found here. You can watch the Guardian repository in order to be notified each time a release is made.
This is the underlying tool written in Golang that is enables all features. For more details and to understand how to use the cli see Guardian CLI.
[!NOTE] While some features can be used independently, unless otherwise noted, all features have been designed to work with GitHub products (e.g. Pull Requests, Issues, Repositories).
state rm
or terraform apply
commands.For more details on the user experience for an engineer developing terraform, see Developer Workflow. For more details on the admin experience, see Guardian Admin.
For more details on how to use the Guardian CLI for terraform actuation see the relevant CLI doc:
You can get started with using Guardian for terraform actuation by Creating the Terraform Actuation GitHub Workflows.
For more information on using iam drift detection see the IAM Drift CLI Docs.
You can get started with using Guardian for drift detection by Creating the Drift Detection GitHub Workflows.
[!TIP] Consider using in conjunction with Statefile Drift Detection in order to locate outdated terraform state files that may incorrectly yield IAM drift.
For more information on using statefile drift detection see the Statefile Drift CLI Docs.
You can get started with using Guardian for drift detection by Creating the Drift Detection GitHub Workflows.
guardian policy
allows you to embed a set of policies within your code review
process.
guardian policy fetch-data
- Fetches data from the corresponding code review
platform to provide additional context for evaluating policies. Requires
contents: "read"
permission.
The result is written to a local file, guardian_policy_context.json
.
Use --include-teams
flag to return teams data in the payload. Requires
members: "read"
permission; Not available in default workflow token
permissions. See github-token-minter.
// Example
{
"github": {
"pull_request_approvers": {
"users": ["example-username"],
"teams": ["example-team-name"]
},
"actor": {
"username": "actor-name",
"access_level": "admin",
"teams": ["parent-team-of-actor"]
}
}
}
guardian policy enforce
- Accepts a file of OPA evaluation results, and
enforces the policies according to expected enforcement rules.
deny
- Blocks the changes with a detailed error message.
Policy results must be in the following format:
{
"name_of_policy": {
"deny": [
{
"msg": "Sample deny message."
}
]
}
}
missing_approvals
- Assigns principals to the change request and fails the
status check until the required approvals are met.
pull-requests: "write"
permissions for GitHub workflows. Note:
the default workflow token cannot assign teams to pull requests. See
github-token-minter.Policy results must be in the following format:
{
"name_of_policy": {
"missing_approvals": [
{
"assign_team_reviewers": [...] # github team names,
"assign_user_reviewers": [...] # github usernames,
"msg": "missing approvals for changes related to..."
}
],
}
}
You can add policy evaluation and enforcement to your Guardian Plan workflow with the following steps:
[!IMPORTANT] The
tfplan.json
file is only available within the same job as theguardian plan
command.
// guardian-plan.yml
# ...
# Within the Guardian Plan Job
# ...
- name: 'Aggregate Policy Data'
shell: 'bash'
env:
# used to call GitHub API's for data aggregation
GUARDIAN_GITHUB_TOKEN: '<TOKEN>'
run: |-
guardian policy fetch-data
- name: 'Setup OPA'
uses: 'open-policy-agent/setup-opa@v2'
with:
version: 'latest'
# Use the policy definitions from the main/approved branch
- name: 'Checkout'
uses: 'actions/checkout@v4'
with:
ref: '${{ github.event.pull_request.base.sha }}'
path: 'guardian-policy/main'
- name: 'Evaluate Policy'
id: 'opa_eval'
shell: 'bash'
run: |-
DECISION=$(opa eval --input "${DIRECTORY}/tfplan.json" \
--format raw \
--data ./guardian-policy/main/policy \
--data ./guardian_policy_context.json \
"data.guardian")
echo "$DECISION" > policy_results.json
- name: 'Enforce Policy'
shell: 'bash'
env:
GITHUB_TOKEN: '<TOKEN>'
run: |-
guardian policy enforce \
-dir=${DIRECTORY} \
-results-file=policy_results.json
Create a PR to propose Terraform changes
Guardian will run terraform plan
for the configured working directories and
will create a pull request comment with the plan diff for easy review
gs://<BUCKET_NAME>/guardian-plans/<OWNER>/<REPO>/<PR_NUMBER>/<TERRAFORM_WORKING_DIRECTORY_PATH>/tfplan.binary
gs://my-terraform-state/guardian-plans/owner/repo/20/terraform/production/tfplan.binary
Have your PR reviewed and approved by a CODEOWNER
If you need to re-run your plan, push new changes or re-run the workflow to generate new plan files
git commit --allow-empty -m "sync" && git push origin BRANCH
to push
an empty commit to re-triggerWhen the PR is merged Guardian will automatically run terraform apply
for
the plan file created for each working directory and post the results as PR
comments
Regardless of success or failure of apply, Guardian will delete all plan files
terraform apply
The Guardian Admin
workflow can be used to run commands manually as the
service account for Guardian. This process can only be done by someone with
Admin permissions (may require a breakglass) and is helpful in fixing error
scenarios with Terraform.
Guardian Admin
Run workflow
drop down in the top right areamain
apply -input=false -auto-approve
The Guardian Run
workflow can be used to run a limited set of Terraform commands manually as the service account for Guardian. This allows any user with
write permissions for the repository to run this workflow and maintain their Terraform configurations.
Guardian Run
Run workflow
drop down in the top right areamain
plan
.Guardian recommends the use of pull_request_target
for the Guardian Plan
action. This is for two reasons:
pull_request_target
is run in the context head commit on the default branch
(usually main
), this ensures that only the approved and merged workflow is
run for any given pull request.@refs/heads/main
for the workflows,
additionally ensuring only approved and merged workflows can impersonate the
highly privileged service account. This prevents someone from copying the
workflows with the pull_request
trigger and using the service account
however they want.Because pull_request_target
runs in the context of the head commit on the
repository default branch, we need to checkout the pull request branch for
Guardian to run terraform plan
on the proposed changes. This has been
documented as a security issue.
To make this process secure, Guardian should only be run in internal
or
private
repositories and should disable the use of forks. This makes the
pull_request_target
operate with the same default behavior as pull_request
but still ensures only the approved and merged workflow is being executed for
the pull request.
Terraform service accounts have elevated privileges, following WIF Best Practices is recommended when setting up guardian to use Workload Identity Federation. Below is an example attribute configuration that can be used to set up guardian in a secure manner.
The following attribute mappings map claims from the GitHub Actions JWT to Google STS token attributes.
google.subject=assertion.sub
attribute.actor=assertion.actor
attribute.aud=assertion.aud
attribute.ref=assertion.ref
attribute.repository_owner_id=assertion.repository_owner_id
attribute.repository_id=assertion.repository_id
attribute.repository_visibility=assertion.repository_visibility
attribute.workflow_ref=assertion.workflow_ref
The following attribute condition verifies that the request is coming from your GitHub organization and repository as well as restricting access to only the guardian workflows that run on the main branch.
attribute.repository_owner_id == "<your-repository-owner-id>" &&
attribute.repository_id == "<your-repository-id>" &&
attribute.repository_visibility != "public" &&
attribute.ref == "refs/heads/main" &&
attribute.workflow_ref in [
"<your-repository-owner-name>/<your-repository-name>/.github/workflows/guardian-admin.yml@refs/heads/main",
"<your-repository-owner-name>/<your-repository-name>/.github/workflows/guardian-apply.yml@refs/heads/main",
"<your-repository-owner-name>/<your-repository-name>/.github/workflows/guardian-plan.yml@refs/heads/main",
"<your-repository-owner-name>/<your-repository-name>/.github/workflows/guardian-run.yml@refs/heads/main",
]
You can find the id
and owner.id
of your repository by using GitHub's REST API.
$ OWNER_NAME="owner"
$ REPO_NAME="repo"
$ curl https://api.github.com/repos/$OWNER_NAME/$REPO_NAME | jq '. | {"id": .id, "owner": { "id": .owner.id }}'
{
"id": 12345,
"owner": {
"id": 9876
}
}
internal
or private
Allow forking
is disabled, Guardian recommends the use of
pull_request_target
, see aboveRulesets are required to enable a safe and secure process. Ensuring
these values are set properly prevents multiple pull requests from stepping on
each other. By enabling Require branches to be up to date before merging
, pull
requests merged at the same time will cause one to fail and be forced to pull
the changes from the default branch. This will kick of the planning process to
ensure the latest changes are always merged.
plan_success
job
status check, this is required to ensure the
Require branches to be up to date before merging
is enforced.If you want to use Terraform modules located in private GitHub repositories then you will need to configure git with the necessary permissions in order for Guardian to access these modules.
USERNAME=user-defined-name
TOKEN=your-token-with-read-acccess
git config --global url."https://${USERNAME}:${TOKEN}@github.com".insteadOf "https://github.com"
The USERNAME
variable has no functional purpose and can be an arbitrary string. It is required
to be present in the URL, but GitHub will identify the user based on the token. It is recommended to provide a descriptive username and/or comment here so that developers know where this token came from (e.g. guardian-pat-token).
We recommend using github-token-minter to generate short-lived access tokens token on demand for this purpose in your GitHub Actions.
Guardian will only run Terraform commands for directories that have a Terraform
backend configuration.
This means if you add a new folder and you want Guardian to run the
terraform plan
and terraform apply
commands from that directory as the root
of the module, you should include a backend configuration within that directory.
To use Guardian in your repository, see the
template installation instructions
in the abc.templates
folder.
To use Guardian drift detection in your repository, see the
template installation instructions
in the abc.templates
folder.
We collect non-identifiable usage metrics using
abcxyz/abc-updater. You can opt out of
these metrics by setting the environment variable GUARDIAN_NO_METRICS
to "true" in your shell.
Currently, data is collected on:
Along with each metric, the following metadata is recorded:
Metrics data is retained for 24 months.