terraform/builtin/providers/azurerm/provider.go

365 lines
13 KiB
Go
Raw Normal View History

package azurerm
import (
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"fmt"
"log"
"reflect"
"strings"
"sync"
"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/helper/mutexkv"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
riviera "github.com/jen20/riviera/azure"
)
// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
var p *schema.Provider
p = &schema.Provider{
Schema: map[string]*schema.Schema{
"subscription_id": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_SUBSCRIPTION_ID", ""),
},
"client_id": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID", ""),
},
"client_secret": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET", ""),
},
"tenant_id": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""),
},
"environment": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_ENVIRONMENT", "public"),
},
"skip_provider_registration": {
Type: schema.TypeBool,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_SKIP_PROVIDER_REGISTRATION", false),
},
},
DataSourcesMap: map[string]*schema.Resource{
"azurerm_client_config": dataSourceArmClientConfig(),
},
ResourcesMap: map[string]*schema.Resource{
// These resources use the Azure ARM SDK
"azurerm_availability_set": resourceArmAvailabilitySet(),
"azurerm_cdn_endpoint": resourceArmCdnEndpoint(),
"azurerm_cdn_profile": resourceArmCdnProfile(),
"azurerm_container_registry": resourceArmContainerRegistry(),
"azurerm_container_service": resourceArmContainerService(),
provider/azurerm: Add Load Balancer resources (#9199) * provider/azurerm: Add AzureRM Loadbalancer resource Adds support for the elusive Azure LoadBalancer * [x] `azurerm_lb` * [x] `azurerm_lb_backend_address_pool` * [x] `azurerm_lb_rule` * [x] `azurerm_lb_nat_rule` * [x] `azurerm_lb_probe` * [x] `azurerm_lb_nat_pool` Test Results: ``` make testacc TEST=./builtin/providers/azurerm TESTARGS='-run=TestAccAzureRMLoadbalancer' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) TF_ACC=1 go test ./builtin/providers/azurerm -v -run=TestAccAzureRMLoadbalancer -timeout 120m === RUN TestAccAzureRMLoadbalancerBackEndAddressPool_basic --- PASS: TestAccAzureRMLoadbalancerBackEndAddressPool_basic (207.26s) === RUN TestAccAzureRMLoadbalancerBackEndAddressPool_removal --- PASS: TestAccAzureRMLoadbalancerBackEndAddressPool_removal (165.89s) === RUN TestAccAzureRMLoadbalancerNatRule_basic --- PASS: TestAccAzureRMLoadbalancerNatRule_basic (179.30s) === RUN TestAccAzureRMLoadbalancerNatRule_removal --- PASS: TestAccAzureRMLoadbalancerNatRule_removal (180.73s) === RUN TestAccAzureRMLoadbalancerRule_basic --- PASS: TestAccAzureRMLoadbalancerRule_basic (170.40s) === RUN TestAccAzureRMLoadbalancerRule_removal --- PASS: TestAccAzureRMLoadbalancerRule_removal (204.23s) === RUN TestAccAzureRMLoadbalancer_basic --- PASS: TestAccAzureRMLoadbalancer_basic (136.03s) === RUN TestAccAzureRMLoadbalancer_frontEndConfig --- PASS: TestAccAzureRMLoadbalancer_frontEndConfig (214.47s) === RUN TestAccAzureRMLoadbalancer_tags --- PASS: TestAccAzureRMLoadbalancer_tags (215.52s) === RUN TestAccAzureRMLoadbalancerProbe_basic --- PASS: TestAccAzureRMLoadbalancerProbe_basic (183.36s) === RUN TestAccAzureRMLoadbalancerProbe_removal --- PASS: TestAccAzureRMLoadbalancerProbe_removal (185.86s) === RUN TestAccAzureRMLoadbalancerNatPool_basic --- PASS: TestAccAzureRMLoadbalancerNatPool_basic (161.47s) === RUN TestAccAzureRMLoadbalancerNatPool_removal --- PASS: TestAccAzureRMLoadbalancerNatPool_removal (167.38s) PASS ok github.com/hashicorp/terraform/builtin/providers/azurerm 1673.852s ``` * provider/azurerm: Documentation for the ARM LB resources
2016-10-07 20:14:26 +02:00
"azurerm_eventhub": resourceArmEventHub(),
"azurerm_eventhub_authorization_rule": resourceArmEventHubAuthorizationRule(),
"azurerm_eventhub_consumer_group": resourceArmEventHubConsumerGroup(),
"azurerm_eventhub_namespace": resourceArmEventHubNamespace(),
provider/azurerm: Add Load Balancer resources (#9199) * provider/azurerm: Add AzureRM Loadbalancer resource Adds support for the elusive Azure LoadBalancer * [x] `azurerm_lb` * [x] `azurerm_lb_backend_address_pool` * [x] `azurerm_lb_rule` * [x] `azurerm_lb_nat_rule` * [x] `azurerm_lb_probe` * [x] `azurerm_lb_nat_pool` Test Results: ``` make testacc TEST=./builtin/providers/azurerm TESTARGS='-run=TestAccAzureRMLoadbalancer' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) TF_ACC=1 go test ./builtin/providers/azurerm -v -run=TestAccAzureRMLoadbalancer -timeout 120m === RUN TestAccAzureRMLoadbalancerBackEndAddressPool_basic --- PASS: TestAccAzureRMLoadbalancerBackEndAddressPool_basic (207.26s) === RUN TestAccAzureRMLoadbalancerBackEndAddressPool_removal --- PASS: TestAccAzureRMLoadbalancerBackEndAddressPool_removal (165.89s) === RUN TestAccAzureRMLoadbalancerNatRule_basic --- PASS: TestAccAzureRMLoadbalancerNatRule_basic (179.30s) === RUN TestAccAzureRMLoadbalancerNatRule_removal --- PASS: TestAccAzureRMLoadbalancerNatRule_removal (180.73s) === RUN TestAccAzureRMLoadbalancerRule_basic --- PASS: TestAccAzureRMLoadbalancerRule_basic (170.40s) === RUN TestAccAzureRMLoadbalancerRule_removal --- PASS: TestAccAzureRMLoadbalancerRule_removal (204.23s) === RUN TestAccAzureRMLoadbalancer_basic --- PASS: TestAccAzureRMLoadbalancer_basic (136.03s) === RUN TestAccAzureRMLoadbalancer_frontEndConfig --- PASS: TestAccAzureRMLoadbalancer_frontEndConfig (214.47s) === RUN TestAccAzureRMLoadbalancer_tags --- PASS: TestAccAzureRMLoadbalancer_tags (215.52s) === RUN TestAccAzureRMLoadbalancerProbe_basic --- PASS: TestAccAzureRMLoadbalancerProbe_basic (183.36s) === RUN TestAccAzureRMLoadbalancerProbe_removal --- PASS: TestAccAzureRMLoadbalancerProbe_removal (185.86s) === RUN TestAccAzureRMLoadbalancerNatPool_basic --- PASS: TestAccAzureRMLoadbalancerNatPool_basic (161.47s) === RUN TestAccAzureRMLoadbalancerNatPool_removal --- PASS: TestAccAzureRMLoadbalancerNatPool_removal (167.38s) PASS ok github.com/hashicorp/terraform/builtin/providers/azurerm 1673.852s ``` * provider/azurerm: Documentation for the ARM LB resources
2016-10-07 20:14:26 +02:00
"azurerm_lb": resourceArmLoadBalancer(),
"azurerm_lb_backend_address_pool": resourceArmLoadBalancerBackendAddressPool(),
"azurerm_lb_nat_rule": resourceArmLoadBalancerNatRule(),
"azurerm_lb_nat_pool": resourceArmLoadBalancerNatPool(),
"azurerm_lb_probe": resourceArmLoadBalancerProbe(),
"azurerm_lb_rule": resourceArmLoadBalancerRule(),
2017-03-03 23:46:33 +01:00
"azurerm_managed_disk": resourceArmManagedDisk(),
"azurerm_key_vault": resourceArmKeyVault(),
"azurerm_local_network_gateway": resourceArmLocalNetworkGateway(),
"azurerm_network_interface": resourceArmNetworkInterface(),
"azurerm_network_security_group": resourceArmNetworkSecurityGroup(),
"azurerm_network_security_rule": resourceArmNetworkSecurityRule(),
"azurerm_public_ip": resourceArmPublicIp(),
"azurerm_redis_cache": resourceArmRedisCache(),
"azurerm_route": resourceArmRoute(),
"azurerm_route_table": resourceArmRouteTable(),
"azurerm_servicebus_namespace": resourceArmServiceBusNamespace(),
"azurerm_servicebus_subscription": resourceArmServiceBusSubscription(),
"azurerm_servicebus_topic": resourceArmServiceBusTopic(),
"azurerm_storage_account": resourceArmStorageAccount(),
"azurerm_storage_blob": resourceArmStorageBlob(),
"azurerm_storage_container": resourceArmStorageContainer(),
"azurerm_storage_share": resourceArmStorageShare(),
"azurerm_storage_queue": resourceArmStorageQueue(),
"azurerm_storage_table": resourceArmStorageTable(),
"azurerm_subnet": resourceArmSubnet(),
"azurerm_template_deployment": resourceArmTemplateDeployment(),
provider/azurerm: add traffic manager resources (#7826) * provider/azurerm: vendor arm/trafficmanager package * provider/azurerm: add azurerm_traffic_manager_profile resource * provider/azurerm: add azurerm_traffic_manager_endpoint resource * provider/azurerm: document traffic manager resources * provider/azurerm: use short type argument for traffic manager endpoint The resource now takes the short type for example azureEndpoints instead of the long form Microsoft.Network/TrafficManagerProfiles/azureEndpoints. ``` TF_ACC=1 go test ./builtin/providers/azurerm -v -run TestAccAzureRMTrafficManagerEndpoint -timeout 120m === RUN TestAccAzureRMTrafficManagerEndpoint_basic --- PASS: TestAccAzureRMTrafficManagerEndpoint_basic (179.72s) === RUN TestAccAzureRMTrafficManagerEndpoint_basicDisableExternal --- PASS: TestAccAzureRMTrafficManagerEndpoint_basicDisableExternal (171.36s) === RUN TestAccAzureRMTrafficManagerEndpoint_updateWeight --- PASS: TestAccAzureRMTrafficManagerEndpoint_updateWeight (167.24s) === RUN TestAccAzureRMTrafficManagerEndpoint_updatePriority --- PASS: TestAccAzureRMTrafficManagerEndpoint_updatePriority (192.91s) === RUN TestAccAzureRMTrafficManagerEndpoint_nestedEndpoints --- PASS: TestAccAzureRMTrafficManagerEndpoint_nestedEndpoints (111.18s) PASS ok github.com/hashicorp/terraform/builtin/providers/azurerm 822.534s ``` * provider/azurerm: remove unnecesary dereferences in traffic manager resources ``` TF_ACC=1 go test ./builtin/providers/azurerm -v -run TestAccAzureRMTrafficManager -timeout 120m === RUN TestAccAzureRMTrafficManagerEndpoint_basic --- PASS: TestAccAzureRMTrafficManagerEndpoint_basic (176.08s) === RUN TestAccAzureRMTrafficManagerEndpoint_basicDisableExternal --- PASS: TestAccAzureRMTrafficManagerEndpoint_basicDisableExternal (172.28s) === RUN TestAccAzureRMTrafficManagerEndpoint_updateWeight --- PASS: TestAccAzureRMTrafficManagerEndpoint_updateWeight (148.97s) === RUN TestAccAzureRMTrafficManagerEndpoint_updatePriority --- PASS: TestAccAzureRMTrafficManagerEndpoint_updatePriority (101.18s) === RUN TestAccAzureRMTrafficManagerEndpoint_nestedEndpoints --- PASS: TestAccAzureRMTrafficManagerEndpoint_nestedEndpoints (88.33s) === RUN TestAccAzureRMTrafficManagerProfile_weighted --- PASS: TestAccAzureRMTrafficManagerProfile_weighted (80.92s) === RUN TestAccAzureRMTrafficManagerProfile_performance --- PASS: TestAccAzureRMTrafficManagerProfile_performance (82.98s) === RUN TestAccAzureRMTrafficManagerProfile_priority --- PASS: TestAccAzureRMTrafficManagerProfile_priority (81.07s) === RUN TestAccAzureRMTrafficManagerProfile_withTags --- PASS: TestAccAzureRMTrafficManagerProfile_withTags (102.50s) PASS ok github.com/hashicorp/terraform/builtin/providers/azurerm 1034.458s ```
2016-08-01 00:46:15 +02:00
"azurerm_traffic_manager_endpoint": resourceArmTrafficManagerEndpoint(),
"azurerm_traffic_manager_profile": resourceArmTrafficManagerProfile(),
"azurerm_virtual_machine_extension": resourceArmVirtualMachineExtensions(),
"azurerm_virtual_machine": resourceArmVirtualMachine(),
"azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(),
"azurerm_virtual_network": resourceArmVirtualNetwork(),
"azurerm_virtual_network_peering": resourceArmVirtualNetworkPeering(),
// These resources use the Riviera SDK
"azurerm_dns_a_record": resourceArmDnsARecord(),
"azurerm_dns_aaaa_record": resourceArmDnsAAAARecord(),
"azurerm_dns_cname_record": resourceArmDnsCNameRecord(),
"azurerm_dns_mx_record": resourceArmDnsMxRecord(),
"azurerm_dns_ns_record": resourceArmDnsNsRecord(),
"azurerm_dns_srv_record": resourceArmDnsSrvRecord(),
"azurerm_dns_txt_record": resourceArmDnsTxtRecord(),
"azurerm_dns_zone": resourceArmDnsZone(),
"azurerm_resource_group": resourceArmResourceGroup(),
"azurerm_search_service": resourceArmSearchService(),
"azurerm_sql_database": resourceArmSqlDatabase(),
"azurerm_sql_firewall_rule": resourceArmSqlFirewallRule(),
"azurerm_sql_server": resourceArmSqlServer(),
},
}
p.ConfigureFunc = providerConfigure(p)
return p
}
// Config is the configuration structure used to instantiate a
// new Azure management client.
type Config struct {
ManagementURL string
SubscriptionID string
ClientID string
ClientSecret string
TenantID string
Environment string
SkipProviderRegistration bool
validateCredentialsOnce sync.Once
}
func (c *Config) validate() error {
var err *multierror.Error
if c.SubscriptionID == "" {
err = multierror.Append(err, fmt.Errorf("Subscription ID must be configured for the AzureRM provider"))
}
if c.ClientID == "" {
err = multierror.Append(err, fmt.Errorf("Client ID must be configured for the AzureRM provider"))
}
if c.ClientSecret == "" {
err = multierror.Append(err, fmt.Errorf("Client Secret must be configured for the AzureRM provider"))
}
if c.TenantID == "" {
err = multierror.Append(err, fmt.Errorf("Tenant ID must be configured for the AzureRM provider"))
}
if c.Environment == "" {
err = multierror.Append(err, fmt.Errorf("Environment must be configured for the AzureRM provider"))
}
return err.ErrorOrNil()
}
func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
return func(d *schema.ResourceData) (interface{}, error) {
config := &Config{
SubscriptionID: d.Get("subscription_id").(string),
ClientID: d.Get("client_id").(string),
ClientSecret: d.Get("client_secret").(string),
TenantID: d.Get("tenant_id").(string),
Environment: d.Get("environment").(string),
SkipProviderRegistration: d.Get("skip_provider_registration").(bool),
}
if err := config.validate(); err != nil {
return nil, err
}
client, err := config.getArmClient()
if err != nil {
return nil, err
}
client.StopContext = p.StopContext()
// replaces the context between tests
p.MetaReset = func() error {
client.StopContext = p.StopContext()
return nil
}
// List all the available providers and their registration state to avoid unnecessary
// requests. This also lets us check if the provider credentials are correct.
providerList, err := client.providers.List(nil, "")
if err != nil {
return nil, fmt.Errorf("Unable to list provider registration status, it is possible that this is due to invalid "+
"credentials or the service principal does not have permission to use the Resource Manager API, Azure "+
"error: %s", err)
}
if !config.SkipProviderRegistration {
err = registerAzureResourceProvidersWithSubscription(*providerList.Value, client.providers)
if err != nil {
return nil, err
}
}
return client, nil
}
}
func registerProviderWithSubscription(providerName string, client resources.ProvidersClient) error {
_, err := client.Register(providerName)
if err != nil {
return fmt.Errorf("Cannot register provider %s with Azure Resource Manager: %s.", providerName, err)
}
return nil
}
var providerRegistrationOnce sync.Once
// registerAzureResourceProvidersWithSubscription uses the providers client to register
// all Azure resource providers which the Terraform provider may require (regardless of
// whether they are actually used by the configuration or not). It was confirmed by Microsoft
// that this is the approach their own internal tools also take.
func registerAzureResourceProvidersWithSubscription(providerList []resources.Provider, client resources.ProvidersClient) error {
var err error
providerRegistrationOnce.Do(func() {
providers := map[string]struct{}{
"Microsoft.Compute": struct{}{},
"Microsoft.Cache": struct{}{},
"Microsoft.ContainerRegistry": struct{}{},
"Microsoft.ContainerService": struct{}{},
"Microsoft.Network": struct{}{},
"Microsoft.Cdn": struct{}{},
"Microsoft.Storage": struct{}{},
"Microsoft.Sql": struct{}{},
"Microsoft.Search": struct{}{},
"Microsoft.Resources": struct{}{},
"Microsoft.ServiceBus": struct{}{},
"Microsoft.KeyVault": struct{}{},
"Microsoft.EventHub": struct{}{},
}
// filter out any providers already registered
for _, p := range providerList {
if _, ok := providers[*p.Namespace]; !ok {
continue
}
if strings.ToLower(*p.RegistrationState) == "registered" {
log.Printf("[DEBUG] Skipping provider registration for namespace %s\n", *p.Namespace)
delete(providers, *p.Namespace)
}
}
var wg sync.WaitGroup
wg.Add(len(providers))
for providerName := range providers {
go func(p string) {
defer wg.Done()
log.Printf("[DEBUG] Registering provider with namespace %s\n", p)
if innerErr := registerProviderWithSubscription(p, client); err != nil {
err = innerErr
}
}(providerName)
}
wg.Wait()
})
return err
}
// armMutexKV is the instance of MutexKV for ARM resources
var armMutexKV = mutexkv.NewMutexKV()
func azureStateRefreshFunc(resourceURI string, client *ArmClient, command riviera.APICall) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
req := client.rivieraClient.NewRequestForURI(resourceURI)
req.Command = command
res, err := req.Execute()
if err != nil {
return nil, "", fmt.Errorf("Error executing %T command in azureStateRefreshFunc", req.Command)
}
var value reflect.Value
if reflect.ValueOf(res.Parsed).Kind() == reflect.Ptr {
value = reflect.ValueOf(res.Parsed).Elem()
} else {
value = reflect.ValueOf(res.Parsed)
}
for i := 0; i < value.NumField(); i++ { // iterates through every struct type field
tag := value.Type().Field(i).Tag // returns the tag string
tagValue := tag.Get("mapstructure")
if tagValue == "provisioningState" {
return res.Parsed, value.Field(i).Elem().String(), nil
}
}
panic(fmt.Errorf("azureStateRefreshFunc called on structure %T with no mapstructure:provisioningState tag. This is a bug", res.Parsed))
}
}
// Resource group names can be capitalised, but we store them in lowercase.
// Use a custom diff function to avoid creation of new resources.
func resourceAzurermResourceGroupNameDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
return strings.ToLower(old) == strings.ToLower(new)
}
// ignoreCaseDiffSuppressFunc is a DiffSuppressFunc from helper/schema that is
// used to ignore any case-changes in a return value.
func ignoreCaseDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool {
return strings.ToLower(old) == strings.ToLower(new)
}
// ignoreCaseStateFunc is a StateFunc from helper/schema that converts the
// supplied value to lower before saving to state for consistency.
func ignoreCaseStateFunc(val interface{}) string {
return strings.ToLower(val.(string))
}
func userDataStateFunc(v interface{}) string {
switch s := v.(type) {
case string:
s = base64Encode(s)
hash := sha1.Sum([]byte(s))
return hex.EncodeToString(hash[:])
default:
return ""
}
}
// base64Encode encodes data if the input isn't already encoded using
// base64.StdEncoding.EncodeToString. If the input is already base64 encoded,
// return the original input unchanged.
func base64Encode(data string) string {
// Check whether the data is already Base64 encoded; don't double-encode
if isBase64Encoded(data) {
return data
}
// data has not been encoded encode and return
return base64.StdEncoding.EncodeToString([]byte(data))
}
func isBase64Encoded(data string) bool {
_, err := base64.StdEncoding.DecodeString(data)
return err == nil
}