Merge branch 'azurerm-provider-registration' of https://github.com/BedeGaming/terraform into BedeGaming-azurerm-provider-registration
This commit is contained in:
commit
81dc5ee328
|
@ -147,15 +147,6 @@ func (c *Config) getArmClient() (*ArmClient, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating Riviera client: %s", err)
|
||||
}
|
||||
|
||||
// validate that the credentials are correct using Riviera. Note that this must be
|
||||
// done _before_ using the Microsoft SDK, because Riviera handles errors. Using a
|
||||
// namespace registration instead of a simple OAuth token refresh guarantees that
|
||||
// service delegation is correct. This has the effect of registering Microsoft.Compute
|
||||
// which is neccessary anyway.
|
||||
if err := registerProviderWithSubscription("Microsoft.Compute", rivieraClient); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.rivieraClient = rivieraClient
|
||||
|
||||
oauthConfig, err := azure.PublicCloud.OAuthConfigForTenant(c.TenantID)
|
||||
|
|
|
@ -2,11 +2,12 @@ package azurerm
|
|||
|
||||
import (
|
||||
"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"
|
||||
|
@ -43,6 +44,12 @@ func Provider() terraform.ResourceProvider {
|
|||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""),
|
||||
},
|
||||
|
||||
"skip_provider_registration": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("ARM_SKIP_PROVIDER_REGISTRATION", false),
|
||||
},
|
||||
},
|
||||
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
|
@ -127,6 +134,7 @@ type Config struct {
|
|||
ClientID string
|
||||
ClientSecret string
|
||||
TenantID string
|
||||
SkipProviderRegistration bool
|
||||
|
||||
validateCredentialsOnce sync.Once
|
||||
}
|
||||
|
@ -157,6 +165,7 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
|
|||
ClientID: d.Get("client_id").(string),
|
||||
ClientSecret: d.Get("client_secret").(string),
|
||||
TenantID: d.Get("tenant_id").(string),
|
||||
SkipProviderRegistration: d.Get("skip_provider_registration").(bool),
|
||||
}
|
||||
|
||||
if err := config.validate(); err != nil {
|
||||
|
@ -170,30 +179,30 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
|
|||
|
||||
client.StopContext = p.StopContext()
|
||||
|
||||
err = registerAzureResourceProvidersWithSubscription(client.rivieraClient)
|
||||
// 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 *riviera.Client) error {
|
||||
request := client.NewRequest()
|
||||
request.Command = riviera.RegisterResourceProvider{
|
||||
Namespace: providerName,
|
||||
}
|
||||
|
||||
response, err := request.Execute()
|
||||
func registerProviderWithSubscription(providerName string, client resources.ProvidersClient) error {
|
||||
_, err := client.Register(providerName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot request provider registration for Azure Resource Manager: %s.", err)
|
||||
}
|
||||
|
||||
if !response.IsSuccessful() {
|
||||
return fmt.Errorf("Credentials for accessing the Azure Resource Manager API are likely " +
|
||||
"to be incorrect, or\n the service principal does not have permission to use " +
|
||||
"the Azure Service Management\n API.")
|
||||
return fmt.Errorf("Cannot register provider %s with Azure Resource Manager: %s.", providerName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -205,29 +214,42 @@ var providerRegistrationOnce sync.Once
|
|||
// 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(client *riviera.Client) error {
|
||||
func registerAzureResourceProvidersWithSubscription(providerList []resources.Provider, client resources.ProvidersClient) error {
|
||||
var err error
|
||||
providerRegistrationOnce.Do(func() {
|
||||
// We register Microsoft.Compute during client initialization
|
||||
providers := []string{
|
||||
"Microsoft.Cache",
|
||||
"Microsoft.ContainerRegistry",
|
||||
"Microsoft.Network",
|
||||
"Microsoft.Cdn",
|
||||
"Microsoft.Storage",
|
||||
"Microsoft.Sql",
|
||||
"Microsoft.Search",
|
||||
"Microsoft.Resources",
|
||||
"Microsoft.ServiceBus",
|
||||
"Microsoft.KeyVault",
|
||||
"Microsoft.EventHub",
|
||||
providers := map[string]struct{}{
|
||||
"Microsoft.Compute": struct{}{},
|
||||
"Microsoft.Cache": struct{}{},
|
||||
"Microsoft.ContainerRegistry": 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 {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -76,6 +76,12 @@ The following arguments are supported:
|
|||
* `tenant_id` - (Optional) The tenant ID to use. It can also be sourced from the
|
||||
`ARM_TENANT_ID` environment variable.
|
||||
|
||||
* `skip_provider_registration` - (Optional) Prevents the provier from registering
|
||||
the ARM provider namespaces, this can be used if you don't wish to give the Active
|
||||
Directory Application permission to register resource providers. It can also be
|
||||
sourced from the `ARM_SKIP_PROVIDER_REGISTRATION` environment variable, defaults
|
||||
to `false`.
|
||||
|
||||
## Creating Credentials
|
||||
|
||||
Azure requires that an application is added to Azure Active Directory to generate the `client_id`, `client_secret`, and `tenant_id` needed by Terraform (`subscription_id` can be recovered from your Azure account details).
|
||||
|
|
Loading…
Reference in New Issue