-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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: Azure Front Door #3933
Changes from 49 commits
73b0c47
7b2be6d
6dcab9c
1cb59ce
200f2a8
8291122
7d66366
0f412eb
4440bab
04adc2f
cf9096f
00a51cd
68b84d0
c40fcc9
35c4502
7481722
88e5f6a
0a42aa7
156b97b
ca743f9
c94c75a
aab4faf
f871c66
0b8f259
e2ebb0a
36fd014
51bef98
ea00b84
7bf9a73
161ac42
e35c193
011ac40
df4d523
4372071
5a004ae
e471f1a
cb3ba6c
1e505b4
ac6a9fa
5c9144e
b721403
822ef9d
d54b7fc
262b1f9
ba08bca
e251d71
f25eed4
d21382e
28383a9
c592646
5f49486
f39e2d6
3ed1561
4425f84
b99238f
fe5bf89
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package frontdoor | ||
|
||
import ( | ||
"github.com/Azure/azure-sdk-for-go/services/preview/frontdoor/mgmt/2019-04-01/frontdoor" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/common" | ||
) | ||
|
||
type Client struct { | ||
FrontDoorsClient *frontdoor.FrontDoorsClient | ||
FrontDoorsFrontendClient *frontdoor.FrontendEndpointsClient | ||
} | ||
|
||
func BuildClient(o *common.ClientOptions) *Client { | ||
frontDoorsClient := frontdoor.NewFrontDoorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) | ||
o.ConfigureClient(&frontDoorsClient.Client, o.ResourceManagerAuthorizer) | ||
|
||
frontDoorsFrontendClient := frontdoor.NewFrontendEndpointsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) | ||
o.ConfigureClient(&frontDoorsFrontendClient.Client, o.ResourceManagerAuthorizer) | ||
|
||
return &Client{ | ||
FrontDoorsClient: &frontDoorsClient, | ||
FrontDoorsFrontendClient: &frontDoorsFrontendClient, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package helper | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/Azure/azure-sdk-for-go/services/preview/frontdoor/mgmt/2019-04-01/frontdoor" | ||
) | ||
|
||
func DoesBackendPoolExists(backendPoolName string, backendPools []interface{}) error { | ||
if backendPoolName == "" { | ||
return fmt.Errorf(`"backend_pool_name" cannot be empty`) | ||
} | ||
|
||
for _, bps := range backendPools { | ||
backendPool := bps.(map[string]interface{}) | ||
if backendPool["name"].(string) == backendPoolName { | ||
return nil | ||
} | ||
} | ||
|
||
return fmt.Errorf(`unable to locate "backend_pool_name":%q in configuration file`, backendPoolName) | ||
} | ||
|
||
func AzureKeyVaultCertificateHasValues(customHttpsConfiguration map[string]interface{}, MatchAllKeys bool) bool { | ||
certificateSecretName := customHttpsConfiguration["azure_key_vault_certificate_secret_name"] | ||
certificateSecretVersion := customHttpsConfiguration["azure_key_vault_certificate_secret_version"] | ||
certificateVaultId := customHttpsConfiguration["azure_key_vault_certificate_vault_id"] | ||
|
||
if MatchAllKeys { | ||
if strings.TrimSpace(certificateSecretName.(string)) != "" && strings.TrimSpace(certificateSecretVersion.(string)) != "" && strings.TrimSpace(certificateVaultId.(string)) != "" { | ||
return true | ||
} | ||
} else { | ||
if strings.TrimSpace(certificateSecretName.(string)) != "" || strings.TrimSpace(certificateSecretVersion.(string)) != "" || strings.TrimSpace(certificateVaultId.(string)) != "" { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
func IsFrontDoorFrontendEndpointConfigurable(currentState frontdoor.CustomHTTPSProvisioningState, customHttpsProvisioningEnabled bool, frontendEndpointName string, resourceGroup string) error { | ||
action := "disable" | ||
if customHttpsProvisioningEnabled { | ||
action = "enable" | ||
} | ||
|
||
switch currentState { | ||
case frontdoor.CustomHTTPSProvisioningStateDisabling, frontdoor.CustomHTTPSProvisioningStateEnabling, frontdoor.CustomHTTPSProvisioningStateFailed: | ||
return fmt.Errorf("Unable to %s the Front Door Frontend Endpoint %q (Resource Group %q) Custom Domain HTTPS state because the Frontend Endpoint is currently in the %q state", action, frontendEndpointName, resourceGroup, currentState) | ||
default: | ||
return nil | ||
} | ||
} | ||
|
||
func NormalizeCustomHTTPSProvisioningStateToBool(provisioningState frontdoor.CustomHTTPSProvisioningState) bool { | ||
isEnabled := false | ||
if provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabled || provisioningState == frontdoor.CustomHTTPSProvisioningStateEnabling { | ||
isEnabled = true | ||
} | ||
|
||
return isEnabled | ||
} | ||
|
||
func GetFrontDoorSubResourceId(subscriptionId string, resourceGroup string, frontDoorName string, resourceType string, resourceName string) string { | ||
if strings.TrimSpace(subscriptionId) == "" || strings.TrimSpace(resourceGroup) == "" || strings.TrimSpace(frontDoorName) == "" || strings.TrimSpace(resourceType) == "" || strings.TrimSpace(resourceName) == "" { | ||
return "" | ||
} | ||
|
||
return fmt.Sprintf("/subscriptions/%s/resourcegroups/%s/providers/Microsoft.Network/Frontdoors/%s/%s/%s", subscriptionId, resourceGroup, frontDoorName, resourceType, resourceName) | ||
} | ||
|
||
func GetFrontDoorBasicRouteConfigurationType(i interface{}) string { | ||
_, ok := i.(frontdoor.ForwardingConfiguration) | ||
if !ok { | ||
_, ok := i.(frontdoor.RedirectConfiguration) | ||
if !ok { | ||
return "" | ||
} | ||
return "RedirectConfiguration" | ||
} else { | ||
return "ForwardingConfiguration" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,172 @@ | ||||||
package validate | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we just move this one up into the front door package? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried moving it but go got confused with the path. |
||||||
|
||||||
import ( | ||||||
"fmt" | ||||||
|
||||||
"github.com/Azure/azure-sdk-for-go/services/preview/frontdoor/mgmt/2019-04-01/frontdoor" | ||||||
"github.com/hashicorp/terraform/helper/schema" | ||||||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" | ||||||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/frontdoor/helper" | ||||||
) | ||||||
|
||||||
//Frontdoor name must begin with a letter or number, end with a letter or number and may contain only letters, numbers or hyphens. | ||||||
func FrontDoorName(i interface{}, k string) (_ []string, errors []error) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would then become
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep. Fixed. |
||||||
if m, regexErrs := validate.RegExHelper(i, k, `(^[\da-zA-Z])([-\da-zA-Z]{3,61})([\da-zA-Z]$)`); !m { | ||||||
errors = append(regexErrs, fmt.Errorf(`%q must be between 5 and 63 characters in length and begin with a letter or number, end with a letter or number and may contain only letters, numbers or hyphens.`, k)) | ||||||
} | ||||||
|
||||||
return nil, errors | ||||||
} | ||||||
|
||||||
func BackendPoolRoutingRuleName(i interface{}, k string) (_ []string, errors []error) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, Fixed. |
||||||
if m, regexErrs := validate.RegExHelper(i, k, `(^[\da-zA-Z])([-\da-zA-Z]{1,88})([\da-zA-Z]$)`); !m { | ||||||
errors = append(regexErrs, fmt.Errorf(`%q must be between 1 and 90 characters in length and begin with a letter or number, end with a letter or number and may contain only letters, numbers or hyphens.`, k)) | ||||||
} | ||||||
|
||||||
return nil, errors | ||||||
} | ||||||
|
||||||
func FrontdoorSettings(d *schema.ResourceDiff) error { | ||||||
routingRules := d.Get("routing_rule").([]interface{}) | ||||||
configFrontendEndpoints := d.Get("frontend_endpoint").([]interface{}) | ||||||
backendPools := d.Get("backend_pool").([]interface{}) | ||||||
loadBalancingSettings := d.Get("backend_pool_load_balancing").([]interface{}) | ||||||
healthProbeSettings := d.Get("backend_pool_health_probe").([]interface{}) | ||||||
|
||||||
if len(configFrontendEndpoints) == 0 { | ||||||
return fmt.Errorf(`"frontend_endpoint": must have at least one "frontend_endpoint" defined, found 0`) | ||||||
} | ||||||
|
||||||
// Loop over all of the Routing Rules and validate that only one type of configuration is defined per Routing Rule | ||||||
for _, rr := range routingRules { | ||||||
routingRule := rr.(map[string]interface{}) | ||||||
routingRuleName := routingRule["name"] | ||||||
found := false | ||||||
|
||||||
redirectConfig := routingRule["redirect_configuration"].([]interface{}) | ||||||
forwardConfig := routingRule["forwarding_configuration"].([]interface{}) | ||||||
|
||||||
// Check 0. validate that at least one routing configuration exists per routing rule | ||||||
if len(redirectConfig) == 0 && len(forwardConfig) == 0 { | ||||||
return fmt.Errorf(`"routing_rule":%q is invalid. you must have either a "redirect_configuration" or a "forwarding_configuration" defined for the "routing_rule":%q `, routingRuleName, routingRuleName) | ||||||
} | ||||||
|
||||||
// Check 1. validate that only one configuration type is defined per routing rule | ||||||
if len(redirectConfig) == 1 && len(forwardConfig) == 1 { | ||||||
return fmt.Errorf(`"routing_rule":%q is invalid. "redirect_configuration" conflicts with "forwarding_configuration". You can only have one configuration type per each routing rule`, routingRuleName) | ||||||
} | ||||||
|
||||||
// Check 2. routing rule is a forwarding_configuration type make sure the backend_pool_name exists in the configuration file | ||||||
if len(forwardConfig) > 0 { | ||||||
fc := forwardConfig[0].(map[string]interface{}) | ||||||
if err := helper.DoesBackendPoolExists(fc["backend_pool_name"].(string), backendPools); err != nil { | ||||||
return fmt.Errorf(`"routing_rule":%q is invalid. %+v`, routingRuleName, err) | ||||||
} | ||||||
} | ||||||
|
||||||
// Check 3. validate that each routing rule frontend_endpoints are actually defined in the resource schema | ||||||
if routingRuleFrontends := routingRule["frontend_endpoints"].([]interface{}); len(routingRuleFrontends) > 0 { | ||||||
|
||||||
for _, routingRuleFrontend := range routingRuleFrontends { | ||||||
// | ||||||
//TODO: Refactor to helper function that returns an error | ||||||
// | ||||||
// Get the name of the frontend defined in the routing rule | ||||||
routingRulefrontendName := routingRuleFrontend.(string) | ||||||
found = false | ||||||
|
||||||
// Loop over all of the defined frontend endpoints in the config | ||||||
// seeing if we find the routing rule frontend in the list | ||||||
for _, configFrontendEndpoint := range configFrontendEndpoints { | ||||||
configFrontend := configFrontendEndpoint.(map[string]interface{}) | ||||||
configFrontendName := configFrontend["name"] | ||||||
if routingRulefrontendName == configFrontendName { | ||||||
found = true | ||||||
break | ||||||
} | ||||||
} | ||||||
|
||||||
if !found { | ||||||
return fmt.Errorf(`"routing_rule":%q "frontend_endpoints":%q was not found in the configuration file. verify you have the "frontend_endpoint":%q defined in the configuration file`, routingRuleName, routingRulefrontendName, routingRulefrontendName) | ||||||
} | ||||||
} | ||||||
} else { | ||||||
return fmt.Errorf(`"routing_rule": %q must have at least one "frontend_endpoints" defined`, routingRuleName) | ||||||
} | ||||||
} | ||||||
|
||||||
// Verify backend pool load balancing settings and health probe settings are defined in the resource schema | ||||||
for _, bps := range backendPools { | ||||||
backendPool := bps.(map[string]interface{}) | ||||||
backendPoolName := backendPool["name"] | ||||||
backendPoolLoadBalancingName := backendPool["load_balancing_name"] | ||||||
backendPoolHealthProbeName := backendPool["health_probe_name"] | ||||||
found := false | ||||||
|
||||||
// Verify backend pool load balancing settings name exists | ||||||
if len(loadBalancingSettings) > 0 { | ||||||
for _, lbs := range loadBalancingSettings { | ||||||
loadBalancing := lbs.(map[string]interface{}) | ||||||
loadBalancingName := loadBalancing["name"] | ||||||
|
||||||
if loadBalancingName == backendPoolLoadBalancingName { | ||||||
found = true | ||||||
break | ||||||
} | ||||||
} | ||||||
|
||||||
if !found { | ||||||
return fmt.Errorf(`"backend_pool":%q "load_balancing_name":%q was not found in the configuration file. verify you have the "backend_pool_load_balancing":%q defined in the configuration file`, backendPoolName, backendPoolLoadBalancingName, backendPoolLoadBalancingName) | ||||||
} | ||||||
} | ||||||
|
||||||
found = false | ||||||
|
||||||
// Verify health probe settings name exists | ||||||
if len(healthProbeSettings) > 0 { | ||||||
for _, hps := range healthProbeSettings { | ||||||
healthProbe := hps.(map[string]interface{}) | ||||||
healthProbeName := healthProbe["name"] | ||||||
|
||||||
if healthProbeName == backendPoolHealthProbeName { | ||||||
found = true | ||||||
break | ||||||
} | ||||||
} | ||||||
|
||||||
if !found { | ||||||
return fmt.Errorf(`"backend_pool":%q "health_probe_name":%q was not found in the configuration file. verify you have the "backend_pool_health_probe":%q defined in the configuration file`, backendPoolName, backendPoolHealthProbeName, backendPoolHealthProbeName) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
// Verify frontend endpoints custom https configuration is valid if defined | ||||||
for _, configFrontendEndpoint := range configFrontendEndpoints { | ||||||
if configFrontend := configFrontendEndpoint.(map[string]interface{}); len(configFrontend) > 0 { | ||||||
FrontendName := configFrontend["name"] | ||||||
customHttpsEnabled := configFrontend["custom_https_provisioning_enabled"].(bool) | ||||||
|
||||||
if chc := configFrontend["custom_https_configuration"].([]interface{}); len(chc) > 0 { | ||||||
if !customHttpsEnabled { | ||||||
return fmt.Errorf(`"frontend_endpoint":%q "custom_https_configuration" is invalid because "custom_https_provisioning_enabled" is set to "false". please remove the "custom_https_configuration" block from the configuration file`, FrontendName) | ||||||
} | ||||||
|
||||||
customHttpsConfiguration := chc[0].(map[string]interface{}) | ||||||
certificateSource := customHttpsConfiguration["certificate_source"] | ||||||
if certificateSource == string(frontdoor.CertificateSourceAzureKeyVault) { | ||||||
if !helper.AzureKeyVaultCertificateHasValues(customHttpsConfiguration, true) { | ||||||
return fmt.Errorf(`"frontend_endpoint":%q "custom_https_configuration" is invalid, all of the following keys must have values in the "custom_https_configuration" block: "azure_key_vault_certificate_secret_name", "azure_key_vault_certificate_secret_version", and "azure_key_vault_certificate_vault_id"`, FrontendName) | ||||||
} | ||||||
} else { | ||||||
if helper.AzureKeyVaultCertificateHasValues(customHttpsConfiguration, false) { | ||||||
return fmt.Errorf(`"frontend_endpoint":%q "custom_https_configuration" is invalid, all of the following keys must be removed from the "custom_https_configuration" block: "azure_key_vault_certificate_secret_name", "azure_key_vault_certificate_secret_version", and "azure_key_vault_certificate_vault_id"`, FrontendName) | ||||||
} | ||||||
} | ||||||
} else if customHttpsEnabled { | ||||||
return fmt.Errorf(`"frontend_endpoint":%q configuration is invalid because "custom_https_provisioning_enabled" is set to "true" and the "custom_https_configuration" block is undefined. please add the "custom_https_configuration" block to the configuration file`, FrontendName) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
return nil | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we just move this one up into the front door package?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried moving it but go got confused with the path.