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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error creating Riviera client: %s", err)
|
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
|
client.rivieraClient = rivieraClient
|
||||||
|
|
||||||
oauthConfig, err := azure.PublicCloud.OAuthConfigForTenant(c.TenantID)
|
oauthConfig, err := azure.PublicCloud.OAuthConfigForTenant(c.TenantID)
|
||||||
|
|
|
@ -2,11 +2,12 @@ package azurerm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/terraform/helper/mutexkv"
|
"github.com/hashicorp/terraform/helper/mutexkv"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
@ -43,6 +44,12 @@ func Provider() terraform.ResourceProvider {
|
||||||
Required: true,
|
Required: true,
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""),
|
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{
|
DataSourcesMap: map[string]*schema.Resource{
|
||||||
|
@ -123,10 +130,11 @@ func Provider() terraform.ResourceProvider {
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ManagementURL string
|
ManagementURL string
|
||||||
|
|
||||||
SubscriptionID string
|
SubscriptionID string
|
||||||
ClientID string
|
ClientID string
|
||||||
ClientSecret string
|
ClientSecret string
|
||||||
TenantID string
|
TenantID string
|
||||||
|
SkipProviderRegistration bool
|
||||||
|
|
||||||
validateCredentialsOnce sync.Once
|
validateCredentialsOnce sync.Once
|
||||||
}
|
}
|
||||||
|
@ -153,10 +161,11 @@ func (c *Config) validate() error {
|
||||||
func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
|
func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
|
||||||
return func(d *schema.ResourceData) (interface{}, error) {
|
return func(d *schema.ResourceData) (interface{}, error) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
SubscriptionID: d.Get("subscription_id").(string),
|
SubscriptionID: d.Get("subscription_id").(string),
|
||||||
ClientID: d.Get("client_id").(string),
|
ClientID: d.Get("client_id").(string),
|
||||||
ClientSecret: d.Get("client_secret").(string),
|
ClientSecret: d.Get("client_secret").(string),
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
|
SkipProviderRegistration: d.Get("skip_provider_registration").(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.validate(); err != nil {
|
if err := config.validate(); err != nil {
|
||||||
|
@ -170,30 +179,30 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
|
||||||
|
|
||||||
client.StopContext = p.StopContext()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
return client, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerProviderWithSubscription(providerName string, client *riviera.Client) error {
|
func registerProviderWithSubscription(providerName string, client resources.ProvidersClient) error {
|
||||||
request := client.NewRequest()
|
_, err := client.Register(providerName)
|
||||||
request.Command = riviera.RegisterResourceProvider{
|
|
||||||
Namespace: providerName,
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := request.Execute()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot request provider registration for Azure Resource Manager: %s.", err)
|
return fmt.Errorf("Cannot register provider %s with Azure Resource Manager: %s.", providerName, 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 nil
|
return nil
|
||||||
|
@ -205,29 +214,42 @@ var providerRegistrationOnce sync.Once
|
||||||
// all Azure resource providers which the Terraform provider may require (regardless of
|
// 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
|
// 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.
|
// 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
|
var err error
|
||||||
providerRegistrationOnce.Do(func() {
|
providerRegistrationOnce.Do(func() {
|
||||||
// We register Microsoft.Compute during client initialization
|
providers := map[string]struct{}{
|
||||||
providers := []string{
|
"Microsoft.Compute": struct{}{},
|
||||||
"Microsoft.Cache",
|
"Microsoft.Cache": struct{}{},
|
||||||
"Microsoft.ContainerRegistry",
|
"Microsoft.ContainerRegistry": struct{}{},
|
||||||
"Microsoft.Network",
|
"Microsoft.Network": struct{}{},
|
||||||
"Microsoft.Cdn",
|
"Microsoft.Cdn": struct{}{},
|
||||||
"Microsoft.Storage",
|
"Microsoft.Storage": struct{}{},
|
||||||
"Microsoft.Sql",
|
"Microsoft.Sql": struct{}{},
|
||||||
"Microsoft.Search",
|
"Microsoft.Search": struct{}{},
|
||||||
"Microsoft.Resources",
|
"Microsoft.Resources": struct{}{},
|
||||||
"Microsoft.ServiceBus",
|
"Microsoft.ServiceBus": struct{}{},
|
||||||
"Microsoft.KeyVault",
|
"Microsoft.KeyVault": struct{}{},
|
||||||
"Microsoft.EventHub",
|
"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
|
var wg sync.WaitGroup
|
||||||
wg.Add(len(providers))
|
wg.Add(len(providers))
|
||||||
for _, providerName := range providers {
|
for providerName := range providers {
|
||||||
go func(p string) {
|
go func(p string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
log.Printf("[DEBUG] Registering provider with namespace %s\n", p)
|
||||||
if innerErr := registerProviderWithSubscription(p, client); err != nil {
|
if innerErr := registerProviderWithSubscription(p, client); err != nil {
|
||||||
err = innerErr
|
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
|
* `tenant_id` - (Optional) The tenant ID to use. It can also be sourced from the
|
||||||
`ARM_TENANT_ID` environment variable.
|
`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
|
## 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).
|
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