Skip to content

Commit

Permalink
Add support for Terraform v1.10 (#2178)
Browse files Browse the repository at this point in the history
* Bump tflint-plugin-sdk to v0.22.0

* Add `ephemeralasnull` function

Follow up of hashicorp/terraform#35363
Follow up of hashicorp/terraform#35652

* Introduce ephemeral input variables

Follow up of hashicorp/terraform#35273
Follow up of hashicorp/terraform#35985

Terraform throws an error if you use ephemeral values for the count meta-argument,
but TFLint does not. This is because the reason for throwing an error is that
plan cannot have ephemeral values, which is not an issue in the context of static analysis.
In the future, we can throw an error if we need to match this behavior.

* Functions that allow marks must also deal with unknown values

Follow up of hashicorp/terraform#35985

* Add `terraform.applying` symbol

Follow up of hashicorp/terraform@7c928fc

* Introduce ephemeral resources

Follow up of hashicorp/terraform#35727
Follow up of hashicorp/terraform#35728

Ephemeral resource addresses are like resources in that they always
resolve to unknown values, but they differ in that they are marked
as ephemeral, which can have a subtle effect on the return value of
the ephemeralasnull function.

* `issensitive` must return unknown for unknown args without `sensitive`

Follow up of hashicorp/terraform#36012

* Fix `templatefile` function for unknown/marked values

Follow up of hashicorp/terraform#36118
Follow up of hashicorp/terraform#36127

* Update collections to use for-range method

Follow up of hashicorp/terraform#35818

* Include context when variable default has nested problem

Follow up of hashicorp/terraform#35465

* Allow marked values in dynamic block `for_each`

Follow up of hashicorp/hcl#679

Previously, for_each in dynamic blocks did not allow marked values
such as sensitive. However, hashicorp/hcl#679
now supports this by propagating the marks to expanded children.

The reason behind this is to add a new mark called "ephemeral",
so we'll pull the changes to support Terraform 1.10.

Note that tfhcl's dynamic block support has incomplete mark propagation
since marked values resolve to unknown values. This is because in the past
the marked values could not be sent over the wire protocol,
and may be fixed in the near future.

* Do not return ephemeral values to unsupported plugins

Because ephemeral values are likely to contain secrets,
return ErrSensitive for plugins that do not support it
to prevent unintended disclosure.

* Add E2E tests for ephemeral values and marked dynamic blocks

* Update Terraform compatibility guide
  • Loading branch information
wata727 authored Jan 11, 2025
1 parent 0686ef9 commit b638d2f
Show file tree
Hide file tree
Showing 40 changed files with 1,126 additions and 222 deletions.
3 changes: 3 additions & 0 deletions docs/developer-guide/api_compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ TFLint version: v0.42.0+
- https://github.com/terraform-linters/tflint/pull/1722
- https://github.com/terraform-linters/tflint-plugin-sdk/pull/235
- https://github.com/terraform-linters/tflint-plugin-sdk/pull/239
- Ephemeral value is introduced in SDK v0.22.0 and TFLint v0.55.0. TFLint returns ErrSensitive instead of ephemeral values to plugins built with SDK v0.21.0.
- https://github.com/terraform-linters/tflint/pull/2178
- https://github.com/terraform-linters/tflint-plugin-sdk/pull/358
20 changes: 14 additions & 6 deletions docs/user-guide/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ TFLint interprets the [Terraform language](https://developer.hashicorp.com/terra

The parser supports Terraform v1.x syntax and semantics. The language compatibility on Terraform v1.x is defined by [Compatibility Promises](https://developer.hashicorp.com/terraform/language/v1-compatibility-promises). TFLint follows this promise. New features are only supported in newer TFLint versions, and bug and experimental features compatibility are not guaranteed.

The latest supported version is Terraform v1.9.
The latest supported version is Terraform v1.10.

## Input Variables

Expand Down Expand Up @@ -32,7 +32,7 @@ resource "aws_instance" "foo" {
}
```

Sensitive variables are ignored. This is to avoid unintended disclosure.
Sensitive or ephemeral variables are ignored. This is to avoid unintended disclosure.

```hcl
variable "instance_type" {
Expand Down Expand Up @@ -101,7 +101,7 @@ resource "aws_instance" "foo" {
}
```

## The `path.*` and `terraform.workspace` Values
## The `path.*` and `terraform.*` Values

TFLint supports [filesystem and workspace info](https://developer.hashicorp.com/terraform/language/expressions/references#filesystem-and-workspace-info).

Expand All @@ -110,14 +110,20 @@ TFLint supports [filesystem and workspace info](https://developer.hashicorp.com/
- `path.cwd`
- `terraform.workspace`.

The [`terraform.applying`](https://developer.hashicorp.com/terraform/language/functions/terraform-applying) always resolves to false.

## Unsupported Named Values

The values below are state-dependent and cannot be determined statically, so TFLint resolves them to unknown values.
The values below are state-dependent or cannot be determined statically, so TFLint resolves them to unknown values.

- `<RESOURCE TYPE>.<NAME>`
- `resource.<RESOURCE TYPE>.<NAME>`
- `ephemeral.<RESOURCE TYPE>.<NAME>`
- `module.<MODULE NAME>`
- `data.<DATA TYPE>.<NAME>`
- `self`
- `self.<NAME>`

The `ephemeral.<RESOURCE TYPE>.<NAME>` always resolves to an unknown value marked as ephemeral, which is the same in most cases as anything else, but some rules may treat it differently.

## Functions

Expand All @@ -143,7 +149,9 @@ resource "aws_instance" "dynamic" {
}
```

Similar to support for meta-arguments, some rules may process a dynamic block as-is without expansion. If the `for_each` is unknown, the block will be empty.
Similar to support for meta-arguments, some rules may process a dynamic block as-is without expansion.

If the `for_each` is unknown, the expanded block will be empty. If the `for_each` is sensitive or ephemeral, TFLint expands dynamic blocks like Terraform does, but the mark does not propagate to children and expressions containing iterators resolve to unknown. This is a backwards compatibility limitation that may be resolved in a future version.

## Modules

Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ require (
github.com/sourcegraph/go-lsp v0.0.0-20200429204803-219e11d77f5d
github.com/sourcegraph/jsonrpc2 v0.2.0
github.com/spf13/afero v1.11.0
github.com/terraform-linters/tflint-plugin-sdk v0.21.0
github.com/terraform-linters/tflint-plugin-sdk v0.22.0
github.com/terraform-linters/tflint-ruleset-terraform v0.10.0
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zclconf/go-cty v1.16.0
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940
github.com/zclconf/go-cty-yaml v1.1.0
golang.org/x/crypto v0.32.0
golang.org/x/net v0.33.0
golang.org/x/net v0.34.0
golang.org/x/oauth2 v0.25.0
golang.org/x/text v0.21.0
google.golang.org/grpc v1.69.2
Expand Down Expand Up @@ -139,17 +139,17 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.23.0 // indirect
golang.org/x/tools v0.29.0 // indirect
google.golang.org/api v0.172.0 // indirect
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/protobuf v1.35.1 // indirect
google.golang.org/protobuf v1.36.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -696,8 +696,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/terraform-linters/tflint-plugin-sdk v0.21.0 h1:RoorxuuWh1RuL09PWAmaCKw/hmb9QP5dukGXZiB0fs8=
github.com/terraform-linters/tflint-plugin-sdk v0.21.0/go.mod h1:f7ruoYh44RQvnZRxpWhn8JFkpEVlQFT8wC9MhIF0Rp4=
github.com/terraform-linters/tflint-plugin-sdk v0.22.0 h1:holOVJW0hjf0wkjtnYyPWRooQNp8ETUcKE86rdYkH5U=
github.com/terraform-linters/tflint-plugin-sdk v0.22.0/go.mod h1:Cag3YJjBpHdQzI/limZR+Cj7WYPLTIE61xsCdIXoeUI=
github.com/terraform-linters/tflint-ruleset-terraform v0.10.0 h1:L+3K3oGvZe5UdQ9F6PMQ6n69A2+Q11dBSg+5nTvxJi8=
github.com/terraform-linters/tflint-ruleset-terraform v0.10.0/go.mod h1:wT8nMRBpCg1cIL0Td3LQ3XPcnTTHwBhbCNrFp4jWFrI=
github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=
Expand Down Expand Up @@ -818,8 +818,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -867,8 +867,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -1055,8 +1055,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
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 @@ -1284,8 +1284,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
Expand Down
4 changes: 2 additions & 2 deletions integrationtest/inspection/inspection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,9 @@ func TestIntegration(t *testing.T) {
Dir: "eval-on-root-context",
},
{
Name: "sensitive variable",
Name: "marked values",
Command: "tflint --format json",
Dir: "sensitive",
Dir: "marked",
},
{
Name: "just attributes",
Expand Down
48 changes: 48 additions & 0 deletions integrationtest/inspection/marked/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
variable "no_marked" {
default = "t2.micro"
}

variable "sensitive" {
sensitive = true
default = "t2.micro"
}

variable "ephemeral" {
ephemeral = true
default = "t2.micro"
}

variable "marked_set" {
sensitive = true
default = [true]
}

resource "aws_instance" "no_marked" {
instance_type = var.no_marked
}

resource "aws_instance" "sensitive" {
instance_type = var.sensitive
}

resource "aws_instance" "ephemeral" {
instance_type = var.ephemeral
}

resource "aws_s3_bucket" "main" {
dynamic "lifecycle_rule" {
for_each = var.marked_set

content {
enabled = lifecycle_rule.value
}
}

dynamic "lifecycle_rule" {
for_each = var.marked_set

content {
enabled = true
}
}
}
85 changes: 85 additions & 0 deletions integrationtest/inspection/marked/result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"issues": [
{
"rule": {
"name": "aws_instance_example_type",
"severity": "error",
"link": ""
},
"message": "instance type is t2.micro",
"range": {
"filename": "main.tf",
"start": {
"line": 21,
"column": 19
},
"end": {
"line": 21,
"column": 32
}
},
"callers": []
},
{
"rule": {
"name": "aws_s3_bucket_example_lifecycle_rule",
"severity": "error",
"link": ""
},
"message": "`lifecycle_rule` block found",
"range": {
"filename": "main.tf",
"start": {
"line": 33,
"column": 3
},
"end": {
"line": 33,
"column": 27
}
},
"callers": []
},
{
"rule": {
"name": "aws_s3_bucket_example_lifecycle_rule",
"severity": "error",
"link": ""
},
"message": "`lifecycle_rule` block found",
"range": {
"filename": "main.tf",
"start": {
"line": 41,
"column": 3
},
"end": {
"line": 41,
"column": 27
}
},
"callers": []
},
{
"rule": {
"name": "aws_s3_bucket_example_lifecycle_rule",
"severity": "error",
"link": ""
},
"message": "`enabled` attribute found: true",
"range": {
"filename": "main.tf",
"start": {
"line": 45,
"column": 17
},
"end": {
"line": 45,
"column": 21
}
},
"callers": []
}
],
"errors": []
}
17 changes: 0 additions & 17 deletions integrationtest/inspection/sensitive/main.tf

This file was deleted.

25 changes: 0 additions & 25 deletions integrationtest/inspection/sensitive/result.json

This file was deleted.

6 changes: 5 additions & 1 deletion plugin/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
hcl "github.com/hashicorp/hcl/v2"
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
"github.com/terraform-linters/tflint-plugin-sdk/plugin/plugin2host"
"github.com/terraform-linters/tflint-plugin-sdk/terraform/lang/marks"
sdk "github.com/terraform-linters/tflint-plugin-sdk/tflint"
"github.com/terraform-linters/tflint/terraform"
"github.com/terraform-linters/tflint/tflint"
Expand Down Expand Up @@ -145,7 +146,10 @@ func (s *GRPCServer) EvaluateExpr(expr hcl.Expression, opts sdk.EvaluateExprOpti

// SDK v0.16+ introduces client-side handling of unknown/NULL/sensitive values.
if s.clientSDKVersion != nil && s.clientSDKVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.16.0"))) {
return val, nil
// Before SDK v0.22, ephemeral marks are ignored, so retrun ErrSensitive to prevent secrets ​​from being leaked.
if !marks.Contains(val, marks.Ephemeral) || s.clientSDKVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.22.0"))) {
return val, nil
}
}

if val.ContainsMarked() {
Expand Down
Loading

0 comments on commit b638d2f

Please sign in to comment.