From 145bf42806a4b29ba18f083d238a4f73928b6726 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sat, 29 Oct 2016 15:49:28 -0700 Subject: [PATCH] provider/aws: IAM policy document: normalize wildcard principals There are three equivalent forms for expressing "everyone" (including anonymous) in IAM policies: - "Principals": "*" - "Principals": {"AWS": "*"} - "Principals": {"*": "*"} The more-constrained syntax used by our aws_iam_policy_document data source means that the user can only express the latter two of these directly. However, when returning IAM policies from the API AWS likes to normalize to the first form, causing unresolvable diffs. This fixes #9335 by handling the "everyone" case as a special case, serializing it in JSON as the "*" shorthand form. This change does *not* address the normalization of hand-written policies containing such elements. A similar change would need to be made in the external package github.com/jen20/awspolicyequivalence in order to avoid the issue for hand-written policies. --- ...ata_source_aws_iam_policy_document_test.go | 30 +++++++++++++++++++ builtin/providers/aws/iam_policy_model.go | 17 +++++++++++ 2 files changed, 47 insertions(+) diff --git a/builtin/providers/aws/data_source_aws_iam_policy_document_test.go b/builtin/providers/aws/data_source_aws_iam_policy_document_test.go index a50a8ae29..a720d181a 100644 --- a/builtin/providers/aws/data_source_aws_iam_policy_document_test.go +++ b/builtin/providers/aws/data_source_aws_iam_policy_document_test.go @@ -106,6 +106,24 @@ data "aws_iam_policy_document" "test" { not_resources = ["arn:aws:s3:::*"] } + # Normalization of wildcard principals + statement { + effect = "Allow" + actions = ["kinesis:*"] + principals { + type = "AWS" + identifiers = ["*"] + } + } + statement { + effect = "Allow" + actions = ["firehose:*"] + principals { + type = "*" + identifiers = ["*"] + } + } + } ` @@ -156,6 +174,18 @@ var testAccAWSIAMPolicyDocumentExpectedJSON = `{ "Effect": "Deny", "NotAction": "s3:*", "NotResource": "arn:aws:s3:::*" + }, + { + "Sid": "", + "Effect": "Allow", + "Action": "kinesis:*", + "Principal": "*" + }, + { + "Sid": "", + "Effect": "Allow", + "Action": "firehose:*", + "Principal": "*" } ] }` diff --git a/builtin/providers/aws/iam_policy_model.go b/builtin/providers/aws/iam_policy_model.go index 59192fbf1..81306971d 100644 --- a/builtin/providers/aws/iam_policy_model.go +++ b/builtin/providers/aws/iam_policy_model.go @@ -40,6 +40,23 @@ type IAMPolicyStatementConditionSet []IAMPolicyStatementCondition func (ps IAMPolicyStatementPrincipalSet) MarshalJSON() ([]byte, error) { raw := map[string]interface{}{} + // As a special case, IAM considers the string value "*" to be + // equivalent to "AWS": "*", and normalizes policies as such. + // We'll follow their lead and do the same normalization here. + // IAM also considers {"*": "*"} to be equivalent to this. + if len(ps) == 1 { + p := ps[0] + if p.Type == "AWS" || p.Type == "*" { + if sv, ok := p.Identifiers.(string); ok && sv == "*" { + return []byte(`"*"`), nil + } + + if av, ok := p.Identifiers.([]string); ok && len(av) == 1 && av[0] == "*" { + return []byte(`"*"`), nil + } + } + } + for _, p := range ps { switch i := p.Identifiers.(type) { case []string: