diff --git a/aws/config.go b/aws/config.go index fa9b8354cc9..7cd9aecb24e 100644 --- a/aws/config.go +++ b/aws/config.go @@ -87,6 +87,7 @@ import ( "github.com/aws/aws-sdk-go/service/redshift" "github.com/aws/aws-sdk-go/service/route53" "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3control" "github.com/aws/aws-sdk-go/service/secretsmanager" "github.com/aws/aws-sdk-go/service/securityhub" "github.com/aws/aws-sdk-go/service/servicecatalog" @@ -150,6 +151,7 @@ type Config struct { RdsEndpoint string R53Endpoint string S3Endpoint string + S3ControlEndpoint string SnsEndpoint string SqsEndpoint string StsEndpoint string @@ -198,6 +200,7 @@ type AWSClient struct { appautoscalingconn *applicationautoscaling.ApplicationAutoScaling autoscalingconn *autoscaling.AutoScaling s3conn *s3.S3 + s3controlconn *s3control.S3Control secretsmanagerconn *secretsmanager.SecretsManager securityhubconn *securityhub.SecurityHub scconn *servicecatalog.ServiceCatalog @@ -433,6 +436,7 @@ func (c *Config) Client() (interface{}, error) { awsKmsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KmsEndpoint)}) awsRdsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.RdsEndpoint)}) awsS3Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.S3Endpoint)}) + awsS3ControlSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.S3ControlEndpoint)}) awsSnsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.SnsEndpoint)}) awsSqsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.SqsEndpoint)}) awsStsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.StsEndpoint)}) @@ -567,6 +571,7 @@ func (c *Config) Client() (interface{}, error) { client.redshiftconn = redshift.New(sess) client.simpledbconn = simpledb.New(sess) client.s3conn = s3.New(awsS3Sess) + client.s3controlconn = s3control.New(awsS3ControlSess) client.scconn = servicecatalog.New(sess) client.sdconn = servicediscovery.New(sess) client.sesConn = ses.New(sess) diff --git a/aws/provider.go b/aws/provider.go index 7d107c649a4..b9e44241183 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -614,6 +614,7 @@ func Provider() terraform.ResourceProvider { "aws_ses_event_destination": resourceAwsSesEventDestination(), "aws_ses_identity_notification_topic": resourceAwsSesNotificationTopic(), "aws_ses_template": resourceAwsSesTemplate(), + "aws_s3_account_public_access_block": resourceAwsS3AccountPublicAccessBlock(), "aws_s3_bucket": resourceAwsS3Bucket(), "aws_s3_bucket_policy": resourceAwsS3BucketPolicy(), "aws_s3_bucket_object": resourceAwsS3BucketObject(), @@ -812,6 +813,8 @@ func init() { "s3_endpoint": "Use this to override the default endpoint URL constructed from the `region`.\n", + "s3control_endpoint": "Use this to override the default endpoint URL constructed from the `region`.\n", + "sns_endpoint": "Use this to override the default endpoint URL constructed from the `region`.\n", "sqs_endpoint": "Use this to override the default endpoint URL constructed from the `region`.\n", @@ -923,6 +926,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { config.R53Endpoint = endpoints["r53"].(string) config.RdsEndpoint = endpoints["rds"].(string) config.S3Endpoint = endpoints["s3"].(string) + config.S3ControlEndpoint = endpoints["s3control"].(string) config.SnsEndpoint = endpoints["sns"].(string) config.SqsEndpoint = endpoints["sqs"].(string) config.StsEndpoint = endpoints["sts"].(string) @@ -1128,6 +1132,12 @@ func endpointsSchema() *schema.Schema { Default: "", Description: descriptions["s3_endpoint"], }, + "s3control": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: descriptions["s3control_endpoint"], + }, "sns": { Type: schema.TypeString, Optional: true, diff --git a/aws/resource_aws_s3_account_public_access_block.go b/aws/resource_aws_s3_account_public_access_block.go new file mode 100644 index 00000000000..e809824b22a --- /dev/null +++ b/aws/resource_aws_s3_account_public_access_block.go @@ -0,0 +1,182 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/s3control" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsS3AccountPublicAccessBlock() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsS3AccountPublicAccessBlockCreate, + Read: resourceAwsS3AccountPublicAccessBlockRead, + Update: resourceAwsS3AccountPublicAccessBlockUpdate, + Delete: resourceAwsS3AccountPublicAccessBlockDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "account_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validateAwsAccountId, + }, + "block_public_acls": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "block_public_policy": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "ignore_public_acls": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "restrict_public_buckets": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + } +} + +func resourceAwsS3AccountPublicAccessBlockCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).s3controlconn + + accountID := meta.(*AWSClient).accountid + if v, ok := d.GetOk("account_id"); ok { + accountID = v.(string) + } + + input := &s3control.PutPublicAccessBlockInput{ + AccountId: aws.String(accountID), + PublicAccessBlockConfiguration: &s3control.PublicAccessBlockConfiguration{ + BlockPublicAcls: aws.Bool(d.Get("block_public_acls").(bool)), + BlockPublicPolicy: aws.Bool(d.Get("block_public_policy").(bool)), + IgnorePublicAcls: aws.Bool(d.Get("ignore_public_acls").(bool)), + RestrictPublicBuckets: aws.Bool(d.Get("restrict_public_buckets").(bool)), + }, + } + + log.Printf("[DEBUG] Creating S3 Account Public Access Block: %s", input) + _, err := conn.PutPublicAccessBlock(input) + if err != nil { + return fmt.Errorf("error creating S3 Account Public Access Block: %s", err) + } + + d.SetId(accountID) + + return resourceAwsS3AccountPublicAccessBlockRead(d, meta) +} + +func resourceAwsS3AccountPublicAccessBlockRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).s3controlconn + + input := &s3control.GetPublicAccessBlockInput{ + AccountId: aws.String(d.Id()), + } + + // Retry for eventual consistency on creation + var output *s3control.GetPublicAccessBlockOutput + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + var err error + output, err = conn.GetPublicAccessBlock(input) + + if d.IsNewResource() && isAWSErr(err, s3control.ErrCodeNoSuchPublicAccessBlockConfiguration, "") { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if isAWSErr(err, s3control.ErrCodeNoSuchPublicAccessBlockConfiguration, "") { + log.Printf("[WARN] S3 Account Public Access Block (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading S3 Account Public Access Block: %s", err) + } + + if output == nil || output.PublicAccessBlockConfiguration == nil { + return fmt.Errorf("error reading S3 Account Public Access Block (%s): missing public access block configuration", d.Id()) + } + + d.Set("account_id", d.Id()) + d.Set("block_public_acls", output.PublicAccessBlockConfiguration.BlockPublicAcls) + d.Set("block_public_policy", output.PublicAccessBlockConfiguration.BlockPublicPolicy) + d.Set("ignore_public_acls", output.PublicAccessBlockConfiguration.IgnorePublicAcls) + d.Set("restrict_public_buckets", output.PublicAccessBlockConfiguration.RestrictPublicBuckets) + + return nil +} + +func resourceAwsS3AccountPublicAccessBlockUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).s3controlconn + + input := &s3control.PutPublicAccessBlockInput{ + AccountId: aws.String(d.Id()), + PublicAccessBlockConfiguration: &s3control.PublicAccessBlockConfiguration{ + BlockPublicAcls: aws.Bool(d.Get("block_public_acls").(bool)), + BlockPublicPolicy: aws.Bool(d.Get("block_public_policy").(bool)), + IgnorePublicAcls: aws.Bool(d.Get("ignore_public_acls").(bool)), + RestrictPublicBuckets: aws.Bool(d.Get("restrict_public_buckets").(bool)), + }, + } + + log.Printf("[DEBUG] Updating S3 Account Public Access Block: %s", input) + _, err := conn.PutPublicAccessBlock(input) + if err != nil { + return fmt.Errorf("error updating S3 Account Public Access Block (%s): %s", d.Id(), err) + } + + // Workaround API eventual consistency issues. This type of logic should not normally be used. + // We cannot reliably determine when the Read after Update might be properly updated. + // Rather than introduce complicated retry logic, we presume that a lack of an update error + // means our update succeeded with our expected values. + d.Set("block_public_acls", input.PublicAccessBlockConfiguration.BlockPublicAcls) + d.Set("block_public_policy", input.PublicAccessBlockConfiguration.BlockPublicPolicy) + d.Set("ignore_public_acls", input.PublicAccessBlockConfiguration.IgnorePublicAcls) + d.Set("restrict_public_buckets", input.PublicAccessBlockConfiguration.RestrictPublicBuckets) + + // Skip normal Read after Update due to eventual consistency issues + return nil +} + +func resourceAwsS3AccountPublicAccessBlockDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).s3controlconn + + input := &s3control.DeletePublicAccessBlockInput{ + AccountId: aws.String(d.Id()), + } + + _, err := conn.DeletePublicAccessBlock(input) + + if isAWSErr(err, s3control.ErrCodeNoSuchPublicAccessBlockConfiguration, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting S3 Account Public Access Block (%s): %s", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_s3_account_public_access_block_test.go b/aws/resource_aws_s3_account_public_access_block_test.go new file mode 100644 index 00000000000..6b61a5ed5cc --- /dev/null +++ b/aws/resource_aws_s3_account_public_access_block_test.go @@ -0,0 +1,440 @@ +package aws + +import ( + "fmt" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/s3control" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +// S3 account-level settings must run serialized +// for TeamCity environment +func TestAccAWSS3Account(t *testing.T) { + testCases := map[string]map[string]func(t *testing.T){ + "PublicAccessBlock": { + "basic": testAccAWSS3AccountPublicAccessBlock_basic, + "disappears": testAccAWSS3AccountPublicAccessBlock_disappears, + "AccountId": testAccAWSS3AccountPublicAccessBlock_AccountId, + "BlockPublicAcls": testAccAWSS3AccountPublicAccessBlock_BlockPublicAcls, + "BlockPublicPolicy": testAccAWSS3AccountPublicAccessBlock_BlockPublicPolicy, + "IgnorePublicAcls": testAccAWSS3AccountPublicAccessBlock_IgnorePublicAcls, + "RestrictPublicBuckets": testAccAWSS3AccountPublicAccessBlock_RestrictPublicBuckets, + }, + } + + for group, m := range testCases { + m := m + t.Run(group, func(t *testing.T) { + for name, tc := range m { + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + // Explicitly sleep between tests for eventual consistency + time.Sleep(5 * time.Second) + }) + } + }) + } +} + +func testAccAWSS3AccountPublicAccessBlock_basic(t *testing.T) { + var configuration1 s3control.PublicAccessBlockConfiguration + resourceName := "aws_s3_account_public_access_block.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3AccountPublicAccessBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSS3AccountPublicAccessBlockConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration1), + testAccCheckResourceAttrAccountID(resourceName, "account_id"), + resource.TestCheckResourceAttr(resourceName, "block_public_acls", "false"), + resource.TestCheckResourceAttr(resourceName, "block_public_policy", "false"), + resource.TestCheckResourceAttr(resourceName, "ignore_public_acls", "false"), + resource.TestCheckResourceAttr(resourceName, "restrict_public_buckets", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAWSS3AccountPublicAccessBlock_disappears(t *testing.T) { + var configuration1 s3control.PublicAccessBlockConfiguration + resourceName := "aws_s3_account_public_access_block.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3AccountPublicAccessBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSS3AccountPublicAccessBlockConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration1), + testAccCheckAWSS3AccountPublicAccessBlockDisappears(), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccAWSS3AccountPublicAccessBlock_AccountId(t *testing.T) { + var configuration1 s3control.PublicAccessBlockConfiguration + resourceName := "aws_s3_account_public_access_block.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3AccountPublicAccessBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSS3AccountPublicAccessBlockConfigAccountId(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration1), + testAccCheckResourceAttrAccountID(resourceName, "account_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAWSS3AccountPublicAccessBlock_BlockPublicAcls(t *testing.T) { + var configuration1, configuration2, configuration3 s3control.PublicAccessBlockConfiguration + resourceName := "aws_s3_account_public_access_block.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3AccountPublicAccessBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSS3AccountPublicAccessBlockConfigBlockPublicAcls(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration1), + resource.TestCheckResourceAttr(resourceName, "block_public_acls", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSS3AccountPublicAccessBlockConfigBlockPublicAcls(false), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration2), + resource.TestCheckResourceAttr(resourceName, "block_public_acls", "false"), + ), + }, + { + Config: testAccAWSS3AccountPublicAccessBlockConfigBlockPublicAcls(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration3), + resource.TestCheckResourceAttr(resourceName, "block_public_acls", "true"), + ), + }, + }, + }) +} + +func testAccAWSS3AccountPublicAccessBlock_BlockPublicPolicy(t *testing.T) { + var configuration1, configuration2, configuration3 s3control.PublicAccessBlockConfiguration + resourceName := "aws_s3_account_public_access_block.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3AccountPublicAccessBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSS3AccountPublicAccessBlockConfigBlockPublicPolicy(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration1), + resource.TestCheckResourceAttr(resourceName, "block_public_policy", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSS3AccountPublicAccessBlockConfigBlockPublicPolicy(false), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration2), + resource.TestCheckResourceAttr(resourceName, "block_public_policy", "false"), + ), + }, + { + Config: testAccAWSS3AccountPublicAccessBlockConfigBlockPublicPolicy(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration3), + resource.TestCheckResourceAttr(resourceName, "block_public_policy", "true"), + ), + }, + }, + }) +} + +func testAccAWSS3AccountPublicAccessBlock_IgnorePublicAcls(t *testing.T) { + var configuration1, configuration2, configuration3 s3control.PublicAccessBlockConfiguration + resourceName := "aws_s3_account_public_access_block.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3AccountPublicAccessBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSS3AccountPublicAccessBlockConfigIgnorePublicAcls(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration1), + resource.TestCheckResourceAttr(resourceName, "ignore_public_acls", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSS3AccountPublicAccessBlockConfigIgnorePublicAcls(false), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration2), + resource.TestCheckResourceAttr(resourceName, "ignore_public_acls", "false"), + ), + }, + { + Config: testAccAWSS3AccountPublicAccessBlockConfigIgnorePublicAcls(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration3), + resource.TestCheckResourceAttr(resourceName, "ignore_public_acls", "true"), + ), + }, + }, + }) +} + +func testAccAWSS3AccountPublicAccessBlock_RestrictPublicBuckets(t *testing.T) { + var configuration1, configuration2, configuration3 s3control.PublicAccessBlockConfiguration + resourceName := "aws_s3_account_public_access_block.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3AccountPublicAccessBlockDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSS3AccountPublicAccessBlockConfigRestrictPublicBuckets(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration1), + resource.TestCheckResourceAttr(resourceName, "restrict_public_buckets", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSS3AccountPublicAccessBlockConfigRestrictPublicBuckets(false), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration2), + resource.TestCheckResourceAttr(resourceName, "restrict_public_buckets", "false"), + ), + }, + { + Config: testAccAWSS3AccountPublicAccessBlockConfigRestrictPublicBuckets(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName, &configuration3), + resource.TestCheckResourceAttr(resourceName, "restrict_public_buckets", "true"), + ), + }, + }, + }) +} + +func testAccCheckAWSS3AccountPublicAccessBlockExists(resourceName string, configuration *s3control.PublicAccessBlockConfiguration) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No S3 Account Public Access Block ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).s3controlconn + + input := &s3control.GetPublicAccessBlockInput{ + AccountId: aws.String(rs.Primary.ID), + } + + // Retry for eventual consistency + var output *s3control.GetPublicAccessBlockOutput + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + var err error + output, err = conn.GetPublicAccessBlock(input) + + if isAWSErr(err, s3control.ErrCodeNoSuchPublicAccessBlockConfiguration, "") { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if err != nil { + return err + } + + if output == nil || output.PublicAccessBlockConfiguration == nil { + return fmt.Errorf("S3 Account Public Access Block not found") + } + + *configuration = *output.PublicAccessBlockConfiguration + + return nil + } +} + +func testAccCheckAWSS3AccountPublicAccessBlockDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).s3controlconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_s3_account_public_access_block" { + continue + } + + input := &s3control.GetPublicAccessBlockInput{ + AccountId: aws.String(rs.Primary.ID), + } + + // Retry for eventual consistency + err := resource.Retry(1*time.Minute, func() *resource.RetryError { + _, err := conn.GetPublicAccessBlock(input) + + if isAWSErr(err, s3control.ErrCodeNoSuchPublicAccessBlockConfiguration, "") { + return nil + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return resource.RetryableError(fmt.Errorf("S3 Account Public Access Block (%s) still exists", rs.Primary.ID)) + }) + + if err != nil { + return err + } + } + + return nil +} + +func testAccCheckAWSS3AccountPublicAccessBlockDisappears() resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).s3controlconn + accountID := testAccProvider.Meta().(*AWSClient).accountid + + deleteInput := &s3control.DeletePublicAccessBlockInput{ + AccountId: aws.String(accountID), + } + + _, err := conn.DeletePublicAccessBlock(deleteInput) + + if err != nil { + return err + } + + getInput := &s3control.GetPublicAccessBlockInput{ + AccountId: aws.String(accountID), + } + + // Retry for eventual consistency + return resource.Retry(1*time.Minute, func() *resource.RetryError { + _, err := conn.GetPublicAccessBlock(getInput) + + if isAWSErr(err, s3control.ErrCodeNoSuchPublicAccessBlockConfiguration, "") { + return nil + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return resource.RetryableError(fmt.Errorf("S3 Account Public Access Block (%s) still exists", accountID)) + }) + } +} + +func testAccAWSS3AccountPublicAccessBlockConfig() string { + return fmt.Sprintf(` +resource "aws_s3_account_public_access_block" "test" {} +`) +} + +func testAccAWSS3AccountPublicAccessBlockConfigAccountId() string { + return fmt.Sprintf(` +data "aws_caller_identity" "test" {} + +resource "aws_s3_account_public_access_block" "test" { + account_id = "${data.aws_caller_identity.test.account_id}" +} +`) +} + +func testAccAWSS3AccountPublicAccessBlockConfigBlockPublicAcls(blockPublicAcls bool) string { + return fmt.Sprintf(` +resource "aws_s3_account_public_access_block" "test" { + block_public_acls = %t +} +`, blockPublicAcls) +} + +func testAccAWSS3AccountPublicAccessBlockConfigBlockPublicPolicy(blockPublicPolicy bool) string { + return fmt.Sprintf(` +resource "aws_s3_account_public_access_block" "test" { + block_public_policy = %t +} +`, blockPublicPolicy) +} + +func testAccAWSS3AccountPublicAccessBlockConfigIgnorePublicAcls(ignorePublicAcls bool) string { + return fmt.Sprintf(` +resource "aws_s3_account_public_access_block" "test" { + ignore_public_acls = %t +} +`, ignorePublicAcls) +} + +func testAccAWSS3AccountPublicAccessBlockConfigRestrictPublicBuckets(restrictPublicBuckets bool) string { + return fmt.Sprintf(` +resource "aws_s3_account_public_access_block" "test" { + restrict_public_buckets = %t +} +`, restrictPublicBuckets) +} diff --git a/website/aws.erb b/website/aws.erb index 7bde4379f4c..6cb4e36392c 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -2138,6 +2138,10 @@ S3 Resources