From 66acfb1b3f423a9e471cbe4310b842f7ff2f1d0e Mon Sep 17 00:00:00 2001 From: Madhav Vishnubhatta Date: Wed, 20 Nov 2024 20:52:32 +0000 Subject: [PATCH 1/6] Added new resource aws_vpc_block_public_access_options --- internal/service/ec2/service_package_gen.go | 4 + .../ec2/vpc_block_public_access_options.go | 324 ++++++++++++++++++ .../vpc_block_public_access_options_test.go | 178 ++++++++++ names/data/names_data.hcl | 2 +- ..._block_public_access_options.html.markdown | 61 ++++ 5 files changed, 568 insertions(+), 1 deletion(-) create mode 100644 internal/service/ec2/vpc_block_public_access_options.go create mode 100644 internal/service/ec2/vpc_block_public_access_options_test.go create mode 100644 website/docs/r/vpc_block_public_access_options.html.markdown diff --git a/internal/service/ec2/service_package_gen.go b/internal/service/ec2/service_package_gen.go index de1267ac1e7..05915034f5d 100644 --- a/internal/service/ec2/service_package_gen.go +++ b/internal/service/ec2/service_package_gen.go @@ -65,6 +65,10 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic Factory: newResourceSecurityGroupVPCAssociation, Name: "Security Group VPC Association", }, + { + Factory: newResourceVPCBlockPublicAccessOptions, + Name: "VPC Block Public Access Options", + }, { Factory: newSecurityGroupEgressRuleResource, Name: "Security Group Egress Rule", diff --git a/internal/service/ec2/vpc_block_public_access_options.go b/internal/service/ec2/vpc_block_public_access_options.go new file mode 100644 index 00000000000..41fdd9014f8 --- /dev/null +++ b/internal/service/ec2/vpc_block_public_access_options.go @@ -0,0 +1,324 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ec2 + +import ( + "context" + "errors" + "time" + + "github.com/aws/aws-sdk-go-v2/service/ec2" + awstypes "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource("aws_vpc_block_public_access_options", name="VPC Block Public Access Options") +func newResourceVPCBlockPublicAccessOptions(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &resourceVPCBlockPublicAccessOptions{} + r.SetDefaultCreateTimeout(30 * time.Minute) + r.SetDefaultUpdateTimeout(30 * time.Minute) + r.SetDefaultDeleteTimeout(30 * time.Minute) + + return r, nil +} + +const ( + ResNameVPCBlockPublicAccessOptions = "VPC Block Public Access Options" +) + +type resourceVPCBlockPublicAccessOptions struct { + framework.ResourceWithConfigure + framework.WithTimeouts + framework.WithImportByID +} + +func (r *resourceVPCBlockPublicAccessOptions) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_vpc_block_public_access_options" +} + +func (r *resourceVPCBlockPublicAccessOptions) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + names.AttrID: framework.IDAttribute(), + "aws_account_id": schema.StringAttribute{ + Computed: true, + }, + "aws_region": schema.StringAttribute{ + Computed: true, + }, + "internet_gateway_block_mode": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.InternetGatewayBlockMode](), + }, + }, + "last_update_timestamp": schema.StringAttribute{ + CustomType: timetypes.RFC3339Type{}, + Computed: true, + }, + "reason": schema.StringAttribute{ + Computed: true, + }, + }, + Blocks: map[string]schema.Block{ + names.AttrTimeouts: timeouts.Block(ctx, timeouts.Opts{ + Create: true, + Update: true, + Delete: true, + }), + }, + } +} + +func (r *resourceVPCBlockPublicAccessOptions) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().EC2Client(ctx) + + var plan resourceVPCBlockPublicAccessOptionsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + var input ec2.ModifyVpcBlockPublicAccessOptionsInput + + input.InternetGatewayBlockMode = awstypes.InternetGatewayBlockMode(plan.InternetGatewayBlockMode.ValueString()) + + out, err := conn.ModifyVpcBlockPublicAccessOptions(ctx, &input) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, "ModifyVpcBlockPublicAccessOptions", err), + err.Error(), + ) + return + } + + if out == nil || out.VpcBlockPublicAccessOptions == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, out.VpcBlockPublicAccessOptions, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + plan.ID = flex.StringValueToFramework(ctx, r.Meta().AccountID+":"+r.Meta().Region) + + createTimeout := r.CreateTimeout(ctx, plan.Timeouts) + _, err = waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, createTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionWaitingForCreation, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), err), + err.Error(), + ) + return + } + + desc_out, desc_err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) + if desc_err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionSetting, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), desc_err), + desc_err.Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, desc_out.VpcBlockPublicAccessOptions, &plan)...) + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourceVPCBlockPublicAccessOptions) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().EC2Client(ctx) + + var state resourceVPCBlockPublicAccessOptionsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + out, err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionSetting, ResNameVPCBlockPublicAccessOptions, state.ID.String(), err), + err.Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, out.VpcBlockPublicAccessOptions, &state)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceVPCBlockPublicAccessOptions) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + conn := r.Meta().EC2Client(ctx) + + var plan, state resourceVPCBlockPublicAccessOptionsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if !plan.InternetGatewayBlockMode.Equal(state.InternetGatewayBlockMode) { + var input ec2.ModifyVpcBlockPublicAccessOptionsInput + resp.Diagnostics.Append(flex.Expand(ctx, plan, &input, flex.WithFieldNamePrefix("Test"))...) + if resp.Diagnostics.HasError() { + return + } + + out, err := conn.ModifyVpcBlockPublicAccessOptions(ctx, &input) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), err), + err.Error(), + ) + return + } + if out == nil || out.VpcBlockPublicAccessOptions == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, out.VpcBlockPublicAccessOptions, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + } + + updateTimeout := r.UpdateTimeout(ctx, plan.Timeouts) + _, err := waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, updateTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionWaitingForCreation, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), err), + err.Error(), + ) + return + } + + desc_out, desc_err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) + if desc_err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionSetting, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), desc_err), + desc_err.Error(), + ) + return + } + + resp.Diagnostics.Append(flex.Flatten(ctx, desc_out.VpcBlockPublicAccessOptions, &plan)...) + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourceVPCBlockPublicAccessOptions) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().EC2Client(ctx) + + var state resourceVPCBlockPublicAccessOptionsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var input ec2.ModifyVpcBlockPublicAccessOptionsInput + + // On deletion of this resource set the VPC Block Public Access Options to off + input.InternetGatewayBlockMode = awstypes.InternetGatewayBlockModeOff + + out, err := conn.ModifyVpcBlockPublicAccessOptions(ctx, &input) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, state.ID.String(), err), + err.Error(), + ) + return + } + + if out == nil || out.VpcBlockPublicAccessOptions == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, state.ID.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) + _, err = waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, deleteTimeout) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EC2, create.ErrActionWaitingForDeletion, ResNameVPCBlockPublicAccessOptions, state.ID.String(), err), + err.Error(), + ) + return + } +} + +func (r *resourceVPCBlockPublicAccessOptions) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func waitVPCBlockPublicAccessOptionsUpdated(ctx context.Context, conn *ec2.Client, timeout time.Duration) (*awstypes.VpcBlockPublicAccessOptions, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{string(awstypes.VpcBlockPublicAccessStateUpdateInProgress)}, + Target: []string{string(awstypes.VpcBlockPublicAccessStateUpdateComplete)}, + Refresh: statusVPCBlockPublicAccessOptions(ctx, conn), + Timeout: timeout, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*awstypes.VpcBlockPublicAccessOptions); ok { + return out, err + } + + return nil, err +} + +func statusVPCBlockPublicAccessOptions(ctx context.Context, conn *ec2.Client) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + out, err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return out, string(out.VpcBlockPublicAccessOptions.State), nil + } +} + +type resourceVPCBlockPublicAccessOptionsModel struct { + AWSAccountID types.String `tfsdk:"aws_account_id"` + AWSRegion types.String `tfsdk:"aws_region"` + InternetGatewayBlockMode types.String `tfsdk:"internet_gateway_block_mode"` + ID types.String `tfsdk:"id"` + LastUpdateTimestamp timetypes.RFC3339 `tfsdk:"last_update_timestamp"` + Reason types.String `tfsdk:"reason"` + Timeouts timeouts.Value `tfsdk:"timeouts"` +} diff --git a/internal/service/ec2/vpc_block_public_access_options_test.go b/internal/service/ec2/vpc_block_public_access_options_test.go new file mode 100644 index 00000000000..65c965d4dad --- /dev/null +++ b/internal/service/ec2/vpc_block_public_access_options_test.go @@ -0,0 +1,178 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package ec2_test + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/ec2" + awstypes "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/names" + + tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" +) + +func TestAccVPCBlockPublicAccessOptions_basic(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + resourceName := "aws_vpc_block_public_access_options.test" + + rMode := string(awstypes.InternetGatewayBlockModeBlockBidirectional) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.EC2) + testAccPreCheckVPCBlockPublicAccess(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckVPCBlockPublicAccessOptionsDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode), + resource.TestCheckResourceAttrSet(resourceName, "aws_account_id"), + resource.TestCheckResourceAttrSet(resourceName, "aws_region"), + resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), + resource.TestCheckResourceAttrSet(resourceName, "reason"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccVPCBlockPublicAccessOptions_updates(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + resourceName := "aws_vpc_block_public_access_options.test" + + rMode1 := string(awstypes.InternetGatewayBlockModeBlockBidirectional) + rMode2 := string(awstypes.InternetGatewayBlockModeBlockIngress) + rMode3 := string(awstypes.InternetGatewayBlockModeOff) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.EC2) + testAccPreCheckVPCBlockPublicAccess(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EC2), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckVPCBlockPublicAccessOptionsDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode1), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode1), + resource.TestCheckResourceAttrSet(resourceName, "aws_account_id"), + resource.TestCheckResourceAttrSet(resourceName, "aws_region"), + resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), + resource.TestCheckResourceAttrSet(resourceName, "reason"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode2), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode2), + resource.TestCheckResourceAttrSet(resourceName, "aws_account_id"), + resource.TestCheckResourceAttrSet(resourceName, "aws_region"), + resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), + resource.TestCheckResourceAttrSet(resourceName, "reason"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode3), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode3), + resource.TestCheckResourceAttrSet(resourceName, "aws_account_id"), + resource.TestCheckResourceAttrSet(resourceName, "aws_region"), + resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), + resource.TestCheckResourceAttrSet(resourceName, "reason"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckVPCBlockPublicAccessOptionsDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Client(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_vpc_block_public_access_options" { + continue + } + + out, err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) + if out.VpcBlockPublicAccessOptions.InternetGatewayBlockMode == awstypes.InternetGatewayBlockModeOff { + return nil + } + if err != nil { + return create.Error(names.EC2, create.ErrActionCheckingDestroyed, tfec2.ResNameVPCBlockPublicAccessOptions, rs.Primary.ID, err) + } + + return create.Error(names.EC2, create.ErrActionCheckingDestroyed, tfec2.ResNameVPCBlockPublicAccessOptions, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} + +func testAccPreCheckVPCBlockPublicAccess(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Client(ctx) + + input := &ec2.DescribeVpcBlockPublicAccessOptionsInput{} + _, err := conn.DescribeVpcBlockPublicAccessOptions(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccVPCBlockPublicAccessOptionsConfig_basic(rMode string) string { + return fmt.Sprintf(` +resource "aws_vpc_block_public_access_options" "test" { + internet_gateway_block_mode = %[1]q +} +`, rMode) +} diff --git a/names/data/names_data.hcl b/names/data/names_data.hcl index b46f324edbd..23c87836909 100644 --- a/names/data/names_data.hcl +++ b/names/data/names_data.hcl @@ -9454,7 +9454,7 @@ service "ec2" { split_package = "ec2" file_prefix = "vpc_" - doc_prefix = ["default_network_", "default_route_", "default_security_", "default_subnet", "default_vpc", "ec2_managed_", "ec2_network_", "ec2_subnet_", "ec2_traffic_", "egress_only_", "flow_log", "internet_gateway", "main_route_", "nat_", "network_", "prefix_list", "route_", "route\\.", "security_group", "subnet", "vpc_dhcp_", "vpc_endpoint", "vpc_ipv", "vpc_network_performance", "vpc_peering_", "vpc_security_group_", "vpc\\.", "vpcs\\."] + doc_prefix = ["default_network_", "default_route_", "default_security_", "default_subnet", "default_vpc", "ec2_managed_", "ec2_network_", "ec2_subnet_", "ec2_traffic_", "egress_only_", "flow_log", "internet_gateway", "main_route_", "nat_", "network_", "prefix_list", "route_", "route\\.", "security_group", "subnet", "vpc_dhcp_", "vpc_endpoint", "vpc_ipv", "vpc_network_performance", "vpc_peering_", "vpc_security_group_", "vpc\\.", "vpcs\\.", "vpc_block_public_access_"] brand = "Amazon" exclude = true allowed_subcategory = true diff --git a/website/docs/r/vpc_block_public_access_options.html.markdown b/website/docs/r/vpc_block_public_access_options.html.markdown new file mode 100644 index 00000000000..5ade64c0f25 --- /dev/null +++ b/website/docs/r/vpc_block_public_access_options.html.markdown @@ -0,0 +1,61 @@ +--- +subcategory: "VPC (Virtual Private Cloud)" +layout: "aws" +page_title: "AWS: aws_vpc_block_public_access_options" +description: |- + Terraform resource for managing AWS VPC Block Public Access Options in a region. +--- + +# Resource: aws_vpc_block_public_access_options + +Terraform resource for managing an AWS VPC Block Public Access Options. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_vpc_block_public_access_options" "example" { + internet_gateway_block_mode = "block-bidirectional" +} +``` + +## Argument Reference + +The following arguments are required: + +* `internet_gateway_block_mode` - (Required) Block mode. Needs to be one of `block-bidirectional`, `block-ingress`, `off`. If this resource is deleted, then this value will be set to `off` in the AWS account and region. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `aws_account_id` - The AWS account id to which these options apply. +* `aws_region_id` - The AWS region to which these options apply. +* `last_update_timestamp` - Last Update Timestamp +* `reason` - Reason for update. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `60m`) +* `update` - (Default `180m`) +* `delete` - (Default `90m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import VPC Block Public Access Options using the `example_id_arg`. For example: + +```terraform +import { + to = aws_vpc_block_public_access_options.example + id = "111222333444:us-east-1" +} +``` + +Using `terraform import`, import VPC Block Public Access Options using the `example_id_arg`. For example: + +```console +% terraform import aws_vpc_block_public_access_options.example 111222333444:us-east-1 +``` From d696c51195fa9cc7fc76f83adf02d59917536939 Mon Sep 17 00:00:00 2001 From: Madhav Vishnubhatta Date: Thu, 21 Nov 2024 10:21:25 +0000 Subject: [PATCH 2/6] Addressed findings from import lint, lint, semgrep etc. --- .github/labeler-pr-triage.yml | 1 + .../ec2/vpc_block_public_access_options.go | 23 +++++++++---------- .../vpc_block_public_access_options_test.go | 11 ++++----- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/.github/labeler-pr-triage.yml b/.github/labeler-pr-triage.yml index 006d30413bf..04b40b53617 100644 --- a/.github/labeler-pr-triage.yml +++ b/.github/labeler-pr-triage.yml @@ -2241,6 +2241,7 @@ service/vpc: - 'website/**/vpc_security_group_*' - 'website/**/vpc\.*' - 'website/**/vpcs\.*' + - 'website/**/vpc_block_public_access_*' service/vpclattice: - any: - changed-files: diff --git a/internal/service/ec2/vpc_block_public_access_options.go b/internal/service/ec2/vpc_block_public_access_options.go index 41fdd9014f8..8def9ab9c21 100644 --- a/internal/service/ec2/vpc_block_public_access_options.go +++ b/internal/service/ec2/vpc_block_public_access_options.go @@ -54,7 +54,7 @@ func (r *resourceVPCBlockPublicAccessOptions) Schema(ctx context.Context, req re resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ names.AttrID: framework.IDAttribute(), - "aws_account_id": schema.StringAttribute{ + names.AttrAWSAccountID: schema.StringAttribute{ Computed: true, }, "aws_region": schema.StringAttribute{ @@ -122,7 +122,7 @@ func (r *resourceVPCBlockPublicAccessOptions) Create(ctx context.Context, req re plan.ID = flex.StringValueToFramework(ctx, r.Meta().AccountID+":"+r.Meta().Region) createTimeout := r.CreateTimeout(ctx, plan.Timeouts) - _, err = waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, createTimeout) + err = waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, createTimeout) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.EC2, create.ErrActionWaitingForCreation, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), err), @@ -208,11 +208,10 @@ func (r *resourceVPCBlockPublicAccessOptions) Update(ctx context.Context, req re if resp.Diagnostics.HasError() { return } - } updateTimeout := r.UpdateTimeout(ctx, plan.Timeouts) - _, err := waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, updateTimeout) + err := waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, updateTimeout) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.EC2, create.ErrActionWaitingForCreation, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), err), @@ -267,7 +266,7 @@ func (r *resourceVPCBlockPublicAccessOptions) Delete(ctx context.Context, req re } deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) - _, err = waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, deleteTimeout) + err = waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, deleteTimeout) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.EC2, create.ErrActionWaitingForDeletion, ResNameVPCBlockPublicAccessOptions, state.ID.String(), err), @@ -278,24 +277,24 @@ func (r *resourceVPCBlockPublicAccessOptions) Delete(ctx context.Context, req re } func (r *resourceVPCBlockPublicAccessOptions) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp) } -func waitVPCBlockPublicAccessOptionsUpdated(ctx context.Context, conn *ec2.Client, timeout time.Duration) (*awstypes.VpcBlockPublicAccessOptions, error) { +func waitVPCBlockPublicAccessOptionsUpdated(ctx context.Context, conn *ec2.Client, timeout time.Duration) error { stateConf := &retry.StateChangeConf{ - Pending: []string{string(awstypes.VpcBlockPublicAccessStateUpdateInProgress)}, - Target: []string{string(awstypes.VpcBlockPublicAccessStateUpdateComplete)}, + Pending: enum.Slice(awstypes.VpcBlockPublicAccessStateUpdateInProgress), + Target: enum.Slice(awstypes.VpcBlockPublicAccessStateUpdateComplete), Refresh: statusVPCBlockPublicAccessOptions(ctx, conn), Timeout: timeout, ContinuousTargetOccurence: 2, } outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*awstypes.VpcBlockPublicAccessOptions); ok { - return out, err + if _, ok := outputRaw.(*awstypes.VpcBlockPublicAccessOptions); ok { + return err } - return nil, err + return err } func statusVPCBlockPublicAccessOptions(ctx context.Context, conn *ec2.Client) retry.StateRefreshFunc { diff --git a/internal/service/ec2/vpc_block_public_access_options_test.go b/internal/service/ec2/vpc_block_public_access_options_test.go index 65c965d4dad..455a00f253a 100644 --- a/internal/service/ec2/vpc_block_public_access_options_test.go +++ b/internal/service/ec2/vpc_block_public_access_options_test.go @@ -16,9 +16,8 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/names" - tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" + "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccVPCBlockPublicAccessOptions_basic(t *testing.T) { @@ -45,7 +44,7 @@ func TestAccVPCBlockPublicAccessOptions_basic(t *testing.T) { Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode), - resource.TestCheckResourceAttrSet(resourceName, "aws_account_id"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrAWSAccountID), resource.TestCheckResourceAttrSet(resourceName, "aws_region"), resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), resource.TestCheckResourceAttrSet(resourceName, "reason"), @@ -86,7 +85,7 @@ func TestAccVPCBlockPublicAccessOptions_updates(t *testing.T) { Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode1), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode1), - resource.TestCheckResourceAttrSet(resourceName, "aws_account_id"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrAWSAccountID), resource.TestCheckResourceAttrSet(resourceName, "aws_region"), resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), resource.TestCheckResourceAttrSet(resourceName, "reason"), @@ -101,7 +100,7 @@ func TestAccVPCBlockPublicAccessOptions_updates(t *testing.T) { Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode2), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode2), - resource.TestCheckResourceAttrSet(resourceName, "aws_account_id"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrAWSAccountID), resource.TestCheckResourceAttrSet(resourceName, "aws_region"), resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), resource.TestCheckResourceAttrSet(resourceName, "reason"), @@ -116,7 +115,7 @@ func TestAccVPCBlockPublicAccessOptions_updates(t *testing.T) { Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode3), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode3), - resource.TestCheckResourceAttrSet(resourceName, "aws_account_id"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrAWSAccountID), resource.TestCheckResourceAttrSet(resourceName, "aws_region"), resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), resource.TestCheckResourceAttrSet(resourceName, "reason"), From 8d666dc2273d36f6f20cd105c37ae0875aadacc2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Nov 2024 11:28:53 -0500 Subject: [PATCH 3/6] Add CHANGELOG entry. --- .changelog/40233.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/40233.txt diff --git a/.changelog/40233.txt b/.changelog/40233.txt new file mode 100644 index 00000000000..3c2a76c2a68 --- /dev/null +++ b/.changelog/40233.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_vpc_block_public_access_options +``` \ No newline at end of file From c25dc8b1d0987138063f03e5230f22aaded356e4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Nov 2024 12:24:16 -0500 Subject: [PATCH 4/6] r/aws_vpc_block_public_access_options: Tidy up. --- internal/service/ec2/find.go | 16 + internal/service/ec2/service_package_gen.go | 2 +- internal/service/ec2/status.go | 16 + .../ec2/vpc_block_public_access_options.go | 301 ++++++------------ .../vpc_block_public_access_options_test.go | 111 ++----- internal/service/ec2/wait.go | 20 ++ 6 files changed, 181 insertions(+), 285 deletions(-) diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index e457400dc95..5d75a01493f 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -6577,3 +6577,19 @@ func findCapacityBlockOfferings(ctx context.Context, conn *ec2.Client, input *ec return output, nil } + +func findVPCBlockPublicAccessOptions(ctx context.Context, conn *ec2.Client) (*awstypes.VpcBlockPublicAccessOptions, error) { + input := &ec2.DescribeVpcBlockPublicAccessOptionsInput{} + + output, err := conn.DescribeVpcBlockPublicAccessOptions(ctx, input) + + if err != nil { + return nil, err + } + + if output == nil || output.VpcBlockPublicAccessOptions == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.VpcBlockPublicAccessOptions, nil +} diff --git a/internal/service/ec2/service_package_gen.go b/internal/service/ec2/service_package_gen.go index 05915034f5d..bafd6633514 100644 --- a/internal/service/ec2/service_package_gen.go +++ b/internal/service/ec2/service_package_gen.go @@ -66,7 +66,7 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic Name: "Security Group VPC Association", }, { - Factory: newResourceVPCBlockPublicAccessOptions, + Factory: newVPCBlockPublicAccessOptionsResource, Name: "VPC Block Public Access Options", }, { diff --git a/internal/service/ec2/status.go b/internal/service/ec2/status.go index 8b13352bb69..bd367ffac56 100644 --- a/internal/service/ec2/status.go +++ b/internal/service/ec2/status.go @@ -1572,3 +1572,19 @@ func statusNetworkInsightsAnalysis(ctx context.Context, conn *ec2.Client, id str return output, string(output.Status), nil } } + +func statusVPCBlockPublicAccessOptions(ctx context.Context, conn *ec2.Client) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findVPCBlockPublicAccessOptions(ctx, conn) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.State), nil + } +} diff --git a/internal/service/ec2/vpc_block_public_access_options.go b/internal/service/ec2/vpc_block_public_access_options.go index 8def9ab9c21..529de85cc07 100644 --- a/internal/service/ec2/vpc_block_public_access_options.go +++ b/internal/service/ec2/vpc_block_public_access_options.go @@ -5,30 +5,30 @@ package ec2 import ( "context" - "errors" + "fmt" "time" "github.com/aws/aws-sdk-go-v2/service/ec2" awstypes "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" - "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" "github.com/hashicorp/terraform-provider-aws/internal/framework" - "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) // @FrameworkResource("aws_vpc_block_public_access_options", name="VPC Block Public Access Options") -func newResourceVPCBlockPublicAccessOptions(_ context.Context) (resource.ResourceWithConfigure, error) { - r := &resourceVPCBlockPublicAccessOptions{} +func newVPCBlockPublicAccessOptionsResource(context.Context) (resource.ResourceWithConfigure, error) { + r := &vpcBlockPublicAccessOptionsResource{} + r.SetDefaultCreateTimeout(30 * time.Minute) r.SetDefaultUpdateTimeout(30 * time.Minute) r.SetDefaultDeleteTimeout(30 * time.Minute) @@ -36,42 +36,35 @@ func newResourceVPCBlockPublicAccessOptions(_ context.Context) (resource.Resourc return r, nil } -const ( - ResNameVPCBlockPublicAccessOptions = "VPC Block Public Access Options" -) - -type resourceVPCBlockPublicAccessOptions struct { +type vpcBlockPublicAccessOptionsResource struct { framework.ResourceWithConfigure framework.WithTimeouts framework.WithImportByID } -func (r *resourceVPCBlockPublicAccessOptions) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "aws_vpc_block_public_access_options" +func (*vpcBlockPublicAccessOptionsResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_vpc_block_public_access_options" } -func (r *resourceVPCBlockPublicAccessOptions) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ +func (r *vpcBlockPublicAccessOptionsResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - names.AttrID: framework.IDAttribute(), names.AttrAWSAccountID: schema.StringAttribute{ Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "aws_region": schema.StringAttribute{ Computed: true, - }, - "internet_gateway_block_mode": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - enum.FrameworkValidate[awstypes.InternetGatewayBlockMode](), + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), }, }, - "last_update_timestamp": schema.StringAttribute{ - CustomType: timetypes.RFC3339Type{}, - Computed: true, - }, - "reason": schema.StringAttribute{ - Computed: true, + names.AttrID: framework.IDAttribute(), + "internet_gateway_block_mode": schema.StringAttribute{ + CustomType: fwtypes.StringEnumType[awstypes.InternetGatewayBlockMode](), + Required: true, }, }, Blocks: map[string]schema.Block{ @@ -84,240 +77,140 @@ func (r *resourceVPCBlockPublicAccessOptions) Schema(ctx context.Context, req re } } -func (r *resourceVPCBlockPublicAccessOptions) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - conn := r.Meta().EC2Client(ctx) - - var plan resourceVPCBlockPublicAccessOptionsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { +func (r *vpcBlockPublicAccessOptionsResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data vpcBlockPublicAccessOptionsResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - var input ec2.ModifyVpcBlockPublicAccessOptionsInput - - input.InternetGatewayBlockMode = awstypes.InternetGatewayBlockMode(plan.InternetGatewayBlockMode.ValueString()) + conn := r.Meta().EC2Client(ctx) - out, err := conn.ModifyVpcBlockPublicAccessOptions(ctx, &input) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, "ModifyVpcBlockPublicAccessOptions", err), - err.Error(), - ) - return + input := &ec2.ModifyVpcBlockPublicAccessOptionsInput{ + InternetGatewayBlockMode: data.InternetGatewayBlockMode.ValueEnum(), } - if out == nil || out.VpcBlockPublicAccessOptions == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), nil), - errors.New("empty output").Error(), - ) - return - } + output, err := conn.ModifyVpcBlockPublicAccessOptions(ctx, input) + + if err != nil { + response.Diagnostics.AddError("creating VPC Block Public Access Options", err.Error()) - resp.Diagnostics.Append(flex.Flatten(ctx, out.VpcBlockPublicAccessOptions, &plan)...) - if resp.Diagnostics.HasError() { return } - plan.ID = flex.StringValueToFramework(ctx, r.Meta().AccountID+":"+r.Meta().Region) + options, err := waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, r.CreateTimeout(ctx, data.Timeouts)) - createTimeout := r.CreateTimeout(ctx, plan.Timeouts) - err = waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, createTimeout) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionWaitingForCreation, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), err), - err.Error(), - ) - return - } + response.State.SetAttribute(ctx, path.Root(names.AttrID), fwflex.StringToFramework(ctx, output.VpcBlockPublicAccessOptions.AwsRegion)) // Set 'id' so as to taint the resource. + response.Diagnostics.AddError("waiting for VPC Block Public Access Options create", err.Error()) - desc_out, desc_err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) - if desc_err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionSetting, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), desc_err), - desc_err.Error(), - ) return } - resp.Diagnostics.Append(flex.Flatten(ctx, desc_out.VpcBlockPublicAccessOptions, &plan)...) + // Set values for unknowns. + data.AWSAccountID = fwflex.StringToFramework(ctx, options.AwsAccountId) + data.AWSRegion = fwflex.StringToFramework(ctx, options.AwsRegion) + data.ID = data.AWSRegion - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *resourceVPCBlockPublicAccessOptions) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - conn := r.Meta().EC2Client(ctx) - - var state resourceVPCBlockPublicAccessOptionsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - out, err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionSetting, ResNameVPCBlockPublicAccessOptions, state.ID.String(), err), - err.Error(), - ) +func (r *vpcBlockPublicAccessOptionsResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data vpcBlockPublicAccessOptionsResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - resp.Diagnostics.Append(flex.Flatten(ctx, out.VpcBlockPublicAccessOptions, &state)...) - if resp.Diagnostics.HasError() { - return - } + conn := r.Meta().EC2Client(ctx) - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} + options, err := findVPCBlockPublicAccessOptions(ctx, conn) -func (r *resourceVPCBlockPublicAccessOptions) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - conn := r.Meta().EC2Client(ctx) + if tfresource.NotFound(err) { + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) - var plan, state resourceVPCBlockPublicAccessOptionsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { return } - if !plan.InternetGatewayBlockMode.Equal(state.InternetGatewayBlockMode) { - var input ec2.ModifyVpcBlockPublicAccessOptionsInput - resp.Diagnostics.Append(flex.Expand(ctx, plan, &input, flex.WithFieldNamePrefix("Test"))...) - if resp.Diagnostics.HasError() { - return - } - - out, err := conn.ModifyVpcBlockPublicAccessOptions(ctx, &input) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), err), - err.Error(), - ) - return - } - if out == nil || out.VpcBlockPublicAccessOptions == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), nil), - errors.New("empty output").Error(), - ) - return - } - - resp.Diagnostics.Append(flex.Flatten(ctx, out.VpcBlockPublicAccessOptions, &plan)...) - if resp.Diagnostics.HasError() { - return - } - } - - updateTimeout := r.UpdateTimeout(ctx, plan.Timeouts) - err := waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, updateTimeout) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionWaitingForCreation, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), err), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("reading VPC Block Public Access Options (%s)", data.ID.ValueString()), err.Error()) + return } - desc_out, desc_err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) - if desc_err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionSetting, ResNameVPCBlockPublicAccessOptions, plan.ID.String(), desc_err), - desc_err.Error(), - ) + // Set attributes for import. + response.Diagnostics.Append(fwflex.Flatten(ctx, options, &data)...) + if response.Diagnostics.HasError() { return } - resp.Diagnostics.Append(flex.Flatten(ctx, desc_out.VpcBlockPublicAccessOptions, &plan)...) - - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *resourceVPCBlockPublicAccessOptions) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - conn := r.Meta().EC2Client(ctx) - - var state resourceVPCBlockPublicAccessOptionsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *vpcBlockPublicAccessOptionsResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var new vpcBlockPublicAccessOptionsResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &new)...) + if response.Diagnostics.HasError() { return } - var input ec2.ModifyVpcBlockPublicAccessOptionsInput + conn := r.Meta().EC2Client(ctx) + + input := &ec2.ModifyVpcBlockPublicAccessOptionsInput{ + InternetGatewayBlockMode: new.InternetGatewayBlockMode.ValueEnum(), + } - // On deletion of this resource set the VPC Block Public Access Options to off - input.InternetGatewayBlockMode = awstypes.InternetGatewayBlockModeOff + _, err := conn.ModifyVpcBlockPublicAccessOptions(ctx, input) - out, err := conn.ModifyVpcBlockPublicAccessOptions(ctx, &input) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, state.ID.String(), err), - err.Error(), - ) - return - } + response.Diagnostics.AddError(fmt.Sprintf("updating VPC Block Public Access Options (%s)", new.ID.ValueString()), err.Error()) - if out == nil || out.VpcBlockPublicAccessOptions == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionCreating, ResNameVPCBlockPublicAccessOptions, state.ID.String(), nil), - errors.New("empty output").Error(), - ) return } - deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts) - err = waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, deleteTimeout) - if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EC2, create.ErrActionWaitingForDeletion, ResNameVPCBlockPublicAccessOptions, state.ID.String(), err), - err.Error(), - ) + if _, err := waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, r.UpdateTimeout(ctx, new.Timeouts)); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for VPC Block Public Access Options (%s) update", new.ID.ValueString()), err.Error()) + return } -} -func (r *resourceVPCBlockPublicAccessOptions) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp) + response.Diagnostics.Append(response.State.Set(ctx, &new)...) } -func waitVPCBlockPublicAccessOptionsUpdated(ctx context.Context, conn *ec2.Client, timeout time.Duration) error { - stateConf := &retry.StateChangeConf{ - Pending: enum.Slice(awstypes.VpcBlockPublicAccessStateUpdateInProgress), - Target: enum.Slice(awstypes.VpcBlockPublicAccessStateUpdateComplete), - Refresh: statusVPCBlockPublicAccessOptions(ctx, conn), - Timeout: timeout, - ContinuousTargetOccurence: 2, +func (r *vpcBlockPublicAccessOptionsResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var data vpcBlockPublicAccessOptionsResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return } - outputRaw, err := stateConf.WaitForStateContext(ctx) - if _, ok := outputRaw.(*awstypes.VpcBlockPublicAccessOptions); ok { - return err + conn := r.Meta().EC2Client(ctx) + + // On deletion of this resource set the VPC Block Public Access Options to off. + input := &ec2.ModifyVpcBlockPublicAccessOptionsInput{ + InternetGatewayBlockMode: awstypes.InternetGatewayBlockModeOff, } - return err -} + _, err := conn.ModifyVpcBlockPublicAccessOptions(ctx, input) -func statusVPCBlockPublicAccessOptions(ctx context.Context, conn *ec2.Client) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - out, err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) - if tfresource.NotFound(err) { - return nil, "", nil - } + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("deleting VPC Block Public Access Options (%s)", data.ID.ValueString()), err.Error()) - if err != nil { - return nil, "", err - } + return + } + + if _, err := waitVPCBlockPublicAccessOptionsUpdated(ctx, conn, r.DeleteTimeout(ctx, data.Timeouts)); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for VPC Block Public Access Options (%s) delete", data.ID.ValueString()), err.Error()) - return out, string(out.VpcBlockPublicAccessOptions.State), nil + return } } -type resourceVPCBlockPublicAccessOptionsModel struct { - AWSAccountID types.String `tfsdk:"aws_account_id"` - AWSRegion types.String `tfsdk:"aws_region"` - InternetGatewayBlockMode types.String `tfsdk:"internet_gateway_block_mode"` - ID types.String `tfsdk:"id"` - LastUpdateTimestamp timetypes.RFC3339 `tfsdk:"last_update_timestamp"` - Reason types.String `tfsdk:"reason"` - Timeouts timeouts.Value `tfsdk:"timeouts"` +type vpcBlockPublicAccessOptionsResourceModel struct { + AWSAccountID types.String `tfsdk:"aws_account_id"` + AWSRegion types.String `tfsdk:"aws_region"` + ID types.String `tfsdk:"id"` + InternetGatewayBlockMode fwtypes.StringEnum[awstypes.InternetGatewayBlockMode] `tfsdk:"internet_gateway_block_mode"` + Timeouts timeouts.Value `tfsdk:"timeouts"` } diff --git a/internal/service/ec2/vpc_block_public_access_options_test.go b/internal/service/ec2/vpc_block_public_access_options_test.go index 455a00f253a..2f119210514 100644 --- a/internal/service/ec2/vpc_block_public_access_options_test.go +++ b/internal/service/ec2/vpc_block_public_access_options_test.go @@ -5,50 +5,52 @@ package ec2_test import ( "context" - "errors" "fmt" "testing" "github.com/aws/aws-sdk-go-v2/service/ec2" awstypes "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/create" - tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccVPCBlockPublicAccessOptions_basic(t *testing.T) { - ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") +func TestAccVPCBlockPublicAccessOptions_serial(t *testing.T) { + t.Parallel() + + testCases := map[string]func(t *testing.T){ + acctest.CtBasic: testAccVPCBlockPublicAccessOptions_basic, + "update": testAccVPCBlockPublicAccessOptions_update, } - resourceName := "aws_vpc_block_public_access_options.test" + acctest.RunSerialTests1Level(t, testCases, 0) +} +func testAccVPCBlockPublicAccessOptions_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_vpc_block_public_access_options.test" rMode := string(awstypes.InternetGatewayBlockModeBlockBidirectional) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, names.EC2) testAccPreCheckVPCBlockPublicAccess(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.EC2), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckVPCBlockPublicAccessOptionsDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode), - resource.TestCheckResourceAttrSet(resourceName, names.AttrAWSAccountID), - resource.TestCheckResourceAttrSet(resourceName, "aws_region"), - resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), - resource.TestCheckResourceAttrSet(resourceName, "reason"), - ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrAWSAccountID), knownvalue.NotNull()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("aws_region"), knownvalue.NotNull()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("internet_gateway_block_mode"), knownvalue.StringExact(rMode)), + }, }, { ResourceName: resourceName, @@ -59,14 +61,9 @@ func TestAccVPCBlockPublicAccessOptions_basic(t *testing.T) { }) } -func TestAccVPCBlockPublicAccessOptions_updates(t *testing.T) { +func testAccVPCBlockPublicAccessOptions_update(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - resourceName := "aws_vpc_block_public_access_options.test" - rMode1 := string(awstypes.InternetGatewayBlockModeBlockBidirectional) rMode2 := string(awstypes.InternetGatewayBlockModeBlockIngress) rMode3 := string(awstypes.InternetGatewayBlockModeOff) @@ -79,17 +76,13 @@ func TestAccVPCBlockPublicAccessOptions_updates(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, names.EC2), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckVPCBlockPublicAccessOptionsDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode1), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode1), - resource.TestCheckResourceAttrSet(resourceName, names.AttrAWSAccountID), - resource.TestCheckResourceAttrSet(resourceName, "aws_region"), - resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), - resource.TestCheckResourceAttrSet(resourceName, "reason"), - ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("internet_gateway_block_mode"), knownvalue.StringExact(rMode1)), + }, }, { ResourceName: resourceName, @@ -98,62 +91,20 @@ func TestAccVPCBlockPublicAccessOptions_updates(t *testing.T) { }, { Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode2), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode2), - resource.TestCheckResourceAttrSet(resourceName, names.AttrAWSAccountID), - resource.TestCheckResourceAttrSet(resourceName, "aws_region"), - resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), - resource.TestCheckResourceAttrSet(resourceName, "reason"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("internet_gateway_block_mode"), knownvalue.StringExact(rMode2)), + }, }, { Config: testAccVPCBlockPublicAccessOptionsConfig_basic(rMode3), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "internet_gateway_block_mode", rMode3), - resource.TestCheckResourceAttrSet(resourceName, names.AttrAWSAccountID), - resource.TestCheckResourceAttrSet(resourceName, "aws_region"), - resource.TestCheckResourceAttrSet(resourceName, "last_update_timestamp"), - resource.TestCheckResourceAttrSet(resourceName, "reason"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("internet_gateway_block_mode"), knownvalue.StringExact(rMode3)), + }, }, }, }) } -func testAccCheckVPCBlockPublicAccessOptionsDestroy(ctx context.Context) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Client(ctx) - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_vpc_block_public_access_options" { - continue - } - - out, err := conn.DescribeVpcBlockPublicAccessOptions(ctx, &ec2.DescribeVpcBlockPublicAccessOptionsInput{}) - if out.VpcBlockPublicAccessOptions.InternetGatewayBlockMode == awstypes.InternetGatewayBlockModeOff { - return nil - } - if err != nil { - return create.Error(names.EC2, create.ErrActionCheckingDestroyed, tfec2.ResNameVPCBlockPublicAccessOptions, rs.Primary.ID, err) - } - - return create.Error(names.EC2, create.ErrActionCheckingDestroyed, tfec2.ResNameVPCBlockPublicAccessOptions, rs.Primary.ID, errors.New("not destroyed")) - } - - return nil - } -} - func testAccPreCheckVPCBlockPublicAccess(ctx context.Context, t *testing.T) { conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Client(ctx) diff --git a/internal/service/ec2/wait.go b/internal/service/ec2/wait.go index 60b60024b13..eb1320658c2 100644 --- a/internal/service/ec2/wait.go +++ b/internal/service/ec2/wait.go @@ -3195,3 +3195,23 @@ func waitVPNGatewayVPCAttachmentDetached(ctx context.Context, conn *ec2.Client, return nil, err } + +func waitVPCBlockPublicAccessOptionsUpdated(ctx context.Context, conn *ec2.Client, timeout time.Duration) (*awstypes.VpcBlockPublicAccessOptions, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.VpcBlockPublicAccessStateUpdateInProgress), + Target: enum.Slice(awstypes.VpcBlockPublicAccessStateUpdateComplete), + Refresh: statusVPCBlockPublicAccessOptions(ctx, conn), + Timeout: timeout, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*awstypes.VpcBlockPublicAccessOptions); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.Reason))) + + return output, err + } + + return nil, err +} From 189aef64f272f0b0ea976425eba30a43319b2da8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Nov 2024 12:26:13 -0500 Subject: [PATCH 5/6] Correct documentation. --- ...c_block_public_access_options.html.markdown | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/website/docs/r/vpc_block_public_access_options.html.markdown b/website/docs/r/vpc_block_public_access_options.html.markdown index 5ade64c0f25..87c82d3ff21 100644 --- a/website/docs/r/vpc_block_public_access_options.html.markdown +++ b/website/docs/r/vpc_block_public_access_options.html.markdown @@ -31,31 +31,29 @@ The following arguments are required: This resource exports the following attributes in addition to the arguments above: * `aws_account_id` - The AWS account id to which these options apply. -* `aws_region_id` - The AWS region to which these options apply. -* `last_update_timestamp` - Last Update Timestamp -* `reason` - Reason for update. +* `aws_region` - The AWS region to which these options apply. ## Timeouts [Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): -* `create` - (Default `60m`) -* `update` - (Default `180m`) -* `delete` - (Default `90m`) +* `create` - (Default `30m`) +* `update` - (Default `30m`) +* `delete` - (Default `30m`) ## Import -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import VPC Block Public Access Options using the `example_id_arg`. For example: +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import VPC Block Public Access Options using the `aws_region`. For example: ```terraform import { to = aws_vpc_block_public_access_options.example - id = "111222333444:us-east-1" + id = "us-east-1" } ``` -Using `terraform import`, import VPC Block Public Access Options using the `example_id_arg`. For example: +Using `terraform import`, import VPC Block Public Access Options using the `aws_region`. For example: ```console -% terraform import aws_vpc_block_public_access_options.example 111222333444:us-east-1 +% terraform import aws_vpc_block_public_access_options.example us-east-1 ``` From d7fdafdddaaabeac724a841b10514becfc99ef4d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 26 Nov 2024 13:07:16 -0500 Subject: [PATCH 6/6] Run 'make gen'. --- internal/service/ec2/service_package_gen.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/ec2/service_package_gen.go b/internal/service/ec2/service_package_gen.go index bafd6633514..80614b1fc85 100644 --- a/internal/service/ec2/service_package_gen.go +++ b/internal/service/ec2/service_package_gen.go @@ -65,10 +65,6 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic Factory: newResourceSecurityGroupVPCAssociation, Name: "Security Group VPC Association", }, - { - Factory: newVPCBlockPublicAccessOptionsResource, - Name: "VPC Block Public Access Options", - }, { Factory: newSecurityGroupEgressRuleResource, Name: "Security Group Egress Rule", @@ -91,6 +87,10 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic Factory: newTransitGatewayDefaultRouteTablePropagationResource, Name: "Transit Gateway Default Route Table Propagation", }, + { + Factory: newVPCBlockPublicAccessOptionsResource, + Name: "VPC Block Public Access Options", + }, { Factory: newVPCEndpointPrivateDNSResource, Name: "VPC Endpoint Private DNS",