mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-26 17:01:04 -06:00
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:
parent
26509550e4
commit
6d4f702467
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user