diff --git a/azurerm/azurerm_sweeper_test.go b/azurerm/azurerm_sweeper_test.go index 849a6912c885..ad2212eab3db 100644 --- a/azurerm/azurerm_sweeper_test.go +++ b/azurerm/azurerm_sweeper_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/authentication" ) func TestMain(m *testing.M) { @@ -29,7 +30,7 @@ func buildConfigForSweepers() (*ArmClient, error) { return nil, fmt.Errorf("ARM_SUBSCRIPTION_ID, ARM_CLIENT_ID, ARM_CLIENT_SECRET and ARM_TENANT_ID must be set for acceptance tests") } - config := &Config{ + config := &authentication.Config{ SubscriptionID: subscriptionID, ClientID: clientID, ClientSecret: clientSecret, @@ -38,7 +39,7 @@ func buildConfigForSweepers() (*ArmClient, error) { SkipProviderRegistration: false, } - return config.getArmClient() + return getArmClient(config) } func shouldSweepAcceptanceTestResource(name string, resourceLocation string, region string) bool { diff --git a/azurerm/config.go b/azurerm/config.go index 9b20737f9a16..289ffc0b33a3 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -44,6 +44,7 @@ import ( "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/authentication" ) // ArmClient contains the handles to all the specific Azure Resource Manager @@ -213,7 +214,7 @@ func setUserAgent(client *autorest.Client) { } } -func (c *Config) getAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { +func getAuthorizationToken(c *authentication.Config, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) { useServicePrincipal := c.ClientSecret != "" if useServicePrincipal { @@ -245,7 +246,7 @@ func (c *Config) getAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint s // getArmClient is a helper method which returns a fully instantiated // *ArmClient based on the Config's current settings. -func (c *Config) getArmClient() (*ArmClient, error) { +func getArmClient(c *authentication.Config) (*ArmClient, error) { // detect cloud from environment env, envErr := azure.EnvironmentFromName(c.Environment) if envErr != nil { @@ -280,21 +281,21 @@ func (c *Config) getArmClient() (*ArmClient, error) { // Resource Manager endpoints endpoint := env.ResourceManagerEndpoint - auth, err := c.getAuthorizationToken(oauthConfig, endpoint) + auth, err := getAuthorizationToken(c, oauthConfig, endpoint) if err != nil { return nil, err } // Graph Endpoints graphEndpoint := env.GraphEndpoint - graphAuth, err := c.getAuthorizationToken(oauthConfig, graphEndpoint) + graphAuth, err := getAuthorizationToken(c, oauthConfig, graphEndpoint) if err != nil { return nil, err } // Key Vault Endpoints keyVaultAuth := autorest.NewBearerAuthorizerCallback(sender, func(tenantID, resource string) (*autorest.BearerAuthorizer, error) { - keyVaultSpt, err := c.getAuthorizationToken(oauthConfig, resource) + keyVaultSpt, err := getAuthorizationToken(c, oauthConfig, resource) if err != nil { return nil, err } diff --git a/azurerm/helpers/authentication/access_token.go b/azurerm/helpers/authentication/access_token.go new file mode 100644 index 000000000000..6fa06355b8d6 --- /dev/null +++ b/azurerm/helpers/authentication/access_token.go @@ -0,0 +1,55 @@ +package authentication + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/Azure/go-autorest/autorest/adal" + "github.com/Azure/go-autorest/autorest/azure/cli" +) + +type AccessToken struct { + ClientID string + AccessToken *adal.Token + IsCloudShell bool +} + +func findValidAccessTokenForTenant(tokens []cli.Token, tenantId string) (*AccessToken, error) { + for _, accessToken := range tokens { + token, err := accessToken.ToADALToken() + if err != nil { + return nil, fmt.Errorf("[DEBUG] Error converting access token to token: %+v", err) + } + + expirationDate, err := cli.ParseExpirationDate(accessToken.ExpiresOn) + if err != nil { + return nil, fmt.Errorf("Error parsing expiration date: %q", accessToken.ExpiresOn) + } + + if expirationDate.UTC().Before(time.Now().UTC()) { + log.Printf("[DEBUG] Token %q has expired", token.AccessToken) + continue + } + + if !strings.Contains(accessToken.Resource, "management") { + log.Printf("[DEBUG] Resource %q isn't a management domain", accessToken.Resource) + continue + } + + if !strings.HasSuffix(accessToken.Authority, tenantId) { + log.Printf("[DEBUG] Resource %q isn't for the correct Tenant", accessToken.Resource) + continue + } + + validAccessToken := AccessToken{ + ClientID: accessToken.ClientID, + AccessToken: &token, + IsCloudShell: accessToken.RefreshToken == "", + } + return &validAccessToken, nil + } + + return nil, fmt.Errorf("No Access Token was found for the Tenant ID %q", tenantId) +} diff --git a/azurerm/helpers/authentication/access_token_test.go b/azurerm/helpers/authentication/access_token_test.go new file mode 100644 index 000000000000..3eccdd341687 --- /dev/null +++ b/azurerm/helpers/authentication/access_token_test.go @@ -0,0 +1,217 @@ +package authentication + +import ( + "testing" + "time" + + "github.com/Azure/go-autorest/autorest/azure/cli" +) + +func TestAzureFindValidAccessTokenForTenant_InvalidDate(t *testing.T) { + tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" + expectedToken := cli.Token{ + ExpiresOn: "invalid date", + AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", + TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", + RefreshToken: "4ec3874d-ee2e-4980-ba47-b5bac11ddb94", + Resource: "https://management.core.windows.net/", + Authority: tenantId, + } + tokens := []cli.Token{expectedToken} + token, err := findValidAccessTokenForTenant(tokens, tenantId) + + if err == nil { + t.Fatalf("Expected an error to be returned but got nil") + } + + if token != nil { + t.Fatalf("Expected Token to be nil but got: %+v", token) + } +} + +func TestAzureFindValidAccessTokenForTenant_Expired(t *testing.T) { + expirationDate := time.Now().Add(time.Minute * -1) + tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" + expectedToken := cli.Token{ + ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), + AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", + TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", + RefreshToken: "4ec3874d-ee2e-4980-ba47-b5bac11ddb94", + Resource: "https://management.core.windows.net/", + Authority: tenantId, + } + tokens := []cli.Token{expectedToken} + token, err := findValidAccessTokenForTenant(tokens, tenantId) + + if err == nil { + t.Fatalf("Expected an error to be returned but got nil") + } + + if token != nil { + t.Fatalf("Expected Token to be nil but got: %+v", token) + } +} + +func TestAzureFindValidAccessTokenForTenant_ExpiringIn(t *testing.T) { + minutesToVerify := []int{1, 30, 60} + + for _, minute := range minutesToVerify { + expirationDate := time.Now().Add(time.Minute * time.Duration(minute)) + tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" + expectedToken := cli.Token{ + ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), + AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", + TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", + RefreshToken: "4ec3874d-ee2e-4980-ba47-b5bac11ddb94", + Resource: "https://management.core.windows.net/", + Authority: tenantId, + } + tokens := []cli.Token{expectedToken} + token, err := findValidAccessTokenForTenant(tokens, tenantId) + + if err != nil { + t.Fatalf("Expected no error to be returned for minute %d but got %+v", minute, err) + } + + if token == nil { + t.Fatalf("Expected Token to have a value for minute %d but it was nil", minute) + } + + if token.AccessToken.AccessToken != expectedToken.AccessToken { + t.Fatalf("Expected the Access Token to be %q for minute %d but got %q", expectedToken.AccessToken, minute, token.AccessToken.AccessToken) + } + + if token.ClientID != expectedToken.ClientID { + t.Fatalf("Expected the Client ID to be %q for minute %d but got %q", expectedToken.ClientID, minute, token.ClientID) + } + + if token.IsCloudShell != false { + t.Fatalf("Expected `IsCloudShell` to be false for minute %d but got true", minute) + } + } +} + +func TestAzureFindValidAccessTokenForTenant_InvalidManagementDomain(t *testing.T) { + expirationDate := time.Now().Add(1 * time.Hour) + tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" + expectedToken := cli.Token{ + ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), + AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", + TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", + Resource: "https://portal.azure.com/", + Authority: tenantId, + } + tokens := []cli.Token{expectedToken} + token, err := findValidAccessTokenForTenant(tokens, tenantId) + + if err == nil { + t.Fatalf("Expected an error but didn't get one") + } + + if token != nil { + t.Fatalf("Expected Token to be nil but got: %+v", token) + } +} + +func TestAzureFindValidAccessTokenForTenant_DifferentTenant(t *testing.T) { + expirationDate := time.Now().Add(1 * time.Hour) + expectedToken := cli.Token{ + ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), + AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", + TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", + Resource: "https://management.core.windows.net/", + Authority: "9b5095de-5496-4b5e-9bc6-ef2c017b9d35", + } + tokens := []cli.Token{expectedToken} + token, err := findValidAccessTokenForTenant(tokens, "c056adac-c6a6-4ddf-ab20-0f26d47f7eea") + + if err == nil { + t.Fatalf("Expected an error but didn't get one") + } + + if token != nil { + t.Fatalf("Expected Token to be nil but got: %+v", token) + } +} + +func TestAzureFindValidAccessTokenForTenant_ValidFromCloudShell(t *testing.T) { + expirationDate := time.Now().Add(1 * time.Hour) + tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" + expectedToken := cli.Token{ + ExpiresOn: expirationDate.Format(time.RFC3339), + AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", + TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", + Resource: "https://management.core.windows.net/", + Authority: tenantId, + } + tokens := []cli.Token{expectedToken} + token, err := findValidAccessTokenForTenant(tokens, tenantId) + + if err != nil { + t.Fatalf("Expected no error to be returned but got %+v", err) + } + + if token == nil { + t.Fatalf("Expected Token to have a value but it was nil") + } + + if token.AccessToken.AccessToken != expectedToken.AccessToken { + t.Fatalf("Expected the Access Token to be %q but got %q", expectedToken.AccessToken, token.AccessToken.AccessToken) + } + + if token.ClientID != expectedToken.ClientID { + t.Fatalf("Expected the Client ID to be %q but got %q", expectedToken.ClientID, token.ClientID) + } + + if token.IsCloudShell != true { + t.Fatalf("Expected `IsCloudShell` to be true but got false") + } +} + +func TestAzureFindValidAccessTokenForTenant_ValidFromAzureCLI(t *testing.T) { + expirationDate := time.Now().Add(1 * time.Hour) + tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" + expectedToken := cli.Token{ + ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), + AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", + TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", + RefreshToken: "4ec3874d-ee2e-4980-ba47-b5bac11ddb94", + Resource: "https://management.core.windows.net/", + Authority: tenantId, + } + tokens := []cli.Token{expectedToken} + token, err := findValidAccessTokenForTenant(tokens, tenantId) + + if err != nil { + t.Fatalf("Expected no error to be returned but got %+v", err) + } + + if token == nil { + t.Fatalf("Expected Token to have a value but it was nil") + } + + if token.AccessToken.AccessToken != expectedToken.AccessToken { + t.Fatalf("Expected the Access Token to be %q but got %q", expectedToken.AccessToken, token.AccessToken.AccessToken) + } + + if token.ClientID != expectedToken.ClientID { + t.Fatalf("Expected the Client ID to be %q but got %q", expectedToken.ClientID, token.ClientID) + } + + if token.IsCloudShell != false { + t.Fatalf("Expected `IsCloudShell` to be false but got true") + } +} + +func TestAzureFindValidAccessTokenForTenant_NoTokens(t *testing.T) { + tokens := make([]cli.Token, 0) + token, err := findValidAccessTokenForTenant(tokens, "abc123") + + if err == nil { + t.Fatalf("Expected an error but didn't get one") + } + + if token != nil { + t.Fatalf("Expected a null token to be returned but got: %+v", token) + } +} diff --git a/azurerm/helpers/authentication/azure_cli_profile.go b/azurerm/helpers/authentication/azure_cli_profile.go new file mode 100644 index 000000000000..8b563d7ab9cd --- /dev/null +++ b/azurerm/helpers/authentication/azure_cli_profile.go @@ -0,0 +1,33 @@ +package authentication + +import ( + "strings" + + "fmt" + + "github.com/Azure/go-autorest/autorest/azure/cli" +) + +type AzureCLIProfile struct { + cli.Profile +} + +func (a AzureCLIProfile) FindDefaultSubscriptionId() (string, error) { + for _, subscription := range a.Subscriptions { + if subscription.IsDefault { + return subscription.ID, nil + } + } + + return "", fmt.Errorf("No Subscription was Marked as Default in the Azure Profile.") +} + +func (a AzureCLIProfile) FindSubscription(subscriptionId string) (*cli.Subscription, error) { + for _, subscription := range a.Subscriptions { + if strings.EqualFold(subscription.ID, subscriptionId) { + return &subscription, nil + } + } + + return nil, fmt.Errorf("Subscription %q was not found in your Azure CLI credentials. Please verify it exists in `az account list`.", subscriptionId) +} diff --git a/azurerm/helpers/authentication/azure_cli_profile_test.go b/azurerm/helpers/authentication/azure_cli_profile_test.go new file mode 100644 index 000000000000..3b051ce366f3 --- /dev/null +++ b/azurerm/helpers/authentication/azure_cli_profile_test.go @@ -0,0 +1,192 @@ +package authentication + +import ( + "testing" + + "github.com/Azure/go-autorest/autorest/azure/cli" +) + +func TestAzureCLIProfileFindDefaultSubscription(t *testing.T) { + cases := []struct { + Description string + Subscriptions []cli.Subscription + ExpectedSubscriptionId string + ExpectError bool + }{ + { + Description: "Empty Subscriptions", + Subscriptions: []cli.Subscription{}, + ExpectError: true, + }, + { + Description: "Single Subscription", + Subscriptions: []cli.Subscription{ + { + ID: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + IsDefault: true, + }, + }, + ExpectError: false, + ExpectedSubscriptionId: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + }, + { + Description: "Multiple Subscriptions with First as the Default", + Subscriptions: []cli.Subscription{ + { + ID: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + IsDefault: true, + }, + { + ID: "f36508bb-53b9-4aad-a2ac-2df86acf0c31", + IsDefault: false, + }, + }, + ExpectError: false, + ExpectedSubscriptionId: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + }, + { + Description: "Multiple Subscriptions with Second as the Default", + Subscriptions: []cli.Subscription{ + { + ID: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + IsDefault: false, + }, + { + ID: "f36508bb-53b9-4aad-a2ac-2df86acf0c31", + IsDefault: true, + }, + }, + ExpectError: false, + ExpectedSubscriptionId: "f36508bb-53b9-4aad-a2ac-2df86acf0c31", + }, + { + Description: "Multiple Subscriptions with None as the Default", + Subscriptions: []cli.Subscription{ + { + ID: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + IsDefault: false, + }, + { + ID: "f36508bb-53b9-4aad-a2ac-2df86acf0c31", + IsDefault: false, + }, + }, + ExpectError: true, + }, + } + + for _, v := range cases { + profile := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: v.Subscriptions, + }, + } + actualSubscriptionId, err := profile.FindDefaultSubscriptionId() + + if v.ExpectError && err == nil { + t.Fatalf("Expected an error for %q: didn't get one", v.Description) + } + + if !v.ExpectError && err != nil { + t.Fatalf("Expected there to be no error for %q - but got: %v", v.Description, err) + } + + if actualSubscriptionId != v.ExpectedSubscriptionId { + t.Fatalf("Expected Subscription ID to be %q - got %q", v.ExpectedSubscriptionId, actualSubscriptionId) + } + } +} + +func TestAzureCLIProfileFindSubscription(t *testing.T) { + cases := []struct { + Description string + Subscriptions []cli.Subscription + SubscriptionIdToSearchFor string + ExpectError bool + }{ + { + Description: "Empty Subscriptions", + Subscriptions: []cli.Subscription{}, + SubscriptionIdToSearchFor: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + ExpectError: true, + }, + { + Description: "Single Subscription", + SubscriptionIdToSearchFor: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + Subscriptions: []cli.Subscription{ + { + ID: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + IsDefault: true, + }, + }, + ExpectError: false, + }, + { + Description: "Finding the default subscription", + SubscriptionIdToSearchFor: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + Subscriptions: []cli.Subscription{ + { + ID: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + IsDefault: true, + }, + { + ID: "f36508bb-53b9-4aad-a2ac-2df86acf0c31", + IsDefault: false, + }, + }, + ExpectError: false, + }, + { + Description: "Finding a non default Subscription", + SubscriptionIdToSearchFor: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + Subscriptions: []cli.Subscription{ + { + ID: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + IsDefault: false, + }, + { + ID: "f36508bb-53b9-4aad-a2ac-2df86acf0c31", + IsDefault: true, + }, + }, + ExpectError: false, + }, + { + Description: "Multiple Subscriptions with None as the Default", + SubscriptionIdToSearchFor: "224f4ca6-117f-4928-bc0f-3df018feba3e", + Subscriptions: []cli.Subscription{ + { + ID: "7f68fe06-9404-4db8-a5c7-29639dc4b299", + IsDefault: false, + }, + { + ID: "f36508bb-53b9-4aad-a2ac-2df86acf0c31", + IsDefault: false, + }, + }, + ExpectError: true, + }, + } + + for _, v := range cases { + profile := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: v.Subscriptions, + }, + } + + subscription, err := profile.FindSubscription(v.SubscriptionIdToSearchFor) + + if v.ExpectError && err == nil { + t.Fatalf("Expected an error for %q: didn't get one", v.Description) + } + + if !v.ExpectError && err != nil { + t.Fatalf("Expected there to be no error for %q - but got: %v", v.Description, err) + } + + if subscription != nil && subscription.ID != v.SubscriptionIdToSearchFor { + t.Fatalf("Expected to find Subscription ID %q - got %q", subscription.ID, v.SubscriptionIdToSearchFor) + } + } +} diff --git a/azurerm/helpers/authentication/config.go b/azurerm/helpers/authentication/config.go new file mode 100644 index 000000000000..eb276c2e29d2 --- /dev/null +++ b/azurerm/helpers/authentication/config.go @@ -0,0 +1,132 @@ +package authentication + +import ( + "fmt" + + "log" + + "github.com/Azure/go-autorest/autorest/adal" + "github.com/Azure/go-autorest/autorest/azure/cli" +) + +// Config is the configuration structure used to instantiate a +// new Azure management client. +type Config struct { + ManagementURL string + + // Core + ClientID string + SubscriptionID string + TenantID string + Environment string + SkipCredentialsValidation bool + SkipProviderRegistration bool + + // Service Principal Auth + ClientSecret string + + // Bearer Auth + AccessToken *adal.Token + IsCloudShell bool +} + +func (c *Config) LoadTokensFromAzureCLI() error { + profilePath, err := cli.ProfilePath() + if err != nil { + return fmt.Errorf("Error loading the Profile Path from the Azure CLI: %+v", err) + } + + profile, err := cli.LoadProfile(profilePath) + if err != nil { + return fmt.Errorf("Azure CLI Authorization Profile was not found. Please ensure the Azure CLI is installed and then log-in with `az login`.") + } + + cliProfile := AzureCLIProfile{ + Profile: profile, + } + + // find the Subscription ID if it's not specified + if c.SubscriptionID == "" { + // we want to expose a more friendly error to the user, but this is useful for debug purposes + err := c.populateSubscriptionFromCLIProfile(cliProfile) + if err != nil { + log.Printf("Error Populating the Subscription from the CLI Profile: %s", err) + } + } + + // find the Tenant ID and Environment for that subscription if they're not specified + if c.TenantID == "" || c.Environment == "" { + err := c.populateTenantAndEnvironmentFromCLIProfile(cliProfile) + if err != nil { + // we want to expose a more friendly error to the user, but this is useful for debug purposes + log.Printf("Error Populating the Tenant and Environment from the CLI Profile: %s", err) + } + } + + foundToken := false + if c.TenantID != "" { + // pull out the ClientID and the AccessToken from the Azure Access Token + tokensPath, err := cli.AccessTokensPath() + if err != nil { + return fmt.Errorf("Error loading the Tokens Path from the Azure CLI: %+v", err) + } + + tokens, err := cli.LoadTokens(tokensPath) + if err != nil { + return fmt.Errorf("Azure CLI Authorization Tokens were not found. Please ensure the Azure CLI is installed and then log-in with `az login`.") + } + + validToken, _ := findValidAccessTokenForTenant(tokens, c.TenantID) + if validToken != nil { + foundToken, err = c.populateFromAccessToken(validToken) + if err != nil { + return err + } + } + } + + if !foundToken { + return fmt.Errorf("No valid (unexpired) Azure CLI Auth Tokens found. Please run `az login`.") + } + + return nil +} + +func (c *Config) populateSubscriptionFromCLIProfile(cliProfile AzureCLIProfile) error { + subscriptionId, err := cliProfile.FindDefaultSubscriptionId() + if err != nil { + return err + } + + c.SubscriptionID = subscriptionId + return nil +} + +func (c *Config) populateTenantAndEnvironmentFromCLIProfile(cliProfile AzureCLIProfile) error { + subscription, err := cliProfile.FindSubscription(c.SubscriptionID) + if err != nil { + return err + } + + if c.TenantID == "" { + c.TenantID = subscription.TenantID + } + + if c.Environment == "" { + c.Environment = normalizeEnvironmentName(subscription.EnvironmentName) + } + + return nil +} + +func (c *Config) populateFromAccessToken(token *AccessToken) (bool, error) { + if token == nil { + return false, fmt.Errorf("No valid access token was found to populate from") + } + + c.ClientID = token.ClientID + c.AccessToken = token.AccessToken + c.IsCloudShell = token.IsCloudShell + + return true, nil +} diff --git a/azurerm/helpers/authentication/config_test.go b/azurerm/helpers/authentication/config_test.go new file mode 100644 index 000000000000..5e22fbf8a814 --- /dev/null +++ b/azurerm/helpers/authentication/config_test.go @@ -0,0 +1,285 @@ +package authentication + +import ( + "testing" + + "github.com/Azure/go-autorest/autorest/adal" + "github.com/Azure/go-autorest/autorest/azure/cli" +) + +func TestAzurePopulateSubscriptionFromCLIProfile_Missing(t *testing.T) { + config := Config{} + profiles := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: []cli.Subscription{}, + }, + } + + err := config.populateSubscriptionFromCLIProfile(profiles) + if err == nil { + t.Fatalf("Expected an error to be returned - but didn't get one") + } +} + +func TestAzurePopulateSubscriptionFromCLIProfile_NoDefault(t *testing.T) { + config := Config{} + profiles := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: []cli.Subscription{ + { + IsDefault: false, + ID: "abc123", + }, + }, + }, + } + + err := config.populateSubscriptionFromCLIProfile(profiles) + if err == nil { + t.Fatalf("Expected an error to be returned - but didn't get one") + } +} + +func TestAzurePopulateSubscriptionFromCLIProfile_Default(t *testing.T) { + subscriptionId := "abc123" + config := Config{} + profiles := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: []cli.Subscription{ + { + IsDefault: true, + ID: subscriptionId, + }, + }, + }, + } + + err := config.populateSubscriptionFromCLIProfile(profiles) + if err != nil { + t.Fatalf("Expected no error to be returned - but got: %+v", err) + } + + if config.SubscriptionID != subscriptionId { + t.Fatalf("Expected the Subscription ID to be %q but got %q", subscriptionId, config.SubscriptionID) + } +} + +func TestAzurePopulateTenantAndEnvironmentFromCLIProfile_Empty(t *testing.T) { + config := Config{} + profiles := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: []cli.Subscription{}, + }, + } + + err := config.populateTenantAndEnvironmentFromCLIProfile(profiles) + if err == nil { + t.Fatalf("Expected an error to be returned - but didn't get one") + } +} + +func TestAzurePopulateTenantAndEnvironmentFromCLIProfile_MissingSubscription(t *testing.T) { + config := Config{ + SubscriptionID: "bcd234", + } + profiles := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: []cli.Subscription{ + { + IsDefault: false, + ID: "abc123", + }, + }, + }, + } + + err := config.populateTenantAndEnvironmentFromCLIProfile(profiles) + if err == nil { + t.Fatalf("Expected an error to be returned - but didn't get one") + } +} + +func TestAzurePopulateTenantAndEnvironmentFromCLIProfile_PopulateEnvironment(t *testing.T) { + config := Config{ + SubscriptionID: "abc123", + TenantID: "bcd234", + } + profiles := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: []cli.Subscription{ + { + IsDefault: false, + ID: "abc123", + EnvironmentName: "public", + }, + }, + }, + } + + err := config.populateTenantAndEnvironmentFromCLIProfile(profiles) + if err != nil { + t.Fatalf("Expected no error to be returned - but got: %+v", err) + } + + if config.SubscriptionID != "abc123" { + t.Fatalf("Expected Subscription ID to be 'abc123' - got %q", config.SubscriptionID) + } + + if config.TenantID != "bcd234" { + t.Fatalf("Expected Tenant ID to be 'bcd234' - got %q", config.TenantID) + } + + if config.Environment != "public" { + t.Fatalf("Expected Tenant ID to be 'public' - got %q", config.Environment) + } +} + +func TestAzurePopulateTenantAndEnvironmentFromCLIProfile_NormaliseAndPopulateEnvironment(t *testing.T) { + config := Config{ + SubscriptionID: "abc123", + TenantID: "bcd234", + } + profiles := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: []cli.Subscription{ + { + IsDefault: false, + ID: "abc123", + }, + }, + }, + } + + err := config.populateTenantAndEnvironmentFromCLIProfile(profiles) + if err != nil { + t.Fatalf("Expected no error to be returned - but got: %+v", err) + } + + if config.SubscriptionID != "abc123" { + t.Fatalf("Expected Subscription ID to be 'abc123' - got %q", config.SubscriptionID) + } + + if config.TenantID != "bcd234" { + t.Fatalf("Expected Tenant ID to be 'bcd234' - got %q", config.TenantID) + } + + if config.Environment != "public" { + t.Fatalf("Expected Tenant ID to be 'public' - got %q", config.Environment) + } +} + +func TestAzurePopulateTenantAndEnvironmentFromCLIProfile_PopulateTenantId(t *testing.T) { + config := Config{ + SubscriptionID: "abc123", + Environment: "public", + } + profiles := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: []cli.Subscription{ + { + IsDefault: false, + ID: "abc123", + TenantID: "bcd234", + }, + }, + }, + } + + err := config.populateTenantAndEnvironmentFromCLIProfile(profiles) + if err != nil { + t.Fatalf("Expected no error to be returned - but got: %+v", err) + } + + if config.SubscriptionID != "abc123" { + t.Fatalf("Expected Subscription ID to be 'abc123' - got %q", config.SubscriptionID) + } + + if config.TenantID != "bcd234" { + t.Fatalf("Expected Tenant ID to be 'bcd234' - got %q", config.TenantID) + } + + if config.Environment != "public" { + t.Fatalf("Expected Tenant ID to be 'public' - got %q", config.Environment) + } +} + +func TestAzurePopulateTenantAndEnvironmentFromCLIProfile_Complete(t *testing.T) { + config := Config{ + SubscriptionID: "abc123", + TenantID: "bcd234", + Environment: "public", + } + profiles := AzureCLIProfile{ + Profile: cli.Profile{ + Subscriptions: []cli.Subscription{ + { + IsDefault: false, + ID: "abc123", + }, + }, + }, + } + + err := config.populateTenantAndEnvironmentFromCLIProfile(profiles) + if err != nil { + t.Fatalf("Expected no error to be returned - but got: %+v", err) + } + + if config.SubscriptionID != "abc123" { + t.Fatalf("Expected Subscription ID to be 'abc123' - got %q", config.SubscriptionID) + } + + if config.TenantID != "bcd234" { + t.Fatalf("Expected Tenant ID to be 'bcd234' - got %q", config.TenantID) + } + + if config.Environment != "public" { + t.Fatalf("Expected Tenant ID to be 'public' - got %q", config.Environment) + } +} + +func TestAzurePopulateFromAccessToken_Missing(t *testing.T) { + config := Config{} + + successful, err := config.populateFromAccessToken(nil) + if err == nil { + t.Fatalf("Expected an error but didn't get one") + } + + if successful { + t.Fatalf("Expected the population of a null token to be false, got true") + } +} + +func TestAzurePopulateFromAccessToken_Exists(t *testing.T) { + config := Config{} + + token := AccessToken{ + AccessToken: &adal.Token{ + AccessToken: "abc123", + }, + ClientID: "bcd234", + IsCloudShell: true, + } + + successful, err := config.populateFromAccessToken(&token) + if err != nil { + t.Fatalf("Expected no error but got: %+v", err) + } + + if !successful { + t.Fatalf("Expected the population of an existing token to be successful, it wasn't") + } + + if config.IsCloudShell != token.IsCloudShell { + t.Fatalf("Expected `IsCloudShell` to be %t, got %t", token.IsCloudShell, config.IsCloudShell) + } + + if config.ClientID != token.ClientID { + t.Fatalf("Expected `ClientID` to be %q, got %q", token.ClientID, config.ClientID) + } + + if config.AccessToken != token.AccessToken { + t.Fatalf("Expected `AccessToken` to be %+v, got %+v", token.AccessToken, config.AccessToken) + } +} diff --git a/azurerm/helpers/authentication/environment.go b/azurerm/helpers/authentication/environment.go new file mode 100644 index 000000000000..b18da1f22568 --- /dev/null +++ b/azurerm/helpers/authentication/environment.go @@ -0,0 +1,16 @@ +package authentication + +import "strings" + +func normalizeEnvironmentName(input string) string { + // Environment is stored as `Azure{Environment}Cloud` + output := strings.ToLower(input) + output = strings.TrimPrefix(output, "azure") + output = strings.TrimSuffix(output, "cloud") + + // however Azure Public is `AzureCloud` in the CLI Profile and not `AzurePublicCloud`. + if output == "" { + return "public" + } + return output +} diff --git a/azurerm/helpers/authentication/environment_test.go b/azurerm/helpers/authentication/environment_test.go new file mode 100644 index 000000000000..efe57b7110ca --- /dev/null +++ b/azurerm/helpers/authentication/environment_test.go @@ -0,0 +1,23 @@ +package authentication + +import ( + "testing" +) + +func TestAzureEnvironmentNames(t *testing.T) { + testData := map[string]string{ + "": "public", + "AzureChinaCloud": "china", + "AzureCloud": "public", + "AzureGermanCloud": "german", + "AZUREUSGOVERNMENTCLOUD": "usgovernment", + "AzurePublicCloud": "public", + } + + for input, expected := range testData { + actual := normalizeEnvironmentName(input) + if actual != expected { + t.Fatalf("Expected %q for input %q: got %q!", expected, input, actual) + } + } +} diff --git a/azurerm/helpers/authentication/validation.go b/azurerm/helpers/authentication/validation.go new file mode 100644 index 000000000000..8c777145a2cd --- /dev/null +++ b/azurerm/helpers/authentication/validation.go @@ -0,0 +1,51 @@ +package authentication + +import ( + "fmt" + + "github.com/hashicorp/go-multierror" +) + +func (c *Config) ValidateBearerAuth() error { + var err *multierror.Error + + if c.AccessToken == nil { + err = multierror.Append(err, fmt.Errorf("Access Token was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`")) + } + + if c.ClientID == "" { + err = multierror.Append(err, fmt.Errorf("Client ID was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`")) + } + + if c.SubscriptionID == "" { + err = multierror.Append(err, fmt.Errorf("Subscription ID was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`")) + } + + if c.TenantID == "" { + err = multierror.Append(err, fmt.Errorf("Tenant ID was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`")) + } + + return err.ErrorOrNil() +} + +func (c *Config) ValidateServicePrincipal() 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() +} diff --git a/azurerm/helpers/authentication/validation_test.go b/azurerm/helpers/authentication/validation_test.go new file mode 100644 index 000000000000..99cab000e609 --- /dev/null +++ b/azurerm/helpers/authentication/validation_test.go @@ -0,0 +1,166 @@ +package authentication + +import ( + "testing" + + "github.com/Azure/go-autorest/autorest/adal" +) + +func TestAzureValidateBearerAuth(t *testing.T) { + cases := []struct { + Description string + Config Config + ExpectError bool + }{ + { + Description: "Empty Configuration", + Config: Config{}, + ExpectError: true, + }, + { + Description: "Missing Access Token", + Config: Config{ + ClientID: "62e73395-5017-43b6-8ebf-d6c30a514cf1", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + }, + ExpectError: true, + }, + { + Description: "Missing Client ID", + Config: Config{ + AccessToken: &adal.Token{}, + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + }, + ExpectError: true, + }, + { + Description: "Missing Subscription ID", + Config: Config{ + AccessToken: &adal.Token{}, + ClientID: "62e73395-5017-43b6-8ebf-d6c30a514cf1", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + }, + ExpectError: true, + }, + { + Description: "Missing Tenant ID", + Config: Config{ + AccessToken: &adal.Token{}, + ClientID: "62e73395-5017-43b6-8ebf-d6c30a514cf1", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + }, + ExpectError: true, + }, + { + Description: "Valid Configuration", + Config: Config{ + AccessToken: &adal.Token{}, + ClientID: "62e73395-5017-43b6-8ebf-d6c30a514cf1", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + }, + ExpectError: false, + }, + } + + for _, v := range cases { + err := v.Config.ValidateBearerAuth() + + if v.ExpectError && err == nil { + t.Fatalf("Expected an error for %q: didn't get one", v.Description) + } + + if !v.ExpectError && err != nil { + t.Fatalf("Expected there to be no error for %q - but got: %v", v.Description, err) + } + } +} + +func TestAzureValidateServicePrincipal(t *testing.T) { + cases := []struct { + Description string + Config Config + ExpectError bool + }{ + { + Description: "Empty Configuration", + Config: Config{}, + ExpectError: true, + }, + { + Description: "Missing Client ID", + Config: Config{ + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + ClientSecret: "Does Hammer Time have Daylight Savings Time?", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + Environment: "public", + }, + ExpectError: true, + }, + { + Description: "Missing Subscription ID", + Config: Config{ + ClientID: "62e73395-5017-43b6-8ebf-d6c30a514cf1", + ClientSecret: "Does Hammer Time have Daylight Savings Time?", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + Environment: "public", + }, + ExpectError: true, + }, + { + Description: "Missing Client Secret", + Config: Config{ + ClientID: "62e73395-5017-43b6-8ebf-d6c30a514cf1", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + Environment: "public", + }, + ExpectError: true, + }, + { + Description: "Missing Tenant ID", + Config: Config{ + ClientID: "62e73395-5017-43b6-8ebf-d6c30a514cf1", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + ClientSecret: "Does Hammer Time have Daylight Savings Time?", + Environment: "public", + }, + ExpectError: true, + }, + { + Description: "Missing Environment", + Config: Config{ + ClientID: "62e73395-5017-43b6-8ebf-d6c30a514cf1", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + ClientSecret: "Does Hammer Time have Daylight Savings Time?", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + }, + ExpectError: true, + }, + { + Description: "Valid Configuration", + Config: Config{ + ClientID: "62e73395-5017-43b6-8ebf-d6c30a514cf1", + SubscriptionID: "8e8b5e02-5c13-4822-b7dc-4232afb7e8c2", + ClientSecret: "Does Hammer Time have Daylight Savings Time?", + TenantID: "9834f8d0-24b3-41b7-8b8d-c611c461a129", + Environment: "public", + }, + ExpectError: false, + }, + } + + for _, v := range cases { + err := v.Config.ValidateServicePrincipal() + + if v.ExpectError && err == nil { + t.Fatalf("Expected an error for %q: didn't get one", v.Description) + } + + if !v.ExpectError && err != nil { + t.Fatalf("Expected there to be no error for %q - but got: %v", v.Description, err) + } + } +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 8c58bad0ae45..5f600e5ff4bd 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -8,15 +8,12 @@ import ( "log" "strings" "sync" - "time" "github.com/Azure/azure-sdk-for-go/arm/resources/resources" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure/cli" - "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform/helper/mutexkv" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/authentication" ) // Provider returns a terraform.ResourceProvider. @@ -181,164 +178,9 @@ func Provider() terraform.ResourceProvider { return p } -// Config is the configuration structure used to instantiate a -// new Azure management client. -type Config struct { - ManagementURL string - - // Core - ClientID string - SubscriptionID string - TenantID string - Environment string - SkipCredentialsValidation bool - SkipProviderRegistration bool - - // Service Principal Auth - ClientSecret string - - // Bearer Auth - AccessToken *adal.Token - IsCloudShell bool -} - -func (c *Config) validateServicePrincipal() 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 (c *Config) validateBearerAuth() error { - var err *multierror.Error - - if c.AccessToken == nil { - err = multierror.Append(err, fmt.Errorf("Access Token was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`")) - } - - if c.ClientID == "" { - err = multierror.Append(err, fmt.Errorf("Client ID was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`")) - } - - if c.SubscriptionID == "" { - err = multierror.Append(err, fmt.Errorf("Subscription ID was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`")) - } - - if c.TenantID == "" { - err = multierror.Append(err, fmt.Errorf("Tenant ID was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`")) - } - - return err.ErrorOrNil() -} - -func (c *Config) LoadTokensFromAzureCLI() error { - profilePath, err := cli.ProfilePath() - if err != nil { - return fmt.Errorf("Error loading the Profile Path from the Azure CLI: %+v", err) - } - - profile, err := cli.LoadProfile(profilePath) - if err != nil { - return fmt.Errorf("Azure CLI Authorization Profile was not found. Please ensure the Azure CLI is installed and then log-in with `az login`.") - } - - // pull out the TenantID and Subscription ID from the Azure Profile, if not set - if c.SubscriptionID == "" && c.TenantID == "" { - for _, subscription := range profile.Subscriptions { - if subscription.IsDefault { - c.SubscriptionID = subscription.ID - c.TenantID = subscription.TenantID - c.Environment = normalizeEnvironmentName(subscription.EnvironmentName) - break - } - } - } - - foundToken := false - if c.TenantID != "" { - // pull out the ClientID and the AccessToken from the Azure Access Token - tokensPath, err := cli.AccessTokensPath() - if err != nil { - return fmt.Errorf("Error loading the Tokens Path from the Azure CLI: %+v", err) - } - - tokens, err := cli.LoadTokens(tokensPath) - if err != nil { - return fmt.Errorf("Azure CLI Authorization Tokens were not found. Please ensure the Azure CLI is installed and then log-in with `az login`.") - } - - for _, accessToken := range tokens { - token, err := accessToken.ToADALToken() - if err != nil { - return fmt.Errorf("[DEBUG] Error converting access token to token: %+v", err) - } - - expirationDate, err := cli.ParseExpirationDate(accessToken.ExpiresOn) - if err != nil { - return fmt.Errorf("Error parsing expiration date: %q", accessToken.ExpiresOn) - } - - if expirationDate.UTC().Before(time.Now().UTC()) { - log.Printf("[DEBUG] Token '%s' has expired", token.AccessToken) - continue - } - - if !strings.Contains(accessToken.Resource, "management") { - log.Printf("[DEBUG] Resource '%s' isn't a management domain", accessToken.Resource) - continue - } - - if !strings.HasSuffix(accessToken.Authority, c.TenantID) { - log.Printf("[DEBUG] Resource '%s' isn't for the correct Tenant", accessToken.Resource) - continue - } - - c.ClientID = accessToken.ClientID - c.AccessToken = &token - c.IsCloudShell = accessToken.RefreshToken == "" - foundToken = true - break - } - } - - if !foundToken { - return fmt.Errorf("No valid (unexpired) Azure CLI Auth Tokens found. Please run `az login`.") - } - - return nil -} - -func normalizeEnvironmentName(input string) string { - // Environment is stored as `Azure{Environment}Cloud` - output := strings.ToLower(input) - output = strings.TrimPrefix(output, "azure") - output = strings.TrimSuffix(output, "cloud") - - // however Azure Public is `AzureCloud` in the CLI Profile and not `AzurePublicCloud`. - if output == "" { - return "public" - } - return output -} - func providerConfigure(p *schema.Provider) schema.ConfigureFunc { return func(d *schema.ResourceData) (interface{}, error) { - config := &Config{ + config := &authentication.Config{ SubscriptionID: d.Get("subscription_id").(string), ClientID: d.Get("client_id").(string), ClientSecret: d.Get("client_secret").(string), @@ -350,7 +192,7 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc { if config.ClientSecret != "" { log.Printf("[DEBUG] Client Secret specified - using Service Principal for Authentication") - if err := config.validateServicePrincipal(); err != nil { + if err := config.ValidateServicePrincipal(); err != nil { return nil, err } } else { @@ -359,12 +201,12 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc { return nil, err } - if err := config.validateBearerAuth(); err != nil { + if err := config.ValidateBearerAuth(); err != nil { return nil, fmt.Errorf("Please specify either a Service Principal, or log in with the Azure CLI (using `az login`)") } } - client, err := config.getArmClient() + client, err := getArmClient(config) if err != nil { return nil, err } diff --git a/azurerm/provider_test.go b/azurerm/provider_test.go index 676bcb9f0973..f0a1b235ba7f 100644 --- a/azurerm/provider_test.go +++ b/azurerm/provider_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/authentication" ) var testAccProviders map[string]terraform.ResourceProvider @@ -84,7 +85,7 @@ func testArmEnvironment() (*azure.Environment, error) { return &env, nil } -func testGetAzureConfig(t *testing.T) *Config { +func testGetAzureConfig(t *testing.T) *authentication.Config { if os.Getenv(resource.TestEnvVar) == "" { t.Skip(fmt.Sprintf("Integration test skipped unless env '%s' set", resource.TestEnvVar)) return nil @@ -93,7 +94,7 @@ func testGetAzureConfig(t *testing.T) *Config { environment := testArmEnvironmentName() // we deliberately don't use the main config - since we care about - config := Config{ + config := authentication.Config{ SubscriptionID: os.Getenv("ARM_SUBSCRIPTION_ID"), ClientID: os.Getenv("ARM_CLIENT_ID"), TenantID: os.Getenv("ARM_TENANT_ID"), @@ -110,7 +111,7 @@ func TestAccAzureRMResourceProviderRegistration(t *testing.T) { return } - armClient, err := config.getArmClient() + armClient, err := getArmClient(config) if err != nil { t.Fatalf("Error building ARM Client: %+v", err) } diff --git a/azurerm/resource_arm_container_registry_migrate_test.go b/azurerm/resource_arm_container_registry_migrate_test.go index 94d58be2dbf7..f7828e7e4bf6 100644 --- a/azurerm/resource_arm_container_registry_migrate_test.go +++ b/azurerm/resource_arm_container_registry_migrate_test.go @@ -17,7 +17,7 @@ func TestAccAzureRMContainerRegistryMigrateState(t *testing.T) { t.SkipNow() return } - client, err := config.getArmClient() + client, err := getArmClient(config) if err != nil { t.Fatal(fmt.Errorf("Error building ARM Client: %+v", err)) return