Skip to content

Commit

Permalink
Merge pull request #1563 from snyk/feat/tfstate-discovery-tfc
Browse files Browse the repository at this point in the history
State discovery for Terraform cloud
  • Loading branch information
sundowndev-snyk authored Jul 25, 2022
2 parents 90a15f9 + cfd8c53 commit 351e216
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 33 deletions.
14 changes: 11 additions & 3 deletions pkg/cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,18 @@ func retrieveBackendsFromHCL(workdir string) ([]config.SupplierConfig, error) {
continue
}

var cfg *config.SupplierConfig
ws := hcl.GetCurrentWorkspaceName(path.Dir(match))
if supplierConfig := body.Backend.SupplierConfig(ws); supplierConfig != nil {
globaloutput.Printf(color.WhiteString("Using Terraform state %s found in %s. Use the --from flag to specify another state file.\n"), supplierConfig, match)
supplierConfigs = append(supplierConfigs, *supplierConfig)

if body.Cloud != nil {
cfg = body.Cloud.SupplierConfig(ws)
}
if body.Backend != nil {
cfg = body.Backend.SupplierConfig(ws)
}
if cfg != nil {
globaloutput.Printf(color.WhiteString("Using Terraform state %s found in %s. Use the --from flag to specify another state file.\n"), cfg, match)
supplierConfigs = append(supplierConfigs, *cfg)
}
}

Expand Down
29 changes: 1 addition & 28 deletions pkg/terraform/hcl/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,6 @@ import (
"github.com/stretchr/testify/assert"
)

func TestHCL_getCurrentWorkspaceName(t *testing.T) {
cases := []struct {
name string
dir string
want string
}{
{
name: "test with non-default workspace",
dir: "testdata/foo_workspace",
want: "foo",
},
{
name: "test with non-existing directory",
dir: "testdata/noenvfile",
want: "default",
},
}

for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
workspace := GetCurrentWorkspaceName(tt.dir)
assert.Equal(t, tt.want, workspace)
})
}
}

func TestBackend_SupplierConfig(t *testing.T) {
cases := []struct {
name string
Expand All @@ -45,7 +19,6 @@ func TestBackend_SupplierConfig(t *testing.T) {
name: "test with no backend block",
filename: "testdata/no_backend_block.tf",
want: nil,
wantErr: "testdata/no_backend_block.tf:1,11-11: Missing backend block; A backend block is required.",
},
{
name: "test with local backend block",
Expand Down Expand Up @@ -118,7 +91,7 @@ func TestBackend_SupplierConfig(t *testing.T) {
}

ws := GetCurrentWorkspaceName(path.Dir(tt.filename))
if hcl.Backend.SupplierConfig(ws) == nil {
if hcl.Backend == nil || hcl.Backend.SupplierConfig(ws) == nil {
assert.Nil(t, tt.want)
return
}
Expand Down
34 changes: 34 additions & 0 deletions pkg/terraform/hcl/cloud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package hcl

import (
"path"

"github.com/hashicorp/hcl/v2"
"github.com/snyk/driftctl/pkg/iac/config"
"github.com/snyk/driftctl/pkg/iac/terraform/state"
"github.com/snyk/driftctl/pkg/iac/terraform/state/backend"
)

type CloudWorkspacesBlock struct {
Name string `hcl:"name,optional"`
Tags []string `hcl:"tags,optional"`
Remain hcl.Body `hcl:",remain"`
}

type CloudBlock struct {
Organization string `hcl:"organization"`
Workspaces CloudWorkspacesBlock `hcl:"workspaces,block"`
Remain hcl.Body `hcl:",remain"`
}

func (c CloudBlock) SupplierConfig(workspace string) *config.SupplierConfig {
// If a workspace is specified in HCL, use it rather than the current environment
if c.Workspaces.Name != "" {
workspace = c.Workspaces.Name
}
return &config.SupplierConfig{
Key: state.TerraformStateReaderSupplier,
Backend: backend.BackendKeyTFCloud,
Path: path.Join(c.Organization, workspace),
}
}
55 changes: 55 additions & 0 deletions pkg/terraform/hcl/cloud_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package hcl

import (
"testing"

"github.com/snyk/driftctl/pkg/iac/config"
"github.com/stretchr/testify/assert"
)

func TestCloud_SupplierConfig(t *testing.T) {
cases := []struct {
name string
filename string
want *config.SupplierConfig
wantErr string
}{
{
name: "test with cloud block and default workspace",
filename: "testdata/cloud_block.tf",
want: &config.SupplierConfig{
Key: "tfstate",
Backend: "tfcloud",
Path: "example_corp/default",
},
},
{
name: "test with cloud block and non-default workspace",
filename: "testdata/cloud_block_workspace.tf",
want: &config.SupplierConfig{
Key: "tfstate",
Backend: "tfcloud",
Path: "example_corp/my-workspace",
},
},
}

for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
hcl, err := ParseTerraformFromHCL(tt.filename)
if tt.wantErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tt.wantErr)
return
}

if hcl.Cloud == nil {
assert.Nil(t, tt.want)
return
}

assert.Equal(t, *tt.want, *hcl.Cloud.SupplierConfig("default"))
})
}
}
5 changes: 3 additions & 2 deletions pkg/terraform/hcl/hcl.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ type MainBodyBlock struct {
}

type TerraformBlock struct {
Backend BackendBlock `hcl:"backend,block"`
Remain hcl.Body `hcl:",remain"`
Backend *BackendBlock `hcl:"backend,block"`
Cloud *CloudBlock `hcl:"cloud,block"`
Remain hcl.Body `hcl:",remain"`
}

func ParseTerraformFromHCL(filename string) (*TerraformBlock, error) {
Expand Down
33 changes: 33 additions & 0 deletions pkg/terraform/hcl/hcl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package hcl

import (
"testing"

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

func TestHCL_getCurrentWorkspaceName(t *testing.T) {
cases := []struct {
name string
dir string
want string
}{
{
name: "test with non-default workspace",
dir: "testdata/foo_workspace",
want: "foo",
},
{
name: "test with non-existing directory",
dir: "testdata/noenvfile",
want: "default",
},
}

for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
workspace := GetCurrentWorkspaceName(tt.dir)
assert.Equal(t, tt.want, workspace)
})
}
}
8 changes: 8 additions & 0 deletions pkg/terraform/hcl/testdata/cloud_block.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
terraform {
cloud {
organization = "example_corp"
hostname = "app.terraform.io" # Optional; defaults to app.terraform.io

workspaces {}
}
}
10 changes: 10 additions & 0 deletions pkg/terraform/hcl/testdata/cloud_block_workspace.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
cloud {
organization = "example_corp"
hostname = "app.terraform.io" # Optional; defaults to app.terraform.io

workspaces {
name = "my-workspace"
}
}
}

0 comments on commit 351e216

Please sign in to comment.