Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

S3 Control: Access Point cross-account access & Storage Lens advanced metrics #28564

Merged
merged 13 commits into from
Dec 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/28564.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_s3control_storage_lens_configuration: Add `advanced_cost_optimization_metrics`, `advanced_data_protection_metrics`, and `detailed_status_code_metrics` arguments to the `storage_lens_configuration.account_level` and `storage_lens_configuration.account_level.bucket_level` configuration blocks
```

```release-note:enhancement
resource/aws_s3_access_point: Add `bucket_account_id` argument
```
16 changes: 0 additions & 16 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/service/route53resolver"
"github.com/hashicorp/terraform-provider-aws/internal/service/rum"
"github.com/hashicorp/terraform-provider-aws/internal/service/s3"
"github.com/hashicorp/terraform-provider-aws/internal/service/s3control"
"github.com/hashicorp/terraform-provider-aws/internal/service/s3outposts"
"github.com/hashicorp/terraform-provider-aws/internal/service/sagemaker"
"github.com/hashicorp/terraform-provider-aws/internal/service/scheduler"
Expand Down Expand Up @@ -870,9 +869,6 @@ func New(ctx context.Context) (*schema.Provider, error) {
"aws_s3_bucket_objects": s3.DataSourceBucketObjects(), // DEPRECATED: use aws_s3_objects instead
"aws_s3_bucket_policy": s3.DataSourceBucketPolicy(),

"aws_s3_account_public_access_block": s3control.DataSourceAccountPublicAccessBlock(),
"aws_s3control_multi_region_access_point": s3control.DataSourceMultiRegionAccessPoint(),

"aws_sagemaker_prebuilt_ecr_image": sagemaker.DataSourcePrebuiltECRImage(),

"aws_secretsmanager_random_password": secretsmanager.DataSourceRandomPassword(),
Expand Down Expand Up @@ -2006,18 +2002,6 @@ func New(ctx context.Context) (*schema.Provider, error) {
"aws_s3_object_copy": s3.ResourceObjectCopy(),
"aws_s3_bucket_object": s3.ResourceBucketObject(), // DEPRECATED: use aws_s3_object instead

"aws_s3_access_point": s3control.ResourceAccessPoint(),
"aws_s3control_access_point_policy": s3control.ResourceAccessPointPolicy(),
"aws_s3_account_public_access_block": s3control.ResourceAccountPublicAccessBlock(),
"aws_s3control_bucket": s3control.ResourceBucket(),
"aws_s3control_bucket_lifecycle_configuration": s3control.ResourceBucketLifecycleConfiguration(),
"aws_s3control_bucket_policy": s3control.ResourceBucketPolicy(),
"aws_s3control_multi_region_access_point": s3control.ResourceMultiRegionAccessPoint(),
"aws_s3control_multi_region_access_point_policy": s3control.ResourceMultiRegionAccessPointPolicy(),
"aws_s3control_object_lambda_access_point": s3control.ResourceObjectLambdaAccessPoint(),
"aws_s3control_object_lambda_access_point_policy": s3control.ResourceObjectLambdaAccessPointPolicy(),
"aws_s3control_storage_lens_configuration": s3control.ResourceStorageLensConfiguration(),

"aws_s3outposts_endpoint": s3outposts.ResourceEndpoint(),

"aws_sagemaker_app": sagemaker.ResourceApp(),
Expand Down
121 changes: 81 additions & 40 deletions internal/service/s3control/access_point.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package s3control

import (
"context"
"fmt"
"log"
"strings"
Expand All @@ -9,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/s3control"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
Expand All @@ -17,12 +20,16 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/verify"
)

func ResourceAccessPoint() *schema.Resource {
func init() {
_sp.registerSDKResourceFactory("aws_s3_access_point", resourceAccessPoint)
}

func resourceAccessPoint() *schema.Resource {
return &schema.Resource{
Create: resourceAccessPointCreate,
Read: resourceAccessPointRead,
Update: resourceAccessPointUpdate,
Delete: resourceAccessPointDelete,
CreateWithoutTimeout: resourceAccessPointCreate,
ReadWithoutTimeout: resourceAccessPointRead,
UpdateWithoutTimeout: resourceAccessPointUpdate,
DeleteWithoutTimeout: resourceAccessPointDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
Expand Down Expand Up @@ -50,6 +57,13 @@ func ResourceAccessPoint() *schema.Resource {
ForceNew: true,
ValidateFunc: validation.NoZeroValues,
},
"bucket_account_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: verify.ValidAccountID,
},
"domain_name": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -140,7 +154,7 @@ func ResourceAccessPoint() *schema.Resource {
}
}

func resourceAccessPointCreate(d *schema.ResourceData, meta interface{}) error {
func resourceAccessPointCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).S3ControlConn()

accountID := meta.(*conns.AWSClient).AccountID
Expand All @@ -155,6 +169,10 @@ func resourceAccessPointCreate(d *schema.ResourceData, meta interface{}) error {
Name: aws.String(name),
}

if v, ok := d.GetOk("bucket_account_id"); ok {
input.BucketAccountId = aws.String(v.(string))
}

if v, ok := d.GetOk("public_access_block_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
input.PublicAccessBlockConfiguration = expandPublicAccessBlockConfiguration(v.([]interface{})[0].(map[string]interface{}))
}
Expand All @@ -163,23 +181,22 @@ func resourceAccessPointCreate(d *schema.ResourceData, meta interface{}) error {
input.VpcConfiguration = expandVPCConfiguration(v.([]interface{})[0].(map[string]interface{}))
}

log.Printf("[DEBUG] Creating S3 Access Point: %s", input)
output, err := conn.CreateAccessPoint(input)
output, err := conn.CreateAccessPointWithContext(ctx, input)

if err != nil {
return fmt.Errorf("error creating S3 Access Point (%s): %w", name, err)
return diag.Errorf("creating S3 Access Point (%s): %s", name, err)
}

resourceID, err := AccessPointCreateResourceID(aws.StringValue(output.AccessPointArn))

if err != nil {
return err
return diag.FromErr(err)
}

accountID, name, err = AccessPointParseResourceID(resourceID)

if err != nil {
return err
return diag.FromErr(err)
}

d.SetId(resourceID)
Expand All @@ -188,7 +205,7 @@ func resourceAccessPointCreate(d *schema.ResourceData, meta interface{}) error {
policy, err := structure.NormalizeJsonString(v.(string))

if err != nil {
return fmt.Errorf("policy (%s) is invalid JSON: %w", v.(string), err)
return diag.Errorf("policy (%s) is invalid JSON: %s", v.(string), err)
}

input := &s3control.PutAccessPointPolicyInput{
Expand All @@ -197,29 +214,28 @@ func resourceAccessPointCreate(d *schema.ResourceData, meta interface{}) error {
Policy: aws.String(policy),
}

log.Printf("[DEBUG] Creating S3 Access Point policy: %s", input)
_, err = conn.PutAccessPointPolicy(input)
_, err = conn.PutAccessPointPolicyWithContext(ctx, input)

if err != nil {
return fmt.Errorf("error creating S3 Access Point (%s) policy: %w", d.Id(), err)
return diag.Errorf("creating S3 Access Point (%s) policy: %s", d.Id(), err)
}
}

return resourceAccessPointRead(d, meta)
return resourceAccessPointRead(ctx, d, meta)
}

func resourceAccessPointRead(d *schema.ResourceData, meta interface{}) error {
func resourceAccessPointRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).S3ControlConn()

accountID, name, err := AccessPointParseResourceID(d.Id())

if err != nil {
return err
return diag.FromErr(err)
}

s3OnOutposts := arn.IsARN(name)

output, err := FindAccessPointByAccountIDAndName(conn, accountID, name)
output, err := FindAccessPointByTwoPartKey(ctx, conn, accountID, name)

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] S3 Access Point (%s) not found, removing from state", d.Id())
Expand All @@ -228,14 +244,14 @@ func resourceAccessPointRead(d *schema.ResourceData, meta interface{}) error {
}

if err != nil {
return fmt.Errorf("error reading S3 Access Point (%s): %w", d.Id(), err)
return diag.Errorf("reading S3 Access Point (%s): %s", d.Id(), err)
}

if s3OnOutposts {
accessPointARN, err := arn.Parse(name)

if err != nil {
return err
return diag.FromErr(err)
}

// https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3onoutposts.html#amazons3onoutposts-resources-for-iam-policies.
Expand Down Expand Up @@ -270,26 +286,27 @@ func resourceAccessPointRead(d *schema.ResourceData, meta interface{}) error {

d.Set("account_id", accountID)
d.Set("alias", output.Alias)
d.Set("bucket_account_id", output.BucketAccountId)
d.Set("domain_name", meta.(*conns.AWSClient).RegionalHostname(fmt.Sprintf("%s-%s.s3-accesspoint", aws.StringValue(output.Name), accountID)))
d.Set("endpoints", aws.StringValueMap(output.Endpoints))
d.Set("name", output.Name)
d.Set("network_origin", output.NetworkOrigin)
if output.PublicAccessBlockConfiguration != nil {
if err := d.Set("public_access_block_configuration", []interface{}{flattenPublicAccessBlockConfiguration(output.PublicAccessBlockConfiguration)}); err != nil {
return fmt.Errorf("error setting public_access_block_configuration: %w", err)
return diag.Errorf("setting public_access_block_configuration: %s", err)
}
} else {
d.Set("public_access_block_configuration", nil)
}
if output.VpcConfiguration != nil {
if err := d.Set("vpc_configuration", []interface{}{flattenVPCConfiguration(output.VpcConfiguration)}); err != nil {
return fmt.Errorf("error setting vpc_configuration: %w", err)
return diag.Errorf("setting vpc_configuration: %s", err)
}
} else {
d.Set("vpc_configuration", nil)
}

policy, status, err := FindAccessPointPolicyAndStatusByAccountIDAndName(conn, accountID, name)
policy, status, err := FindAccessPointPolicyAndStatusByTwoPartKey(ctx, conn, accountID, name)

if err == nil && policy != "" {
if s3OnOutposts {
Expand All @@ -301,35 +318,35 @@ func resourceAccessPointRead(d *schema.ResourceData, meta interface{}) error {
policyToSet, err := verify.PolicyToSet(d.Get("policy").(string), policy)

if err != nil {
return err
return diag.FromErr(err)
}

d.Set("policy", policyToSet)
} else if policy == "" || tfresource.NotFound(err) {
d.Set("has_public_access_policy", false)
d.Set("policy", nil)
} else {
return fmt.Errorf("error reading S3 Access Point (%s) policy: %w", d.Id(), err)
return diag.Errorf("reading S3 Access Point (%s) policy: %s", d.Id(), err)
}

return nil
}

func resourceAccessPointUpdate(d *schema.ResourceData, meta interface{}) error {
func resourceAccessPointUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).S3ControlConn()

accountID, name, err := AccessPointParseResourceID(d.Id())

if err != nil {
return err
return diag.FromErr(err)
}

if d.HasChange("policy") {
if v, ok := d.GetOk("policy"); ok && v.(string) != "" && v.(string) != "{}" {
policy, err := structure.NormalizeJsonString(v.(string))

if err != nil {
return fmt.Errorf("policy (%s) is invalid JSON: %w", v.(string), err)
return diag.Errorf("policy (%s) is invalid JSON: %s", v.(string), err)
}

input := &s3control.PutAccessPointPolicyInput{
Expand All @@ -338,39 +355,37 @@ func resourceAccessPointUpdate(d *schema.ResourceData, meta interface{}) error {
Policy: aws.String(policy),
}

log.Printf("[DEBUG] Updating S3 Access Point policy: %s", input)
_, err = conn.PutAccessPointPolicy(input)
_, err = conn.PutAccessPointPolicyWithContext(ctx, input)

if err != nil {
return fmt.Errorf("error updating S3 Access Point (%s) policy: %w", d.Id(), err)
return diag.Errorf("updating S3 Access Point (%s) policy: %s", d.Id(), err)
}
} else {
log.Printf("[DEBUG] Deleting S3 Access Point policy: %s", d.Id())
_, err := conn.DeleteAccessPointPolicy(&s3control.DeleteAccessPointPolicyInput{
_, err := conn.DeleteAccessPointPolicyWithContext(ctx, &s3control.DeleteAccessPointPolicyInput{
AccountId: aws.String(accountID),
Name: aws.String(name),
})

if err != nil {
return fmt.Errorf("error deleting S3 Access Point (%s) policy: %w", d.Id(), err)
return diag.Errorf("deleting S3 Access Point (%s) policy: %s", d.Id(), err)
}
}
}

return resourceAccessPointRead(d, meta)
return resourceAccessPointRead(ctx, d, meta)
}

func resourceAccessPointDelete(d *schema.ResourceData, meta interface{}) error {
func resourceAccessPointDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*conns.AWSClient).S3ControlConn()

accountID, name, err := AccessPointParseResourceID(d.Id())

if err != nil {
return err
return diag.FromErr(err)
}

log.Printf("[DEBUG] Deleting S3 Access Point: %s", d.Id())
_, err = conn.DeleteAccessPoint(&s3control.DeleteAccessPointInput{
_, err = conn.DeleteAccessPointWithContext(ctx, &s3control.DeleteAccessPointInput{
AccountId: aws.String(accountID),
Name: aws.String(name),
})
Expand All @@ -380,12 +395,38 @@ func resourceAccessPointDelete(d *schema.ResourceData, meta interface{}) error {
}

if err != nil {
return fmt.Errorf("error deleting S3 Access Point (%s): %w", d.Id(), err)
return diag.Errorf("deleting S3 Access Point (%s): %s", d.Id(), err)
}

return nil
}

func FindAccessPointByTwoPartKey(ctx context.Context, conn *s3control.S3Control, accountID string, name string) (*s3control.GetAccessPointOutput, error) {
input := &s3control.GetAccessPointInput{
AccountId: aws.String(accountID),
Name: aws.String(name),
}

output, err := conn.GetAccessPointWithContext(ctx, input)

if tfawserr.ErrCodeEquals(err, errCodeNoSuchAccessPoint) {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil {
return nil, tfresource.NewEmptyResultError(input)
}

return output, nil
}

const accessPointResourceIDSeparator = ":"

func AccessPointCreateResourceID(accessPointARN string) (string, error) {
Expand Down
Loading