AWS multiple regions in JSON format

This is what I tested in 0.12 and it’s working, but I want to make sure this is officially supported as the other JSON format that was converted from HCL directly contains duplicated keys which is (debatable) valid JSON but cannot be handled by our toolchains:

{
  "provider": [
    {
      "aws": {
        "region": "us-east-1"
      }
    },
    {
      "aws": {
        "alias": "primary",
        "region": "us-east-1"
      }
    },
    {
      "aws": {
        "alias": "secondary",
        "region": "us-east-2"
      }
    }
  ],
}

This one works too, and it’s convert from HCL directly:

{
  "provider": {
    "aws": {
      "region": "us-east-1"
    },
    "aws": {
      "alias": "primary",
      "region": "us-east-1"
    },
    "aws": {
      "alias": "secondary",
      "region": "us-east-2"
    }
  }
}

The HCL version:

provider "aws" {
  region  = "us-east-1"
}

provider "aws" {
  alias  = "primary"
  region = "us-east-1"
}

provider "aws" {
  alias  = "secondary"
  region = "us-east-2"
}

Hi @hangxie,

The mapping from Terraform native syntax to JSON is described in more detail in the JSON Configuration Syntax documentation. The relevant section for this question is Nested Block Mapping; although in this case the provider block isn’t “nested” in the sense of being inside another block, in practice the rules for processing blocks are the same at all nesting levels.

The usage of provider blocks with alias set is similar to provisioner blocks inside resource where often the block labels alone are not sufficient to make the JSON property name unique, and so indeed you can use the array-of-objects form shown in your first example with provider blocks, similarly to what is shown for provisioner blocks in the documentation.

This syntax generalizes for anything defined as being a nested block in the native syntax. For example, you could write variable blocks that way too, even though it’d be unnecessary in that case because they have unique labels by definition:

{
  "variable": [
    {
      "foo": {
        "type": "string"
      }
    },
    {
      "foo": {
        "type": "string"
      }
    }
  ]
}

This is a situation where we’re representing a concept that doesn’t exist in JSON, so Terraform’s configuration decoder is using its knowledge that provider, provisioner, and variable are all defined to be block types in their respective contexts and using this more liberal/flexible processing model to strike a compromise between conveniently representing simple cases where the labels are unique while still being able to handle the more complex cases like yours using common JSON tools.


Giving a totally invalid value for the "provider" property causes Terraform to produce an error message that betrays a bit how it’s processing this:

Error: Incorrect JSON value type

  on json-blocks.tf.json line 2:
   2:   "provider": "invalid"

Either a JSON object or JSON array of objects is required here, to specify
name labels for this block.

Whenever the property name is specified as being a block type rather than an attribute, the decoder will check to see if the given property value is either a JSON object or JSON array, and it will then traverse that value to go looking for label keys, either by looking directly at the given object properties or by iterating over the array and looking at the properties of each nested object in turn.

Since Terraform is just scanning over the contents anyway, it can and will accept duplicate property names if they describe something that’s valid per the Terraform language schema, but indeed most software generating JSON cannot produce such a thing and so the array-of-objects form is easier to produce programmatically.

Thanks for the detailed explanation, it’s really helpful.