mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-18 12:42:58 -06:00
4c254cc2be
This is part of a general effort to move all of Terraform's non-library package surface under internal in order to reinforce that these are for internal use within Terraform only. If you were previously importing packages under this prefix into an external codebase, you could pin to an earlier release tag as an interim solution until you've make a plan to achieve the same functionality some other way.
243 lines
8.0 KiB
Go
243 lines
8.0 KiB
Go
package azure
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs"
|
|
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers"
|
|
|
|
"github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources"
|
|
armStorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-01-01/storage"
|
|
"github.com/Azure/go-autorest/autorest"
|
|
"github.com/Azure/go-autorest/autorest/azure"
|
|
"github.com/hashicorp/go-azure-helpers/authentication"
|
|
"github.com/hashicorp/go-azure-helpers/sender"
|
|
"github.com/hashicorp/terraform/internal/httpclient"
|
|
)
|
|
|
|
type ArmClient struct {
|
|
// These Clients are only initialized if an Access Key isn't provided
|
|
groupsClient *resources.GroupsClient
|
|
storageAccountsClient *armStorage.AccountsClient
|
|
containersClient *containers.Client
|
|
blobsClient *blobs.Client
|
|
|
|
// azureAdStorageAuth is only here if we're using AzureAD Authentication but is an Authorizer for Storage
|
|
azureAdStorageAuth *autorest.Authorizer
|
|
|
|
accessKey string
|
|
environment azure.Environment
|
|
resourceGroupName string
|
|
storageAccountName string
|
|
sasToken string
|
|
}
|
|
|
|
func buildArmClient(ctx context.Context, config BackendConfig) (*ArmClient, error) {
|
|
env, err := authentication.AzureEnvironmentByNameFromEndpoint(ctx, config.MetadataHost, config.Environment)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := ArmClient{
|
|
environment: *env,
|
|
resourceGroupName: config.ResourceGroupName,
|
|
storageAccountName: config.StorageAccountName,
|
|
}
|
|
|
|
// if we have an Access Key - we don't need the other clients
|
|
if config.AccessKey != "" {
|
|
client.accessKey = config.AccessKey
|
|
return &client, nil
|
|
}
|
|
|
|
// likewise with a SAS token
|
|
if config.SasToken != "" {
|
|
client.sasToken = config.SasToken
|
|
return &client, nil
|
|
}
|
|
|
|
builder := authentication.Builder{
|
|
ClientID: config.ClientID,
|
|
SubscriptionID: config.SubscriptionID,
|
|
TenantID: config.TenantID,
|
|
CustomResourceManagerEndpoint: config.CustomResourceManagerEndpoint,
|
|
MetadataHost: config.MetadataHost,
|
|
Environment: config.Environment,
|
|
ClientSecretDocsLink: "https://www.terraform.io/docs/providers/azurerm/guides/service_principal_client_secret.html",
|
|
|
|
// Service Principal (Client Certificate)
|
|
ClientCertPassword: config.ClientCertificatePassword,
|
|
ClientCertPath: config.ClientCertificatePath,
|
|
|
|
// Service Principal (Client Secret)
|
|
ClientSecret: config.ClientSecret,
|
|
|
|
// Managed Service Identity
|
|
MsiEndpoint: config.MsiEndpoint,
|
|
|
|
// Feature Toggles
|
|
SupportsAzureCliToken: true,
|
|
SupportsClientCertAuth: true,
|
|
SupportsClientSecretAuth: true,
|
|
SupportsManagedServiceIdentity: config.UseMsi,
|
|
}
|
|
armConfig, err := builder.Build()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error building ARM Config: %+v", err)
|
|
}
|
|
|
|
oauthConfig, err := armConfig.BuildOAuthConfig(env.ActiveDirectoryEndpoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sender := sender.BuildSender("backend/remote-state/azure")
|
|
auth, err := armConfig.GetAuthorizationToken(sender, oauthConfig, env.TokenAudience)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if config.UseAzureADAuthentication {
|
|
storageAuth, err := armConfig.GetAuthorizationToken(sender, oauthConfig, env.ResourceIdentifiers.Storage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client.azureAdStorageAuth = &storageAuth
|
|
}
|
|
|
|
accountsClient := armStorage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, armConfig.SubscriptionID)
|
|
client.configureClient(&accountsClient.Client, auth)
|
|
client.storageAccountsClient = &accountsClient
|
|
|
|
groupsClient := resources.NewGroupsClientWithBaseURI(env.ResourceManagerEndpoint, armConfig.SubscriptionID)
|
|
client.configureClient(&groupsClient.Client, auth)
|
|
client.groupsClient = &groupsClient
|
|
|
|
return &client, nil
|
|
}
|
|
|
|
func buildArmEnvironment(config BackendConfig) (*azure.Environment, error) {
|
|
// TODO: can we remove this?
|
|
// https://github.com/hashicorp/terraform/issues/27156
|
|
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) (*blobs.Client, error) {
|
|
if c.sasToken != "" {
|
|
log.Printf("[DEBUG] Building the Blob Client from a SAS Token")
|
|
storageAuth, err := autorest.NewSASTokenAuthorizer(c.sasToken)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error building Authorizer: %+v", err)
|
|
}
|
|
|
|
blobsClient := blobs.NewWithEnvironment(c.environment)
|
|
c.configureClient(&blobsClient.Client, storageAuth)
|
|
return &blobsClient, nil
|
|
}
|
|
|
|
if c.azureAdStorageAuth != nil {
|
|
blobsClient := blobs.NewWithEnvironment(c.environment)
|
|
c.configureClient(&blobsClient.Client, *c.azureAdStorageAuth)
|
|
return &blobsClient, nil
|
|
}
|
|
|
|
accessKey := c.accessKey
|
|
if accessKey == "" {
|
|
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)
|
|
}
|
|
|
|
if keys.Keys == nil {
|
|
return nil, fmt.Errorf("Nil key returned for storage account %q", c.storageAccountName)
|
|
}
|
|
|
|
accessKeys := *keys.Keys
|
|
accessKey = *accessKeys[0].Value
|
|
}
|
|
|
|
storageAuth, err := autorest.NewSharedKeyAuthorizer(c.storageAccountName, accessKey, autorest.SharedKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error building Authorizer: %+v", err)
|
|
}
|
|
|
|
blobsClient := blobs.NewWithEnvironment(c.environment)
|
|
c.configureClient(&blobsClient.Client, storageAuth)
|
|
return &blobsClient, nil
|
|
}
|
|
|
|
func (c ArmClient) getContainersClient(ctx context.Context) (*containers.Client, error) {
|
|
if c.sasToken != "" {
|
|
log.Printf("[DEBUG] Building the Container Client from a SAS Token")
|
|
storageAuth, err := autorest.NewSASTokenAuthorizer(c.sasToken)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error building Authorizer: %+v", err)
|
|
}
|
|
|
|
containersClient := containers.NewWithEnvironment(c.environment)
|
|
c.configureClient(&containersClient.Client, storageAuth)
|
|
return &containersClient, nil
|
|
}
|
|
|
|
if c.azureAdStorageAuth != nil {
|
|
containersClient := containers.NewWithEnvironment(c.environment)
|
|
c.configureClient(&containersClient.Client, *c.azureAdStorageAuth)
|
|
return &containersClient, nil
|
|
}
|
|
|
|
accessKey := c.accessKey
|
|
if accessKey == "" {
|
|
log.Printf("[DEBUG] Building the Container 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)
|
|
}
|
|
|
|
if keys.Keys == nil {
|
|
return nil, fmt.Errorf("Nil key returned for storage account %q", c.storageAccountName)
|
|
}
|
|
|
|
accessKeys := *keys.Keys
|
|
accessKey = *accessKeys[0].Value
|
|
}
|
|
|
|
storageAuth, err := autorest.NewSharedKeyAuthorizer(c.storageAccountName, accessKey, autorest.SharedKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error building Authorizer: %+v", err)
|
|
}
|
|
|
|
containersClient := containers.NewWithEnvironment(c.environment)
|
|
c.configureClient(&containersClient.Client, storageAuth)
|
|
return &containersClient, nil
|
|
}
|
|
|
|
func (c *ArmClient) configureClient(client *autorest.Client, auth autorest.Authorizer) {
|
|
client.UserAgent = buildUserAgent()
|
|
client.Authorizer = auth
|
|
client.Sender = buildSender()
|
|
client.SkipResourceProviderRegistration = false
|
|
client.PollingDuration = 60 * time.Minute
|
|
}
|
|
|
|
func buildUserAgent() string {
|
|
userAgent := httpclient.UserAgentString()
|
|
|
|
// append the CloudShell version to the user agent if it exists
|
|
if azureAgent := os.Getenv("AZURE_HTTP_USER_AGENT"); azureAgent != "" {
|
|
userAgent = fmt.Sprintf("%s %s", userAgent, azureAgent)
|
|
}
|
|
|
|
return userAgent
|
|
}
|