backend/azurerm: support for custom resource manager endpoints (#19460)

* backend/azurerm: removing the `arm_` prefix from keys

* removing the deprecated fields test because the deprecation makes it fail

* authentication: support for custom resource manager endpoints

* Adding debug prefixes to the log statements
This commit is contained in:
Tom Harvey 2018-11-26 14:42:16 +01:00 committed by GitHub
parent 26509550e4
commit 6d4f702467
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 128 additions and 47 deletions

View File

@ -32,10 +32,11 @@ type ArmClient struct {
} }
func buildArmClient(config BackendConfig) (*ArmClient, error) { func buildArmClient(config BackendConfig) (*ArmClient, error) {
env, err := authentication.DetermineEnvironment(config.Environment) env, err := buildArmEnvironment(config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
client := ArmClient{ client := ArmClient{
environment: *env, environment: *env,
resourceGroupName: config.ResourceGroupName, resourceGroupName: config.ResourceGroupName,
@ -55,12 +56,13 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) {
} }
builder := authentication.Builder{ builder := authentication.Builder{
ClientID: config.ClientID, ClientID: config.ClientID,
ClientSecret: config.ClientSecret, ClientSecret: config.ClientSecret,
SubscriptionID: config.SubscriptionID, SubscriptionID: config.SubscriptionID,
TenantID: config.TenantID, TenantID: config.TenantID,
Environment: config.Environment, CustomResourceManagerEndpoint: config.CustomResourceManagerEndpoint,
MsiEndpoint: config.MsiEndpoint, Environment: config.Environment,
MsiEndpoint: config.MsiEndpoint,
// Feature Toggles // Feature Toggles
SupportsClientSecretAuth: true, SupportsClientSecretAuth: true,
@ -77,7 +79,7 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) {
return nil, err return nil, err
} }
auth, err := armConfig.GetAuthorizationToken(oauthConfig, env.ResourceManagerEndpoint) auth, err := armConfig.GetAuthorizationToken(oauthConfig, env.TokenAudience)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -93,9 +95,19 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) {
return &client, nil return &client, nil
} }
func buildArmEnvironment(config BackendConfig) (*azure.Environment, error) {
if config.CustomResourceManagerEndpoint != "" {
log.Printf("[DEBUG] Loading Environment from Endpoint %q", config.CustomResourceManagerEndpoint)
return authentication.LoadEnvironmentFromUrl(config.CustomResourceManagerEndpoint)
}
log.Printf("[DEBUG] Loading Environment %q", config.Environment)
return authentication.DetermineEnvironment(config.Environment)
}
func (c ArmClient) getBlobClient(ctx context.Context) (*storage.BlobStorageClient, error) { func (c ArmClient) getBlobClient(ctx context.Context) (*storage.BlobStorageClient, error) {
if c.accessKey != "" { if c.accessKey != "" {
log.Printf("Building the Blob Client from an Access Token") log.Printf("[DEBUG] Building the Blob Client from an Access Token")
storageClient, err := storage.NewBasicClientOnSovereignCloud(c.storageAccountName, c.accessKey, c.environment) storageClient, err := storage.NewBasicClientOnSovereignCloud(c.storageAccountName, c.accessKey, c.environment)
if err != nil { if err != nil {
return nil, fmt.Errorf("Error creating storage client for storage account %q: %s", c.storageAccountName, err) return nil, fmt.Errorf("Error creating storage client for storage account %q: %s", c.storageAccountName, err)
@ -105,7 +117,7 @@ func (c ArmClient) getBlobClient(ctx context.Context) (*storage.BlobStorageClien
} }
if c.sasToken != "" { if c.sasToken != "" {
log.Printf("Building the Blob Client from a SAS Token") log.Printf("[DEBUG] Building the Blob Client from a SAS Token")
token := strings.TrimPrefix(c.sasToken, "?") token := strings.TrimPrefix(c.sasToken, "?")
uri, err := url.ParseQuery(token) uri, err := url.ParseQuery(token)
if err != nil { if err != nil {
@ -117,7 +129,7 @@ func (c ArmClient) getBlobClient(ctx context.Context) (*storage.BlobStorageClien
return &client, nil return &client, nil
} }
log.Printf("Building the Blob Client from an Access Token (using user credentials)") log.Printf("[DEBUG] Building the Blob Client from an Access Token (using user credentials)")
keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName) keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName)
if err != nil { if err != nil {
return nil, fmt.Errorf("Error retrieving keys for Storage Account %q: %s", c.storageAccountName, err) return nil, fmt.Errorf("Error retrieving keys for Storage Account %q: %s", c.storageAccountName, err)

View File

@ -99,6 +99,13 @@ func New() backend.Backend {
DefaultFunc: schema.EnvDefaultFunc("ARM_MSI_ENDPOINT", ""), DefaultFunc: schema.EnvDefaultFunc("ARM_MSI_ENDPOINT", ""),
}, },
"endpoint": {
Type: schema.TypeString,
Optional: true,
Description: "A custom Endpoint used to access the Azure Resource Manager API's.",
DefaultFunc: schema.EnvDefaultFunc("ARM_ENDPOINT", ""),
},
// Deprecated fields // Deprecated fields
"arm_client_id": { "arm_client_id": {
Type: schema.TypeString, Type: schema.TypeString,
@ -127,8 +134,6 @@ func New() backend.Backend {
Description: "The Tenant ID.", Description: "The Tenant ID.",
Deprecated: "`arm_tenant_id` has been replaced by `tenant_id`", Deprecated: "`arm_tenant_id` has been replaced by `tenant_id`",
}, },
// TODO: support for custom resource manager endpoints
}, },
} }
@ -151,16 +156,17 @@ type BackendConfig struct {
StorageAccountName string StorageAccountName string
// Optional // Optional
AccessKey string AccessKey string
ClientID string ClientID string
ClientSecret string ClientSecret string
Environment string CustomResourceManagerEndpoint string
MsiEndpoint string Environment string
ResourceGroupName string MsiEndpoint string
SasToken string ResourceGroupName string
SubscriptionID string SasToken string
TenantID string SubscriptionID string
UseMsi bool TenantID string
UseMsi bool
} }
func (b *Backend) configure(ctx context.Context) error { func (b *Backend) configure(ctx context.Context) error {
@ -180,17 +186,18 @@ func (b *Backend) configure(ctx context.Context) error {
tenantId := valueFromDeprecatedField(data, "tenant_id", "arm_tenant_id") tenantId := valueFromDeprecatedField(data, "tenant_id", "arm_tenant_id")
config := BackendConfig{ config := BackendConfig{
AccessKey: data.Get("access_key").(string), AccessKey: data.Get("access_key").(string),
ClientID: clientId, ClientID: clientId,
ClientSecret: clientSecret, ClientSecret: clientSecret,
Environment: data.Get("environment").(string), CustomResourceManagerEndpoint: data.Get("endpoint").(string),
MsiEndpoint: data.Get("msi_endpoint").(string), Environment: data.Get("environment").(string),
ResourceGroupName: data.Get("resource_group_name").(string), MsiEndpoint: data.Get("msi_endpoint").(string),
SasToken: data.Get("sas_token").(string), ResourceGroupName: data.Get("resource_group_name").(string),
StorageAccountName: data.Get("storage_account_name").(string), SasToken: data.Get("sas_token").(string),
SubscriptionID: subscriptionId, StorageAccountName: data.Get("storage_account_name").(string),
TenantID: tenantId, SubscriptionID: subscriptionId,
UseMsi: data.Get("use_msi").(bool), TenantID: tenantId,
UseMsi: data.Get("use_msi").(bool),
} }
armClient, err := buildArmClient(config) armClient, err := buildArmClient(config)

View File

@ -55,6 +55,7 @@ func TestBackendAccessKeyBasic(t *testing.T) {
"key": res.storageKeyName, "key": res.storageKeyName,
"access_key": res.storageAccountAccessKey, "access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
backend.TestBackendStates(t, b) backend.TestBackendStates(t, b)
@ -82,6 +83,7 @@ func TestBackendManagedServiceIdentityBasic(t *testing.T) {
"subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"), "subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"),
"tenant_id": os.Getenv("ARM_TENANT_ID"), "tenant_id": os.Getenv("ARM_TENANT_ID"),
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
backend.TestBackendStates(t, b) backend.TestBackendStates(t, b)
@ -111,6 +113,7 @@ func TestBackendSASTokenBasic(t *testing.T) {
"key": res.storageKeyName, "key": res.storageKeyName,
"sas_token": *sasToken, "sas_token": *sasToken,
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
backend.TestBackendStates(t, b) backend.TestBackendStates(t, b)
@ -139,6 +142,43 @@ func TestBackendServicePrincipalBasic(t *testing.T) {
"client_id": os.Getenv("ARM_CLIENT_ID"), "client_id": os.Getenv("ARM_CLIENT_ID"),
"client_secret": os.Getenv("ARM_CLIENT_SECRET"), "client_secret": os.Getenv("ARM_CLIENT_SECRET"),
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
backend.TestBackendStates(t, b)
}
func TestBackendServicePrincipalCustomEndpoint(t *testing.T) {
testAccAzureBackend(t)
// this is only applicable for Azure Stack.
endpoint := os.Getenv("ARM_ENDPOINT")
if endpoint == "" {
t.Skip("Skipping as ARM_ENDPOINT isn't configured")
}
rs := acctest.RandString(4)
res := testResourceNames(rs, "testState")
armClient := buildTestClient(t, res)
ctx := context.TODO()
err := armClient.buildTestResources(ctx, &res)
defer armClient.destroyTestResources(ctx, res)
if err != nil {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
"resource_group_name": res.resourceGroup,
"subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"),
"tenant_id": os.Getenv("ARM_TENANT_ID"),
"client_id": os.Getenv("ARM_CLIENT_ID"),
"client_secret": os.Getenv("ARM_CLIENT_SECRET"),
"environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": endpoint,
})).(*Backend) })).(*Backend)
backend.TestBackendStates(t, b) backend.TestBackendStates(t, b)
@ -163,6 +203,7 @@ func TestBackendAccessKeyLocked(t *testing.T) {
"key": res.storageKeyName, "key": res.storageKeyName,
"access_key": res.storageAccountAccessKey, "access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
@ -171,6 +212,7 @@ func TestBackendAccessKeyLocked(t *testing.T) {
"key": res.storageKeyName, "key": res.storageKeyName,
"access_key": res.storageAccountAccessKey, "access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
backend.TestBackendStateLocks(t, b1, b2) backend.TestBackendStateLocks(t, b1, b2)
@ -200,6 +242,7 @@ func TestBackendServicePrincipalLocked(t *testing.T) {
"client_id": os.Getenv("ARM_CLIENT_ID"), "client_id": os.Getenv("ARM_CLIENT_ID"),
"client_secret": os.Getenv("ARM_CLIENT_SECRET"), "client_secret": os.Getenv("ARM_CLIENT_SECRET"),
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
@ -212,6 +255,7 @@ func TestBackendServicePrincipalLocked(t *testing.T) {
"client_id": os.Getenv("ARM_CLIENT_ID"), "client_id": os.Getenv("ARM_CLIENT_ID"),
"client_secret": os.Getenv("ARM_CLIENT_SECRET"), "client_secret": os.Getenv("ARM_CLIENT_SECRET"),
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
backend.TestBackendStateLocks(t, b1, b2) backend.TestBackendStateLocks(t, b1, b2)

View File

@ -35,6 +35,7 @@ func TestRemoteClientAccessKeyBasic(t *testing.T) {
"key": res.storageKeyName, "key": res.storageKeyName,
"access_key": res.storageAccountAccessKey, "access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
state, err := b.StateMgr(backend.DefaultStateName) state, err := b.StateMgr(backend.DefaultStateName)
@ -67,6 +68,7 @@ func TestRemoteClientManagedServiceIdentityBasic(t *testing.T) {
"subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"), "subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"),
"tenant_id": os.Getenv("ARM_TENANT_ID"), "tenant_id": os.Getenv("ARM_TENANT_ID"),
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
state, err := b.StateMgr(backend.DefaultStateName) state, err := b.StateMgr(backend.DefaultStateName)
@ -101,6 +103,7 @@ func TestRemoteClientSasTokenBasic(t *testing.T) {
"key": res.storageKeyName, "key": res.storageKeyName,
"sas_token": *sasToken, "sas_token": *sasToken,
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
state, err := b.StateMgr(backend.DefaultStateName) state, err := b.StateMgr(backend.DefaultStateName)
@ -134,6 +137,7 @@ func TestRemoteClientServicePrincipalBasic(t *testing.T) {
"client_id": os.Getenv("ARM_CLIENT_ID"), "client_id": os.Getenv("ARM_CLIENT_ID"),
"client_secret": os.Getenv("ARM_CLIENT_SECRET"), "client_secret": os.Getenv("ARM_CLIENT_SECRET"),
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
state, err := b.StateMgr(backend.DefaultStateName) state, err := b.StateMgr(backend.DefaultStateName)
@ -163,6 +167,7 @@ func TestRemoteClientAccessKeyLocks(t *testing.T) {
"key": res.storageKeyName, "key": res.storageKeyName,
"access_key": res.storageAccountAccessKey, "access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
@ -171,6 +176,7 @@ func TestRemoteClientAccessKeyLocks(t *testing.T) {
"key": res.storageKeyName, "key": res.storageKeyName,
"access_key": res.storageAccountAccessKey, "access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
s1, err := b1.StateMgr(backend.DefaultStateName) s1, err := b1.StateMgr(backend.DefaultStateName)
@ -209,6 +215,7 @@ func TestRemoteClientServicePrincipalLocks(t *testing.T) {
"client_id": os.Getenv("ARM_CLIENT_ID"), "client_id": os.Getenv("ARM_CLIENT_ID"),
"client_secret": os.Getenv("ARM_CLIENT_SECRET"), "client_secret": os.Getenv("ARM_CLIENT_SECRET"),
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
@ -221,6 +228,7 @@ func TestRemoteClientServicePrincipalLocks(t *testing.T) {
"client_id": os.Getenv("ARM_CLIENT_ID"), "client_id": os.Getenv("ARM_CLIENT_ID"),
"client_secret": os.Getenv("ARM_CLIENT_SECRET"), "client_secret": os.Getenv("ARM_CLIENT_SECRET"),
"environment": os.Getenv("ARM_ENVIRONMENT"), "environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend) })).(*Backend)
s1, err := b1.StateMgr(backend.DefaultStateName) s1, err := b1.StateMgr(backend.DefaultStateName)

View File

@ -16,7 +16,8 @@ import (
) )
const ( const (
sasSignedVersion = "2017-07-29" // required for Azure Stack
sasSignedVersion = "2015-04-05"
) )
// verify that we are doing ACC tests or the Azure tests specifically // verify that we are doing ACC tests or the Azure tests specifically
@ -45,9 +46,6 @@ func buildTestClient(t *testing.T, res resourceNames) *ArmClient {
msiEnabled := strings.EqualFold(os.Getenv("ARM_USE_MSI"), "true") msiEnabled := strings.EqualFold(os.Getenv("ARM_USE_MSI"), "true")
environment := os.Getenv("ARM_ENVIRONMENT") environment := os.Getenv("ARM_ENVIRONMENT")
// location isn't used in this method, but is in the other test methods
location := os.Getenv("ARM_LOCATION")
hasCredentials := (clientID != "" && clientSecret != "") || msiEnabled hasCredentials := (clientID != "" && clientSecret != "") || msiEnabled
if !hasCredentials { if !hasCredentials {
t.Fatal("Azure credentials missing or incomplete") t.Fatal("Azure credentials missing or incomplete")
@ -65,19 +63,25 @@ func buildTestClient(t *testing.T, res resourceNames) *ArmClient {
t.Fatalf("Missing ARM_ENVIRONMENT") t.Fatalf("Missing ARM_ENVIRONMENT")
} }
// location isn't used in this method, but is in the other test methods
location := os.Getenv("ARM_LOCATION")
if location == "" { if location == "" {
t.Fatalf("Missing ARM_LOCATION") t.Fatalf("Missing ARM_LOCATION")
} }
// Endpoint is optional (only for Stack)
endpoint := os.Getenv("ARM_ENDPOINT")
armClient, err := buildArmClient(BackendConfig{ armClient, err := buildArmClient(BackendConfig{
SubscriptionID: subscriptionID, SubscriptionID: subscriptionID,
TenantID: tenantID, TenantID: tenantID,
ClientID: clientID, ClientID: clientID,
ClientSecret: clientSecret, ClientSecret: clientSecret,
Environment: environment, CustomResourceManagerEndpoint: endpoint,
ResourceGroupName: res.resourceGroup, Environment: environment,
StorageAccountName: res.storageAccountName, ResourceGroupName: res.resourceGroup,
UseMsi: msiEnabled, StorageAccountName: res.storageAccountName,
UseMsi: msiEnabled,
}) })
if err != nil { if err != nil {
t.Fatalf("Failed to build ArmClient: %+v", err) t.Fatalf("Failed to build ArmClient: %+v", err)
@ -99,7 +103,9 @@ func buildSasToken(accountName, accessKey string) (*string, error) {
signedVersion := sasSignedVersion signedVersion := sasSignedVersion
utcNow := time.Now().UTC() utcNow := time.Now().UTC()
startDate := utcNow.Format(time.RFC3339)
// account for servers being up to 5 minutes out
startDate := utcNow.Add(time.Minute * -5).Format(time.RFC3339)
endDate := utcNow.Add(time.Hour * 24).Format(time.RFC3339) endDate := utcNow.Add(time.Hour * 24).Format(time.RFC3339)
sasToken, err := sasStorage.ComputeSASToken(accountName, accessKey, permissions, services, resourceTypes, sasToken, err := sasStorage.ComputeSASToken(accountName, accessKey, permissions, services, resourceTypes,

View File

@ -152,6 +152,10 @@ The following configuration options are supported:
* `environment` - (Optional) The Azure Environment which should be used. This can also be sourced from the `ARM_ENVIRONMENT` environment variable. Possible values are `public`, `china`, `german`, `stack` and `usgovernment`. Defaults to `public`. * `environment` - (Optional) The Azure Environment which should be used. This can also be sourced from the `ARM_ENVIRONMENT` environment variable. Possible values are `public`, `china`, `german`, `stack` and `usgovernment`. Defaults to `public`.
* `endpoint` - (Optional) The Custom Endpoint for Azure Resource Manager. This can also be sourced from the `ARM_ENDPOINT` environment variable.
~> **NOTE:** An `endpoint` should only be configured when using Azure Stack.
--- ---
When authenticating using the Managed Service Identity (MSI) - the following fields are also supported: When authenticating using the Managed Service Identity (MSI) - the following fields are also supported: