Skip to content

Commit

Permalink
add support for aws shield subscription resource fixes hashicorp#21430
Browse files Browse the repository at this point in the history
  • Loading branch information
iwarapter committed May 22, 2024
1 parent d46d2ca commit bdbbf49
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 0 deletions.
4 changes: 4 additions & 0 deletions internal/service/shield/service_package_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

209 changes: 209 additions & 0 deletions internal/service/shield/subscription.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package shield

import (
"context"
"errors"
"github.com/aws/aws-sdk-go-v2/service/shield"
awstypes "github.com/aws/aws-sdk-go-v2/service/shield/types"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"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/resource/schema/stringdefault"
"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/errs"
"github.com/hashicorp/terraform-provider-aws/internal/framework"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/names"
)

// Function annotations are used for resource registration to the Provider. DO NOT EDIT.
// @FrameworkResource("aws_shield_subscription", name="Subscription")
func newResourceSubscription(_ context.Context) (resource.ResourceWithConfigure, error) {
return &resourceSubscription{}, nil
}

const (
ResNameSubscription = "Subscription"
)

type resourceSubscription struct {
framework.ResourceWithConfigure
framework.WithTimeouts
}

func (r *resourceSubscription) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = "aws_shield_subscription"
}

func (r *resourceSubscription) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
names.AttrID: framework.IDAttribute(),
"auto_renew": schema.StringAttribute{
Description: "Whether to automatically renew the subscription when it expires.",
Optional: true,
Computed: true,
Default: stringdefault.StaticString(string(awstypes.AutoRenewEnabled)),
Validators: []validator.String{
stringvalidator.OneOf(string(awstypes.AutoRenewEnabled), string(awstypes.AutoRenewDisabled)),
},
},
},
}
}

func (r *resourceSubscription) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
conn := r.Meta().ShieldClient(ctx)

var plan resourceSubscriptionData
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}
plan.ID = types.StringValue(r.Meta().AccountID)
if plan.AutoRenew.Equal(types.StringValue(string(awstypes.AutoRenewDisabled))) {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.Shield, create.ErrActionCreating, ResNameSubscription, plan.ID.String(), nil),
errors.New("subscription auto_renew flag cannot be changed earlier than 30 days before subscription end and later than 1 day before subscription end").Error(),
)
return
}

in := &shield.CreateSubscriptionInput{}
out, err := conn.CreateSubscription(ctx, in)
if err != nil {
// in error messages at this point.
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.Shield, create.ErrActionCreating, ResNameSubscription, plan.ID.String(), err),
err.Error(),
)
return
}
if out == nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.Shield, create.ErrActionCreating, ResNameSubscription, plan.ID.String(), nil),
errors.New("empty output").Error(),
)
return
}
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
}

func (r *resourceSubscription) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
conn := r.Meta().ShieldClient(ctx)

var state resourceSubscriptionData
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

out, err := findSubscriptionByID(ctx, conn)
if tfresource.NotFound(err) {
resp.State.RemoveResource(ctx)
return
}
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.Shield, create.ErrActionSetting, ResNameSubscription, state.ID.String(), err),
err.Error(),
)
return
}
state.AutoRenew = types.StringValue(string(out.AutoRenew))
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *resourceSubscription) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
conn := r.Meta().ShieldClient(ctx)

var plan, state resourceSubscriptionData
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

in := &shield.UpdateSubscriptionInput{
AutoRenew: awstypes.AutoRenew(plan.AutoRenew.ValueString()),
}

out, err := conn.UpdateSubscription(ctx, in)
if err != nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.Shield, create.ErrActionUpdating, ResNameSubscription, plan.ID.String(), err),
err.Error(),
)
return
}
if out == nil {
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.Shield, create.ErrActionUpdating, ResNameSubscription, plan.ID.String(), nil),
errors.New("empty output").Error(),
)
return
}
plan.ID = types.StringValue(r.Meta().AccountID)
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}

func (r *resourceSubscription) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
conn := r.Meta().ShieldClient(ctx)

var state resourceSubscriptionData
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

in := &shield.DeleteSubscriptionInput{}

_, err := conn.DeleteSubscription(ctx, in)
if err != nil {
if errs.IsA[*awstypes.ResourceNotFoundException](err) {
return
}
resp.Diagnostics.AddError(
create.ProblemStandardMessage(names.Shield, create.ErrActionDeleting, ResNameSubscription, state.ID.String(), err),
err.Error(),
)
return
}
}

func (r *resourceSubscription) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp)
}

func findSubscriptionByID(ctx context.Context, conn *shield.Client) (*awstypes.Subscription, error) {
in := &shield.DescribeSubscriptionInput{}

out, err := conn.DescribeSubscription(ctx, in)
if err != nil {
if errs.IsA[*awstypes.ResourceNotFoundException](err) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: in,
}
}
return nil, err
}

if out == nil || out.Subscription == nil {
return nil, tfresource.NewEmptyResultError(in)
}

return out.Subscription, nil
}

type resourceSubscriptionData struct {
ID types.String `tfsdk:"id"`
AutoRenew types.String `tfsdk:"auto_renew"`
}
90 changes: 90 additions & 0 deletions internal/service/shield/subscription_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package shield_test

import (
"context"
"errors"
"fmt"
awstypes "github.com/aws/aws-sdk-go-v2/service/shield/types"
"testing"

"github.com/aws/aws-sdk-go-v2/service/shield"
"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"

tfshield "github.com/hashicorp/terraform-provider-aws/internal/service/shield"
)

func TestAccShieldSubscription_basic(t *testing.T) {
ctx := acctest.Context(t)
t.Skipf("running this test signs up an account for $3000 yearly commitment to shield advanced")
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}

var subscription shield.DescribeSubscriptionOutput
resourceName := "aws_shield_subscription.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckPartitionHasService(t, names.ShieldEndpointID)
},
ErrorCheck: acctest.ErrorCheck(t, names.ShieldServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccSubscriptionConfig_basic(string(awstypes.AutoRenewEnabled)),
Check: resource.ComposeTestCheckFunc(
testAccCheckSubscriptionExists(ctx, resourceName, &subscription),
resource.TestCheckResourceAttr(resourceName, "auto_renew", string(awstypes.AutoRenewEnabled)),
),
Destroy: false,
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
Destroy: false,
},
},
})
}

func testAccCheckSubscriptionExists(ctx context.Context, name string, subscription *shield.DescribeSubscriptionOutput) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return create.Error(names.Shield, create.ErrActionCheckingExistence, tfshield.ResNameSubscription, name, errors.New("not found"))
}

if rs.Primary.ID == "" {
return create.Error(names.Shield, create.ErrActionCheckingExistence, tfshield.ResNameSubscription, name, errors.New("not set"))
}

conn := acctest.Provider.Meta().(*conns.AWSClient).ShieldClient(ctx)
resp, err := conn.DescribeSubscription(ctx, &shield.DescribeSubscriptionInput{})

if err != nil {
return create.Error(names.Shield, create.ErrActionCheckingExistence, tfshield.ResNameSubscription, rs.Primary.ID, err)
}

*subscription = *resp

return nil
}
}

func testAccSubscriptionConfig_basic(autoRenew string) string {
return fmt.Sprintf(`
resource "aws_shield_subscription" "test" {
auto_renew = %[1]q
}
`, autoRenew)
}
50 changes: 50 additions & 0 deletions website/docs/r/shield_subscription.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
subcategory: "Shield"
layout: "aws"
page_title: "AWS: aws_shield_subscription"
description: |-
Terraform resource for managing an AWS Shield Subscription.
---

# Resource: aws_shield_subscription

Terraform resource for managing an AWS Shield Subscription.

## Example Usage

### Basic Usage

```terraform
resource "aws_shield_subscription" "example" {
auto_renew = "ENABLED"
}
```

## Argument Reference

The following arguments are optional:

* `auto_renew` - (Optional) automated renewal for the subscription. Valid values are `ENABLED` or `DISABLED`. Default is `ENABLED`.

## Attribute Reference

This resource exports the following attributes in addition to the arguments above:

* `id` - Account ID is used.

## Import

In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Shield Subscription using the `AWS Account ID`. For example:

```terraform
import {
to = aws_shield_subscription.example
id = "1234567890"
}
```

Using `terraform import`, import Shield Subscription using the `AWS Account ID`. For example:

```console
% terraform import aws_shield_subscription.example 1234567890
```

0 comments on commit bdbbf49

Please sign in to comment.