hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.74k stars 9.1k forks source link

aws_iam_policy policy must be array, even for Statement with single item, from 3.69 #22879

Open acw-eng opened 2 years ago

acw-eng commented 2 years ago

Community Note

Affected Resource(s)

Description

As of provider version 3.69, the policy of an aws_iam_policy expects the "Statement": to be an array, even if the "Statement": only contains a single item.

Here is a simple example that will reproduce the bug:

resource "aws_iam_policy" "foo" {
  name        = "foo"
  description = "example"
  policy      = file("${path.module}/pathtopolicy/policy.json")
}

policy.json can contain any single statement policy such as:

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "ec2:DescribeInstances",
        "Resource": "*"
    }
}

If you use provider version 3.69 or later, this will fail to create with the following error:

error creating IAM policy foo: MalformedPolicyDocument: The policy failed legacy parsing

However, with provider version 3.68 or earlier, this will create successfully.

To get the resource to create successfully using provider version 3.69 or later, one must format policy.json as such:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ec2:DescribeInstances",
            "Resource": "*"
        }
    ]
}

Creating a "Statement": without the leading and trailing [] is a valid IAM policy. It has always worked and can be tested in the AWS console as a valid policy.

The policy can be created using provider 3.68 for example, and then Terraform can be run again using a later provider, and it will apply cleanly with no changes detected.

There are no differences in the jsonencode string that the Terraform plan provides as the policy in either provider.

templatefile() is also affected.

What is particularly odd is that aws_iam_role still accepts an assume_role_policy from a file() or templatefile() with a "Statement:" without the leading and trailing [] such as

{
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "AllowAssume",
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::${ACCOUNT_ID}:root"
        },
        "Action": "sts:AssumeRole"
    }
}

Therefore it looks like only aws_iam_policy is affected (others may be too, but those are the only two I've tried).

Terraform version

Terraform v1.1.4 on MacOS 12.2, amd64

ericrichtert commented 2 years ago

We have the same issue. The JSON with 'Statement' as a single value will fail, 'Statement' as an array will work.

The failing JSON is no problem in the AWS console.

tymik commented 1 year ago

The issue seems to be already spotted in at least two SO questions: https://stackoverflow.com/questions/70789771/terraform-json-malformedpolicydocument-the-policy-failed-legacy-parsing https://stackoverflow.com/questions/75312843/terraform-iam-policy-creation-malformedpolicydocument-the-policy-failed-legac

I have copied my policy implicitly from the AWS docs that comes with no array for Statement so I believe this needs to be addressed.

My TF Version:

terraform --version
Terraform v1.3.9
on linux_amd64
+ provider registry.terraform.io/hashicorp/aws v5.15.0
cannontrodder commented 1 month ago

What has absolutely thrown me here is I have imported policies into terraform and dumped a valid json representation into my local repo. When passing that in from a file() call, terraform considers them the SAME as in aws but it's only the create that fails here. Not a bug though, the docs do hint at this:

We suggest using jsonencode() or aws_iam_policy_document when assigning a value to policy. They seamlessly translate Terraform language into JSON, enabling you to maintain consistency within your configuration without the need for context switches. Also, you can sidestep potential complications arising from formatting discrepancies, whitespace inconsistencies, and other nuances inherent to JSON.

Always better to describe as hcl BUT in my case I had to automate the import of MANY policies so querying them from AWS and dumping the json to disk wasn't simple to do in hcl format.