Skip to content

Commit

Permalink
New schemas tfstates3 and tfstategs (#49)
Browse files Browse the repository at this point in the history
Adds new schemes that can allow uses of direct path to Terraform state in the bucket: one for GCS another for S3.

The change requires #47 to be applied first as it supports GCS buckets implemented by tfstate-lookup v0.2.0

Resolves #45
  • Loading branch information
dex4er authored May 3, 2021
1 parent e390ce8 commit bed2158
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 5 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,48 @@ Remote backends like S3 or GCS are also supported. When a remote backend is used

Just specify the path to that file, so that `vals` is able to transparently make the remote state contents available for you.

### Terraform in GCS bucket (tfstategs)

- `ref+tfstategs://bucket/path/to/some.tfstate/RESOURCE_NAME`

Examples:

- `ref+tfstategs://bucket/path/to/some.tfstate/google_compute_disk.instance.id`

It allows to use Terraform state stored in GCS bucket with the direct URL to it. You can try to read the state with command:

```
$ tfstate-lookup -s gs://bucket-with-terraform-state/terraform.tfstate google_compute_disk.instance.source_image_id
5449927740744213880
```

which is equivalent to the following input for `vals`:

```
$ echo 'foo: ref+tfstategs://bucket-with-terraform-state/terraform.tfstate/google_compute_disk.instance.source_image_id' | vals eval -f -
```

### Terraform in S3 bucket (tfstates3)

- `ref+tfstates3://bucket/path/to/some.tfstate/RESOURCE_NAME`

Examples:

- `ref+tfstates3://bucket/path/to/some.tfstate/aws_vpc.main.id`

It allows to use Terraform state stored in AWS S3 bucket with the direct URL to it. You can try to read the state with command:

```
$ tfstate-lookup -s s3://bucket-with-terraform-state/terraform.tfstate module.vpc.aws_vpc.this[0].arn
arn:aws:ec2:us-east-2:ACCOUNT_ID:vpc/vpc-0cb48a12e4df7ad4c
```

which is equivalent to the following input for `vals`:

```
$ echo 'foo: ref+tfstates3://bucket-with-terraform-state/terraform.tfstate/module.vpc.aws_vpc.this[0].arn' | vals eval -f -
```

### SOPS

- The whole content of a SOPS-encrypted file: `ref+sops://base64_data_or_path_to_file?key_type=[filepath|base64]&format=[binary|dotenv|yaml]`
Expand Down
27 changes: 24 additions & 3 deletions pkg/providers/tfstate/tfstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import (
)

type provider struct {
backend string
}

func New(cfg api.StaticConfig) *provider {
func New(cfg api.StaticConfig, backend string) *provider {
p := &provider{}
p.backend = backend
return p
}

Expand All @@ -27,9 +29,9 @@ func (p *provider) GetString(key string) (string, error) {
f := strings.Join(splits[:pos], string(os.PathSeparator))
k := strings.Join(splits[pos:], string(os.PathSeparator))

state, err := tfstate.ReadFile(f)
state, err := p.ReadTFState(f, k)
if err != nil {
return "", fmt.Errorf("reading tfstate for %s: %w", key, err)
return "", err
}

// key is something like "aws_vpc.main.id" (RESOURCE_TYPE.RESOURCE_NAME.FIELD)
Expand All @@ -42,6 +44,25 @@ func (p *provider) GetString(key string) (string, error) {
return attrs.String(), nil
}

// Read state either from file or from backend
func (p *provider) ReadTFState(f, k string) (*tfstate.TFState, error) {
switch p.backend {
case "":
state, err := tfstate.ReadFile(f)
if err != nil {
return nil, fmt.Errorf("reading tfstate for %s: %w", k, err)
}
return state, nil
default:
url := p.backend + "://" + f
state, err := tfstate.ReadURL(url)
if err != nil {
return nil, fmt.Errorf("reading tfstate for %s: %w", k, err)
}
return state, nil
}
}

func (p *provider) GetStringMap(key string) (map[string]interface{}, error) {
return nil, fmt.Errorf("path fragment is not supported for tfstate provider")
}
6 changes: 5 additions & 1 deletion pkg/stringprovider/stringprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ func New(provider api.StaticConfig) (api.LazyLoadedStringProvider, error) {
case "gcpsecrets":
return gcpsecrets.New(provider), nil
case "tfstate":
return tfstate.New(provider), nil
return tfstate.New(provider, ""), nil
case "tfstategs":
return tfstate.New(provider, "gs"), nil
case "tfstates3":
return tfstate.New(provider, "s3"), nil
case "azurekeyvault":
return azurekeyvault.New(provider), nil
}
Expand Down
10 changes: 9 additions & 1 deletion vals.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const (
ProviderFile = "file"
ProviderGCPSecretManager = "gcpsecrets"
ProviderTFState = "tfstate"
ProviderTFStateGS = "tfstategs"
ProviderTFStateS3 = "tfstates3"
ProviderAzureKeyVault = "azurekeyvault"
)

Expand Down Expand Up @@ -158,7 +160,13 @@ func (r *Runtime) Eval(template map[string]interface{}) (map[string]interface{},
p := gcpsecrets.New(conf)
return p, nil
case ProviderTFState:
p := tfstate.New(conf)
p := tfstate.New(conf, "")
return p, nil
case ProviderTFStateGS:
p := tfstate.New(conf, "gs")
return p, nil
case ProviderTFStateS3:
p := tfstate.New(conf, "s3")
return p, nil
case ProviderAzureKeyVault:
p := azurekeyvault.New(conf)
Expand Down

0 comments on commit bed2158

Please sign in to comment.