Skip to content

Commit

Permalink
azurerm_cdn_endpoint_custom_domain - user managed https allows key …
Browse files Browse the repository at this point in the history
…vault secret (#17114)

Co-authored-by: kt <[email protected]>
  • Loading branch information
magodo and katbyte authored Aug 18, 2022
1 parent f1754fe commit 2a89d57
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 118 deletions.
275 changes: 166 additions & 109 deletions internal/services/cdn/cdn_endpoint_custom_domain_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/cdn/mgmt/2020-09-01/cdn"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/features"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/cdn/validate"
keyvaultClient "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/client"
Expand All @@ -21,6 +22,112 @@ import (
)

func resourceArmCdnEndpointCustomDomain() *pluginsdk.Resource {
schema := map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.CdnEndpointCustomDomainName(),
},

"cdn_endpoint_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.EndpointID,
},

"host_name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},
"cdn_managed_https": {
Type: pluginsdk.TypeList,
Optional: true,
MinItems: 1,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"certificate_type": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(cdn.CertificateTypeShared),
string(cdn.CertificateTypeDedicated),
}, false),
},
"protocol_type": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(cdn.ProtocolTypeServerNameIndication),
string(cdn.ProtocolTypeIPBased),
}, false),
},
"tls_version": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
string(cdn.MinimumTLSVersionTLS10),
string(cdn.MinimumTLSVersionTLS12),
string(cdn.MinimumTLSVersionNone),
}, false),
Default: string(cdn.MinimumTLSVersionTLS12),
},
},
},
ConflictsWith: []string{"user_managed_https"},
},

"user_managed_https": {
Type: pluginsdk.TypeList,
Optional: true,
MinItems: 1,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"tls_version": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
string(cdn.MinimumTLSVersionTLS10),
string(cdn.MinimumTLSVersionTLS12),
string(cdn.MinimumTLSVersionNone),
}, false),
Default: string(cdn.MinimumTLSVersionTLS12),
},
},
},
ConflictsWith: []string{"cdn_managed_https"},
},
}
if !features.FourPointOhBeta() {
schema["user_managed_https"].Elem.(*pluginsdk.Resource).Schema["key_vault_certificate_id"] = &pluginsdk.Schema{
Type: pluginsdk.TypeString,
Optional: true,
Computed: true,
ValidateFunc: keyvaultValidate.NestedItemIdWithOptionalVersion,
ExactlyOneOf: []string{"user_managed_https.0.key_vault_certificate_id", "user_managed_https.0.key_vault_secret_id"},
Deprecated: "This is deprecated in favor of `key_vault_secret_id` as the service is actually looking for a secret, not a certificate",
}

schema["user_managed_https"].Elem.(*pluginsdk.Resource).Schema["key_vault_secret_id"] = &pluginsdk.Schema{
Type: pluginsdk.TypeString,
Optional: true,
Computed: true,
ValidateFunc: keyvaultValidate.NestedItemIdWithOptionalVersion,
ExactlyOneOf: []string{"user_managed_https.0.key_vault_certificate_id", "user_managed_https.0.key_vault_secret_id"},
}
} else {
schema["user_managed_https"].Elem.(*pluginsdk.Resource).Schema["key_vault_secret_id"] = &pluginsdk.Schema{
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: keyvaultValidate.NestedItemIdWithOptionalVersion,
}
}

return &pluginsdk.Resource{
Create: resourceArmCdnEndpointCustomDomainCreate,
Read: resourceArmCdnEndpointCustomDomainRead,
Expand All @@ -39,92 +146,7 @@ func resourceArmCdnEndpointCustomDomain() *pluginsdk.Resource {
Delete: pluginsdk.DefaultTimeout(12 * time.Hour),
},

Schema: map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.CdnEndpointCustomDomainName(),
},

"cdn_endpoint_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.EndpointID,
},

"host_name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},
"cdn_managed_https": {
Type: pluginsdk.TypeList,
Optional: true,
MinItems: 1,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"certificate_type": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(cdn.CertificateTypeShared),
string(cdn.CertificateTypeDedicated),
}, false),
},
"protocol_type": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(cdn.ProtocolTypeServerNameIndication),
string(cdn.ProtocolTypeIPBased),
}, false),
},
"tls_version": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
string(cdn.MinimumTLSVersionTLS10),
string(cdn.MinimumTLSVersionTLS12),
string(cdn.MinimumTLSVersionNone),
}, false),
Default: string(cdn.MinimumTLSVersionTLS12),
},
},
},
ConflictsWith: []string{"user_managed_https"},
},

"user_managed_https": {
Type: pluginsdk.TypeList,
Optional: true,
MinItems: 1,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"key_vault_certificate_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: keyvaultValidate.NestedItemIdWithOptionalVersion,
},
"tls_version": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
string(cdn.MinimumTLSVersionTLS10),
string(cdn.MinimumTLSVersionTLS12),
string(cdn.MinimumTLSVersionNone),
}, false),
Default: string(cdn.MinimumTLSVersionTLS12),
},
},
},
ConflictsWith: []string{"cdn_managed_https"},
},
},
Schema: schema,
}
}

Expand Down Expand Up @@ -351,12 +373,21 @@ func resourceArmCdnEndpointCustomDomainRead(d *pluginsdk.ResourceData, meta inte
case cdn.UserManagedHTTPSParameters:
var isVersioned bool
if b := d.Get("user_managed_https").([]interface{}); len(b) == 1 {
if certIdRaw := b[0].(map[string]interface{})["key_vault_certificate_id"].(string); certIdRaw != "" {
certId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(certIdRaw)
b := b[0].(map[string]interface{})

secretIdRaw := b["key_vault_secret_id"].(string)
if !features.FourPointOhBeta() {
if secretIdRaw == "" {
secretIdRaw = b["key_vault_certificate_id"].(string)
}
}

if secretIdRaw != "" {
id, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(secretIdRaw)
if err != nil {
return fmt.Errorf("parsing Key Vault Certificate Id %q: %v", certIdRaw, err)
return fmt.Errorf("parsing Key Vault Secret Id %q: %v", secretIdRaw, err)
}
isVersioned = certId.Version != ""
isVersioned = id.Version != ""
}
}
settings, err := flattenArmCdnEndpointCustomDomainUserManagedHttpsSettings(ctx, params, keyVaultsClient, isVersioned)
Expand Down Expand Up @@ -422,17 +453,24 @@ func expandArmCdnEndpointCustomDomainUserManagedHttpsSettings(ctx context.Contex

raw := input[0].(map[string]interface{})

keyVaultCertId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(raw["key_vault_certificate_id"].(string))
idLiteral := raw["key_vault_secret_id"].(string)
if !features.FourPointOhBeta() {
if idLiteral == "" {
idLiteral = raw["key_vault_certificate_id"].(string)
}
}

keyVaultSecretId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(idLiteral)
if err != nil {
return nil, err
}

keyVaultIdRaw, err := clients.KeyVault.KeyVaultIDFromBaseUrl(ctx, clients.Resource, keyVaultCertId.KeyVaultBaseUrl)
keyVaultIdRaw, err := clients.KeyVault.KeyVaultIDFromBaseUrl(ctx, clients.Resource, keyVaultSecretId.KeyVaultBaseUrl)
if err != nil {
return nil, fmt.Errorf("retrieving the Resource ID the Key Vault at URL %q: %s", keyVaultCertId.KeyVaultBaseUrl, err)
return nil, fmt.Errorf("retrieving the Resource ID the Key Vault at URL %q: %s", keyVaultSecretId.KeyVaultBaseUrl, err)
}
if keyVaultIdRaw == nil {
return nil, fmt.Errorf("unexpected nil Key Vault ID retrieved at URL %q", keyVaultCertId.KeyVaultBaseUrl)
return nil, fmt.Errorf("unexpected nil Key Vault ID retrieved at URL %q", keyVaultSecretId.KeyVaultBaseUrl)
}
keyVaultId, err := keyvaultParse.VaultID(*keyVaultIdRaw)
if err != nil {
Expand All @@ -445,8 +483,8 @@ func expandArmCdnEndpointCustomDomainUserManagedHttpsSettings(ctx context.Contex
SubscriptionID: &keyVaultId.SubscriptionId,
ResourceGroupName: &keyVaultId.ResourceGroup,
VaultName: &keyVaultId.Name,
SecretName: &keyVaultCertId.Name,
SecretVersion: &keyVaultCertId.Version,
SecretName: &keyVaultSecretId.Name,
SecretVersion: &keyVaultSecretId.Version,
UpdateRule: utils.String("NoAction"),
DeleteRule: utils.String("NoAction"),
},
Expand Down Expand Up @@ -507,31 +545,50 @@ func flattenArmCdnEndpointCustomDomainUserManagedHttpsSettings(ctx context.Conte
keyVaultId := keyvaultParse.NewVaultID(subscriptionId, resourceGroupName, vaultName)
keyVaultBaseUrl, err := keyVaultsClient.BaseUriForKeyVault(ctx, keyVaultId)
if err != nil {
return nil, fmt.Errorf("looking up Key Vault Certificate %q vault url from id %q: %+v", vaultName, keyVaultId, err)
return nil, fmt.Errorf("looking up Key Vault Secret %q vault url from id %q: %+v", vaultName, keyVaultId, err)
}
cert, err := keyVaultsClient.ManagementClient.GetCertificate(ctx, *keyVaultBaseUrl, secretName, secretVersion)

secret, err := keyVaultsClient.ManagementClient.GetSecret(ctx, *keyVaultBaseUrl, secretName, secretVersion)
if err != nil {
return nil, err
}
if cert.ID == nil {
return nil, fmt.Errorf("unexpected null Key Vault Certificate retrieved for Key Vault %s / Secret Name %s / Secret Version %s", keyVaultId, secretName, secretVersion)
if secret.ID == nil {
return nil, fmt.Errorf("unexpected null Key Vault Secret retrieved for Key Vault %s / Secret Name %s / Secret Version %s", keyVaultId, secretName, secretVersion)
}
certId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(*cert.ID)
secretId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(*secret.ID)
if err != nil {
return nil, err
}

certIdLiteral := certId.ID()
secretIdLiteral := secretId.ID()
if !isVersioned {
certIdLiteral = certId.VersionlessID()
secretIdLiteral = secretId.VersionlessID()
}

return []interface{}{
map[string]interface{}{
"key_vault_certificate_id": certIdLiteral,
"tls_version": string(input.MinimumTLSVersion),
},
}, nil
m := map[string]interface{}{
"key_vault_secret_id": secretIdLiteral,
"tls_version": string(input.MinimumTLSVersion),
}

if features.FourPointOhBeta() {
return []interface{}{m}, nil
}

// We try to retrieve the certificate with the given secret name and version. If it returns error, then we tolerate the error and simply setting empty string for the certificate id.
// As in this case, the users might be using a secret rather than a certificate.
var certIdLiteral string
cert, err := keyVaultsClient.ManagementClient.GetCertificate(ctx, *keyVaultBaseUrl, secretName, secretVersion)
if err == nil && cert.ID != nil {
certId, err := keyvaultParse.ParseOptionallyVersionedNestedItemID(*cert.ID)
if err == nil {
}
certIdLiteral = certId.ID()
if !isVersioned {
certIdLiteral = certId.VersionlessID()
}
}
m["key_vault_certificate_id"] = certIdLiteral
return []interface{}{m}, nil
}

func enableArmCdnEndpointCustomDomainHttps(ctx context.Context, client *cdn.CustomDomainsClient, id parse.CustomDomainId, params cdn.BasicCustomDomainHTTPSParameters) error {
Expand Down
Loading

0 comments on commit 2a89d57

Please sign in to comment.