Skip to content

Commit

Permalink
Merge pull request #5 from breml/fixes
Browse files Browse the repository at this point in the history
Fixes
  • Loading branch information
breml authored May 31, 2024
2 parents 3e7c1b3 + 912b578 commit 18ac060
Show file tree
Hide file tree
Showing 28 changed files with 906 additions and 143 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Declare files that will always have LF line endings on checkout
*.golden text eol=lf
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ tfreveal

# Go workspace file
go.work
go.work.sum

dist/
testdata/tmp

# === Terraform ===

Expand All @@ -39,8 +41,8 @@ crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json
Expand Down
33 changes: 28 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
# tfreveal

terraform does mask sensitive values in the output (e.g. from `terraform plan`)
in order to protect them from being reveals to 3rd parties.
Terraform does mask sensitive values in the output (e.g. from `terraform plan`)
in order to protect them from being revealed to 3rd parties.

Sometimes it is neccessary to see the exact changes, terraform will perform to the
infrastructure including all the changes to sensitive values. So far, terraform
Sometimes it is neccessary to see the exact changes, Terraform will perform to the
infrastructure including all the changes to sensitive values. So far, Terraform
does not provide a feature to forcefully unmask the sensitive values in the
[concise diff plan outputs](https://www.hashicorp.com/blog/terraform-0-14-adds-a-new-concise-diff-format-to-terraform-plans).
The general advice given by the terraform maintainers is to use the JSON output
The general advice given by the Terraform maintainers is to use the JSON output
in such cases. While the JSON output does provide all the necessary information,
it is not perticularely easy to read for humans and to spot small differences.
It gets even more complicated, if the sensitive values contain larger JSON
encoded values.

There exists instructions using for example `jq`, but the process stays manual,
cumbersome and error prone.

`tfreveal` is here to fix this and provide an easy way to show the concise diff
plan outputs with all sensitive values revealed.

## Usage

The plan file generated from Terraform can be directly piped to `tfreveal`:

```bash
$ terraform plan -out plan.out
$ terraform show -json plan.out | tfreveal
```

Alternatively, the plan file can also be passed as argument:

```bash
$ terraform plan -out plan.out
$ terraform show -json plan.out > plan.json
$ tfreveal plan.json
```

## Trademarks

All other trademarks referenced herein are the property of their respective owners.
4 changes: 4 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ tasks:
gen-all:
cmds:
- for:
- advanced
- outputs
- resource_create
- resource_delete
- resource_replace
- resource_update
- sensitive
task: generate-one
vars:
RESOURCE_DIR: '{{ .ITEM }}'
Expand All @@ -33,3 +36,4 @@ tasks:
- terraform -chdir=testdata/tmp plan -out plan.out
- terraform -chdir=testdata/tmp show -json plan.out | jq . > testdata/{{ .RESOURCE_DIR }}/plan.json
- rm -rf testdata/tmp
- go run . --no-color testdata/{{ .RESOURCE_DIR }}/plan.json > testdata/{{ .RESOURCE_DIR }}/output.golden
30 changes: 19 additions & 11 deletions format.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import (
)

func (a *App) resourceChanges(plan tfjson.Plan) string {
colorize := colorstring.Colorize{
Colors: colorstring.DefaultColors,
Disable: a.noColor,
}

buf := strings.Builder{}

for _, v := range plan.ResourceChanges {
Expand All @@ -24,14 +29,17 @@ func (a *App) resourceChanges(plan tfjson.Plan) string {

diffString := a.diff(v.Change)

if len(v.Change.ReplacePaths) > 0 {
buf.WriteString(colorize.Color(fmt.Sprintf(" [white]# %s[reset] must be [light_red]replaced[reset]\n", v.Address)))
}
buf.WriteString(fmt.Sprintf("%s %s = %s\n", a.marker(v.Change.Actions), v.Address, indent(diffString, 2)))
}

if buf.Len() == 0 {
return ""
}

return fmt.Sprintf("Changes to Resources:\n%s", buf.String())
return fmt.Sprintf("Changes to Resources:\n\n%s", buf.String())
}

func (a *App) outputChanges(plan tfjson.Plan) string {
Expand All @@ -51,14 +59,14 @@ func (a *App) outputChanges(plan tfjson.Plan) string {

diffString := a.diff(v)

buf.WriteString(fmt.Sprintf("%s %s = %s\n", a.marker(v.Actions), k, indent(diffString, 2)))
buf.WriteString(fmt.Sprintf("%s %s = %s", a.marker(v.Actions), k, indent(diffString, 2)))
}

if buf.Len() == 0 {
return ""
}

return fmt.Sprintf("Changes to Outputs:\n%s", buf.String())
return fmt.Sprintf("Changes to Outputs:\n\n%s", buf.String())
}

func (a *App) diff(change *tfjson.Change) string {
Expand All @@ -68,7 +76,7 @@ func (a *App) diff(change *tfjson.Change) string {

err := maputil.Walk(change.AfterUnknown, func(value interface{}, path []string, isLeaf bool) error {
if val, _ := value.(bool); val {
maputil.DeepSet(change.After, path, "<known after>")
maputil.DeepSet(change.After, path, "(known after apply)")
}
return nil
})
Expand Down Expand Up @@ -120,22 +128,22 @@ func (a *App) marker(actions tfjson.Actions) string {
}
switch {
case actions.Create():
return colorize.Color(" [green][bold]+[reset]")
return colorize.Color(" [green]+[reset]")
case actions.CreateBeforeDestroy():
return colorize.Color("[green][bold]+[reset]/[red][bold]-[reset]")
return colorize.Color("[green]+[reset]/[red]-[reset]")
case actions.Delete():
return colorize.Color(" [red][bold]-[reset]")
return colorize.Color(" [red]-[reset]")
case actions.DestroyBeforeCreate():
return colorize.Color("[red][bold]-[reset]/[green][bold]+[reset]")
return colorize.Color("[red]-[reset]/[green]+[reset]")
case actions.NoOp():
return colorize.Color("")
case actions.Read():
return colorize.Color("")
case actions.Replace():
return colorize.Color("[red][bold]-[reset]/[green][bold]+[reset]")
return colorize.Color("[red]-[reset]/[green]+[reset]")
case actions.Update():
return colorize.Color(" [yellow][bold]~[reset]")
return colorize.Color(" [yellow]~[reset]")
default:
return colorize.Color("[red][bold]! [reset]")
return colorize.Color("[red]! [reset]")
}
}
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ module github.com/breml/tfreveal
go 1.21.7

require (
github.com/breml/jsondiffprinter v0.0.5
github.com/breml/jsondiffprinter v0.0.6
github.com/ghetzel/go-stockutil v1.11.4
github.com/hashicorp/terraform-json v0.22.1
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.27.2
github.com/wI2L/jsondiff v0.5.2
)
Expand All @@ -25,9 +26,9 @@ require (
github.com/juliangruber/go-intersect v1.1.0 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/tidwall/gjson v1.17.1 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
Expand All @@ -37,5 +38,6 @@ require (
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/neurosnap/sentences.v1 v1.0.7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/client-go v0.24.1 // indirect
)
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/breml/jsondiffprinter v0.0.5 h1:Zd8yn3S1oj76NclUXDTkexbwW5u86t09oV1T5wUJKeE=
github.com/breml/jsondiffprinter v0.0.5/go.mod h1:729UQJYOSFQicxccpbA7hNFRzaKmVsY7E32/WUGB/Us=
github.com/breml/jsondiffprinter v0.0.6 h1:AVl1+WxzmNdW7zcq8/Y8HCtPvdXPfoWQacSCUC9qSiI=
github.com/breml/jsondiffprinter v0.0.6/go.mod h1:ZQNpX6l3HHFVdv8ETUIkBrQKbilrUlFVH0Uc0JP+ohQ=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
Expand Down Expand Up @@ -240,6 +240,7 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/neurosnap/sentences v1.0.6 h1:iBVUivNtlwGkYsJblWV8GGVFmXzZzak907Ci8aA0VTE=
github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
Expand Down Expand Up @@ -520,8 +521,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down Expand Up @@ -628,6 +629,7 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
Expand Down
21 changes: 17 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"

tfjson "github.com/hashicorp/terraform-json"
"github.com/mitchellh/colorstring"
"github.com/urfave/cli/v2"
)

Expand All @@ -20,17 +21,28 @@ func main() {
func main0(osArgs []string) error {
app := App{}

executionPlanLegend := colorstring.Color(`
Resource actions are indicated with the following symbols:
[green]+[reset] create
[yellow]~[reset] update in-place
[red]-[reset] destroy
[red]-[reset]/[green]+[reset] destroy and then create replacement
[green]+[reset]/[red]-[reset] create replacement and then destroy
`)
_ = executionPlanLegend

cliapp := &cli.App{
Name: "tfreveal",
Usage: "Show Terraform plan file with all sensitive values revealed.",
Action: app.TerraformReveal,
Usage: "Show an execution plan with all sensitive values revealed.",
Action: app.Reveal,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "no-color",
Usage: "Disable colorized output",
Destination: &app.noColor,
},
},
CustomAppHelpTemplate: cli.AppHelpTemplate + executionPlanLegend,
}
return cliapp.Run(osArgs)
}
Expand All @@ -39,7 +51,7 @@ type App struct {
noColor bool
}

func (a *App) TerraformReveal(c *cli.Context) error {
func (a *App) Reveal(c *cli.Context) error {
var source io.Reader = os.Stdin

if c.Args().Present() {
Expand All @@ -62,8 +74,9 @@ func (a *App) TerraformReveal(c *cli.Context) error {
return fmt.Errorf(`unmarshal Terraform plan json: %w`, err)
}

fmt.Println("The provided execution plan contains the following changes.")
fmt.Println()
fmt.Print(a.resourceChanges(plan))

fmt.Print(a.outputChanges(plan))

return nil
Expand Down
42 changes: 42 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"io"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/require"
)

func TestMain0(t *testing.T) {
files, err := filepath.Glob(filepath.Join("testdata", "*"))
require.NoError(t, err)

for _, filename := range files {
filename := filename
t.Run(filename, func(t *testing.T) {
defer func(orig *os.File) {
os.Stdout = orig
}(os.Stdout)

r, w, err := os.Pipe()
require.NoError(t, err)

os.Stdout = w

err = main0([]string{"tfreveal", "--no-color", filepath.Join(filename, "plan.json")})
require.NoError(t, err)
err = w.Close()
require.NoError(t, err)

out, err := io.ReadAll(r)
require.NoError(t, err)

want, err := os.ReadFile(filepath.Join(filename, "output.golden"))
require.NoError(t, err)

require.Equal(t, string(want), string(out))
})
}
}
Loading

0 comments on commit 18ac060

Please sign in to comment.