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