2

I am trying to use a file which contains load balancer iam policy for my AWS in terraform. However when i run the terraform script, i get an error stating:

 Error: "policy" contains an invalid JSON policy
│ 
│   with module.iam.aws_iam_policy.test-AWSLoadBalancerControllerIAMPolicy,
│   on ../resources/IAM/main.tf line 77, in resource "aws_iam_policy" "test-AWSLoadBalancerControllerIAMPolicy":
│   77:  policy = jsonencode(var.policy_file)

I am importing the content of the policy file with jsonencode function.

resource "aws_iam_policy" "test-AWSLoadBalancerControllerIAMPolicy" {
 name = var.aws_loadbalancer_controller_policy
 policy = jsonencode(var.policy_file)
}

the value of the iam_policy_file variable above is passed in like file("../resources/IAM/policy.json") and the content of that file is shown below, i have checked to see if this json file parses online as a valid json and it does, however Terraform throws the error as if there it is badly formatted.

policy.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:CreateServiceLinkedRole"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeAccountAttributes",
                "ec2:DescribeAddresses",
                "ec2:DescribeAvailabilityZones",
                "ec2:DescribeInternetGateways",
                "ec2:DescribeVpcs",
                "ec2:DescribeVpcPeeringConnections",
                "ec2:DescribeSubnets",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeInstances",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeTags",
                "ec2:GetCoipPoolUsage",
                "ec2:DescribeCoipPools",
                "elasticloadbalancing:DescribeLoadBalancers",
                "elasticloadbalancing:DescribeLoadBalancerAttributes",
                "elasticloadbalancing:DescribeListeners",
                "elasticloadbalancing:DescribeListenerCertificates",
                "elasticloadbalancing:DescribeSSLPolicies",
                "elasticloadbalancing:DescribeRules",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DescribeTargetGroupAttributes",
                "elasticloadbalancing:DescribeTargetHealth",
                "elasticloadbalancing:DescribeTags"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "cognito-idp:DescribeUserPoolClient",
                "acm:ListCertificates",
                "acm:DescribeCertificate",
                "iam:ListServerCertificates",
                "iam:GetServerCertificate",
                "waf-regional:GetWebACL",
                "waf-regional:GetWebACLForResource",
                "waf-regional:AssociateWebACL",
                "waf-regional:DisassociateWebACL",
                "wafv2:GetWebACL",
                "wafv2:GetWebACLForResource",
                "wafv2:AssociateWebACL",
                "wafv2:DisassociateWebACL",
                "shield:GetSubscriptionState",
                "shield:DescribeProtection",
                "shield:CreateProtection",
                "shield:DeleteProtection"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:CreateSecurityGroup"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:CreateTags"
            ],
            "Resource": "arn:aws:ec2:*:*:security-group/*",
            "Condition": {
                "StringEquals": {
                    "ec2:CreateAction": "CreateSecurityGroup"
                },
                "Null": {
                    "aws:RequestTag/elbv2.k8s.aws/cluster": "false"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:CreateTags",
                "ec2:DeleteTags"
            ],
            "Resource": "arn:aws:ec2:*:*:security-group/*",
            "Condition": {
                "Null": {
                    "aws:RequestTag/elbv2.k8s.aws/cluster": "true",
                    "aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:RevokeSecurityGroupIngress",
                "ec2:DeleteSecurityGroup"
            ],
            "Resource": "*",
            "Condition": {
                "Null": {
                    "aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:CreateLoadBalancer",
                "elasticloadbalancing:CreateTargetGroup"
            ],
            "Resource": "*",
            "Condition": {
                "Null": {
                    "aws:RequestTag/elbv2.k8s.aws/cluster": "false"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:CreateListener",
                "elasticloadbalancing:DeleteListener",
                "elasticloadbalancing:CreateRule",
                "elasticloadbalancing:DeleteRule"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:AddTags",
                "elasticloadbalancing:RemoveTags"
            ],
            "Resource": [
                "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*",
                "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*",
                "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
            ],
            "Condition": {
                "Null": {
                    "aws:RequestTag/elbv2.k8s.aws/cluster": "true",
                    "aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:AddTags",
                "elasticloadbalancing:RemoveTags"
            ],
            "Resource": [
                "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*",
                "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*",
                "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*",
                "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:ModifyLoadBalancerAttributes",
                "elasticloadbalancing:SetIpAddressType",
                "elasticloadbalancing:SetSecurityGroups",
                "elasticloadbalancing:SetSubnets",
                "elasticloadbalancing:DeleteLoadBalancer",
                "elasticloadbalancing:ModifyTargetGroup",
                "elasticloadbalancing:ModifyTargetGroupAttributes",
                "elasticloadbalancing:DeleteTargetGroup"
            ],
            "Resource": "*",
            "Condition": {
                "Null": {
                    "aws:ResourceTag/elbv2.k8s.aws/cluster": "false"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:DeregisterTargets"
            ],
            "Resource": "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:SetWebAcl",
                "elasticloadbalancing:ModifyListener",
                "elasticloadbalancing:AddListenerCertificates",
                "elasticloadbalancing:RemoveListenerCertificates",
                "elasticloadbalancing:ModifyRule"
            ],
            "Resource": "*"
        }
    ]
}

I downloaded this file from the AWS documentation here

I have also tried to make it work by reading in the file from an http response as shown below and i still get the same issue of Invalid JSON

data "http" "policy_json" {
  url = "https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.4/docs/install/iam_policy.json"
}

resource "aws_iam_policy" "test-AWSLoadBalancerControllerIAMPolicy" {
  name = var.aws_loadbalancer_controller_policy
  policy = data.http.policy_json.request_body
}
eagercoder
  • 123
  • 1
  • 4
  • 1
    surely not *request* body, you mean [`.response_body`](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http#response_body) right? – mdaniel Oct 26 '22 at 15:38
  • While in your initial attempt: `jsonencode(var.policy_file)`. I would assume the filename in itself isn't a valid json, should have tried to `data "local_file"` from that variable, then jsonencode the output of that data, as suggested by your error. Otherwise, @mdaniel suggestion might help as well – SYN Oct 26 '22 at 16:22
  • I specifically didn't dig into that part of the question, but AIUI `jsonencode(legal_json_str)` is going to emit _double encoded_ JSON, but the bogus http version was easy to spot. I've never gotten `terraform console` to do anything for me in order to quickly test that theory, though – mdaniel Oct 26 '22 at 20:51

1 Answers1

1

You must make sure that you are assigning a string containing a JSON representation of an object.

It's hard to follow exactly how you have this set up from your description, but here are some potential problems you should check for:

  1. If var.policy_file contains a filename, then JSON encoding that filename will produce a JSON string containing the filename. For example, if var.policy_file is ../resources/IAM/policy.json then JSON encoding that will produce literally "../resources/IAM/policy.json", which is not a valid IAM policy.
  2. If var.policy_file contains the direct result of file("../resources/IAM/policy.json") then you are asking Terraform to create a JSON encoding of a string that already contains JSON content. For example, if your file contains {"Version": "2012-10-17"} then JSON encoding this will produce "{\"Version\": \"2012-10-17\"}" -- a JSON string which happens to contain more JSON, which is also not a valid IAM policy.

In both of the situations above you would not need to use jsonencode at all, because this file presumably already contains JSON content.

For case 1, you can say policy = file(var.policy_file) to directly assign the contents of the file.

For case 2, you can say policy = var.policy_file to directly assign the JSON string that's in that variable.

You only need to use jsonencode if you are intending to assign a Terraform object value that has not already been JSON encoded, like this:

policy = jsonencode({
  "Version" = "2012-10-17"
  # ...
})

In that case, jsonencode is needed to transform the non-string Terraform object value into a JSON string that the policy argument is expecting.

Martin Atkins
  • 2,508
  • 20
  • 20