Skip to content

Commit

Permalink
Add merging of duplicate SIDs, add override_json support
Browse files Browse the repository at this point in the history
  • Loading branch information
devonbleak committed Feb 5, 2018
1 parent 399264e commit 91862d2
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 7 deletions.
30 changes: 26 additions & 4 deletions aws/data_source_aws_iam_policy_document.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,18 @@ func dataSourceAwsIamPolicyDocument() *schema.Resource {
Read: dataSourceAwsIamPolicyDocumentRead,

Schema: map[string]*schema.Schema{
"override_json": {
Type: schema.TypeString,
Optional: true,
},
"policy_id": {
Type: schema.TypeString,
Optional: true,
},
"source_json": {
Type: schema.TypeString,
Optional: true,
},
"statement": {
Type: schema.TypeList,
Required: true,
Expand Down Expand Up @@ -85,10 +93,6 @@ func dataSourceAwsIamPolicyDocument() *schema.Resource {
},
},
},
"source_json": {
Type: schema.TypeString,
Optional: true,
},
"json": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -159,6 +163,24 @@ func dataSourceAwsIamPolicyDocumentRead(d *schema.ResourceData, meta interface{}

doc.Statements = append(doc.Statements, stmts...)

// merge in our override_json
if overrideJson, hasOverrideJson := d.GetOk("override_json"); hasOverrideJson {
overrideDoc := &IAMPolicyDoc{}
if err := json.Unmarshal([]byte(overrideJson.(string)), overrideDoc); err != nil {
return err
}

if len(overrideDoc.Id) > 0 {
doc.Id = overrideDoc.Id
}

if len(overrideDoc.Statements) > 0 {
doc.Statements = append(doc.Statements, overrideDoc.Statements...)
}
}

doc.DeDupSids()

jsonDoc, err := json.MarshalIndent(doc, "", " ")
if err != nil {
// should never happen if the above code is correct
Expand Down
115 changes: 115 additions & 0 deletions aws/data_source_aws_iam_policy_document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,40 @@ func TestAccAWSDataSourceIAMPolicyDocument_source(t *testing.T) {
})
}

func TestAccAWSDataSourceIAMPolicyDocument_sourceConflicting(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccAWSIAMPolicyDocumentSourceConflictingConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckStateValue("data.aws_iam_policy_document.test_source_conflicting", "json",
testAccAWSIAMPolicyDocumentSourceConflictingExpectedJSON,
),
),
},
},
})
}

func TestAccAWSDataSourceIAMPolicyDocument_override(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccAWSIAMPolicyDocumentOverrideConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckStateValue("data.aws_iam_policy_document.test_override", "json",
testAccAWSIAMPolicyDocumentOverrideExpectedJSON,
),
),
},
},
})
}

func testAccCheckStateValue(id, name, value string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[id]
Expand Down Expand Up @@ -395,3 +429,84 @@ var testAccAWSIAMPolicyDocumentSourceBlankExpectedJSON = `{
}
]
}`

var testAccAWSIAMPolicyDocumentSourceConflictingConfig = `
data "aws_iam_policy_document" "test_source" {
statement {
sid = "SourceJSONTestConflicting"
actions = ["iam:*"]
resources = ["*"]
}
}
data "aws_iam_policy_document" "test_source_conflicting" {
source_json = "${data.aws_iam_policy_document.test_source.json}"
statement {
sid = "SourceJSONTestConflicting"
actions = ["*"]
resources = ["*"]
}
}
`

var testAccAWSIAMPolicyDocumentSourceConflictingExpectedJSON = `{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SourceJSONTestConflicting",
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}`

var testAccAWSIAMPolicyDocumentOverrideConfig = `
data "aws_iam_policy_document" "override" {
statement {
sid = "SidToOverwrite"
actions = ["s3:*"]
resources = ["*"]
}
}
data "aws_iam_policy_document" "test_override" {
override_json = "${data.aws_iam_policy_document.override.json}"
statement {
actions = ["ec2:*"]
resources = ["*"]
}
statement {
sid = "SidToOverwrite"
actions = ["s3:*"]
resources = [
"arn:aws:s3:::somebucket",
"arn:aws:s3:::somebucket/*",
]
}
}
`

var testAccAWSIAMPolicyDocumentOverrideExpectedJSON = `{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*"
},
{
"Sid": "SidToOverwrite",
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}`
16 changes: 16 additions & 0 deletions aws/iam_policy_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@ type IAMPolicyStatementCondition struct {
type IAMPolicyStatementPrincipalSet []IAMPolicyStatementPrincipal
type IAMPolicyStatementConditionSet []IAMPolicyStatementCondition

func (self *IAMPolicyDoc) DeDupSids() {
// de-dupe the statements by traversing backwards and removing duplicate Sids
sidsSeen := map[string]bool{}
l := len(self.Statements) - 1
for i := range self.Statements {
if sid := self.Statements[l-i].Sid; len(sid) > 0 {
if sidsSeen[sid] {
// we've seen this sid already so remove the duplicate
self.Statements = append(self.Statements[:l-i], self.Statements[l-i+1:]...)
}
// mark this sid seen
sidsSeen[sid] = true
}
}
}

func (ps IAMPolicyStatementPrincipalSet) MarshalJSON() ([]byte, error) {
raw := map[string]interface{}{}

Expand Down
124 changes: 121 additions & 3 deletions website/docs/d/iam_policy_document.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,14 @@ valid to use literal JSON strings within your configuration, or to use the
The following arguments are supported:

* `policy_id` (Optional) - An ID for the policy document.
* `source_json` (Optional) - An IAM policy document to import and add the
statements from. Use this to, for example, customize resource policies in
modules.
* `source_json` (Optional) - An IAM policy document to import as a base for the
current policy document. Statements with non-blank `sid`s in the current
policy document will overwrite statements with the same `sid` in the source
json. Statements without an `sid` cannot be overwritten.
* `override_json` (Optional) - An IAM policy document to import and override the
current policy document. Statements with non-blank `sid`s in the override
document will overwrite statements with the same `sid` in the current document.
Statements without an `sid` cannot be overwritten.
* `statement` (Required) - A nested configuration block (described below)
configuring one *statement* to be included in the policy document.

Expand Down Expand Up @@ -169,3 +174,116 @@ data "aws_iam_policy_document" "event_stream_bucket_role_assume_role_policy" {
}
}
```

## Example with Source and Override

Showing how you can use `source_json` and `override_json`

```hcl
data "aws_iam_policy_document" "source" {
statement {
actions = ["ec2:*"]
resources = ["*"]
}
statement {
sid = "SidToOverwrite"
actions = ["s3:*"]
resources = ["*"]
}
}
data "aws_iam_policy_document" "source_json_example" {
source_json = "${data.aws_iam_policy_document.source.json}"
statement {
sid = "SidToOverwrite"
actions = ["s3:*"]
resources = [
"arn:aws:s3:::somebucket",
"arn:aws:s3:::somebucket/*",
]
}
}
data "aws_iam_policy_document" "override" {
statement {
sid = "SidToOverwrite"
actions = ["s3:*"]
resources = ["*"]
}
}
data "aws_iam_policy_document" "override_json_example" {
override_json = "${data.aws_iam_policy_document.override.json}"
statement {
actions = ["ec2:*"]
resources = ["*"]
}
statement {
sid = "SidToOverwrite"
actions = ["s3:*"]
resources = [
"arn:aws:s3:::somebucket",
"arn:aws:s3:::somebucket/*",
]
}
}
```

`data.aws_iam_policy_document.source_json_example.json` will evaluate to:

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*"
},
{
"Sid": "SidToOverwrite",
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::somebucket/*",
"arn:aws:s3:::somebucket"
]
}
]
}
```

`data.aws_iam_policy_document.override_json_example.json` will evaluate to:

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*"
},
{
"Sid": "SidToOverwrite",
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
```

You can also combine `source_json` and `override_json` in the same document.

0 comments on commit 91862d2

Please sign in to comment.