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

Fix issues with frontdoor custom https configuration #7498

Merged
merged 19 commits into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 125 additions & 89 deletions azurerm/internal/services/frontdoor/frontdoor_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/frontdoor/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
Expand All @@ -32,6 +33,9 @@ func resourceArmFrontDoor() *schema.Resource {
State: schema.ImportStatePassthrough,
},

//MigrateState: resourceAzureRMFrontDoorMigrateState,
luigibk marked this conversation as resolved.
Show resolved Hide resolved
SchemaVersion: 1,

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(6 * time.Hour),
Read: schema.DefaultTimeout(5 * time.Minute),
Expand Down Expand Up @@ -423,17 +427,21 @@ func resourceArmFrontDoor() *schema.Resource {
Default: 0,
},
"custom_https_provisioning_enabled": {
Type: schema.TypeBool,
Required: true,
Type: schema.TypeBool,
Optional: true,
Computed: true,
Deprecated: "Deprecated in favour of `custom_https_configuration` resource",
luigibk marked this conversation as resolved.
Show resolved Hide resolved
},
"web_application_firewall_policy_link_id": {
Type: schema.TypeString,
Optional: true,
},
"custom_https_configuration": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Deprecated: "Deprecated in favour of `custom_https_configuration` resource",
luigibk marked this conversation as resolved.
Show resolved Hide resolved
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"certificate_source": {
Expand Down Expand Up @@ -601,40 +609,55 @@ func resourceArmFrontDoorCreateUpdate(d *schema.ResourceData, meta interface{})
}

if properties := resp.FrontendEndpointProperties; properties != nil {
if provisioningState := properties.CustomHTTPSProvisioningState; provisioningState != "" {
// Check to see if we are going to change the CustomHTTPSProvisioningState, if so check to
// see if its current state is configurable, if not return an error...
if customHttpsProvisioningEnabled != NormalizeCustomHTTPSProvisioningStateToBool(provisioningState) {
if err := IsFrontDoorFrontendEndpointConfigurable(provisioningState, customHttpsProvisioningEnabled, frontendEndpointName, resourceGroup); err != nil {
return err
}
}
customHttpsConfigurationNew := frontendEndpoint["custom_https_configuration"].([]interface{})
err := resourceArmFrontDoorFrontendEndpointCustomHttpsConfigurationUpdate(ctx, *resp.ID, customHttpsProvisioningEnabled, name, frontendEndpointName, resourceGroup, properties.CustomHTTPSProvisioningState, properties.CustomHTTPSConfiguration, customHttpsConfigurationNew, meta)
if err != nil {
return fmt.Errorf("Unable to update Custom HTTPS configuration for Frontend Endpoint %q (Resource Group %q): %+v", frontendEndpointName, resourceGroup, err)
luigibk marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

if customHttpsProvisioningEnabled && provisioningState == frontdoor.CustomHTTPSProvisioningStateDisabled {
// Build a custom Https configuration based off the config file to send to the enable call
// NOTE: I do not need to check to see if this exists since I already do that in the validation code
chc := frontendEndpoint["custom_https_configuration"].([]interface{})
customHTTPSConfiguration := chc[0].(map[string]interface{})
minTLSVersion := frontdoor.OneFullStopTwo // Default to TLS 1.2
if httpsConfig := properties.CustomHTTPSConfiguration; httpsConfig != nil {
minTLSVersion = httpsConfig.MinimumTLSVersion
}
customHTTPSConfigurationUpdate := makeCustomHttpsConfiguration(customHTTPSConfiguration, minTLSVersion)
// Enable Custom Domain HTTPS for the Frontend Endpoint
if err := resourceArmFrontDoorFrontendEndpointEnableHttpsProvisioning(ctx, true, name, frontendEndpointName, resourceGroup, customHTTPSConfigurationUpdate, meta); err != nil {
return fmt.Errorf("Unable enable Custom Domain HTTPS for Frontend Endpoint %q (Resource Group %q): %+v", frontendEndpointName, resourceGroup, err)
}
} else if !customHttpsProvisioningEnabled && provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabled {
// Disable Custom Domain HTTPS for the Frontend Endpoint
if err := resourceArmFrontDoorFrontendEndpointEnableHttpsProvisioning(ctx, false, name, frontendEndpointName, resourceGroup, frontdoor.CustomHTTPSConfiguration{}, meta); err != nil {
return fmt.Errorf("Unable to disable Custom Domain HTTPS for Frontend Endpoint %q (Resource Group %q): %+v", frontendEndpointName, resourceGroup, err)
}
return resourceArmFrontDoorRead(d, meta)
}

func resourceArmFrontDoorFrontendEndpointCustomHttpsConfigurationUpdate(ctx context.Context, frontendEndpointId string, customHttpsProvisioningEnabled bool, frontDoorName string, frontendEndpointName string, resourceGroup string, provisioningState frontdoor.CustomHTTPSProvisioningState, customHTTPSConfigurationCurrent *frontdoor.CustomHTTPSConfiguration, customHttpsConfigurationNew []interface{}, meta interface{}) error {
// Locking to prevent parallel changes causing issues
locks.ByID(frontendEndpointId)
defer locks.UnlockByID(frontendEndpointId)

if provisioningState != "" {
// Check to see if we are going to change the CustomHTTPSProvisioningState, if so check to
// see if its current state is configurable, if not return an error...
if customHttpsProvisioningEnabled != NormalizeCustomHTTPSProvisioningStateToBool(provisioningState) {
if err := IsFrontDoorFrontendEndpointConfigurable(provisioningState, customHttpsProvisioningEnabled, frontendEndpointName, resourceGroup); err != nil {
return err
}
}

if customHttpsProvisioningEnabled {
// Build a custom Https configuration based off the config file to send to the enable call
// NOTE: I do not need to check to see if this exists since I already do that in the validation code
customHTTPSConfiguration := customHttpsConfigurationNew[0].(map[string]interface{})
minTLSVersion := frontdoor.OneFullStopTwo // Default to TLS 1.2
if httpsConfig := customHTTPSConfigurationCurrent; httpsConfig != nil {
minTLSVersion = httpsConfig.MinimumTLSVersion
}
customHTTPSConfigurationUpdate := makeCustomHttpsConfiguration(customHTTPSConfiguration, minTLSVersion)
if provisioningState == frontdoor.CustomHTTPSProvisioningStateDisabled || customHTTPSConfigurationUpdate != *customHTTPSConfigurationCurrent {
// Enable Custom Domain HTTPS for the Frontend Endpoint
if err := resourceArmFrontDoorFrontendEndpointEnableHttpsProvisioning(ctx, true, frontDoorName, frontendEndpointName, resourceGroup, customHTTPSConfigurationUpdate, meta); err != nil {
return fmt.Errorf("Unable to enable/update Custom Domain HTTPS for Frontend Endpoint %q (Resource Group %q): %+v", frontendEndpointName, resourceGroup, err)
}
}
} else if !customHttpsProvisioningEnabled && provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabled {
// Disable Custom Domain HTTPS for the Frontend Endpoint
if err := resourceArmFrontDoorFrontendEndpointEnableHttpsProvisioning(ctx, false, frontDoorName, frontendEndpointName, resourceGroup, frontdoor.CustomHTTPSConfiguration{}, meta); err != nil {
return fmt.Errorf("Unable to disable Custom Domain HTTPS for Frontend Endpoint %q (Resource Group %q): %+v", frontendEndpointName, resourceGroup, err)
}
}
}

return resourceArmFrontDoorRead(d, meta)
return nil
luigibk marked this conversation as resolved.
Show resolved Hide resolved
}

func resourceArmFrontDoorFrontendEndpointEnableHttpsProvisioning(ctx context.Context, enableCustomHttpsProvisioning bool, frontDoorName string, frontendEndpointName string, resourceGroup string, customHTTPSConfiguration frontdoor.CustomHTTPSConfiguration, meta interface{}) error {
Expand Down Expand Up @@ -715,7 +738,7 @@ func resourceArmFrontDoorRead(d *schema.ResourceData, meta interface{}) error {

if frontendEndpoints := properties.FrontendEndpoints; frontendEndpoints != nil {
if resp.Name != nil {
if frontDoorFrontendEndpoints, err := flattenArmFrontDoorFrontendEndpoint(ctx, frontendEndpoints, resourceGroup, *resp.Name, meta); frontDoorFrontendEndpoints != nil {
if frontDoorFrontendEndpoints, err := flattenArmFrontDoorFrontendEndpoints(ctx, frontendEndpoints, resourceGroup, *resp.Name, meta); frontDoorFrontendEndpoints != nil {
if err := d.Set("frontend_endpoint", frontDoorFrontendEndpoints); err != nil {
return fmt.Errorf("setting `frontend_endpoint`: %+v", err)
}
Expand Down Expand Up @@ -1268,87 +1291,100 @@ func flattenArmFrontDoorBackend(input *[]frontdoor.Backend) []interface{} {
return output
}

func flattenArmFrontDoorFrontendEndpoint(ctx context.Context, input *[]frontdoor.FrontendEndpoint, resourceGroup string, frontDoorName string, meta interface{}) ([]interface{}, error) {
func flattenArmFrontDoorFrontendEndpoints(ctx context.Context, input *[]frontdoor.FrontendEndpoint, resourceGroup string, frontDoorName string, meta interface{}) ([]interface{}, error) {
if input == nil {
return make([]interface{}, 0), fmt.Errorf("cannot read Front Door Frontend Endpoint (Resource Group %q): slice is empty", resourceGroup)
}

output := make([]interface{}, 0)

for _, v := range *input {
result := make(map[string]interface{})
customHttpsConfiguration := make([]interface{}, 0)
chc := make(map[string]interface{})
result, err := flattenArmFrontDoorFrontendEndpoint(ctx, &v, resourceGroup, frontDoorName, meta)
if err != nil {
return make([]interface{}, 0), fmt.Errorf("retrieving Front Door Frontend Endpoint Custom HTTPS Configuration %q (Resource Group %q): %+v", *v.Name, resourceGroup, err)
}

if name := v.Name; name != nil {
result["name"] = *name
output = append(output, result)
}

// Need to call frontEndEndpointClient here to get customConfiguration information from that client
// because the information is hidden from the main frontDoorClient "by design"...
client := meta.(*clients.Client).Frontdoor.FrontDoorsFrontendClient
return output, nil
}

resp, err := client.Get(ctx, resourceGroup, frontDoorName, *name)
if err != nil {
return make([]interface{}, 0), fmt.Errorf("retrieving Front Door Frontend Endpoint Custom HTTPS Configuration %q (Resource Group %q): %+v", *name, resourceGroup, err)
func flattenArmFrontDoorFrontendEndpoint(ctx context.Context, input *frontdoor.FrontendEndpoint, resourceGroup string, frontDoorName string, meta interface{}) (map[string]interface{}, error) {
if input == nil {
return make(map[string]interface{}), fmt.Errorf("cannot read Front Door Frontend Endpoint (Resource Group %q): endpoint is empty", resourceGroup)
}

output := make(map[string]interface{})
customHttpsConfiguration := make([]interface{}, 0)
chc := make(map[string]interface{})

if name := input.Name; name != nil {
output["name"] = *name

// Need to call frontEndEndpointClient here to get customConfiguration information from that client
// because the information is hidden from the main frontDoorClient "by design"...
client := meta.(*clients.Client).Frontdoor.FrontDoorsFrontendClient

resp, err := client.Get(ctx, resourceGroup, frontDoorName, *name)
if err != nil {
return make(map[string]interface{}), fmt.Errorf("retrieving Front Door Frontend Endpoint Custom HTTPS Configuration %q (Resource Group %q): %+v", *name, resourceGroup, err)
}
if resp.ID == nil {
return make(map[string]interface{}), fmt.Errorf("cannot read Front Door Frontend Endpoint Custom HTTPS Configuration %q (Resource Group %q) ID", *name, resourceGroup)
}

output["id"] = resp.ID

if properties := resp.FrontendEndpointProperties; properties != nil {
luigibk marked this conversation as resolved.
Show resolved Hide resolved
if hostName := properties.HostName; hostName != nil {
output["host_name"] = *hostName
}
if resp.ID == nil {
return make([]interface{}, 0), fmt.Errorf("cannot read Front Door Frontend Endpoint Custom HTTPS Configuration %q (Resource Group %q) ID", *name, resourceGroup)

if sessionAffinityEnabled := properties.SessionAffinityEnabledState; sessionAffinityEnabled != "" {
output["session_affinity_enabled"] = sessionAffinityEnabled == frontdoor.SessionAffinityEnabledStateEnabled
}

result["id"] = resp.ID
if sessionAffinityTtlSeconds := properties.SessionAffinityTTLSeconds; sessionAffinityTtlSeconds != nil {
output["session_affinity_ttl_seconds"] = *sessionAffinityTtlSeconds
}

if properties := resp.FrontendEndpointProperties; properties != nil {
if hostName := properties.HostName; hostName != nil {
result["host_name"] = *hostName
}
if waf := properties.WebApplicationFirewallPolicyLink; waf != nil {
output["web_application_firewall_policy_link_id"] = *waf.ID
}

if sessionAffinityEnabled := properties.SessionAffinityEnabledState; sessionAffinityEnabled != "" {
result["session_affinity_enabled"] = sessionAffinityEnabled == frontdoor.SessionAffinityEnabledStateEnabled
if properties.CustomHTTPSConfiguration != nil {
customHTTPSConfiguration := properties.CustomHTTPSConfiguration
if customHTTPSConfiguration.CertificateSource == frontdoor.CertificateSourceAzureKeyVault {
if kvcsp := customHTTPSConfiguration.KeyVaultCertificateSourceParameters; kvcsp != nil {
chc["certificate_source"] = string(frontdoor.CertificateSourceAzureKeyVault)
chc["azure_key_vault_certificate_vault_id"] = *kvcsp.Vault.ID
chc["azure_key_vault_certificate_secret_name"] = *kvcsp.SecretName
chc["azure_key_vault_certificate_secret_version"] = *kvcsp.SecretVersion
}
} else {
chc["certificate_source"] = string(frontdoor.CertificateSourceFrontDoor)
}

if sessionAffinityTtlSeconds := properties.SessionAffinityTTLSeconds; sessionAffinityTtlSeconds != nil {
result["session_affinity_ttl_seconds"] = *sessionAffinityTtlSeconds
}
chc["minimum_tls_version"] = string(customHTTPSConfiguration.MinimumTLSVersion)

if waf := properties.WebApplicationFirewallPolicyLink; waf != nil {
result["web_application_firewall_policy_link_id"] = *waf.ID
}
if provisioningState := properties.CustomHTTPSProvisioningState; provisioningState != "" {
chc["provisioning_state"] = provisioningState
if provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabled || provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabling {
output["custom_https_provisioning_enabled"] = true

if properties.CustomHTTPSConfiguration != nil {
customHTTPSConfiguration := properties.CustomHTTPSConfiguration
if customHTTPSConfiguration.CertificateSource == frontdoor.CertificateSourceAzureKeyVault {
if kvcsp := customHTTPSConfiguration.KeyVaultCertificateSourceParameters; kvcsp != nil {
chc["certificate_source"] = string(frontdoor.CertificateSourceAzureKeyVault)
chc["azure_key_vault_certificate_vault_id"] = *kvcsp.Vault.ID
chc["azure_key_vault_certificate_secret_name"] = *kvcsp.SecretName
chc["azure_key_vault_certificate_secret_version"] = *kvcsp.SecretVersion
if provisioningSubstate := properties.CustomHTTPSProvisioningSubstate; provisioningSubstate != "" {
chc["provisioning_substate"] = provisioningSubstate
}
} else {
chc["certificate_source"] = string(frontdoor.CertificateSourceFrontDoor)
output["custom_https_provisioning_enabled"] = false
}

chc["minimum_tls_version"] = string(customHTTPSConfiguration.MinimumTLSVersion)

if provisioningState := properties.CustomHTTPSProvisioningState; provisioningState != "" {
chc["provisioning_state"] = provisioningState
if provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabled || provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabling {
result["custom_https_provisioning_enabled"] = true

if provisioningSubstate := properties.CustomHTTPSProvisioningSubstate; provisioningSubstate != "" {
chc["provisioning_substate"] = provisioningSubstate
}
} else {
result["custom_https_provisioning_enabled"] = false
}

customHttpsConfiguration = append(customHttpsConfiguration, chc)
result["custom_https_configuration"] = customHttpsConfiguration
}
customHttpsConfiguration = append(customHttpsConfiguration, chc)
output["custom_https_configuration"] = customHttpsConfiguration
}
}
}

output = append(output, result)
}

return output, nil
Expand Down
5 changes: 3 additions & 2 deletions azurerm/internal/services/frontdoor/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource {
// SupportedResources returns the supported Resources supported by this Service
func (r Registration) SupportedResources() map[string]*schema.Resource {
return map[string]*schema.Resource{
"azurerm_frontdoor": resourceArmFrontDoor(),
"azurerm_frontdoor_firewall_policy": resourceArmFrontDoorFirewallPolicy()}
"azurerm_frontdoor": resourceArmFrontDoor(),
"azurerm_frontdoor_firewall_policy": resourceArmFrontDoorFirewallPolicy(),
"azurerm_frontdoor_custom_https_configuration": resourceArmFrontDoorCustomHttpsConfiguration()}
}
Loading