tflint icon indicating copy to clipboard operation
tflint copied to clipboard

TFLint errors on JSON syntax with root array of blocks

Open mariusz-slowinski opened this issue 8 months ago • 2 comments

Summary

Correct terraform import statements in import.tf.json file are reported by tflint as invalid.

Failed to check ruleset; failed to check "terraform_deprecated_index" rule: import.tf.json:1,1-2: Incorrect JSON value type; A JSON object is required here, setting the arguments for this block., and 4 other diagnostic(s)

Annotation does not help because JSON comment changes import file structure so it can't be used by terraform later.

Command

tflint

Terraform Configuration

[
  {
    "import": {
      "id": "/subscriptions/<redacted>",
      "to": "azurerm_resource_group.main"
    }
  }
]

TFLint Configuration

tflint {
    required_version = ">= 0.50"
}

Output

18:18:14 config.go:175: [INFO] Load config: .tflint.hcl
18:18:14 config.go:183: [INFO] file not found
18:18:14 config.go:190: [INFO] Load config: /home/maslow/.tflint.hcl
18:18:14 config.go:347: [DEBUG] Config loaded
18:18:14 config.go:348: [DEBUG]   CallModuleType: local
18:18:14 config.go:349: [DEBUG]   CallModuleTypeSet: false
18:18:14 config.go:350: [DEBUG]   Force: false
18:18:14 config.go:351: [DEBUG]   ForceSet: false
18:18:14 config.go:352: [DEBUG]   DisabledByDefault: false
18:18:14 config.go:353: [DEBUG]   DisabledByDefaultSet: false
18:18:14 config.go:354: [DEBUG]   PluginDir: 
18:18:14 config.go:355: [DEBUG]   PluginDirSet: false
18:18:14 config.go:356: [DEBUG]   Format: 
18:18:14 config.go:357: [DEBUG]   FormatSet: false
18:18:14 config.go:358: [DEBUG]   Varfiles: 
18:18:14 config.go:359: [DEBUG]   Variables: 
18:18:14 config.go:360: [DEBUG]   Only: 
18:18:14 config.go:361: [DEBUG]   IgnoreModules:
18:18:14 config.go:365: [DEBUG]   Rules:
18:18:14 config.go:369: [DEBUG]   Plugins:
18:18:14 config.go:476: [INFO] The "terraform" plugin block is not found. Enable the plugin "terraform" automatically
18:18:14 option.go:91: [DEBUG] CLI Options
18:18:14 option.go:92: [DEBUG]   CallModuleType: local
18:18:14 option.go:93: [DEBUG]   Force: false
18:18:14 option.go:94: [DEBUG]   Format: 
18:18:14 option.go:95: [DEBUG]   Varfiles: 
18:18:14 option.go:96: [DEBUG]   Variables: 
18:18:14 option.go:97: [DEBUG]   EnableRules: 
18:18:14 option.go:98: [DEBUG]   DisableRules: 
18:18:14 option.go:99: [DEBUG]   Only: 
18:18:14 option.go:100: [DEBUG]   EnablePlugins: 
18:18:14 option.go:101: [DEBUG]   IgnoreModules:
18:18:14 loader.go:39: [INFO] Initialize new loader
18:18:14 module_mgr.go:63: [INFO] Module manifest file found. Initializing...
18:18:14 loader.go:81: [INFO] Building the root module while calling local child modules...
18:18:14 loader.go:108: [DEBUG] Trying to load the local module: name=resource-group dir=../modules/resource-group
18:18:14 runner.go:46: [INFO] Initialize new runner for root
18:18:14 input_value.go:67: [INFO] TF_VAR_* environment variable found: key=TF_VAR_environment
18:18:14 input_value.go:67: [INFO] TF_VAR_* environment variable found: key=TF_VAR_azdo_org_guid
18:18:14 input_value.go:67: [INFO] TF_VAR_* environment variable found: key=TF_VAR_azdo_org_name
18:18:14 discovery.go:33: [INFO] Plugin "terraform" is not installed, but the bundled plugin is available.
18:18:14 discovery.go:54: [INFO] Plugin "terraform" found
18:18:14 [DEBUG] cmdrunner/cmd_runner.go:73: starting plugin: path=/usr/local/bin/tflint args=["/usr/local/bin/tflint", "--act-as-bundled-plugin"]
18:18:14 [DEBUG] cmdrunner/cmd_runner.go:80: plugin started: path=/usr/local/bin/tflint pid=88738
18:18:14 [DEBUG] [email protected]/client.go:827: waiting for RPC address: plugin=/usr/local/bin/tflint
18:18:14 [DEBUG] [email protected]/client.go:1216: tflint: 18:18:14 [DEBUG] [email protected]/server.go:419: plugin address: network=unix address=/tmp/plugin191908899
18:18:14 [DEBUG] [email protected]/client.go:880: using plugin: version=11
18:18:14 [DEBUG] host2plugin/client.go:124: starting host-side gRPC server
18:18:14 [DEBUG] [email protected]/client.go:1216: tflint: 18:18:14 [ERROR] interceptor/logging.go:18: failed to gRPC request: direction=host2plugin method=/proto.RuleSet/Check err="rpc error: code = Aborted desc = failed to check \"terraform_deprecated_index\" rule: import.tf.json:1,1-2: Incorrect JSON value type; A JSON object is required here, setting the arguments for this block., and 4 other diagnostic(s)"
18:18:14 [DEBUG] [email protected]/grpc_stdio.go:142: stdio: received EOF, stopping recv loop: err="rpc error: code = Unavailable desc = error reading from server: EOF"
18:18:14 [INFO]  [email protected]/client.go:780: plugin process exited: plugin=/usr/local/bin/tflint id=88738
18:18:14 [DEBUG] [email protected]/client.go:558: plugin exited
Failed to check ruleset; failed to check "terraform_deprecated_index" rule: import.tf.json:1,1-2: Incorrect JSON value type; A JSON object is required here, setting the arguments for this block., and 4 other diagnostic(s)

TFLint Version

0.53.0

Terraform Version

No response

Operating System

  • [x] Linux
  • [ ] macOS
  • [ ] Windows

mariusz-slowinski avatar Apr 01 '25 16:04 mariusz-slowinski

I can work on this.

harivamsi9 avatar Apr 01 '25 17:04 harivamsi9

Is this documented anywhere?

https://developer.hashicorp.com/terraform/language/syntax/json

At the root of any JSON-based Terraform configuration is a JSON object.

Since import blocks are unlabeled, you can't have a imports be a map like resources et al. But I'd certainly expect {"import": [{}]} and not [{"import": {}].

The only specific reference I can find is here:

https://discuss.hashicorp.com/t/import-blocks-in-json-format/64373

It seems that you've discovered an undocumented feature in the Terraform JSON syntax, which has nothing to do with import.

[
  {
    "resource": {
      "null_resource": {
        "foo": {},
        "bar": {}
      }
    }
  },
  {
    "resource": {
      "null_resource": {
        "baz": {}
      }
    }
  }
]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # null_resource.bar will be created
  + resource "null_resource" "bar" {
      + id = (known after apply)
    }

  # null_resource.baz will be created
  + resource "null_resource" "baz" {
      + id = (known after apply)
    }

  # null_resource.foo will be created
  + resource "null_resource" "foo" {
      + id = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.

While supporting this "array of blocks" syntax would be fine, it's not a priority and you should probably just fix your configuration.

bendrucker avatar Apr 01 '25 23:04 bendrucker

While supporting this "array of blocks" syntax would be fine, it's not a priority and you should probably just fix your configuration.

How do you fix it in your configuration? For example, my main.tf.json contains a list of module blocks. What's the solution here? Have a block per module import? That doesn't sound like a configuration change.

vasily-kartashov avatar Jul 11 '25 23:07 vasily-kartashov

https://developer.hashicorp.com/terraform/language/syntax/json#module-blocks

The structure is essentially the same for all labeled blocks:

{
  "module": {
    "vpc": {
      "source": "terraform-aws-modules/vpc/aws",
      "name": "test-vpc",
      "cidr": "10.0.0.0/16"
    },
    "security_group": {
      "source": "terraform-aws-modules/security-group/aws",
      "name": "test-sg",
      "description": "Test security group"
    }
  }
}

The only difference between resource and module is the number of labels (2 vs. 1) which determines the level of nesting of the objects inside the map.

Unlabeled blocks like import only differ in that they are a list structure. At the top level, the order is meaningless, but in nested blocks, order can matter.

bendrucker avatar Jul 11 '25 23:07 bendrucker

https://github.com/terraform-linters/tflint-plugin-sdk/pull/422 will make this syntax supported, while https://github.com/terraform-linters/tflint-ruleset-terraform/pull/297 will make it trigger a warning by default and recommend the object syntax. But you can always suppress that whereas currently the object syntax is required.

bendrucker avatar Oct 16 '25 02:10 bendrucker