Skip to content

Commit 17af98c

Browse files
committedOct 13, 2020
resource/aws_s3_access_point: Support S3 on Outposts
Reference: #15412 Reference: #15616 Output from acceptance testing: ``` --- SKIP: TestAccAWSS3AccessPoint_Bucket_Arn (2.35s) --- PASS: TestAccAWSS3AccessPoint_bucketDisappears (25.12s) --- PASS: TestAccAWSS3AccessPoint_disappears (30.49s) --- PASS: TestAccAWSS3AccessPoint_VpcConfiguration (33.78s) --- PASS: TestAccAWSS3AccessPoint_PublicAccessBlockConfiguration (34.38s) --- PASS: TestAccAWSS3AccessPoint_basic (34.93s) --- PASS: TestAccAWSS3AccessPoint_Policy (91.95s) ```
1 parent d95964f commit 17af98c

File tree

3 files changed

+123
-21
lines changed

3 files changed

+123
-21
lines changed
 

‎aws/resource_aws_s3_access_point.go

+36-13
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,24 @@ func resourceAwsS3AccessPointCreate(d *schema.ResourceData, meta interface{}) er
137137
}
138138

139139
log.Printf("[DEBUG] Creating S3 Access Point: %s", input)
140-
_, err := conn.CreateAccessPoint(input)
140+
output, err := conn.CreateAccessPoint(input)
141+
141142
if err != nil {
142-
return fmt.Errorf("error creating S3 Access Point: %s", err)
143+
return fmt.Errorf("error creating S3 Control Access Point (%s): %w", name, err)
144+
}
145+
146+
if output == nil {
147+
return fmt.Errorf("error creating S3 Control Access Point (%s): empty response", name)
143148
}
144149

145-
d.SetId(fmt.Sprintf("%s:%s", accountId, name))
150+
parsedARN, err := arn.Parse(aws.StringValue(output.AccessPointArn))
151+
152+
if err == nil && strings.HasPrefix(parsedARN.Resource, "outpost/") {
153+
d.SetId(aws.StringValue(output.AccessPointArn))
154+
name = aws.StringValue(output.AccessPointArn)
155+
} else {
156+
d.SetId(fmt.Sprintf("%s:%s", accountId, name))
157+
}
146158

147159
if v, ok := d.GetOk("policy"); ok {
148160
log.Printf("[DEBUG] Putting S3 Access Point policy: %s", d.Id())
@@ -183,19 +195,23 @@ func resourceAwsS3AccessPointRead(d *schema.ResourceData, meta interface{}) erro
183195
return fmt.Errorf("error reading S3 Access Point (%s): %s", d.Id(), err)
184196
}
185197

186-
name = aws.StringValue(output.Name)
187-
arn := arn.ARN{
188-
AccountID: accountId,
189-
Partition: meta.(*AWSClient).partition,
190-
Region: meta.(*AWSClient).region,
191-
Resource: fmt.Sprintf("accesspoint/%s", name),
192-
Service: "s3",
198+
if strings.HasPrefix(name, "arn:") {
199+
d.Set("arn", name)
200+
} else {
201+
builtARN := arn.ARN{
202+
AccountID: accountId,
203+
Partition: meta.(*AWSClient).partition,
204+
Region: meta.(*AWSClient).region,
205+
Resource: fmt.Sprintf("accesspoint/%s", aws.StringValue(output.Name)),
206+
Service: "s3",
207+
}
208+
d.Set("arn", builtARN.String())
193209
}
210+
194211
d.Set("account_id", accountId)
195-
d.Set("arn", arn.String())
196212
d.Set("bucket", output.Bucket)
197-
d.Set("domain_name", meta.(*AWSClient).RegionalHostname(fmt.Sprintf("%s-%s.s3-accesspoint", name, accountId)))
198-
d.Set("name", name)
213+
d.Set("domain_name", meta.(*AWSClient).RegionalHostname(fmt.Sprintf("%s-%s.s3-accesspoint", aws.StringValue(output.Name), accountId)))
214+
d.Set("name", output.Name)
199215
d.Set("network_origin", output.NetworkOrigin)
200216
if err := d.Set("public_access_block_configuration", flattenS3AccessPointPublicAccessBlockConfiguration(output.PublicAccessBlockConfiguration)); err != nil {
201217
return fmt.Errorf("error setting public_access_block_configuration: %s", err)
@@ -298,7 +314,14 @@ func resourceAwsS3AccessPointDelete(d *schema.ResourceData, meta interface{}) er
298314
return nil
299315
}
300316

317+
// s3AccessPointParseId returns the Account ID and Access Point Name (S3) or ARN (S3 on Outposts)
301318
func s3AccessPointParseId(id string) (string, string, error) {
319+
parsedARN, err := arn.Parse(id)
320+
321+
if err == nil {
322+
return parsedARN.AccountID, id, nil
323+
}
324+
302325
parts := strings.SplitN(id, ":", 2)
303326

304327
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {

‎aws/resource_aws_s3_access_point_test.go

+72
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,46 @@ func TestAccAWSS3AccessPoint_bucketDisappears(t *testing.T) {
165165
})
166166
}
167167

168+
func TestAccAWSS3AccessPoint_Bucket_Arn(t *testing.T) {
169+
var v s3control.GetAccessPointOutput
170+
rName := acctest.RandomWithPrefix("tf-acc-test")
171+
resourceName := "aws_s3_access_point.test"
172+
173+
resource.ParallelTest(t, resource.TestCase{
174+
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSOutpostsOutposts(t) },
175+
Providers: testAccProviders,
176+
CheckDestroy: testAccCheckAWSS3AccessPointDestroy,
177+
Steps: []resource.TestStep{
178+
{
179+
Config: testAccAWSS3AccessPointConfig_Bucket_Arn(rName),
180+
Check: resource.ComposeTestCheckFunc(
181+
testAccCheckAWSS3AccessPointExists(resourceName, &v),
182+
testAccCheckResourceAttrAccountID(resourceName, "account_id"),
183+
testAccCheckResourceAttrRegionalARN(resourceName, "arn", "s3-outposts", fmt.Sprintf("outpost/[^/]+/accesspoint/%s", rName)),
184+
resource.TestCheckResourceAttrPair(resourceName, "bucket", "aws_s3control_bucket.test", "arn"),
185+
testAccMatchResourceAttrRegionalHostname(resourceName, "domain_name", "s3-accesspoint", regexp.MustCompile(fmt.Sprintf("^%s-\\d{12}", rName))),
186+
resource.TestCheckResourceAttr(resourceName, "has_public_access_policy", "false"),
187+
resource.TestCheckResourceAttr(resourceName, "name", rName),
188+
resource.TestCheckResourceAttr(resourceName, "network_origin", "VPC"),
189+
resource.TestCheckResourceAttr(resourceName, "policy", ""),
190+
resource.TestCheckResourceAttr(resourceName, "public_access_block_configuration.#", "1"),
191+
resource.TestCheckResourceAttr(resourceName, "public_access_block_configuration.0.block_public_acls", "true"),
192+
resource.TestCheckResourceAttr(resourceName, "public_access_block_configuration.0.block_public_policy", "true"),
193+
resource.TestCheckResourceAttr(resourceName, "public_access_block_configuration.0.ignore_public_acls", "true"),
194+
resource.TestCheckResourceAttr(resourceName, "public_access_block_configuration.0.restrict_public_buckets", "true"),
195+
resource.TestCheckResourceAttr(resourceName, "vpc_configuration.#", "1"),
196+
resource.TestCheckResourceAttrPair(resourceName, "vpc_configuration.0.vpc_id", "aws_vpc.test", "id"),
197+
),
198+
},
199+
{
200+
ResourceName: resourceName,
201+
ImportState: true,
202+
ImportStateVerify: true,
203+
},
204+
},
205+
})
206+
}
207+
168208
func TestAccAWSS3AccessPoint_Policy(t *testing.T) {
169209
var v s3control.GetAccessPointOutput
170210
rName := acctest.RandomWithPrefix("tf-acc-test")
@@ -477,6 +517,38 @@ resource "aws_s3_access_point" "test" {
477517
`, bucketName, accessPointName)
478518
}
479519

520+
func testAccAWSS3AccessPointConfig_Bucket_Arn(rName string) string {
521+
return fmt.Sprintf(`
522+
data "aws_outposts_outposts" "test" {}
523+
524+
data "aws_outposts_outpost" "test" {
525+
id = tolist(data.aws_outposts_outposts.test.ids)[0]
526+
}
527+
528+
resource "aws_s3control_bucket" "test" {
529+
bucket = %[1]q
530+
outpost_id = data.aws_outposts_outpost.test.id
531+
}
532+
533+
resource "aws_vpc" "test" {
534+
cidr_block = "10.0.0.0/16"
535+
536+
tags = {
537+
Name = %[1]q
538+
}
539+
}
540+
541+
resource "aws_s3_access_point" "test" {
542+
bucket = aws_s3_bucket.test.arn
543+
name = %[1]q
544+
545+
vpc_configuration {
546+
vpc_id = aws_vpc.test.id
547+
}
548+
}
549+
`, rName)
550+
}
551+
480552
func testAccAWSS3AccessPointConfig_policy(rName string) string {
481553
return fmt.Sprintf(`
482554
resource "aws_s3_bucket" "test" {

‎website/docs/r/s3_access_point.html.markdown

+15-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Provides a resource to manage an S3 Access Point.
1414

1515
## Example Usage
1616

17-
### Basic Usage
17+
### AWS Partition Bucket
1818

1919
```hcl
2020
resource "aws_s3_bucket" "example" {
@@ -27,17 +27,18 @@ resource "aws_s3_access_point" "example" {
2727
}
2828
```
2929

30-
### Access Point Restricted to a VPC
30+
### S3 on Outposts Bucket
3131

3232
```hcl
33-
resource "aws_s3_bucket" "example" {
33+
resource "aws_s3control_bucket" "example" {
3434
bucket = "example"
3535
}
3636
3737
resource "aws_s3_access_point" "example" {
38-
bucket = aws_s3_bucket.example.id
38+
bucket = aws_s3control_bucket.example.arn
3939
name = "example"
4040
41+
# VPC must be specified for S3 on Outposts
4142
vpc_configuration {
4243
vpc_id = aws_vpc.example.id
4344
}
@@ -52,15 +53,15 @@ resource "aws_vpc" "example" {
5253

5354
The following arguments are required:
5455

55-
* `bucket` - (Required) The name of the bucket that you want to associate this access point with.
56+
* `bucket` - (Required) The name of an AWS Partition S3 Bucket or the Amazon Resource Name (ARN) of S3 on Outposts Bucket that you want to associate this access point with.
5657
* `name` - (Required) The name you want to assign to this access point.
5758

5859
The following arguments are optional:
5960

6061
* `account_id` - (Optional) The AWS account ID for the owner of the bucket for which you want to create an access point. Defaults to automatically determined account ID of the Terraform AWS provider.
6162
* `policy` - (Optional) A valid JSON document that specifies the policy that you want to apply to this access point.
6263
* `public_access_block_configuration` - (Optional) Configuration block to manage the `PublicAccessBlock` configuration that you want to apply to this Amazon S3 bucket. You can enable the configuration options in any combination. Detailed below.
63-
* `vpc_configuration` - (Optional) Configuration block to restrict access to this access point to requests from the specified Virtual Private Cloud (VPC). Detailed below.
64+
* `vpc_configuration` - (Optional) Configuration block to restrict access to this access point to requests from the specified Virtual Private Cloud (VPC). Required for S3 on Outposts. Detailed below.
6465

6566
### public_access_block_configuration Configuration Block
6667

@@ -91,13 +92,19 @@ In addition to all arguments above, the following attributes are exported:
9192
* `domain_name` - The DNS domain name of the S3 Access Point in the format _`name`_-_`account_id`_.s3-accesspoint._region_.amazonaws.com.
9293
Note: S3 access points only support secure access by HTTPS. HTTP isn't supported.
9394
* `has_public_access_policy` - Indicates whether this access point currently has a policy that allows public access.
94-
* `id` - AWS account ID and access point name separated by a colon (`:`).
95+
* `id` - For Access Point of an AWS Partition S3 Bucket, the AWS account ID and access point name separated by a colon (`:`). For S3 on Outposts Bucket, the Amazon Resource Name (ARN) of the Access Point.
9596
* `network_origin` - Indicates whether this access point allows access from the public Internet. Values are `VPC` (the access point doesn't allow access from the public Internet) and `Internet` (the access point allows access from the public Internet, subject to the access point and bucket access policies).
9697

9798
## Import
9899

99-
S3 Access Points can be imported using the `account_id` and `name` separated by a colon (`:`), e.g.
100+
For Access Points associated with an AWS Partition S3 Bucket, this resource can be imported using the `account_id` and `name` separated by a colon (`:`), e.g.
100101

101102
```
102103
$ terraform import aws_s3_access_point.example 123456789012:example
103104
```
105+
106+
For Access Points associated with an S3 on Outposts Bucket, this resource can be imported using the Amazon Resource Name (ARN), e.g.
107+
108+
```
109+
$ terraform import aws_s3_access_point.example arn:aws:s3-outposts:us-east-1:123456789012:outpost/op-1234567890123456/accesspoint/example
110+
```

0 commit comments

Comments
 (0)