Reputation: 1
Please see below. First, with only the assume role policy, it works. If I remove the inline the policy, it all validates. When left in, (it looks like this.) It does not validate. I am using Terragrunt, but I believe this is a Terraform error.
resource "aws_iam_role" "test_role" {
name = "my_test_role"
assume_role_policy = jsonencode("${file("..//Policies//policy_assume_role.json")}")
inline_policy {
name = "inline_s3_policy"
policy = jsonencode("${file("..//Policies//policy_s3_bucket.json")}")
}
}
Then my policy_s3_bucket.json looks like this
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::company-terragrunt-terraform-state-123456789-us-east-1"]
},
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::company-terragrunt-terraform-state-123456789-us-east-1/*"]
}
]
}
I get - ““inline_policy.0.policy” contains an invalid JSON policy” … but the JSON is valid. My configured user does have access to those buckets. Also, the assume role policy is working without the s3 inline in there. The assume role policy json looks to be the same format, and I’m pulling it in the same fashion.
Upvotes: 0
Views: 1185
Reputation: 74779
In your configuration you seem to be passing the result of the file
function into the jsonencode
function.
The result of the file
function is always a string representing the UTF-8-encoded contents of the file, and so if your file contains already-encoded JSON then it will return a string containing JSON.
If you pass a string to jsonencode
then it will produce a JSON-formatted string, whereas an IAM policy requires a JSON object, and therefore the API will return an error as shown here.
To be more specific, your current configuration will set policy
to something like the following (truncated for brevity):
"{\n \"Version\": \"2012-10-17\"m\n \"Statement\": ..."
If you know that your external file already contains valid JSON then you can assign the result of file
directly to the policy
argument, like this:
resource "aws_iam_role" "test_role" {
name = "my_test_role"
assume_role_policy = file("${path.module}/../Policies/policy_assume_role.json")
inline_policy {
name = "inline_s3_policy"
policy = file("${path.module}/../Policies/policy_s3_bucket.json")
}
}
If you'd like Terraform to parse the JSON and reencode it -- which will mean that Terraform will check whether the JSON content is valid locally first, and will always generate it in a consistent minified form, you can alternatively pass the file
result to jsondecode
first, and then pass that result to jsonencode
, thereby "round-tripping" through the Terraform language type system and back to JSON again:
resource "aws_iam_role" "test_role" {
name = "my_test_role"
assume_role_policy = jsonencode(jsondecode(file("${path.module}/../Policies/policy_assume_role.json")))
inline_policy {
name = "inline_s3_policy"
policy = jsonencode(jsondecode(file("${path.module}/../Policies/policy_s3_bucket.json")))
}
}
However, this would be a pretty unusual approach and so if you adopt it then I would recommend including a comment explaining why you did it, so that a future reader can understand why this seemingly-redundant transformation is included.
Upvotes: 1