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.
This commit is contained in:
Martin Atkins 2016-10-29 15:49:28 -07:00
parent f16309630e
commit 145bf42806
2 changed files with 47 additions and 0 deletions

View File

@ -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": "*"
}
]
}`

View File

@ -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: