TerraformTerraform

Error: Unsupported argument / An argument named X is not expected here

The argument does not exist on that resource, usually because of a provider version change, a nested block written as an argument, or a module input that was renamed.

easy fix5 min read

the terraform error
Error: Unsupported argument
An argument named "vpc" is not expected here.

Error: Unsupported block type
Blocks of type "ingress" are not expected here.

Error: Missing required argument
The argument "name" is required, but no definition was found.

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

  1. 1

    Check the provider version actually in use

    terraform version && terraform providers

    The argument may have been valid in an older major version. Terraform providers shows the constraints, and the lock file records what was selected, which is what the error is being raised against.

  2. 2

    Ask the provider what arguments it accepts

    terraform providers schema -json | python3 -c "import sys,json; s=json.load(sys.stdin); r=s['provider_schemas']['registry.terraform.io/hashicorp/aws']['resource_schemas']['aws_security_group']; print('\n'.join(sorted(r['block']['attributes'])))"

    This is authoritative for the exact version you have, unlike documentation which shows the latest. Substitute your own provider and resource type.

  3. 3

    For a module, read the variables it actually declares

    grep -rh "^variable" .terraform/modules/vpc/variables.tf 2>/dev/null | head -30

    A module input that was renamed between versions produces this error against the module block. The downloaded copy under .terraform/modules is the version actually in use.

All 8 sections

Terraform validated your configuration against the schema the provider or module declares, and the name is not in it. Three common reasons.

1. A provider major version changed it

The most frequent cause. Arguments are removed and renamed across major versions.

terraform version
terraform providers
provider registry.terraform.io/hashicorp/aws v5.70.0

Ask the provider directly rather than reading documentation for the latest version:

terraform providers schema -json \
  | python3 -c "import sys,json; s=json.load(sys.stdin); \
      r=s['provider_schemas']['registry.terraform.io/hashicorp/aws']['resource_schemas']['aws_security_group']; \
      print('\n'.join(sorted(r['block']['attributes'])))"

That is authoritative for the version you actually have.

Some AWS provider examples that catch people:

RemovedReplacement
aws_s3_bucket inline versioning blockaws_s3_bucket_versioning resource
aws_s3_bucket inline lifecycle_ruleaws_s3_bucket_lifecycle_configuration
aws_s3_bucket inline aclaws_s3_bucket_acl
aws_instance vpcsubnet_id
aws_security_group inline ingressaws_vpc_security_group_ingress_rule

The v4 split of aws_s3_bucket into a dozen separate resources caused a very large number of these errors. Each inline block became its own resource, and each needs importing rather than simply rewriting, because the state addresses changed.

Pin the version so an upgrade is deliberate:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.70"
    }
  }
}

2. A block written as an argument, or vice versa

Error: Unsupported block type
Blocks of type "ingress" are not expected here.

HCL distinguishes the two:

tags = { Name = "web" }      # argument: name, equals, value

ingress {                     # block: name, braces
  from_port = 80
}

Getting it the wrong way round gives one of these two errors. The schema output above tells you which a given name is: attributes are arguments, block_types are blocks.

terraform providers schema -json \
  | python3 -c "import sys,json; s=json.load(sys.stdin); \
      r=s['provider_schemas']['registry.terraform.io/hashicorp/aws']['resource_schemas']['aws_security_group']; \
      print('blocks:', ', '.join(sorted(r['block'].get('block_types',{}))))"

3. A module input was renamed

Error: Unsupported argument
An argument named "enable_nat" is not expected here.
  on main.tf line 12, in module "vpc":

The error names the module block. Read what that version declares:

grep -rh "^variable" .terraform/modules/vpc/*.tf | sort

.terraform/modules/ holds the version actually downloaded, which is what matters. Module registries keep documentation per version, and reading the wrong version's docs is the usual way into this.

Pin modules too:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.13"
}

A source with no version takes the newest on every init -upgrade, which makes this error appear without any change on your part.

"Missing required argument"

The inverse, and often the other half of the same rename:

Error: Missing required argument
The argument "name" is required, but no definition was found.
terraform providers schema -json \
  | python3 -c "import sys,json; s=json.load(sys.stdin); \
      r=s['provider_schemas']['registry.terraform.io/hashicorp/aws']['resource_schemas']['aws_security_group']; \
      print('\n'.join(k for k,v in r['block']['attributes'].items() if v.get('required')))"

Upgrading deliberately

terraform init -upgrade
terraform plan

Read the provider's upgrade guide before a major bump. The AWS provider publishes one per major version listing every removed argument and its replacement, which turns a long debugging session into a checklist.

Where a change also moves state addresses, as with the aws_s3_bucket split, terraform plan proposes destroying and recreating. Use moved blocks or import rather than applying that:

import {
  to = aws_s3_bucket_versioning.assets
  id = "acme-assets"
}

Validate before applying

terraform validate

validate checks configuration against the provider schema without touching state or the network, so it catches every one of these in a second. It belongs in CI ahead of plan:

script:
  - terraform init -lockfile=readonly
  - terraform fmt -check
  - terraform validate
  - terraform plan -out=tfplan

A checklist

  1. terraform version and terraform providers.
  2. terraform providers schema -json for the authoritative argument list.
  3. Check the provider's upgrade guide for that major version.
  4. Argument versus block: = and a value, or a name and braces.
  5. Module error → read variable declarations under .terraform/modules/.
  6. Pin both provider and module versions so upgrades are deliberate.
  7. State addresses changed → moved or import, not destroy and recreate.
  8. Run terraform validate in CI before plan.

Frequently Asked Questions

Why did my configuration stop working without me changing it?

Almost certainly a provider or module version moved. A provider constraint such as ~> 5.0 allows any 5.x release, and a module source with no version takes the newest available, so a terraform init -upgrade, or a fresh CI run with no committed lock file, can pick up a release that renamed or removed the argument. Pin versions and commit .terraform.lock.hcl so version changes only happen when someone makes them.

How do I find out which arguments a resource actually supports?

terraform providers schema -json returns the complete schema for the provider versions you have installed, which is authoritative in a way that documentation is not, since docs show the latest version. The attributes map lists arguments and the block_types map lists nested blocks, which also resolves "Unsupported block type" errors. Filtering for entries marked required gives you the list for "Missing required argument".

What is the difference between Unsupported argument and Unsupported block type?

HCL has two syntaxes: an argument is name = value and a block is name { ... }. Each schema entry is one or the other, so writing ingress = {...} where a block is expected, or tags { ... } where an argument is expected, produces the corresponding error. The provider schema tells you which form each name takes. Confusingly, some providers accept both forms for certain nested structures, which is why the distinction is not always obvious from examples.

Why did the AWS provider v4 upgrade break so many configurations?

Because it split aws_s3_bucket into around a dozen separate resources, so inline versioning, lifecycle_rule, acl, logging and other blocks all became standalone resources such as aws_s3_bucket_versioning. Rewriting the configuration is only half the work: the state addresses changed too, so a plain plan proposes destroying and recreating the bucket. The correct migration uses import blocks or terraform import to bring each new resource into state pointing at the existing bucket.

Can I catch these before running a plan?

Yes. terraform validate checks the configuration against the installed provider schemas without touching state or making network calls, so it finds every unsupported argument, unsupported block and missing required argument in about a second. Put it in CI between init and plan, alongside terraform fmt -check. It will not catch anything requiring state, such as an unknown count, but it eliminates this entire class of error before a plan is attempted.

Reference and practice

Learn the underlying concept

Other Terraform errors