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_certificate_order_key_vault_store #25464

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion .github/labeler-issue-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ service/app-configuration:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_app_configuration((.|\n)*)###'

service/app-service:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(app_service_environment_v3\W+|app_service_environment_v3\W+|app_service_source_control\W+|app_service_source_control_slot\W+|function_app_active_slot\W+|function_app_function\W+|function_app_hybrid_connection\W+|linux_function_app\W+|linux_function_app\W+|linux_function_app_slot\W+|linux_web_app\W+|linux_web_app\W+|linux_web_app_slot\W+|service_plan|source_control_token|static_web_app|web_app_|windows_function_app\W+|windows_function_app\W+|windows_function_app_slot\W+|windows_web_app\W+|windows_web_app\W+|windows_web_app_slot\W+)((.|\n)*)###'
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(app_service_certificate_order_key_vault_store\W+|app_service_environment_v3\W+|app_service_environment_v3\W+|app_service_source_control\W+|app_service_source_control_slot\W+|function_app_active_slot\W+|function_app_function\W+|function_app_hybrid_connection\W+|linux_function_app\W+|linux_function_app\W+|linux_function_app_slot\W+|linux_web_app\W+|linux_web_app\W+|linux_web_app_slot\W+|service_plan|source_control_token|static_web_app|web_app_|windows_function_app\W+|windows_function_app\W+|windows_function_app_slot\W+|windows_web_app\W+|windows_web_app\W+|windows_web_app_slot\W+)((.|\n)*)###'

service/application-insights:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_application_insights((.|\n)*)###'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package appservice

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-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
"github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/appservicecertificateorders"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/web/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
)

type CertificateOrderCertificateResource struct{}

type CertificateOrderCertificateModel struct {
Name string `tfschema:"name"`
CertificateOrderId string `tfschema:"certificate_order_id"`
Location string `tfschema:"location"`
KeyVaultId string `tfschema:"key_vault_id"`
KeyVaultSecretName string `tfschema:"key_vault_secret_name"`
}

func (r CertificateOrderCertificateResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"certificate_order_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.CertificateOrderID,
},

"key_vault_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: commonids.ValidateKeyVaultID,
// TODO -- remove when issue https://github.com/Azure/azure-rest-api-specs/issues/28498 is addressed
DiffSuppressFunc: suppress.CaseDifference,
Comment on lines +52 to +53
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We handle differences in casing by parsing the resource ID insensitively in the read so this doesn't need to be set.

Suggested change
// TODO -- remove when issue https://github.com/Azure/azure-rest-api-specs/issues/28498 is addressed
DiffSuppressFunc: suppress.CaseDifference,

},

"key_vault_secret_name": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: keyVaultValidate.NestedItemName,
},
}
}

func (r CertificateOrderCertificateResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"location": commonschema.LocationComputed(),

"type": {
Type: pluginsdk.TypeString,
Computed: true,
},
Comment on lines +68 to +71
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't expose the top level type field for resources, also this isn't defined in the resource's model struct and isn't being set in the read so this should be removed

Suggested change
"type": {
Type: pluginsdk.TypeString,
Computed: true,
},

}
}

func (r CertificateOrderCertificateResource) ModelObject() interface{} {
return &CertificateOrderCertificateModel{}
}

func (r CertificateOrderCertificateResource) ResourceType() string {
return "azurerm_app_service_certificate_order_key_vault_store"
}

func (r CertificateOrderCertificateResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 60 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
var certificateOrderCertificate CertificateOrderCertificateModel
if err := metadata.Decode(&certificateOrderCertificate); err != nil {
return err
}

client := metadata.Client.AppService.AppServiceCertificatesOrderClient
subscriptionId := metadata.Client.Account.SubscriptionId

certificateOrderId, err := appservicecertificateorders.ParseCertificateOrderID(certificateOrderCertificate.CertificateOrderId)
if err != nil {
return err
}
id := appservicecertificateorders.NewCertificateID(subscriptionId, certificateOrderId.ResourceGroupName, certificateOrderId.CertificateOrderName, certificateOrderCertificate.Name)

keyVaultId, err := commonids.ParseKeyVaultID(certificateOrderCertificate.KeyVaultId)
if err != nil {
return err
}

existing, err := client.GetCertificate(ctx, id)
if err != nil && !response.WasNotFound(existing.HttpResponse) {
return fmt.Errorf("retrieving %s: %v", id, err)
}
if !response.WasNotFound(existing.HttpResponse) {
return metadata.ResourceRequiresImport(r.ResourceType(), id)
}

certOrderCertificate := appservicecertificateorders.AppServiceCertificateResource{
Name: pointer.To(certificateOrderCertificate.Name),
Properties: &appservicecertificateorders.AppServiceCertificate{
KeyVaultId: pointer.To(keyVaultId.ID()),
KeyVaultSecretName: pointer.To(certificateOrderCertificate.KeyVaultSecretName),
},
}

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

metadata.SetID(id)

return nil
},
}
}

func (r CertificateOrderCertificateResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.AppService.AppServiceCertificatesOrderClient
id, err := appservicecertificateorders.ParseCertificateID(metadata.ResourceData.Id())
if err != nil {
return err
}

certificateOrderCertificate, err := client.GetCertificate(ctx, *id)
if err != nil {
if response.WasNotFound(certificateOrderCertificate.HttpResponse) {
return metadata.MarkAsGone(id)
}
return fmt.Errorf("retrieving %s: %+v", id, err)
}

state := CertificateOrderCertificateModel{
Name: id.CertificateName,
}

certificateOrderId := appservicecertificateorders.NewCertificateOrderID(id.SubscriptionId, id.ResourceGroupName, id.CertificateOrderName)
state.CertificateOrderId = certificateOrderId.ID()
Comment on lines +152 to +156
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Name: id.CertificateName,
}
certificateOrderId := appservicecertificateorders.NewCertificateOrderID(id.SubscriptionId, id.ResourceGroupName, id.CertificateOrderName)
state.CertificateOrderId = certificateOrderId.ID()
Name: id.CertificateName,
CertificateOrderId: appservicecertificateorders.NewCertificateOrderID(id.SubscriptionId, id.ResourceGroupName, id.CertificateOrderName)
}


// we need to parse the key vault id insensitively as the resource group part was changed https://github.com/Azure/azure-rest-api-specs/issues/new?assignees=&labels=bug&projects=&template=02_bug.yml&title=%5BBUG%5D
if model := certificateOrderCertificate.Model; model != nil {
state.Location = location.Normalize(model.Location)
if props := model.Properties; props != nil {
if props.KeyVaultId != nil {
keyVaultId, err := commonids.ParseKeyVaultIDInsensitively(*props.KeyVaultId)
if err != nil {
return err
}
state.KeyVaultId = keyVaultId.ID()
}
state.KeyVaultSecretName = pointer.From(props.KeyVaultSecretName)
}
}
if err := metadata.Encode(&state); err != nil {
return fmt.Errorf("encoding: %+v", err)
}

return nil
},
}
}

func (r CertificateOrderCertificateResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 60 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
id, err := appservicecertificateorders.ParseCertificateID(metadata.ResourceData.Id())
if err != nil {
return err
}

client := metadata.Client.AppService.AppServiceCertificatesOrderClient

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

return nil
},
}
}

func (r CertificateOrderCertificateResource) Update() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 60 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
id, err := appservicecertificateorders.ParseCertificateID(metadata.ResourceData.Id())
if err != nil {
return err
}

client := metadata.Client.AppService.AppServiceCertificatesOrderClient

var state CertificateOrderCertificateModel
if err := metadata.Decode(&state); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

existing, err := client.GetCertificate(ctx, *id)
if err != nil {
return fmt.Errorf("retrieving %s: %+v", id, err)
}

model := *existing.Model

if metadata.ResourceData.HasChange("key_vault_id") {
model.Properties.KeyVaultId = pointer.To(state.KeyVaultId)
}

if metadata.ResourceData.HasChange("key_vault_secret_name") {
model.Properties.KeyVaultSecretName = pointer.To(state.KeyVaultSecretName)
}

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

return nil
},
}
}

func (r CertificateOrderCertificateResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return appservicecertificateorders.ValidateCertificateID
}
Loading
Loading