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) {
env, err := authentication.DetermineEnvironment(config.Environment)
env, err := buildArmEnvironment(config)
if err != nil {
return nil, err
}
client := ArmClient{
environment: *env,
resourceGroupName: config.ResourceGroupName,
@ -55,12 +56,13 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) {
}
builder := authentication.Builder{
ClientID: config.ClientID,
ClientSecret: config.ClientSecret,
SubscriptionID: config.SubscriptionID,
TenantID: config.TenantID,
Environment: config.Environment,
MsiEndpoint: config.MsiEndpoint,
ClientID: config.ClientID,
ClientSecret: config.ClientSecret,
SubscriptionID: config.SubscriptionID,
TenantID: config.TenantID,
CustomResourceManagerEndpoint: config.CustomResourceManagerEndpoint,
Environment: config.Environment,
MsiEndpoint: config.MsiEndpoint,
// Feature Toggles
SupportsClientSecretAuth: true,
@ -77,7 +79,7 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) {
return nil, err
}
auth, err := armConfig.GetAuthorizationToken(oauthConfig, env.ResourceManagerEndpoint)
auth, err := armConfig.GetAuthorizationToken(oauthConfig, env.TokenAudience)
if err != nil {
return nil, err
}
@ -93,9 +95,19 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) {
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) {
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)
if err != nil {
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 != "" {
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, "?")
uri, err := url.ParseQuery(token)
if err != nil {
@ -117,7 +129,7 @@ func (c ArmClient) getBlobClient(ctx context.Context) (*storage.BlobStorageClien
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)
if err != nil {
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", ""),
},
"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
"arm_client_id": {
Type: schema.TypeString,
@ -127,8 +134,6 @@ func New() backend.Backend {
Description: "The 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
// Optional
AccessKey string
ClientID string
ClientSecret string
Environment string
MsiEndpoint string
ResourceGroupName string
SasToken string
SubscriptionID string
TenantID string
UseMsi bool
AccessKey string
ClientID string
ClientSecret string
CustomResourceManagerEndpoint string
Environment string
MsiEndpoint string
ResourceGroupName string
SasToken string
SubscriptionID string
TenantID string
UseMsi bool
}
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")
config := BackendConfig{
AccessKey: data.Get("access_key").(string),
ClientID: clientId,
ClientSecret: clientSecret,
Environment: data.Get("environment").(string),
MsiEndpoint: data.Get("msi_endpoint").(string),
ResourceGroupName: data.Get("resource_group_name").(string),
SasToken: data.Get("sas_token").(string),
StorageAccountName: data.Get("storage_account_name").(string),
SubscriptionID: subscriptionId,
TenantID: tenantId,
UseMsi: data.Get("use_msi").(bool),
AccessKey: data.Get("access_key").(string),
ClientID: clientId,
ClientSecret: clientSecret,
CustomResourceManagerEndpoint: data.Get("endpoint").(string),
Environment: data.Get("environment").(string),
MsiEndpoint: data.Get("msi_endpoint").(string),
ResourceGroupName: data.Get("resource_group_name").(string),
SasToken: data.Get("sas_token").(string),
StorageAccountName: data.Get("storage_account_name").(string),
SubscriptionID: subscriptionId,
TenantID: tenantId,
UseMsi: data.Get("use_msi").(bool),
}
armClient, err := buildArmClient(config)

View File

@ -55,6 +55,7 @@ func TestBackendAccessKeyBasic(t *testing.T) {
"key": res.storageKeyName,
"access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
backend.TestBackendStates(t, b)
@ -82,6 +83,7 @@ func TestBackendManagedServiceIdentityBasic(t *testing.T) {
"subscription_id": os.Getenv("ARM_SUBSCRIPTION_ID"),
"tenant_id": os.Getenv("ARM_TENANT_ID"),
"environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
backend.TestBackendStates(t, b)
@ -111,6 +113,7 @@ func TestBackendSASTokenBasic(t *testing.T) {
"key": res.storageKeyName,
"sas_token": *sasToken,
"environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
backend.TestBackendStates(t, b)
@ -139,6 +142,43 @@ func TestBackendServicePrincipalBasic(t *testing.T) {
"client_id": os.Getenv("ARM_CLIENT_ID"),
"client_secret": os.Getenv("ARM_CLIENT_SECRET"),
"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.TestBackendStates(t, b)
@ -163,6 +203,7 @@ func TestBackendAccessKeyLocked(t *testing.T) {
"key": res.storageKeyName,
"access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
@ -171,6 +212,7 @@ func TestBackendAccessKeyLocked(t *testing.T) {
"key": res.storageKeyName,
"access_key": res.storageAccountAccessKey,
"environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
backend.TestBackendStateLocks(t, b1, b2)
@ -200,6 +242,7 @@ func TestBackendServicePrincipalLocked(t *testing.T) {
"client_id": os.Getenv("ARM_CLIENT_ID"),
"client_secret": os.Getenv("ARM_CLIENT_SECRET"),
"environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
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_secret": os.Getenv("ARM_CLIENT_SECRET"),
"environment": os.Getenv("ARM_ENVIRONMENT"),
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
backend.TestBackendStateLocks(t, b1, b2)

View File

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

View File

@ -16,7 +16,8 @@ import (
)
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
@ -45,9 +46,6 @@ func buildTestClient(t *testing.T, res resourceNames) *ArmClient {
msiEnabled := strings.EqualFold(os.Getenv("ARM_USE_MSI"), "true")
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
if !hasCredentials {
t.Fatal("Azure credentials missing or incomplete")
@ -65,19 +63,25 @@ func buildTestClient(t *testing.T, res resourceNames) *ArmClient {
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 == "" {
t.Fatalf("Missing ARM_LOCATION")
}
// Endpoint is optional (only for Stack)
endpoint := os.Getenv("ARM_ENDPOINT")
armClient, err := buildArmClient(BackendConfig{
SubscriptionID: subscriptionID,
TenantID: tenantID,
ClientID: clientID,
ClientSecret: clientSecret,
Environment: environment,
ResourceGroupName: res.resourceGroup,
StorageAccountName: res.storageAccountName,
UseMsi: msiEnabled,
SubscriptionID: subscriptionID,
TenantID: tenantID,
ClientID: clientID,
ClientSecret: clientSecret,
CustomResourceManagerEndpoint: endpoint,
Environment: environment,
ResourceGroupName: res.resourceGroup,
StorageAccountName: res.storageAccountName,
UseMsi: msiEnabled,
})
if err != nil {
t.Fatalf("Failed to build ArmClient: %+v", err)
@ -99,7 +103,9 @@ func buildSasToken(accountName, accessKey string) (*string, error) {
signedVersion := sasSignedVersion
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)
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`.
* `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: