diff --git a/.changelog/33810.txt b/.changelog/33810.txt new file mode 100644 index 00000000000..b97a03f4e17 --- /dev/null +++ b/.changelog/33810.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_ec2_image_block_public_access +``` diff --git a/.github/labeler-pr-triage.yml b/.github/labeler-pr-triage.yml index 5efedfaca02..c925a6637de 100644 --- a/.github/labeler-pr-triage.yml +++ b/.github/labeler-pr-triage.yml @@ -364,6 +364,7 @@ service/ec2: - 'website/**/ec2_capacity_*' - 'website/**/ec2_fleet*' - 'website/**/ec2_host*' + - 'website/**/ec2_image_*' - 'website/**/ec2_instance_*' - 'website/**/ec2_public_ipv4_pool*' - 'website/**/ec2_serial_*' diff --git a/internal/service/ec2/ec2_image_block_public_access.go b/internal/service/ec2/ec2_image_block_public_access.go new file mode 100644 index 00000000000..70cc55399ac --- /dev/null +++ b/internal/service/ec2/ec2_image_block_public_access.go @@ -0,0 +1,113 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ec2 + +import ( + "context" + "log" + "time" + + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "golang.org/x/exp/slices" +) + +// @SDKResource("aws_ec2_image_block_public_access", name="Image Block Public Access") +func ResourceImageBlockPublicAccess() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceImageBlockPublicAccessPut, + ReadWithoutTimeout: resourceImageBlockPublicAccessRead, + UpdateWithoutTimeout: resourceImageBlockPublicAccessPut, + DeleteWithoutTimeout: schema.NoopContext, + + Timeouts: &schema.ResourceTimeout{ + Update: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "state": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(imageBlockPublicAccessState_Values(), false), + }, + }, + } +} + +func resourceImageBlockPublicAccessPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EC2Client(ctx) + + state := d.Get("state").(string) + + if slices.Contains(imageBlockPublicAccessEnabledState_Values(), state) { + input := &ec2.EnableImageBlockPublicAccessInput{ + ImageBlockPublicAccessState: types.ImageBlockPublicAccessEnabledState(state), + } + + _, err := conn.EnableImageBlockPublicAccess(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "enabling EC2 Image Block Public Access: %s", err) + } + } else { + input := &ec2.DisableImageBlockPublicAccessInput{} + + _, err := conn.DisableImageBlockPublicAccess(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "disabling EC2 Image Block Public Access: %s", err) + } + } + + if d.IsNewResource() { + d.SetId(meta.(*conns.AWSClient).Region) + } + + if err := WaitImageBlockPublicAccessState(ctx, conn, state, d.Timeout(schema.TimeoutUpdate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for EC2 Image Block Public Access state (%s): %s", state, err) + } + + return append(diags, resourceImageBlockPublicAccessRead(ctx, d, meta)...) +} + +func resourceImageBlockPublicAccessRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EC2Client(ctx) + + output, err := FindImageBlockPublicAccessState(ctx, conn) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EC2 Image Block Public Access %s not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading EC2 Image Block Public Access (%s): %s", d.Id(), err) + } + + d.Set("state", output) + + return diags +} + +func imageBlockPublicAccessDisabledState_Values() []string { + return enum.Values[types.ImageBlockPublicAccessDisabledState]() +} + +func imageBlockPublicAccessEnabledState_Values() []string { + return enum.Values[types.ImageBlockPublicAccessEnabledState]() +} + +func imageBlockPublicAccessState_Values() []string { + return append(imageBlockPublicAccessEnabledState_Values(), imageBlockPublicAccessDisabledState_Values()...) +} diff --git a/internal/service/ec2/ec2_image_block_public_access_test.go b/internal/service/ec2/ec2_image_block_public_access_test.go new file mode 100644 index 00000000000..9fc83f8af3d --- /dev/null +++ b/internal/service/ec2/ec2_image_block_public_access_test.go @@ -0,0 +1,55 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ec2_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccEC2ImageBlockPublicAccess_serial(t *testing.T) { + t.Parallel() + + testCases := map[string]func(t *testing.T){ + "basic": testAccImageBlockPublicAccess_basic, + } + + acctest.RunSerialTests1Level(t, testCases, 0) +} + +func testAccImageBlockPublicAccess_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_ec2_image_block_public_access.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccImageBlockPublicAccessConfig_basic("unblocked"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "state", "unblocked"), + ), + }, + { + Config: testAccImageBlockPublicAccessConfig_basic("block-new-sharing"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "state", "block-new-sharing"), + ), + }, + }, + }) +} + +func testAccImageBlockPublicAccessConfig_basic(state string) string { + return fmt.Sprintf(` +resource "aws_ec2_image_block_public_access" "test" { + state = %[1]q +} +`, state) +} diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index da58ec8e5b2..a305b545a11 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -7225,3 +7225,18 @@ func FindVerifiedAccessTrustProviderByID(ctx context.Context, conn *ec2_sdkv2.Cl return output, nil } + +func FindImageBlockPublicAccessState(ctx context.Context, conn *ec2_sdkv2.Client) (*string, error) { + input := &ec2_sdkv2.GetImageBlockPublicAccessStateInput{} + output, err := conn.GetImageBlockPublicAccessState(ctx, input) + + if err != nil { + return nil, err + } + + if output == nil || output.ImageBlockPublicAccessState == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.ImageBlockPublicAccessState, nil +} diff --git a/internal/service/ec2/service_package_gen.go b/internal/service/ec2/service_package_gen.go index e0565e9fee9..ec56ede9ead 100644 --- a/internal/service/ec2/service_package_gen.go +++ b/internal/service/ec2/service_package_gen.go @@ -571,6 +571,11 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka IdentifierAttribute: "id", }, }, + { + Factory: ResourceImageBlockPublicAccess, + TypeName: "aws_ec2_image_block_public_access", + Name: "Image Block Public Access", + }, { Factory: ResourceInstanceState, TypeName: "aws_ec2_instance_state", diff --git a/internal/service/ec2/status.go b/internal/service/ec2/status.go index 1f5b3f324f9..ef75a2588e9 100644 --- a/internal/service/ec2/status.go +++ b/internal/service/ec2/status.go @@ -7,6 +7,7 @@ import ( "context" "strconv" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" ec2_sdkv2 "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -1498,3 +1499,19 @@ func StatusInstanceConnectEndpointState(ctx context.Context, conn *ec2_sdkv2.Cli return output, string(output.State), nil } } + +func StatusImageBlockPublicAccessState(ctx context.Context, conn *ec2_sdkv2.Client) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := FindImageBlockPublicAccessState(ctx, conn) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws_sdkv2.ToString(output), nil + } +} diff --git a/internal/service/ec2/wait.go b/internal/service/ec2/wait.go index 28361f156ba..0c433184fc7 100644 --- a/internal/service/ec2/wait.go +++ b/internal/service/ec2/wait.go @@ -3322,3 +3322,15 @@ func WaitInstanceConnectEndpointDeleted(ctx context.Context, conn *ec2_sdkv2.Cli return nil, err } + +func WaitImageBlockPublicAccessState(ctx context.Context, conn *ec2_sdkv2.Client, target string, timeout time.Duration) error { + stateConf := &retry.StateChangeConf{ + Target: []string{target}, + Refresh: StatusImageBlockPublicAccessState(ctx, conn), + Timeout: timeout, + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} diff --git a/names/names_data.csv b/names/names_data.csv index 8029afb2109..808361319bc 100644 --- a/names/names_data.csv +++ b/names/names_data.csv @@ -124,7 +124,7 @@ dax,dax,dax,dax,,dax,,,DAX,DAX,,1,,,aws_dax_,,dax_,DynamoDB Accelerator (DAX),Am dynamodbstreams,dynamodbstreams,dynamodbstreams,dynamodbstreams,,dynamodbstreams,,,DynamoDBStreams,DynamoDBStreams,,1,,,aws_dynamodbstreams_,,dynamodbstreams_,DynamoDB Streams,Amazon,,x,,,, ,,,,,ec2ebs,ec2,,EC2EBS,,,,,aws_(ebs_|volume_attach|snapshot_create),aws_ec2ebs_,ebs_,ebs_;volume_attachment;snapshot_,EBS (EC2),Amazon,x,,x,,,Part of EC2 ebs,ebs,ebs,ebs,,ebs,,,EBS,EBS,,1,,,aws_ebs_,,changewhenimplemented,EBS (Elastic Block Store),Amazon,,x,,,, -ec2,ec2,ec2,ec2,,ec2,ec2,,EC2,EC2,,1,2,aws_(ami|availability_zone|ec2_(availability|capacity|fleet|host|instance|public_ipv4_pool|serial|spot|tag)|eip|instance|key_pair|launch_template|placement_group|spot),aws_ec2_,ec2_,ami;availability_zone;ec2_availability_;ec2_capacity_;ec2_fleet;ec2_host;ec2_instance_;ec2_public_ipv4_pool;ec2_serial_;ec2_spot_;ec2_tag;eip;instance;key_pair;launch_template;placement_group;spot_,EC2 (Elastic Compute Cloud),Amazon,,,,,, +ec2,ec2,ec2,ec2,,ec2,ec2,,EC2,EC2,,1,2,aws_(ami|availability_zone|ec2_(availability|capacity|fleet|host|instance|public_ipv4_pool|serial|spot|tag)|eip|instance|key_pair|launch_template|placement_group|spot),aws_ec2_,ec2_,ami;availability_zone;ec2_availability_;ec2_capacity_;ec2_fleet;ec2_host;ec2_image_;ec2_instance_;ec2_public_ipv4_pool;ec2_serial_;ec2_spot_;ec2_tag;eip;instance;key_pair;launch_template;placement_group;spot_,EC2 (Elastic Compute Cloud),Amazon,,,,,, imagebuilder,imagebuilder,imagebuilder,imagebuilder,,imagebuilder,,,ImageBuilder,Imagebuilder,,1,,,aws_imagebuilder_,,imagebuilder_,EC2 Image Builder,Amazon,,,,,, ec2-instance-connect,ec2instanceconnect,ec2instanceconnect,ec2instanceconnect,,ec2instanceconnect,,,EC2InstanceConnect,EC2InstanceConnect,,1,,,aws_ec2instanceconnect_,,ec2instanceconnect_,EC2 Instance Connect,AWS,,x,,,, ecr,ecr,ecr,ecr,,ecr,,,ECR,ECR,,1,,,aws_ecr_,,ecr_,ECR (Elastic Container Registry),Amazon,,,,,, diff --git a/website/docs/r/ec2_image_block_public_access.markdown b/website/docs/r/ec2_image_block_public_access.markdown new file mode 100644 index 00000000000..871599db420 --- /dev/null +++ b/website/docs/r/ec2_image_block_public_access.markdown @@ -0,0 +1,43 @@ +--- +subcategory: "EC2 (Elastic Compute Cloud)" +layout: "aws" +page_title: "AWS: aws_ec2_image_block_public_access" +description: |- + Provides a regional public access block for AMIs. This prevents AMIs from being made publicly accessible. +--- + +# Resource: aws_ec2_image_block_public_access + +Provides a regional public access block for AMIs. This prevents AMIs from being made publicly accessible. +If you already have public AMIs, they will remain publicly available. + +~> **NOTE:** Deleting this resource does not change the block public access value, the resource in simply removed from state instead. + +## Example Usage + +```terraform +# Prevent making AMIs publicly accessible in the region and account for which the provider is configured +resource "aws_ec2_image_block_public_access" "test" { + state = "block-new-sharing" +} +``` + +## Argument Reference + +This resource supports the following arguments: + +* `state` - (Required) The state of block public access for AMIs at the account level in the configured AWS Region. Valid values: `unblocked` and `block-new-sharing`. + +## Attribute Reference + +This resource exports no additional attributes. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +- `update` - (Default `10m`) + +## Import + +You cannot import this resource.