github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/backend/remote-state/azure/arm_client.go (about) 1 package azure 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "os" 8 "time" 9 10 "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources" 11 armStorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-01-01/storage" 12 "github.com/Azure/go-autorest/autorest" 13 "github.com/Azure/go-autorest/autorest/azure" 14 "github.com/hashicorp/go-azure-helpers/authentication" 15 "github.com/hashicorp/go-azure-helpers/sender" 16 "github.com/hashicorp/terraform/internal/httpclient" 17 "github.com/hashicorp/terraform/version" 18 "github.com/manicminer/hamilton/environments" 19 "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" 20 "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers" 21 ) 22 23 type ArmClient struct { 24 // These Clients are only initialized if an Access Key isn't provided 25 groupsClient *resources.GroupsClient 26 storageAccountsClient *armStorage.AccountsClient 27 containersClient *containers.Client 28 blobsClient *blobs.Client 29 30 // azureAdStorageAuth is only here if we're using AzureAD Authentication but is an Authorizer for Storage 31 azureAdStorageAuth *autorest.Authorizer 32 33 accessKey string 34 environment azure.Environment 35 resourceGroupName string 36 storageAccountName string 37 sasToken string 38 } 39 40 func buildArmClient(ctx context.Context, config BackendConfig) (*ArmClient, error) { 41 env, err := authentication.AzureEnvironmentByNameFromEndpoint(ctx, config.MetadataHost, config.Environment) 42 if err != nil { 43 return nil, err 44 } 45 46 client := ArmClient{ 47 environment: *env, 48 resourceGroupName: config.ResourceGroupName, 49 storageAccountName: config.StorageAccountName, 50 } 51 52 // if we have an Access Key - we don't need the other clients 53 if config.AccessKey != "" { 54 client.accessKey = config.AccessKey 55 return &client, nil 56 } 57 58 // likewise with a SAS token 59 if config.SasToken != "" { 60 client.sasToken = config.SasToken 61 return &client, nil 62 } 63 64 builder := authentication.Builder{ 65 ClientID: config.ClientID, 66 SubscriptionID: config.SubscriptionID, 67 TenantID: config.TenantID, 68 CustomResourceManagerEndpoint: config.CustomResourceManagerEndpoint, 69 MetadataHost: config.MetadataHost, 70 Environment: config.Environment, 71 ClientSecretDocsLink: "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_client_secret", 72 73 // Service Principal (Client Certificate) 74 ClientCertPassword: config.ClientCertificatePassword, 75 ClientCertPath: config.ClientCertificatePath, 76 77 // Service Principal (Client Secret) 78 ClientSecret: config.ClientSecret, 79 80 // Managed Service Identity 81 MsiEndpoint: config.MsiEndpoint, 82 83 // OIDC 84 IDToken: config.OIDCToken, 85 IDTokenFilePath: config.OIDCTokenFilePath, 86 IDTokenRequestURL: config.OIDCRequestURL, 87 IDTokenRequestToken: config.OIDCRequestToken, 88 89 // Feature Toggles 90 SupportsAzureCliToken: true, 91 SupportsClientCertAuth: true, 92 SupportsClientSecretAuth: true, 93 SupportsManagedServiceIdentity: config.UseMsi, 94 SupportsOIDCAuth: config.UseOIDC, 95 UseMicrosoftGraph: true, 96 } 97 armConfig, err := builder.Build() 98 if err != nil { 99 return nil, fmt.Errorf("Error building ARM Config: %+v", err) 100 } 101 102 oauthConfig, err := armConfig.BuildOAuthConfig(env.ActiveDirectoryEndpoint) 103 if err != nil { 104 return nil, err 105 } 106 107 hamiltonEnv, err := environments.EnvironmentFromString(config.Environment) 108 if err != nil { 109 return nil, err 110 } 111 112 sender := sender.BuildSender("backend/remote-state/azure") 113 log.Printf("[DEBUG] Obtaining an MSAL / Microsoft Graph token for Resource Manager..") 114 auth, err := armConfig.GetMSALToken(ctx, hamiltonEnv.ResourceManager, sender, oauthConfig, env.TokenAudience) 115 if err != nil { 116 return nil, err 117 } 118 119 if config.UseAzureADAuthentication { 120 log.Printf("[DEBUG] Obtaining an MSAL / Microsoft Graph token for Storage..") 121 storageAuth, err := armConfig.GetMSALToken(ctx, hamiltonEnv.Storage, sender, oauthConfig, env.ResourceIdentifiers.Storage) 122 if err != nil { 123 return nil, err 124 } 125 client.azureAdStorageAuth = &storageAuth 126 } 127 128 accountsClient := armStorage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, armConfig.SubscriptionID) 129 client.configureClient(&accountsClient.Client, auth) 130 client.storageAccountsClient = &accountsClient 131 132 groupsClient := resources.NewGroupsClientWithBaseURI(env.ResourceManagerEndpoint, armConfig.SubscriptionID) 133 client.configureClient(&groupsClient.Client, auth) 134 client.groupsClient = &groupsClient 135 136 return &client, nil 137 } 138 139 func (c ArmClient) getBlobClient(ctx context.Context) (*blobs.Client, error) { 140 if c.sasToken != "" { 141 log.Printf("[DEBUG] Building the Blob Client from a SAS Token") 142 storageAuth, err := autorest.NewSASTokenAuthorizer(c.sasToken) 143 if err != nil { 144 return nil, fmt.Errorf("Error building Authorizer: %+v", err) 145 } 146 147 blobsClient := blobs.NewWithEnvironment(c.environment) 148 c.configureClient(&blobsClient.Client, storageAuth) 149 return &blobsClient, nil 150 } 151 152 if c.azureAdStorageAuth != nil { 153 blobsClient := blobs.NewWithEnvironment(c.environment) 154 c.configureClient(&blobsClient.Client, *c.azureAdStorageAuth) 155 return &blobsClient, nil 156 } 157 158 accessKey := c.accessKey 159 if accessKey == "" { 160 log.Printf("[DEBUG] Building the Blob Client from an Access Token (using user credentials)") 161 keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName, "") 162 if err != nil { 163 return nil, fmt.Errorf("Error retrieving keys for Storage Account %q: %s", c.storageAccountName, err) 164 } 165 166 if keys.Keys == nil { 167 return nil, fmt.Errorf("Nil key returned for storage account %q", c.storageAccountName) 168 } 169 170 accessKeys := *keys.Keys 171 accessKey = *accessKeys[0].Value 172 } 173 174 storageAuth, err := autorest.NewSharedKeyAuthorizer(c.storageAccountName, accessKey, autorest.SharedKey) 175 if err != nil { 176 return nil, fmt.Errorf("Error building Authorizer: %+v", err) 177 } 178 179 blobsClient := blobs.NewWithEnvironment(c.environment) 180 c.configureClient(&blobsClient.Client, storageAuth) 181 return &blobsClient, nil 182 } 183 184 func (c ArmClient) getContainersClient(ctx context.Context) (*containers.Client, error) { 185 if c.sasToken != "" { 186 log.Printf("[DEBUG] Building the Container Client from a SAS Token") 187 storageAuth, err := autorest.NewSASTokenAuthorizer(c.sasToken) 188 if err != nil { 189 return nil, fmt.Errorf("Error building Authorizer: %+v", err) 190 } 191 192 containersClient := containers.NewWithEnvironment(c.environment) 193 c.configureClient(&containersClient.Client, storageAuth) 194 return &containersClient, nil 195 } 196 197 if c.azureAdStorageAuth != nil { 198 containersClient := containers.NewWithEnvironment(c.environment) 199 c.configureClient(&containersClient.Client, *c.azureAdStorageAuth) 200 return &containersClient, nil 201 } 202 203 accessKey := c.accessKey 204 if accessKey == "" { 205 log.Printf("[DEBUG] Building the Container Client from an Access Token (using user credentials)") 206 keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName, "") 207 if err != nil { 208 return nil, fmt.Errorf("Error retrieving keys for Storage Account %q: %s", c.storageAccountName, err) 209 } 210 211 if keys.Keys == nil { 212 return nil, fmt.Errorf("Nil key returned for storage account %q", c.storageAccountName) 213 } 214 215 accessKeys := *keys.Keys 216 accessKey = *accessKeys[0].Value 217 } 218 219 storageAuth, err := autorest.NewSharedKeyAuthorizer(c.storageAccountName, accessKey, autorest.SharedKey) 220 if err != nil { 221 return nil, fmt.Errorf("Error building Authorizer: %+v", err) 222 } 223 224 containersClient := containers.NewWithEnvironment(c.environment) 225 c.configureClient(&containersClient.Client, storageAuth) 226 return &containersClient, nil 227 } 228 229 func (c *ArmClient) configureClient(client *autorest.Client, auth autorest.Authorizer) { 230 client.UserAgent = buildUserAgent() 231 client.Authorizer = auth 232 client.Sender = buildSender() 233 client.SkipResourceProviderRegistration = false 234 client.PollingDuration = 60 * time.Minute 235 } 236 237 func buildUserAgent() string { 238 userAgent := httpclient.TerraformUserAgent(version.Version) 239 240 // append the CloudShell version to the user agent if it exists 241 if azureAgent := os.Getenv("AZURE_HTTP_USER_AGENT"); azureAgent != "" { 242 userAgent = fmt.Sprintf("%s %s", userAgent, azureAgent) 243 } 244 245 return userAgent 246 }