tfnotify pipes Terraform output into a GitHub comment you can template
A CLI command to parse Terraform execution result and notify it to GitHub
At a glance
- What is it?
- Mercari's tfnotify is a Go CLI that wraps a Terraform plan or apply, parses the output and posts it back as a GitHub comment, with labels, masking and optional AI summaries. It reads from stdin, which is where the awkwardness starts.
- Who is it for?
- Adopt tfnotify if your Terraform runs inside CI and your team lives in pull requests, because it turns a plan into a reviewable comment and a label without anyone opening the CI log. Do not adopt it if you need Slack or other notifiers, since the supported notifier list names GitHub only, and do not adopt it if your pipeline cannot pipe stdin, since that is how the tool receives the Terraform output.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 43 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Terraform runs in CI, but the answer is on a CI page
The problem tfnotify addresses is small and extremely common. Terraform has commands such as plan and apply, and many developers want to know whether the execution of those commands succeeded. Those commands are often executed through CI systems such as Cloud Build or GitHub Actions, and in that case you have to go to the CI page to check the outcome. The project's argument is that this is troublesome, and that it is much more efficient to check the result with the CI system you are already looking at, meaning the pull request. So tfnotify is described as a Go based template engine that creates comments on Terraform operations: it pipes commands directly, captures operation output including exit codes and error details, and offers to post them as a GitHub comment. The audience is platform and infrastructure engineers whose review process happens in a pull request rather than in a build log.
It reads from stdin, so the command has to be piped
tfnotify is just a CLI command, so you can run it locally after grabbing the binary. The important detail is that tfnotify waits for input from stdin, which means the output of the Terraform command has to be piped into it. The documented shape of that is:
$ tfnotify planFor the plan command you also need to specify plan as the argument of tfnotify, and in the case of apply you need to do apply. The set of commands that are supported can be checked with tfnotify --help. The consequence for pipeline design is direct: tfnotify is not a wrapper that runs Terraform for you in the general case, it is a filter that consumes Terraform output. Anything that changes where the output goes, such as a tool that also writes a plan file, has to be composed with that stdin contract rather than around it.
Templates decide what the comment looks like
The comment body is a Go text/template, and the placeholders are the whole vocabulary. The configuration file tfnotify.yaml holds them per Terraform command. The basic shape looks like this:
---
ci: circleci
notifier:
github:
token: $GITHUB_TOKEN
repository:
owner: "mercari"
name: "tfnotify"
terraform:
fmt:
template: |
{{ .Title }}
{{ .Message }}The title is used for a heading such as ## Plan result, the message is a string you can set from the CLI with the --message option, the result is the matched result from parsing, the body is the entire Terraform execution result, and the link is the link of the build page on CI. The templates can use custom functions for formatting notifications, which is what makes this more than string concatenation, and there are separate templates for fmt, plan and apply so each command can present itself differently. The GitHub Enterprise variant adds a base_url under the github notifier, with the example given as an API path on a self hosted GitHub.
Parsing is regex based and produces a single result string
The parsing engine is described as regex based, extracting meaningful information from Terraform plan and apply outputs, and the result lands in one placeholder. The documentation names the two shapes you will meet most often: a matched result such as Plan: 1 to add, and No changes. Everything else in the comment comes from the raw body, so the parser is not trying to reconstruct the plan, it is trying to classify it. That classification is what drives the rest of the behaviour, because the label rules are keyed on the same categories. The tradeoff is worth naming. Regex parsing of human formatted output is fragile across Terraform versions, since the wording it matches against is not a stable API, and the documentation does not describe a structured input mode. If your Terraform output format changes, the labels and the result line are where you will notice it first.
Labels turn a plan into pull request state
Beyond the comment, tfnotify can add a label to a pull request depending on the terraform plan output result, and the documentation notes that this feature is for GitHub labels only. The rules are configured per plan result:
when_add_or_update_only:
label: "add-or-update"
when_destroy:
label: "destroy"
when_no_changes:
label: "no-changes"
when_plan_error:
label: "error"There is a fifth branch that is not a label but a warning. If the plan result contains resource deletion, tfnotify can put a warning message on GitHub, configured through a when_destroy template, and the documented text is a warning that resource deletion will happen and that the plan result should be checked carefully. In a review workflow this is the feature that matters most, because a destructive plan is the case where a human needs to look, and a comment that says it plainly is worth more than a log line nobody reads.
Raw output avoids the escaping that makes plans unreadable
There is a small setting that changes the readability of every comment. Sometimes you do not want Terraform command output HTML escaped, because inside a code block it is better to use raw characters instead of character references, with the documentation giving the example of -/+ turning into -/+ and a double quote turning into ". The escape is disabled with a use_raw_output configuration, and the documented block is short:
---
# ...
terraform:
use_raw_output: trueWith that setting, Terraform output is not HTML escaped at all. The tradeoff is straightforward and worth understanding before you flip it. In a Markdown comment the escaping is what stops a stray angle bracket from breaking the rendering, and turning it off makes the output readable for humans at the cost of a clean render when the output contains markup characters.
CI detection, masking and the optional AI summary
The feature list describes integration with GitHub, CircleCI and Cloud Build, CI/CD awareness that automatically detects and integrates with more than nine CI/CD platforms, a built in masking system that prevents sensitive data exposure, comment management that updates existing comments instead of creating duplicates, and optional AI summaries of infrastructure changes using multiple providers. The config file names the CI to expect with a top level ci key, and the tool finds the build context from the environment, with the dependency list including a CI environment helper. Masking deserves particular attention in a tool that posts infrastructure output into a public comment: whatever your template renders is published, and the masking layer is the only thing standing between a plan containing a secret and everyone reading it. The comment updating behaviour matters too, since repeated runs on the same pull request converge on one comment instead of a wall of them.
Licence, releases and the limits of the notifier list
The project is released under a custom licence that GitHub cannot classify, and the LICENSE file at the repository root is the authoritative text, so read it before you put tfnotify in a build pipeline of a company with an open source review process. Release cadence is steady, with v1.1.12 on 2026-06-26, v1.1.13 on 2026-09-01 and v1.1.14 on 2026-09-10, and the last push was on 2026-09-10. The module path is github.com/mercari/tfnotify/v1, the project targets Go 1.25.1, and the dependencies include urfave/cli for the command line, go-github and the GitHub GraphQL client for GitHub access, sprig for template functions, logrus for logging, a CI environment helper, and a Slack client library even though the supported notifier list names GitHub alone. That last detail is worth noticing, because it suggests Slack support is not far off, and it is also why the current limitation is real: if your team reviews in Slack, tfnotify does not post there yet. The alternative to adopting it is writing a small script against the GitHub API yourself, and the difference is that such a script leaves you to maintain the parsing, the masking and the template rendering that tfnotify gives you.
Editorial conclusion
Adopt tfnotify if your Terraform runs inside CI and your team lives in pull requests, because it turns a plan into a reviewable comment and a label without anyone opening the CI log. Do not adopt it if you need Slack or other notifiers, since the supported notifier list names GitHub only, and do not adopt it if your pipeline cannot pipe stdin, since that is how the tool receives the Terraform output. Before you start, put a tfnotify.yaml next to your job, define the token as an environment variable rather than in the file, check the template placeholders against what your Terraform version prints, and read the LICENSE file, because the project uses a custom licence that GitHub cannot classify.
Frequently asked questions
How do I install tfnotify?
The documentation recommends grabbing the binary from GitHub Releases. Alternatively you can install it with go get -u github.com/mercari/tfnotify, and the module path is github.com/mercari/tfnotify/v1.
How is tfnotify wired into a Terraform command?
tfnotify waits for input from stdin, so the output of the Terraform command has to be piped into it. You also pass the Terraform command as the argument, plan or apply, and the list of supported commands can be checked with tfnotify --help.
What can I put in a tfnotify template?
The documented placeholders are {{ .Title }} for a heading, {{ .Message }} for a string set with the --message option, {{ .Result }} for the parsed result such as Plan: 1 to add or No changes, {{ .Body }} for the entire Terraform execution result, and {{ .Link }} for the build page link on CI.
Where does tfnotify store its configuration and how does it authenticate?
You point tfnotify at a configuration file with the --config option, and it defaults to a tfnotify.yaml or tfnotify.yml file in the current directory. The token is written as an environment variable reference such as $GITHUB_TOKEN in the file and defined in your CI settings, rather than replaced with the real token.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mercari-tfnotify)Was this useful?