From cbed463b44d910f3bf433075e6bd443fcc8861a2 Mon Sep 17 00:00:00 2001 From: Peter McAtominey Date: Thu, 2 Mar 2017 04:58:24 +0000 Subject: [PATCH] state/azure: add environment option for non-public cloud usage (#12364) --- state/remote/azure.go | 51 ++++++++++++++----- state/remote/azure_test.go | 18 +++++-- .../source/docs/backends/types/azure.html.md | 5 ++ 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/state/remote/azure.go b/state/remote/azure.go index dee7a559ce..759356e367 100644 --- a/state/remote/azure.go +++ b/state/remote/azure.go @@ -26,6 +26,11 @@ func azureFactory(conf map[string]string) (Client, error) { return nil, fmt.Errorf("missing 'key' configuration") } + env, err := getAzureEnvironmentFromConf(conf) + if err != nil { + return nil, err + } + accessKey, ok := confOrEnv(conf, "access_key", "ARM_ACCESS_KEY") if !ok { resourceGroupName, ok := conf["resource_group_name"] @@ -34,13 +39,14 @@ func azureFactory(conf map[string]string) (Client, error) { } var err error - accessKey, err = getStorageAccountAccessKey(conf, resourceGroupName, storageAccountName) + accessKey, err = getStorageAccountAccessKey(conf, resourceGroupName, storageAccountName, env) if err != nil { return nil, fmt.Errorf("Couldn't read access key from storage account: %s.", err) } } - storageClient, err := mainStorage.NewBasicClient(storageAccountName, accessKey) + storageClient, err := mainStorage.NewClient(storageAccountName, accessKey, env.StorageEndpointSuffix, + mainStorage.DefaultAPIVersion, true) if err != nil { return nil, fmt.Errorf("Error creating storage client for storage account %q: %s", storageAccountName, err) } @@ -56,13 +62,13 @@ func azureFactory(conf map[string]string) (Client, error) { }, nil } -func getStorageAccountAccessKey(conf map[string]string, resourceGroupName, storageAccountName string) (string, error) { - creds, err := getCredentialsFromConf(conf) +func getStorageAccountAccessKey(conf map[string]string, resourceGroupName, storageAccountName string, env azure.Environment) (string, error) { + creds, err := getCredentialsFromConf(conf, env) if err != nil { return "", err } - oauthConfig, err := azure.PublicCloud.OAuthConfigForTenant(creds.TenantID) + oauthConfig, err := env.OAuthConfigForTenant(creds.TenantID) if err != nil { return "", err } @@ -70,12 +76,12 @@ func getStorageAccountAccessKey(conf map[string]string, resourceGroupName, stora return "", fmt.Errorf("Unable to configure OAuthConfig for tenant %s", creds.TenantID) } - spt, err := azure.NewServicePrincipalToken(*oauthConfig, creds.ClientID, creds.ClientSecret, azure.PublicCloud.ResourceManagerEndpoint) + spt, err := azure.NewServicePrincipalToken(*oauthConfig, creds.ClientID, creds.ClientSecret, env.ResourceManagerEndpoint) if err != nil { return "", err } - accountsClient := storage.NewAccountsClient(creds.SubscriptionID) + accountsClient := storage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, creds.SubscriptionID) accountsClient.Authorizer = spt keys, err := accountsClient.ListKeys(resourceGroupName, storageAccountName) @@ -91,7 +97,7 @@ func getStorageAccountAccessKey(conf map[string]string, resourceGroupName, stora return *accessKeys[0].Value, nil } -func getCredentialsFromConf(conf map[string]string) (*riviera.AzureResourceManagerCredentials, error) { +func getCredentialsFromConf(conf map[string]string, env azure.Environment) (*riviera.AzureResourceManagerCredentials, error) { subscriptionID, ok := confOrEnv(conf, "arm_subscription_id", "ARM_SUBSCRIPTION_ID") if !ok { return nil, fmt.Errorf("missing 'arm_subscription_id' configuration") @@ -110,13 +116,34 @@ func getCredentialsFromConf(conf map[string]string) (*riviera.AzureResourceManag } return &riviera.AzureResourceManagerCredentials{ - SubscriptionID: subscriptionID, - ClientID: clientID, - ClientSecret: clientSecret, - TenantID: tenantID, + SubscriptionID: subscriptionID, + ClientID: clientID, + ClientSecret: clientSecret, + TenantID: tenantID, + ActiveDirectoryEndpoint: env.ActiveDirectoryEndpoint, + ResourceManagerEndpoint: env.ResourceManagerEndpoint, }, nil } +func getAzureEnvironmentFromConf(conf map[string]string) (azure.Environment, error) { + envName, ok := confOrEnv(conf, "environment", "ARM_ENVIRONMENT") + if !ok { + return azure.PublicCloud, nil + } + + env, err := azure.EnvironmentFromName(envName) + if err != nil { + // try again with wrapped value to support readable values like german instead of AZUREGERMANCLOUD + var innerErr error + env, innerErr = azure.EnvironmentFromName(fmt.Sprintf("AZURE%sCLOUD", envName)) + if innerErr != nil { + return env, fmt.Errorf("invalid 'environment' configuration: %s", err) + } + } + + return env, nil +} + func confOrEnv(conf map[string]string, confKey, envVar string) (string, bool) { value, ok := conf[confKey] if ok { diff --git a/state/remote/azure_test.go b/state/remote/azure_test.go index 3e37f58764..4e26f3d75f 100644 --- a/state/remote/azure_test.go +++ b/state/remote/azure_test.go @@ -88,6 +88,7 @@ func getAzureConfig(t *testing.T) map[string]string { "arm_client_id": os.Getenv("ARM_CLIENT_ID"), "arm_client_secret": os.Getenv("ARM_CLIENT_SECRET"), "arm_tenant_id": os.Getenv("ARM_TENANT_ID"), + "environment": os.Getenv("ARM_ENVIRONMENT"), } for k, v := range config { @@ -107,7 +108,11 @@ func getAzureConfig(t *testing.T) map[string]string { } func setup(t *testing.T, conf map[string]string) { - creds, err := getCredentialsFromConf(conf) + env, err := getAzureEnvironmentFromConf(conf) + if err != nil { + t.Fatalf("Error getting Azure environment from conf: %v", err) + } + creds, err := getCredentialsFromConf(conf, env) if err != nil { t.Fatalf("Error getting credentials from conf: %v", err) } @@ -147,11 +152,12 @@ func setup(t *testing.T, conf map[string]string) { } // Create container - accessKey, err := getStorageAccountAccessKey(conf, conf["resource_group_name"], conf["storage_account_name"]) + accessKey, err := getStorageAccountAccessKey(conf, conf["resource_group_name"], conf["storage_account_name"], env) if err != nil { t.Fatalf("Error creating a storage account: %v", err) } - storageClient, err := mainStorage.NewBasicClient(conf["storage_account_name"], accessKey) + storageClient, err := mainStorage.NewClient(conf["storage_account_name"], accessKey, env.StorageEndpointSuffix, + mainStorage.DefaultAPIVersion, true) if err != nil { t.Fatalf("Error creating storage client for storage account %q: %s", conf["storage_account_name"], err) } @@ -163,7 +169,11 @@ func setup(t *testing.T, conf map[string]string) { } func teardown(t *testing.T, conf map[string]string) { - creds, err := getCredentialsFromConf(conf) + env, err := getAzureEnvironmentFromConf(conf) + if err != nil { + t.Fatalf("Error getting Azure environment from conf: %v", err) + } + creds, err := getCredentialsFromConf(conf, env) if err != nil { t.Fatalf("Error getting credentials from conf: %v", err) } diff --git a/website/source/docs/backends/types/azure.html.md b/website/source/docs/backends/types/azure.html.md index be26e5053c..07b650cdcc 100644 --- a/website/source/docs/backends/types/azure.html.md +++ b/website/source/docs/backends/types/azure.html.md @@ -50,3 +50,8 @@ The following configuration options are supported: * `key` - (Required) The key where to place/look for state file inside the container * `access_key` / `ARM_ACCESS_KEY` - (Required) Storage account access key * `lease_id` / `ARM_LEASE_ID` - (Optional) If set, will be used when writing to storage blob. + * `environment` / `ARM_ENVIRONMENT` - (Optional) The cloud environment to use. Supported values are: + * `public` (default) + * `usgovernment` + * `german` + * `china`