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,
@ -59,6 +60,7 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) {
ClientSecret: config.ClientSecret, ClientSecret: config.ClientSecret,
SubscriptionID: config.SubscriptionID, SubscriptionID: config.SubscriptionID,
TenantID: config.TenantID, TenantID: config.TenantID,
CustomResourceManagerEndpoint: config.CustomResourceManagerEndpoint,
Environment: config.Environment, Environment: config.Environment,
MsiEndpoint: config.MsiEndpoint, MsiEndpoint: config.MsiEndpoint,
@ -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
}, },
} }
@ -154,6 +159,7 @@ type BackendConfig struct {
AccessKey string AccessKey string
ClientID string ClientID string
ClientSecret string ClientSecret string
CustomResourceManagerEndpoint string
Environment string Environment string
MsiEndpoint string MsiEndpoint string
ResourceGroupName string ResourceGroupName string
@ -183,6 +189,7 @@ func (b *Backend) configure(ctx context.Context) error {
AccessKey: data.Get("access_key").(string), AccessKey: data.Get("access_key").(string),
ClientID: clientId, ClientID: clientId,
ClientSecret: clientSecret, ClientSecret: clientSecret,
CustomResourceManagerEndpoint: data.Get("endpoint").(string),
Environment: data.Get("environment").(string), Environment: data.Get("environment").(string),
MsiEndpoint: data.Get("msi_endpoint").(string), MsiEndpoint: data.Get("msi_endpoint").(string),
ResourceGroupName: data.Get("resource_group_name").(string), ResourceGroupName: data.Get("resource_group_name").(string),

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,15 +63,21 @@ 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,
CustomResourceManagerEndpoint: endpoint,
Environment: environment, Environment: environment,
ResourceGroupName: res.resourceGroup, ResourceGroupName: res.resourceGroup,
StorageAccountName: res.storageAccountName, StorageAccountName: res.storageAccountName,
@ -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: