Merge pull request #8615 from hashicorp/f-aws-s3-bucket-policy
provider/aws: Add aws_s3_bucket_policy resource
This commit is contained in:
commit
bb3a896cee
|
@ -0,0 +1,15 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/jen20/awspolicyequivalence"
|
||||
)
|
||||
|
||||
func suppressEquivalentAwsPolicyDiffs(k, old, new string, d *schema.ResourceData) bool {
|
||||
equivalent, err := awspolicy.PoliciesAreEquivalent(old, new)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return equivalent
|
||||
}
|
|
@ -308,6 +308,7 @@ func Provider() terraform.ResourceProvider {
|
|||
"aws_ses_receipt_rule": resourceAwsSesReceiptRule(),
|
||||
"aws_ses_receipt_rule_set": resourceAwsSesReceiptRuleSet(),
|
||||
"aws_s3_bucket": resourceAwsS3Bucket(),
|
||||
"aws_s3_bucket_policy": resourceAwsS3BucketPolicy(),
|
||||
"aws_s3_bucket_object": resourceAwsS3BucketObject(),
|
||||
"aws_s3_bucket_notification": resourceAwsS3BucketNotification(),
|
||||
"aws_security_group": resourceAwsSecurityGroup(),
|
||||
|
|
|
@ -8,13 +8,12 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func resourceAwsS3Bucket() *schema.Resource {
|
||||
|
@ -47,9 +46,10 @@ func resourceAwsS3Bucket() *schema.Resource {
|
|||
},
|
||||
|
||||
"policy": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
StateFunc: normalizeJson,
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
|
||||
},
|
||||
|
||||
"cors_rule": &schema.Schema{
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func resourceAwsS3BucketPolicy() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceAwsS3BucketPolicyPut,
|
||||
Read: resourceAwsS3BucketPolicyRead,
|
||||
Update: resourceAwsS3BucketPolicyPut,
|
||||
Delete: resourceAwsS3BucketPolicyDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"bucket": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"policy": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceAwsS3BucketPolicyPut(d *schema.ResourceData, meta interface{}) error {
|
||||
s3conn := meta.(*AWSClient).s3conn
|
||||
|
||||
bucket := d.Get("bucket").(string)
|
||||
policy := d.Get("policy").(string)
|
||||
|
||||
d.SetId(bucket)
|
||||
|
||||
log.Printf("[DEBUG] S3 bucket: %s, put policy: %s", bucket, policy)
|
||||
|
||||
params := &s3.PutBucketPolicyInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Policy: aws.String(policy),
|
||||
}
|
||||
|
||||
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
|
||||
if _, err := s3conn.PutBucketPolicy(params); err != nil {
|
||||
if awserr, ok := err.(awserr.Error); ok {
|
||||
if awserr.Code() == "MalformedPolicy" {
|
||||
return resource.RetryableError(awserr)
|
||||
}
|
||||
}
|
||||
return resource.NonRetryableError(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error putting S3 policy: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceAwsS3BucketPolicyRead(d *schema.ResourceData, meta interface{}) error {
|
||||
s3conn := meta.(*AWSClient).s3conn
|
||||
|
||||
log.Printf("[DEBUG] S3 bucket policy, read for bucket: %s", d.Id())
|
||||
pol, err := s3conn.GetBucketPolicy(&s3.GetBucketPolicyInput{
|
||||
Bucket: aws.String(d.Id()),
|
||||
})
|
||||
|
||||
v := ""
|
||||
if err == nil && pol.Policy != nil {
|
||||
v = *pol.Policy
|
||||
}
|
||||
if err := d.Set("policy", v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceAwsS3BucketPolicyDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
s3conn := meta.(*AWSClient).s3conn
|
||||
|
||||
bucket := d.Get("bucket").(string)
|
||||
|
||||
log.Printf("[DEBUG] S3 bucket: %s, delete policy", bucket)
|
||||
_, err := s3conn.DeleteBucketPolicy(&s3.DeleteBucketPolicyInput{
|
||||
Bucket: aws.String(bucket),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting S3 policy: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/jen20/awspolicyequivalence"
|
||||
)
|
||||
|
||||
func TestAccAWSS3BucketPolicy_basic(t *testing.T) {
|
||||
name := fmt.Sprintf("tf-test-bucket-%d", acctest.RandInt())
|
||||
|
||||
expectedPolicyText := fmt.Sprintf(
|
||||
`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"*"},"Action":"s3:*","Resource":["arn:aws:s3:::%s","arn:aws:s3:::%s/*"]}]}`,
|
||||
name, name)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSS3BucketDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSS3BucketPolicyConfig(name),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"),
|
||||
testAccCheckAWSS3BucketHasPolicy("aws_s3_bucket.bucket", expectedPolicyText),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSS3BucketPolicy_policyUpdate(t *testing.T) {
|
||||
name := fmt.Sprintf("tf-test-bucket-%d", acctest.RandInt())
|
||||
|
||||
expectedPolicyText1 := fmt.Sprintf(
|
||||
`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"*"},"Action":"s3:*","Resource":["arn:aws:s3:::%s","arn:aws:s3:::%s/*"]}]}`,
|
||||
name, name)
|
||||
|
||||
expectedPolicyText2 := fmt.Sprintf(
|
||||
`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"*"},"Action":["s3:DeleteBucket", "s3:ListBucket", "s3:ListBucketVersions"], "Resource":["arn:aws:s3:::%s","arn:aws:s3:::%s/*"]}]}`,
|
||||
name, name)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSS3BucketDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSS3BucketPolicyConfig(name),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"),
|
||||
testAccCheckAWSS3BucketHasPolicy("aws_s3_bucket.bucket", expectedPolicyText1),
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
Config: testAccAWSS3BucketPolicyConfig_updated(name),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"),
|
||||
testAccCheckAWSS3BucketHasPolicy("aws_s3_bucket.bucket", expectedPolicyText2),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckAWSS3BucketHasPolicy(n string, expectedPolicyText string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No S3 Bucket ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AWSClient).s3conn
|
||||
|
||||
policy, err := conn.GetBucketPolicy(&s3.GetBucketPolicyInput{
|
||||
Bucket: aws.String(rs.Primary.ID),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetBucketPolicy error: %v", err)
|
||||
}
|
||||
|
||||
actualPolicyText := *policy.Policy
|
||||
|
||||
equivalent, err := awspolicy.PoliciesAreEquivalent(actualPolicyText, expectedPolicyText)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error testing policy equivalence: %s", err)
|
||||
}
|
||||
if !equivalent {
|
||||
return fmt.Errorf("Non-equivalent policy error:\n\nexpected: %s\n\n got: %s\n",
|
||||
expectedPolicyText, actualPolicyText)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccAWSS3BucketPolicyConfig(bucketName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "bucket" {
|
||||
bucket = "%s"
|
||||
tags {
|
||||
TestName = "TestAccAWSS3BucketPolicy_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_policy" "bucket" {
|
||||
bucket = "${aws_s3_bucket.bucket.bucket}"
|
||||
policy = "${data.aws_iam_policy_document.policy.json}"
|
||||
}
|
||||
|
||||
data "aws_iam_policy_document" "policy" {
|
||||
statement {
|
||||
effect = "Allow"
|
||||
|
||||
actions = [
|
||||
"s3:*",
|
||||
]
|
||||
|
||||
resources = [
|
||||
"${aws_s3_bucket.bucket.arn}",
|
||||
"${aws_s3_bucket.bucket.arn}/*",
|
||||
]
|
||||
|
||||
principals {
|
||||
type = "AWS"
|
||||
identifiers = ["*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
`, bucketName)
|
||||
}
|
||||
|
||||
func testAccAWSS3BucketPolicyConfig_updated(bucketName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "bucket" {
|
||||
bucket = "%s"
|
||||
tags {
|
||||
TestName = "TestAccAWSS3BucketPolicy_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_policy" "bucket" {
|
||||
bucket = "${aws_s3_bucket.bucket.bucket}"
|
||||
policy = "${data.aws_iam_policy_document.policy.json}"
|
||||
}
|
||||
|
||||
data "aws_iam_policy_document" "policy" {
|
||||
statement {
|
||||
effect = "Allow"
|
||||
|
||||
actions = [
|
||||
"s3:DeleteBucket",
|
||||
"s3:ListBucket",
|
||||
"s3:ListBucketVersions"
|
||||
]
|
||||
|
||||
resources = [
|
||||
"${aws_s3_bucket.bucket.arn}",
|
||||
"${aws_s3_bucket.bucket.arn}/*",
|
||||
]
|
||||
|
||||
principals {
|
||||
type = "AWS"
|
||||
identifiers = ["*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
`, bucketName)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
## AWS Policy Equivalence Library
|
||||
|
||||
This library checks for structural equivalence of two AWS policy documents. See Godoc for more information on usage.
|
||||
|
||||
### CI
|
||||
|
||||
<img src="https://travis-ci.org/jen20/awspolicyequivalence.svg?branch=master" alt="Travis CI Build Status" />
|
334
vendor/github.com/jen20/awspolicyequivalence/aws_policy_equivalence.go
generated
vendored
Normal file
334
vendor/github.com/jen20/awspolicyequivalence/aws_policy_equivalence.go
generated
vendored
Normal file
|
@ -0,0 +1,334 @@
|
|||
package awspolicy
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
)
|
||||
|
||||
// PoliciesAreEquivalent tests for the structural equivalence of two
|
||||
// AWS policies. It does not read into the semantics, other than treating
|
||||
// single element string arrays as equivalent to a string without an
|
||||
// array, as the AWS endpoints do.
|
||||
//
|
||||
// It will, however, detect reordering and ignore whitespace.
|
||||
//
|
||||
// Returns true if the policies are structurally equivalent, false
|
||||
// otherwise. If either of the input strings are not valid JSON,
|
||||
// false is returned along with an error.
|
||||
func PoliciesAreEquivalent(policy1, policy2 string) (bool, error) {
|
||||
policy1doc := &awsPolicyDocument{}
|
||||
if err := json.Unmarshal([]byte(policy1), policy1doc); err != nil {
|
||||
return false, errwrap.Wrapf("Error unmarshaling policy: {{err}}", err)
|
||||
}
|
||||
|
||||
policy2doc := &awsPolicyDocument{}
|
||||
if err := json.Unmarshal([]byte(policy2), policy2doc); err != nil {
|
||||
return false, errwrap.Wrapf("Error unmarshaling policy: {{err}}", err)
|
||||
}
|
||||
|
||||
return policy1doc.equals(policy2doc), nil
|
||||
}
|
||||
|
||||
type awsPolicyDocument struct {
|
||||
Version string `json:",omitempty"`
|
||||
Id string `json:",omitempty"`
|
||||
Statements []*awsPolicyStatement `json:"Statement"`
|
||||
}
|
||||
|
||||
func (doc *awsPolicyDocument) equals(other *awsPolicyDocument) bool {
|
||||
// Check the basic fields of the document
|
||||
if doc.Version != other.Version {
|
||||
return false
|
||||
}
|
||||
if doc.Id != other.Id {
|
||||
return false
|
||||
}
|
||||
|
||||
// If we have different number of statements we are very unlikely
|
||||
// to have them be equivalent.
|
||||
if len(doc.Statements) != len(other.Statements) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If we have the same number of statements in the policy, does
|
||||
// each statement in the doc have a corresponding statement in
|
||||
// other which is equal? If no, policies are not equal, if yes,
|
||||
// then they may be.
|
||||
for _, ours := range doc.Statements {
|
||||
found := false
|
||||
for _, theirs := range other.Statements {
|
||||
if ours.equals(theirs) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need to repeat this process the other way around to
|
||||
// ensure we don't have any matching errors.
|
||||
for _, theirs := range other.Statements {
|
||||
found := false
|
||||
for _, ours := range doc.Statements {
|
||||
if theirs.equals(ours) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type awsPolicyStatement struct {
|
||||
Sid string `json:",omitempty"`
|
||||
Effect string `json:",omitempty"`
|
||||
Actions interface{} `json:"Action,omitempty"`
|
||||
NotActions interface{} `json:"NotAction,omitempty"`
|
||||
Resources interface{} `json:"Resource,omitempty"`
|
||||
NotResources interface{} `json:"NotResource,omitempty"`
|
||||
Principals interface{} `json:"Principal,omitempty"`
|
||||
NotPrincipals interface{} `json:"NotPrincipal,omitempty"`
|
||||
Conditions map[string]map[string]interface{} `json:"Condition,omitempty"`
|
||||
}
|
||||
|
||||
func (statement *awsPolicyStatement) equals(other *awsPolicyStatement) bool {
|
||||
if statement.Sid != other.Sid {
|
||||
return false
|
||||
}
|
||||
|
||||
if statement.Effect != other.Effect {
|
||||
return false
|
||||
}
|
||||
|
||||
ourActions := newAWSStringSet(statement.Actions)
|
||||
theirActions := newAWSStringSet(other.Actions)
|
||||
if !ourActions.equals(theirActions) {
|
||||
return false
|
||||
}
|
||||
|
||||
ourNotActions := newAWSStringSet(statement.NotActions)
|
||||
theirNotActions := newAWSStringSet(other.NotActions)
|
||||
if !ourNotActions.equals(theirNotActions) {
|
||||
return false
|
||||
}
|
||||
|
||||
ourResources := newAWSStringSet(statement.Resources)
|
||||
theirResources := newAWSStringSet(other.Resources)
|
||||
if !ourResources.equals(theirResources) {
|
||||
return false
|
||||
}
|
||||
|
||||
ourNotResources := newAWSStringSet(statement.NotResources)
|
||||
theirNotResources := newAWSStringSet(other.NotResources)
|
||||
if !ourNotResources.equals(theirNotResources) {
|
||||
return false
|
||||
}
|
||||
|
||||
ourConditionsBlock := awsConditionsBlock(statement.Conditions)
|
||||
theirConditionsBlock := awsConditionsBlock(other.Conditions)
|
||||
if !ourConditionsBlock.Equals(theirConditionsBlock) {
|
||||
return false
|
||||
}
|
||||
|
||||
if statement.Principals != nil || other.Principals != nil {
|
||||
stringPrincipalsEqual := stringPrincipalsEqual(statement.Principals, other.Principals)
|
||||
mapPrincipalsEqual := mapPrincipalsEqual(statement.Principals, other.Principals)
|
||||
if !(stringPrincipalsEqual || mapPrincipalsEqual) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if statement.NotPrincipals != nil || other.NotPrincipals != nil {
|
||||
stringNotPrincipalsEqual := stringPrincipalsEqual(statement.NotPrincipals, other.NotPrincipals)
|
||||
mapNotPrincipalsEqual := mapPrincipalsEqual(statement.NotPrincipals, other.NotPrincipals)
|
||||
if !(stringNotPrincipalsEqual || mapNotPrincipalsEqual) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func mapPrincipalsEqual(ours, theirs interface{}) bool {
|
||||
ourPrincipalMap, ok := ours.(map[string]interface{})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
theirPrincipalMap, ok := theirs.(map[string]interface{})
|
||||
if ! ok {
|
||||
return false
|
||||
}
|
||||
|
||||
oursNormalized := make(map[string]awsStringSet)
|
||||
for key, val := range ourPrincipalMap {
|
||||
oursNormalized[key] = newAWSStringSet(val)
|
||||
}
|
||||
|
||||
theirsNormalized := make(map[string]awsStringSet)
|
||||
for key, val := range theirPrincipalMap {
|
||||
theirsNormalized[key] = newAWSStringSet(val)
|
||||
}
|
||||
|
||||
for key, ours := range oursNormalized {
|
||||
theirs, ok := theirsNormalized[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if !ours.equals(theirs) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for key, theirs := range theirsNormalized {
|
||||
ours, ok := oursNormalized[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if !theirs.equals(ours) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func stringPrincipalsEqual(ours, theirs interface{}) bool {
|
||||
ourPrincipal, oursIsString := ours.(string)
|
||||
theirPrincipal, theirsIsString := theirs.(string)
|
||||
|
||||
if !(oursIsString && theirsIsString) {
|
||||
return false
|
||||
}
|
||||
|
||||
if ourPrincipal == theirPrincipal {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
type awsConditionsBlock map[string]map[string]interface{}
|
||||
|
||||
func (conditions awsConditionsBlock) Equals(other awsConditionsBlock) bool {
|
||||
if conditions == nil && other != nil || other == nil && conditions != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(conditions) != len(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
oursNormalized := make(map[string]map[string]awsStringSet)
|
||||
for key, condition := range conditions {
|
||||
normalizedCondition := make(map[string]awsStringSet)
|
||||
for innerKey, val := range condition {
|
||||
normalizedCondition[innerKey] = newAWSStringSet(val)
|
||||
}
|
||||
oursNormalized[key] = normalizedCondition
|
||||
}
|
||||
|
||||
theirsNormalized := make(map[string]map[string]awsStringSet)
|
||||
for key, condition := range other {
|
||||
normalizedCondition := make(map[string]awsStringSet)
|
||||
for innerKey, val := range condition {
|
||||
normalizedCondition[innerKey] = newAWSStringSet(val)
|
||||
}
|
||||
theirsNormalized[key] = normalizedCondition
|
||||
}
|
||||
|
||||
for key, ours := range oursNormalized {
|
||||
theirs, ok := theirsNormalized[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for innerKey, oursInner := range ours {
|
||||
theirsInner, ok := theirs[innerKey]
|
||||
if ! ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if !oursInner.equals(theirsInner) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key, theirs := range theirsNormalized {
|
||||
ours, ok := oursNormalized[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for innerKey, theirsInner := range theirs {
|
||||
oursInner, ok := ours[innerKey]
|
||||
if ! ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if !theirsInner.equals(oursInner) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
type awsStringSet []string
|
||||
|
||||
// newAWSStringSet constructs an awsStringSet from an interface{} - which
|
||||
// may be nil, a single string, or []interface{} (each of which is a string).
|
||||
// This corresponds with how structures come off the JSON unmarshaler
|
||||
// without any custom encoding rules.
|
||||
func newAWSStringSet(members interface{}) awsStringSet {
|
||||
if members == nil {
|
||||
return awsStringSet{}
|
||||
}
|
||||
|
||||
if single, ok := members.(string); ok {
|
||||
return awsStringSet{single}
|
||||
}
|
||||
|
||||
if multiple, ok := members.([]interface{}); ok {
|
||||
actions := make([]string, len(multiple))
|
||||
for i, action := range multiple {
|
||||
actions[i] = action.(string)
|
||||
}
|
||||
return awsStringSet(actions)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (actions awsStringSet) equals(other awsStringSet) bool {
|
||||
if len(actions) != len(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
ourMap := map[string]struct{}{}
|
||||
theirMap := map[string]struct{}{}
|
||||
|
||||
for _, action := range actions {
|
||||
ourMap[action] = struct{}{}
|
||||
}
|
||||
|
||||
for _, action := range other {
|
||||
theirMap[action] = struct{}{}
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(ourMap, theirMap)
|
||||
}
|
|
@ -1295,6 +1295,12 @@
|
|||
"path": "github.com/influxdata/influxdb/pkg/escape",
|
||||
"revision": "f233a8bac88d1f2dc282a98186f5a3363b806181"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "cCSJGF1h+suYcgMq7wEm1carknw=",
|
||||
"path": "github.com/jen20/awspolicyequivalence",
|
||||
"revision": "6b9230008577fc3dcd10c104ce8fb16ed679bf66",
|
||||
"revisionTime": "2016-09-01T18:24:20Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "oPpOfZn11Ef6DWOoETxSW9Venzs=",
|
||||
"path": "github.com/jen20/riviera/azure",
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_s3_bucket_policy"
|
||||
sidebar_current: "docs-aws-resource-s3-bucket-policy"
|
||||
description: |-
|
||||
Attaches a policy to an S3 bucket resource.
|
||||
---
|
||||
|
||||
# aws\_s3\_bucket\_policy
|
||||
|
||||
Attaches a policy to an S3 bucket resource.
|
||||
|
||||
## Example Usage
|
||||
|
||||
### Using versioning
|
||||
|
||||
```
|
||||
resource "aws_s3_bucket" "b" {
|
||||
# Arguments
|
||||
}
|
||||
|
||||
data "aws_iam_policy_document" "b" {
|
||||
# Policy statements
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_policy" "b" {
|
||||
bucket = "${aws_s3_bucket.b.bucket}"
|
||||
policy = "${data.aws_iam_policy_document.b.json}"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `bucket` - (Required) The name of the bucket to which to apply the policy.
|
||||
* `policy` - (Required) The text of the policy.
|
|
@ -788,6 +788,9 @@
|
|||
<a href="/docs/providers/aws/r/s3_bucket_object.html">aws_s3_bucket_object</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-aws-resource-s3-bucket-policy") %>>
|
||||
<a href="/docs/providers/aws/r/s3_bucket_policy.html">aws_s3_bucket_policy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
|
Loading…
Reference in New Issue