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

New Resource: azurerm_advisor_suppression #26177

Merged
merged 10 commits into from
Sep 20, 2024
2 changes: 1 addition & 1 deletion .github/labeler-issue-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ service/aadb2c:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_aadb2c_directory((.|\n)*)###'

service/advisor:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_advisor_recommendations((.|\n)*)###'
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_advisor_((.|\n)*)###'

service/analysis:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_analysis_services_server((.|\n)*)###'
Expand Down
1 change: 1 addition & 0 deletions internal/provider/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ func SupportedTypedServices() []sdk.TypedServiceRegistration {
authorization.Registration{},
automanage.Registration{},
automation.Registration{},
advisor.Registration{},
azurestackhci.Registration{},
batch.Registration{},
bot.Registration{},
Expand Down
179 changes: 179 additions & 0 deletions internal/services/advisor/advisor_suppression_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package advisor

import (
"context"
"fmt"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/resource-manager/advisor/2023-01-01/suppressions"
"github.com/hashicorp/terraform-provider-azurerm/helpers/azure"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/advisor/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

var _ sdk.Resource = AdvisorSuppressionResource{}

type AdvisorSuppressionResource struct{}

type AdvisorSuppressionResourceModel struct {
Name string `tfschema:"name"`
SuppressionID string `tfschema:"suppression_id"`
RecommendationID string `tfschema:"recommendation_id"`
ResourceID string `tfschema:"resource_id"`
TTL string `tfschema:"ttl"`
}

func (AdvisorSuppressionResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
},
"recommendation_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
},
"resource_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: azure.ValidateResourceID,
},
"ttl": {
Type: pluginsdk.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validate.Duration,
},
}
}

func (AdvisorSuppressionResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"suppression_id": {
Type: pluginsdk.TypeString,
Computed: true,
},
}
}

func (AdvisorSuppressionResource) ModelObject() interface{} {
return &AdvisorSuppressionResourceModel{}
}

func (AdvisorSuppressionResource) ResourceType() string {
return "azurerm_advisor_suppression"
}

func (r AdvisorSuppressionResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Advisor.SuppressionsClient

var model AdvisorSuppressionResourceModel

if err := metadata.Decode(&model); err != nil {
return err
}

id := suppressions.NewScopedSuppressionID(model.ResourceID, model.RecommendationID, model.Name)

existing, err := client.Get(ctx, id)
if err != nil && !response.WasNotFound(existing.HttpResponse) {
return fmt.Errorf("checking for presence of existing %s: %+v", id, err)
}
if !response.WasNotFound(existing.HttpResponse) {
return metadata.ResourceRequiresImport(r.ResourceType(), id)
}

param := suppressions.SuppressionContract{
Name: pointer.To(model.Name),
Properties: &suppressions.SuppressionProperties{
SuppressionId: pointer.To(model.SuppressionID),
},
}

if model.TTL != "" {
param.Properties.Ttl = pointer.To(model.TTL)
}

if _, err := client.Create(ctx, id, param); err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

metadata.SetID(id)
return nil
},
}
}

func (AdvisorSuppressionResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Advisor.SuppressionsClient

state := AdvisorSuppressionResourceModel{}

id, err := suppressions.ParseScopedSuppressionID(metadata.ResourceData.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return metadata.MarkAsGone(id)
}

return fmt.Errorf("retrieving %s: %+v", id, err)
}

state.Name = id.SuppressionName
state.ResourceID = id.ResourceUri
state.RecommendationID = id.RecommendationId

if model := resp.Model; model != nil {
if props := model.Properties; props != nil {
state.TTL = pointer.From(props.Ttl)
state.SuppressionID = pointer.From(props.SuppressionId)
}
}

return metadata.Encode(&state)
},
}
}

func (AdvisorSuppressionResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Advisor.SuppressionsClient

id, err := suppressions.ParseScopedSuppressionID(metadata.ResourceData.Id())
if err != nil {
return err
}

if _, err := client.Delete(ctx, *id); err != nil {
return fmt.Errorf("deleting %s: %+v", *id, err)
}

return nil
},
}
}

func (AdvisorSuppressionResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return suppressions.ValidateScopedSuppressionID
}
72 changes: 72 additions & 0 deletions internal/services/advisor/advisor_suppression_resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package advisor_test

import (
"context"
"fmt"
"testing"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-sdk/resource-manager/advisor/2023-01-01/suppressions"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

type AdvisorSuppressionResource struct{}

func TestAccAnalysisServicesServer_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_advisor_suppression", "test")
r := AdvisorSuppressionResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func (AdvisorSuppressionResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := suppressions.ParseScopedSuppressionID(state.ID)
if err != nil {
return nil, err
}

resp, err := clients.Advisor.SuppressionsClient.Get(ctx, *id)
if err != nil {
return nil, fmt.Errorf("retrieving %s: %+v", *id, err)
}

return pointer.To(resp.Model != nil && resp.Model.Id != nil), nil
}

func (t AdvisorSuppressionResource) basic(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

data "azurerm_client_config" "current" {}

data "azurerm_advisor_recommendations" "test" {}

# The recommendation_names local variable is used to sort the recommendation names.
locals {
recommendation_names = sort(data.azurerm_advisor_recommendations.test.recommendations[*].recommendation_name)
}

resource "azurerm_advisor_suppression" "test" {
name = "acctest%d"
recommendation_id = local.recommendation_names[0]
resource_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}"
ttl = "00:30:00"
}
`, data.RandomInteger)
}
11 changes: 10 additions & 1 deletion internal/services/advisor/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,30 @@ import (
"fmt"

"github.com/hashicorp/go-azure-sdk/resource-manager/advisor/2023-01-01/getrecommendations"
"github.com/hashicorp/go-azure-sdk/resource-manager/advisor/2023-01-01/suppressions"
"github.com/hashicorp/terraform-provider-azurerm/internal/common"
)

type Client struct {
RecommendationsClient *getrecommendations.GetRecommendationsClient
SuppressionsClient *suppressions.SuppressionsClient
}

func NewClient(o *common.ClientOptions) (*Client, error) {
recommendationsClient, err := getrecommendations.NewGetRecommendationsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Recommendations client: %+v", err)
return nil, fmt.Errorf("building recommendations client: %+v", err)
}
o.Configure(recommendationsClient.Client, o.Authorizers.ResourceManager)

suppressionsClient, err := suppressions.NewSuppressionsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building suppressions client: %+v", err)
}
o.Configure(suppressionsClient.Client, o.Authorizers.ResourceManager)

return &Client{
RecommendationsClient: recommendationsClient,
SuppressionsClient: suppressionsClient,
}, nil
}
17 changes: 16 additions & 1 deletion internal/services/advisor/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import (

type Registration struct{}

var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{}
var (
_ sdk.TypedServiceRegistrationWithAGitHubLabel = Registration{}
_ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{}
)

func (r Registration) AssociatedGitHubLabel() string {
return "service/advisor"
Expand Down Expand Up @@ -39,3 +42,15 @@ func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource {
func (r Registration) SupportedResources() map[string]*pluginsdk.Resource {
return map[string]*pluginsdk.Resource{}
}

// DataSources returns a list of Data Sources supported by this Service
func (r Registration) DataSources() []sdk.DataSource {
return []sdk.DataSource{}
}

// Resources returns a list of Resources supported by this Service
func (r Registration) Resources() []sdk.Resource {
return []sdk.Resource{
AdvisorSuppressionResource{},
}
}
19 changes: 19 additions & 0 deletions internal/services/advisor/validate/duration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package validate

import (
"fmt"
"regexp"
)

func Duration(v interface{}, k string) (warnings []string, errors []error) {
value := v.(string)

if !regexp.MustCompile(`^(?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}:[0-9]{2}$`).Match([]byte(value)) {
errors = append(errors, fmt.Errorf("%q must be in format DD:HH:MM:SS. If DD is 00, it has to be omit", k))
}

return warnings, errors
}
Loading
Loading