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: Azure Front Door #3933

Merged
merged 56 commits into from
Sep 7, 2019
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
73b0c47
WIP: Initial checkin
WodansSon May 14, 2019
7b2be6d
Updates to Frontdoor
WodansSon May 18, 2019
6dcab9c
Merge from Master
WodansSon Jun 28, 2019
1cb59ce
PIP
WodansSon Jun 28, 2019
200f2a8
PIP
WodansSon Jun 29, 2019
8291122
Finalized Schema
WodansSon Jul 10, 2019
7d66366
WIP Current state
WodansSon Jul 10, 2019
0f412eb
WIP adding validation
WodansSon Jul 16, 2019
4440bab
WIP Finished validation code
WodansSon Jul 17, 2019
04adc2f
WIP Expand RoutingRules working
WodansSon Jul 19, 2019
cf9096f
WIP All expand func implemented
WodansSon Jul 23, 2019
00a51cd
WIP: Functional CreateUpdate done
WodansSon Jul 23, 2019
68b84d0
Cast values and start on flatten functions
WodansSon Jul 24, 2019
c40fcc9
WIP: Last fix routing rules
WodansSon Jul 25, 2019
35c4502
WIP: Fully working Frontdoor
WodansSon Jul 26, 2019
7481722
WIP: Wasnt exactly done... fixed destroy
WodansSon Jul 26, 2019
88e5f6a
gofmt code
WodansSon Jul 26, 2019
0a42aa7
Pull Master
WodansSon Jul 26, 2019
156b97b
WIP: Added first test
WodansSon Jul 27, 2019
ca743f9
Fixed casing issue when interacting with portal
WodansSon Jul 29, 2019
c94c75a
Fix bug in fronend endpoint enum
WodansSon Jul 31, 2019
aab4faf
Bug fix for Frontend Endpoint flatten
WodansSon Jul 31, 2019
f871c66
Add data source
WodansSon Aug 1, 2019
0b8f259
Slight update to documentation
WodansSon Aug 1, 2019
e2ebb0a
Added Frontend client code
WodansSon Aug 6, 2019
36fd014
Extending frontend endpoints schema
WodansSon Aug 7, 2019
51bef98
schema and exposed enable and disable for frontend
WodansSon Aug 9, 2019
ea00b84
Enabled custom HTTPS domain setting
WodansSon Aug 10, 2019
7bf9a73
[WIP] Last code changes
WodansSon Aug 12, 2019
161ac42
Tweeking defaults and adding validation rules
WodansSon Aug 14, 2019
e35c193
[WIP] Update test case
WodansSon Aug 14, 2019
011ac40
Updated documentation and exposed cname
WodansSon Aug 14, 2019
df4d523
fixed flatten for Cert Source AzureKeyVault
WodansSon Aug 14, 2019
4372071
Pull Master
WodansSon Aug 16, 2019
5a004ae
Update to latest
WodansSon Aug 16, 2019
e471f1a
Few changes per PR review
WodansSon Aug 16, 2019
cb3ba6c
Fixed lint errors
WodansSon Aug 16, 2019
1e505b4
Missed one client declaration
WodansSon Aug 16, 2019
ac6a9fa
Fix test client
WodansSon Aug 16, 2019
5c9144e
Removed data source fix lint errs
WodansSon Aug 16, 2019
b721403
update CheckDestroy func
WodansSon Aug 16, 2019
822ef9d
Update website/docs/r/front_door.html.markdown
WodansSon Aug 21, 2019
d54b7fc
Update azurerm/resource_arm_front_door.go
WodansSon Aug 21, 2019
262b1f9
Update azurerm/resource_arm_front_door.go
WodansSon Aug 21, 2019
ba08bca
Update test and docs
WodansSon Aug 22, 2019
e251d71
Fixed conflict
WodansSon Aug 22, 2019
f25eed4
Added test case
WodansSon Aug 22, 2019
d21382e
Update test per PR
WodansSon Aug 22, 2019
28383a9
Address PR issues
WodansSon Aug 22, 2019
c592646
Update website/docs/r/front_door.html.markdown
WodansSon Aug 22, 2019
5f49486
Added link to internal bug tracking casing issue
WodansSon Aug 22, 2019
f39e2d6
Merge branch 'nr_frontdoor' of https://github.com/terraform-providers…
WodansSon Aug 22, 2019
3ed1561
gofmt
WodansSon Aug 24, 2019
4425f84
Fixes per PR comments
WodansSon Sep 5, 2019
b99238f
Moving package(s) and refactor
WodansSon Sep 7, 2019
fe5bf89
gofmt
WodansSon Sep 7, 2019
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
3 changes: 3 additions & 0 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/dns"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/eventgrid"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/eventhub"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/frontdoor"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/graph"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/hdinsight"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/iothub"
Expand Down Expand Up @@ -110,6 +111,7 @@ type ArmClient struct {
privateDns *privatedns.Client
eventGrid *eventgrid.Client
eventhub *eventhub.Client
frontdoor *frontdoor.Client
graph *graph.Client
hdinsight *hdinsight.Client
iothub *iothub.Client
Expand Down Expand Up @@ -247,6 +249,7 @@ func getArmClient(c *authentication.Config, skipProviderRegistration bool, partn
client.dns = dns.BuildClient(o)
client.eventGrid = eventgrid.BuildClient(o)
client.eventhub = eventhub.BuildClient(o)
client.frontdoor = frontdoor.BuildClient(o)
client.graph = graph.BuildClient(o)
client.hdinsight = hdinsight.BuildClient(o)
client.iothub = iothub.BuildClient(o)
Expand Down
24 changes: 24 additions & 0 deletions azurerm/internal/services/frontdoor/client.go
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,
}
}
85 changes: 85 additions & 0 deletions azurerm/internal/services/frontdoor/helper/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package helper
Copy link
Collaborator

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?

Copy link
Collaborator Author

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.


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"
}
}
172 changes: 172 additions & 0 deletions azurerm/internal/services/frontdoor/validate/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package validate
Copy link
Collaborator

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?

Copy link
Collaborator Author

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.


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) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This would then become

Suggested change
func FrontDoorName(i interface{}, k string) (_ []string, errors []error) {
func ValidateFrontDoorName(i interface{}, k string) (_ []string, errors []error) {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
func BackendPoolRoutingRuleName(i interface{}, k string) (_ []string, errors []error) {
func ValidateBackendPoolRoutingRuleName(i interface{}, k string) (_ []string, errors []error) {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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
}
1 change: 1 addition & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_firewall_nat_rule_collection": resourceArmFirewallNatRuleCollection(),
"azurerm_firewall_network_rule_collection": resourceArmFirewallNetworkRuleCollection(),
"azurerm_firewall": resourceArmFirewall(),
"azurerm_frontdoor": resourceArmFrontDoor(),
"azurerm_function_app": resourceArmFunctionApp(),
"azurerm_hdinsight_hadoop_cluster": resourceArmHDInsightHadoopCluster(),
"azurerm_hdinsight_hbase_cluster": resourceArmHDInsightHBaseCluster(),
Expand Down
Loading