github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/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 IDTokenRequestURL: config.OIDCRequestURL, 85 IDTokenRequestToken: config.OIDCRequestToken, 86 87 // Feature Toggles 88 SupportsAzureCliToken: true, 89 SupportsClientCertAuth: true, 90 SupportsClientSecretAuth: true, 91 SupportsManagedServiceIdentity: config.UseMsi, 92 SupportsOIDCAuth: config.UseOIDC, 93 UseMicrosoftGraph: true, 94 } 95 armConfig, err := builder.Build() 96 if err != nil { 97 return nil, fmt.Errorf("Error building ARM Config: %+v", err) 98 } 99 100 oauthConfig, err := armConfig.BuildOAuthConfig(env.ActiveDirectoryEndpoint) 101 if err != nil { 102 return nil, err 103 } 104 105 hamiltonEnv, err := environments.EnvironmentFromString(config.Environment) 106 if err != nil { 107 return nil, err 108 } 109 110 sender := sender.BuildSender("backend/remote-state/azure") 111 log.Printf("[DEBUG] Obtaining an MSAL / Microsoft Graph token for Resource Manager..") 112 auth, err := armConfig.GetMSALToken(ctx, hamiltonEnv.ResourceManager, sender, oauthConfig, env.TokenAudience) 113 if err != nil { 114 return nil, err 115 } 116 117 if config.UseAzureADAuthentication { 118 log.Printf("[DEBUG] Obtaining an MSAL / Microsoft Graph token for Storage..") 119 storageAuth, err := armConfig.GetMSALToken(ctx, hamiltonEnv.Storage, sender, oauthConfig, env.ResourceIdentifiers.Storage) 120 if err != nil { 121 return nil, err 122 } 123 client.azureAdStorageAuth = &storageAuth 124 } 125 126 accountsClient := armStorage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, armConfig.SubscriptionID) 127 client.configureClient(&accountsClient.Client, auth) 128 client.storageAccountsClient = &accountsClient 129 130 groupsClient := resources.NewGroupsClientWithBaseURI(env.ResourceManagerEndpoint, armConfig.SubscriptionID) 131 client.configureClient(&groupsClient.Client, auth) 132 client.groupsClient = &groupsClient 133 134 return &client, nil 135 } 136 137 func (c ArmClient) getBlobClient(ctx context.Context) (*blobs.Client, error) { 138 if c.sasToken != "" { 139 log.Printf("[DEBUG] Building the Blob Client from a SAS Token") 140 storageAuth, err := autorest.NewSASTokenAuthorizer(c.sasToken) 141 if err != nil { 142 return nil, fmt.Errorf("Error building Authorizer: %+v", err) 143 } 144 145 blobsClient := blobs.NewWithEnvironment(c.environment) 146 c.configureClient(&blobsClient.Client, storageAuth) 147 return &blobsClient, nil 148 } 149 150 if c.azureAdStorageAuth != nil { 151 blobsClient := blobs.NewWithEnvironment(c.environment) 152 c.configureClient(&blobsClient.Client, *c.azureAdStorageAuth) 153 return &blobsClient, nil 154 } 155 156 accessKey := c.accessKey 157 if accessKey == "" { 158 log.Printf("[DEBUG] Building the Blob Client from an Access Token (using user credentials)") 159 keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName, "") 160 if err != nil { 161 return nil, fmt.Errorf("Error retrieving keys for Storage Account %q: %s", c.storageAccountName, err) 162 } 163 164 if keys.Keys == nil { 165 return nil, fmt.Errorf("Nil key returned for storage account %q", c.storageAccountName) 166 } 167 168 accessKeys := *keys.Keys 169 accessKey = *accessKeys[0].Value 170 } 171 172 storageAuth, err := autorest.NewSharedKeyAuthorizer(c.storageAccountName, accessKey, autorest.SharedKey) 173 if err != nil { 174 return nil, fmt.Errorf("Error building Authorizer: %+v", err) 175 } 176 177 blobsClient := blobs.NewWithEnvironment(c.environment) 178 c.configureClient(&blobsClient.Client, storageAuth) 179 return &blobsClient, nil 180 } 181 182 func (c ArmClient) getContainersClient(ctx context.Context) (*containers.Client, error) { 183 if c.sasToken != "" { 184 log.Printf("[DEBUG] Building the Container Client from a SAS Token") 185 storageAuth, err := autorest.NewSASTokenAuthorizer(c.sasToken) 186 if err != nil { 187 return nil, fmt.Errorf("Error building Authorizer: %+v", err) 188 } 189 190 containersClient := containers.NewWithEnvironment(c.environment) 191 c.configureClient(&containersClient.Client, storageAuth) 192 return &containersClient, nil 193 } 194 195 if c.azureAdStorageAuth != nil { 196 containersClient := containers.NewWithEnvironment(c.environment) 197 c.configureClient(&containersClient.Client, *c.azureAdStorageAuth) 198 return &containersClient, nil 199 } 200 201 accessKey := c.accessKey 202 if accessKey == "" { 203 log.Printf("[DEBUG] Building the Container Client from an Access Token (using user credentials)") 204 keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName, "") 205 if err != nil { 206 return nil, fmt.Errorf("Error retrieving keys for Storage Account %q: %s", c.storageAccountName, err) 207 } 208 209 if keys.Keys == nil { 210 return nil, fmt.Errorf("Nil key returned for storage account %q", c.storageAccountName) 211 } 212 213 accessKeys := *keys.Keys 214 accessKey = *accessKeys[0].Value 215 } 216 217 storageAuth, err := autorest.NewSharedKeyAuthorizer(c.storageAccountName, accessKey, autorest.SharedKey) 218 if err != nil { 219 return nil, fmt.Errorf("Error building Authorizer: %+v", err) 220 } 221 222 containersClient := containers.NewWithEnvironment(c.environment) 223 c.configureClient(&containersClient.Client, storageAuth) 224 return &containersClient, nil 225 } 226 227 func (c *ArmClient) configureClient(client *autorest.Client, auth autorest.Authorizer) { 228 client.UserAgent = buildUserAgent() 229 client.Authorizer = auth 230 client.Sender = buildSender() 231 client.SkipResourceProviderRegistration = false 232 client.PollingDuration = 60 * time.Minute 233 } 234 235 func buildUserAgent() string { 236 userAgent := httpclient.TerraformUserAgent(version.Version) 237 238 // append the CloudShell version to the user agent if it exists 239 if azureAgent := os.Getenv("AZURE_HTTP_USER_AGENT"); azureAgent != "" { 240 userAgent = fmt.Sprintf("%s %s", userAgent, azureAgent) 241 } 242 243 return userAgent 244 }