github.com/mohanarpit/terraform@v0.6.16-0.20160909104007-291f29853544/builtin/providers/azurerm/provider.go (about)

     1  package azurerm
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  
     8  	"sync"
     9  
    10  	"github.com/hashicorp/go-multierror"
    11  	"github.com/hashicorp/terraform/helper/mutexkv"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  	"github.com/hashicorp/terraform/terraform"
    15  	riviera "github.com/jen20/riviera/azure"
    16  )
    17  
    18  // Provider returns a terraform.ResourceProvider.
    19  func Provider() terraform.ResourceProvider {
    20  	return &schema.Provider{
    21  		Schema: map[string]*schema.Schema{
    22  			"subscription_id": {
    23  				Type:        schema.TypeString,
    24  				Required:    true,
    25  				DefaultFunc: schema.EnvDefaultFunc("ARM_SUBSCRIPTION_ID", ""),
    26  			},
    27  
    28  			"client_id": {
    29  				Type:        schema.TypeString,
    30  				Required:    true,
    31  				DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID", ""),
    32  			},
    33  
    34  			"client_secret": {
    35  				Type:        schema.TypeString,
    36  				Required:    true,
    37  				DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET", ""),
    38  			},
    39  
    40  			"tenant_id": {
    41  				Type:        schema.TypeString,
    42  				Required:    true,
    43  				DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""),
    44  			},
    45  		},
    46  
    47  		ResourcesMap: map[string]*schema.Resource{
    48  			// These resources use the Azure ARM SDK
    49  			"azurerm_availability_set":          resourceArmAvailabilitySet(),
    50  			"azurerm_cdn_endpoint":              resourceArmCdnEndpoint(),
    51  			"azurerm_cdn_profile":               resourceArmCdnProfile(),
    52  			"azurerm_local_network_gateway":     resourceArmLocalNetworkGateway(),
    53  			"azurerm_network_interface":         resourceArmNetworkInterface(),
    54  			"azurerm_network_security_group":    resourceArmNetworkSecurityGroup(),
    55  			"azurerm_network_security_rule":     resourceArmNetworkSecurityRule(),
    56  			"azurerm_public_ip":                 resourceArmPublicIp(),
    57  			"azurerm_route":                     resourceArmRoute(),
    58  			"azurerm_route_table":               resourceArmRouteTable(),
    59  			"azurerm_servicebus_namespace":      resourceArmServiceBusNamespace(),
    60  			"azurerm_storage_account":           resourceArmStorageAccount(),
    61  			"azurerm_storage_blob":              resourceArmStorageBlob(),
    62  			"azurerm_storage_container":         resourceArmStorageContainer(),
    63  			"azurerm_storage_queue":             resourceArmStorageQueue(),
    64  			"azurerm_storage_table":             resourceArmStorageTable(),
    65  			"azurerm_subnet":                    resourceArmSubnet(),
    66  			"azurerm_template_deployment":       resourceArmTemplateDeployment(),
    67  			"azurerm_traffic_manager_endpoint":  resourceArmTrafficManagerEndpoint(),
    68  			"azurerm_traffic_manager_profile":   resourceArmTrafficManagerProfile(),
    69  			"azurerm_virtual_machine":           resourceArmVirtualMachine(),
    70  			"azurerm_virtual_machine_scale_set": resourceArmVirtualMachineScaleSet(),
    71  			"azurerm_virtual_network":           resourceArmVirtualNetwork(),
    72  			"azurerm_virtual_network_peering":   resourceArmVirtualNetworkPeering(),
    73  
    74  			// These resources use the Riviera SDK
    75  			"azurerm_dns_a_record":      resourceArmDnsARecord(),
    76  			"azurerm_dns_aaaa_record":   resourceArmDnsAAAARecord(),
    77  			"azurerm_dns_cname_record":  resourceArmDnsCNameRecord(),
    78  			"azurerm_dns_mx_record":     resourceArmDnsMxRecord(),
    79  			"azurerm_dns_ns_record":     resourceArmDnsNsRecord(),
    80  			"azurerm_dns_srv_record":    resourceArmDnsSrvRecord(),
    81  			"azurerm_dns_txt_record":    resourceArmDnsTxtRecord(),
    82  			"azurerm_dns_zone":          resourceArmDnsZone(),
    83  			"azurerm_resource_group":    resourceArmResourceGroup(),
    84  			"azurerm_search_service":    resourceArmSearchService(),
    85  			"azurerm_sql_database":      resourceArmSqlDatabase(),
    86  			"azurerm_sql_firewall_rule": resourceArmSqlFirewallRule(),
    87  			"azurerm_sql_server":        resourceArmSqlServer(),
    88  		},
    89  		ConfigureFunc: providerConfigure,
    90  	}
    91  }
    92  
    93  // Config is the configuration structure used to instantiate a
    94  // new Azure management client.
    95  type Config struct {
    96  	ManagementURL string
    97  
    98  	SubscriptionID string
    99  	ClientID       string
   100  	ClientSecret   string
   101  	TenantID       string
   102  
   103  	validateCredentialsOnce sync.Once
   104  }
   105  
   106  func (c *Config) validate() error {
   107  	var err *multierror.Error
   108  
   109  	if c.SubscriptionID == "" {
   110  		err = multierror.Append(err, fmt.Errorf("Subscription ID must be configured for the AzureRM provider"))
   111  	}
   112  	if c.ClientID == "" {
   113  		err = multierror.Append(err, fmt.Errorf("Client ID must be configured for the AzureRM provider"))
   114  	}
   115  	if c.ClientSecret == "" {
   116  		err = multierror.Append(err, fmt.Errorf("Client Secret must be configured for the AzureRM provider"))
   117  	}
   118  	if c.TenantID == "" {
   119  		err = multierror.Append(err, fmt.Errorf("Tenant ID must be configured for the AzureRM provider"))
   120  	}
   121  
   122  	return err.ErrorOrNil()
   123  }
   124  
   125  func providerConfigure(d *schema.ResourceData) (interface{}, error) {
   126  	config := &Config{
   127  		SubscriptionID: d.Get("subscription_id").(string),
   128  		ClientID:       d.Get("client_id").(string),
   129  		ClientSecret:   d.Get("client_secret").(string),
   130  		TenantID:       d.Get("tenant_id").(string),
   131  	}
   132  
   133  	if err := config.validate(); err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	client, err := config.getArmClient()
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	err = registerAzureResourceProvidersWithSubscription(client.rivieraClient)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	return client, nil
   148  }
   149  
   150  func registerProviderWithSubscription(providerName string, client *riviera.Client) error {
   151  	request := client.NewRequest()
   152  	request.Command = riviera.RegisterResourceProvider{
   153  		Namespace: providerName,
   154  	}
   155  
   156  	response, err := request.Execute()
   157  	if err != nil {
   158  		return fmt.Errorf("Cannot request provider registration for Azure Resource Manager: %s.", err)
   159  	}
   160  
   161  	if !response.IsSuccessful() {
   162  		return fmt.Errorf("Credentials for acessing the Azure Resource Manager API are likely " +
   163  			"to be incorrect, or\n  the service principal does not have permission to use " +
   164  			"the Azure Service Management\n  API.")
   165  	}
   166  
   167  	return nil
   168  }
   169  
   170  var providerRegistrationOnce sync.Once
   171  
   172  // registerAzureResourceProvidersWithSubscription uses the providers client to register
   173  // all Azure resource providers which the Terraform provider may require (regardless of
   174  // whether they are actually used by the configuration or not). It was confirmed by Microsoft
   175  // that this is the approach their own internal tools also take.
   176  func registerAzureResourceProvidersWithSubscription(client *riviera.Client) error {
   177  	var err error
   178  	providerRegistrationOnce.Do(func() {
   179  		// We register Microsoft.Compute during client initialization
   180  		providers := []string{"Microsoft.Network", "Microsoft.Cdn", "Microsoft.Storage", "Microsoft.Sql", "Microsoft.Search", "Microsoft.Resources", "Microsoft.ServiceBus"}
   181  
   182  		var wg sync.WaitGroup
   183  		wg.Add(len(providers))
   184  		for _, providerName := range providers {
   185  			go func(p string) {
   186  				defer wg.Done()
   187  				if innerErr := registerProviderWithSubscription(p, client); err != nil {
   188  					err = innerErr
   189  				}
   190  			}(providerName)
   191  		}
   192  		wg.Wait()
   193  	})
   194  
   195  	return err
   196  }
   197  
   198  // azureRMNormalizeLocation is a function which normalises human-readable region/location
   199  // names (e.g. "West US") to the values used and returned by the Azure API (e.g. "westus").
   200  // In state we track the API internal version as it is easier to go from the human form
   201  // to the canonical form than the other way around.
   202  func azureRMNormalizeLocation(location interface{}) string {
   203  	input := location.(string)
   204  	return strings.Replace(strings.ToLower(input), " ", "", -1)
   205  }
   206  
   207  // armMutexKV is the instance of MutexKV for ARM resources
   208  var armMutexKV = mutexkv.NewMutexKV()
   209  
   210  func azureStateRefreshFunc(resourceURI string, client *ArmClient, command riviera.APICall) resource.StateRefreshFunc {
   211  	return func() (interface{}, string, error) {
   212  		req := client.rivieraClient.NewRequestForURI(resourceURI)
   213  		req.Command = command
   214  
   215  		res, err := req.Execute()
   216  		if err != nil {
   217  			return nil, "", fmt.Errorf("Error executing %T command in azureStateRefreshFunc", req.Command)
   218  		}
   219  
   220  		var value reflect.Value
   221  		if reflect.ValueOf(res.Parsed).Kind() == reflect.Ptr {
   222  			value = reflect.ValueOf(res.Parsed).Elem()
   223  		} else {
   224  			value = reflect.ValueOf(res.Parsed)
   225  		}
   226  
   227  		for i := 0; i < value.NumField(); i++ { // iterates through every struct type field
   228  			tag := value.Type().Field(i).Tag // returns the tag string
   229  			tagValue := tag.Get("mapstructure")
   230  			if tagValue == "provisioningState" {
   231  				return res.Parsed, value.Field(i).Elem().String(), nil
   232  			}
   233  		}
   234  
   235  		panic(fmt.Errorf("azureStateRefreshFunc called on structure %T with no mapstructure:provisioningState tag. This is a bug", res.Parsed))
   236  	}
   237  }