github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/backend/remote-state/azure/backend.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package azure 5 6 import ( 7 "context" 8 "fmt" 9 10 "github.com/terramate-io/tf/backend" 11 "github.com/terramate-io/tf/legacy/helper/schema" 12 ) 13 14 // New creates a new backend for Azure remote state. 15 func New() backend.Backend { 16 s := &schema.Backend{ 17 Schema: map[string]*schema.Schema{ 18 "storage_account_name": { 19 Type: schema.TypeString, 20 Required: true, 21 Description: "The name of the storage account.", 22 }, 23 24 "container_name": { 25 Type: schema.TypeString, 26 Required: true, 27 Description: "The container name.", 28 }, 29 30 "key": { 31 Type: schema.TypeString, 32 Required: true, 33 Description: "The blob key.", 34 }, 35 36 "metadata_host": { 37 Type: schema.TypeString, 38 Required: true, 39 DefaultFunc: schema.EnvDefaultFunc("ARM_METADATA_HOST", ""), 40 Description: "The Metadata URL which will be used to obtain the Cloud Environment.", 41 }, 42 43 "environment": { 44 Type: schema.TypeString, 45 Optional: true, 46 Description: "The Azure cloud environment.", 47 DefaultFunc: schema.EnvDefaultFunc("ARM_ENVIRONMENT", "public"), 48 }, 49 50 "access_key": { 51 Type: schema.TypeString, 52 Optional: true, 53 Description: "The access key.", 54 DefaultFunc: schema.EnvDefaultFunc("ARM_ACCESS_KEY", ""), 55 }, 56 57 "sas_token": { 58 Type: schema.TypeString, 59 Optional: true, 60 Description: "A SAS Token used to interact with the Blob Storage Account.", 61 DefaultFunc: schema.EnvDefaultFunc("ARM_SAS_TOKEN", ""), 62 }, 63 64 "snapshot": { 65 Type: schema.TypeBool, 66 Optional: true, 67 Description: "Enable/Disable automatic blob snapshotting", 68 DefaultFunc: schema.EnvDefaultFunc("ARM_SNAPSHOT", false), 69 }, 70 71 "resource_group_name": { 72 Type: schema.TypeString, 73 Optional: true, 74 Description: "The resource group name.", 75 }, 76 77 "client_id": { 78 Type: schema.TypeString, 79 Optional: true, 80 Description: "The Client ID.", 81 DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID", ""), 82 }, 83 84 "endpoint": { 85 Type: schema.TypeString, 86 Optional: true, 87 Description: "A custom Endpoint used to access the Azure Resource Manager API's.", 88 DefaultFunc: schema.EnvDefaultFunc("ARM_ENDPOINT", ""), 89 }, 90 91 "subscription_id": { 92 Type: schema.TypeString, 93 Optional: true, 94 Description: "The Subscription ID.", 95 DefaultFunc: schema.EnvDefaultFunc("ARM_SUBSCRIPTION_ID", ""), 96 }, 97 98 "tenant_id": { 99 Type: schema.TypeString, 100 Optional: true, 101 Description: "The Tenant ID.", 102 DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""), 103 }, 104 105 // Service Principal (Client Certificate) specific 106 "client_certificate_password": { 107 Type: schema.TypeString, 108 Optional: true, 109 Description: "The password associated with the Client Certificate specified in `client_certificate_path`", 110 DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_CERTIFICATE_PASSWORD", ""), 111 }, 112 "client_certificate_path": { 113 Type: schema.TypeString, 114 Optional: true, 115 Description: "The path to the PFX file used as the Client Certificate when authenticating as a Service Principal", 116 DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_CERTIFICATE_PATH", ""), 117 }, 118 119 // Service Principal (Client Secret) specific 120 "client_secret": { 121 Type: schema.TypeString, 122 Optional: true, 123 Description: "The Client Secret.", 124 DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET", ""), 125 }, 126 127 // Managed Service Identity specific 128 "use_msi": { 129 Type: schema.TypeBool, 130 Optional: true, 131 Description: "Should Managed Service Identity be used?", 132 DefaultFunc: schema.EnvDefaultFunc("ARM_USE_MSI", false), 133 }, 134 "msi_endpoint": { 135 Type: schema.TypeString, 136 Optional: true, 137 Description: "The Managed Service Identity Endpoint.", 138 DefaultFunc: schema.EnvDefaultFunc("ARM_MSI_ENDPOINT", ""), 139 }, 140 141 // OIDC auth specific fields 142 "use_oidc": { 143 Type: schema.TypeBool, 144 Optional: true, 145 DefaultFunc: schema.EnvDefaultFunc("ARM_USE_OIDC", false), 146 Description: "Allow OIDC to be used for authentication", 147 }, 148 "oidc_token": { 149 Type: schema.TypeString, 150 Optional: true, 151 DefaultFunc: schema.EnvDefaultFunc("ARM_OIDC_TOKEN", ""), 152 Description: "A generic JWT token that can be used for OIDC authentication. Should not be used in conjunction with `oidc_request_token`.", 153 }, 154 "oidc_token_file_path": { 155 Type: schema.TypeString, 156 Optional: true, 157 DefaultFunc: schema.EnvDefaultFunc("ARM_OIDC_TOKEN_FILE_PATH", ""), 158 Description: "Path to file containing a generic JWT token that can be used for OIDC authentication. Should not be used in conjunction with `oidc_request_token`.", 159 }, 160 "oidc_request_url": { 161 Type: schema.TypeString, 162 Optional: true, 163 DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_REQUEST_URL", "ACTIONS_ID_TOKEN_REQUEST_URL"}, ""), 164 Description: "The URL of the OIDC provider from which to request an ID token. Needs to be used in conjunction with `oidc_request_token`. This is meant to be used for Github Actions.", 165 }, 166 "oidc_request_token": { 167 Type: schema.TypeString, 168 Optional: true, 169 DefaultFunc: schema.MultiEnvDefaultFunc([]string{"ARM_OIDC_REQUEST_TOKEN", "ACTIONS_ID_TOKEN_REQUEST_TOKEN"}, ""), 170 Description: "The bearer token to use for the request to the OIDC providers `oidc_request_url` URL to fetch an ID token. Needs to be used in conjunction with `oidc_request_url`. This is meant to be used for Github Actions.", 171 }, 172 173 // Feature Flags 174 "use_azuread_auth": { 175 Type: schema.TypeBool, 176 Optional: true, 177 Description: "Should Terraform use AzureAD Authentication to access the Blob?", 178 DefaultFunc: schema.EnvDefaultFunc("ARM_USE_AZUREAD", false), 179 }, 180 }, 181 } 182 183 result := &Backend{Backend: s} 184 result.Backend.ConfigureFunc = result.configure 185 return result 186 } 187 188 type Backend struct { 189 *schema.Backend 190 191 // The fields below are set from configure 192 armClient *ArmClient 193 containerName string 194 keyName string 195 accountName string 196 snapshot bool 197 } 198 199 type BackendConfig struct { 200 // Required 201 StorageAccountName string 202 203 // Optional 204 AccessKey string 205 ClientID string 206 ClientCertificatePassword string 207 ClientCertificatePath string 208 ClientSecret string 209 CustomResourceManagerEndpoint string 210 MetadataHost string 211 Environment string 212 MsiEndpoint string 213 OIDCToken string 214 OIDCTokenFilePath string 215 OIDCRequestURL string 216 OIDCRequestToken string 217 ResourceGroupName string 218 SasToken string 219 SubscriptionID string 220 TenantID string 221 UseMsi bool 222 UseOIDC bool 223 UseAzureADAuthentication bool 224 } 225 226 func (b *Backend) configure(ctx context.Context) error { 227 if b.containerName != "" { 228 return nil 229 } 230 231 // Grab the resource data 232 data := schema.FromContextBackendConfig(ctx) 233 b.containerName = data.Get("container_name").(string) 234 b.accountName = data.Get("storage_account_name").(string) 235 b.keyName = data.Get("key").(string) 236 b.snapshot = data.Get("snapshot").(bool) 237 238 config := BackendConfig{ 239 AccessKey: data.Get("access_key").(string), 240 ClientID: data.Get("client_id").(string), 241 ClientCertificatePassword: data.Get("client_certificate_password").(string), 242 ClientCertificatePath: data.Get("client_certificate_path").(string), 243 ClientSecret: data.Get("client_secret").(string), 244 CustomResourceManagerEndpoint: data.Get("endpoint").(string), 245 MetadataHost: data.Get("metadata_host").(string), 246 Environment: data.Get("environment").(string), 247 MsiEndpoint: data.Get("msi_endpoint").(string), 248 OIDCToken: data.Get("oidc_token").(string), 249 OIDCTokenFilePath: data.Get("oidc_token_file_path").(string), 250 OIDCRequestURL: data.Get("oidc_request_url").(string), 251 OIDCRequestToken: data.Get("oidc_request_token").(string), 252 ResourceGroupName: data.Get("resource_group_name").(string), 253 SasToken: data.Get("sas_token").(string), 254 StorageAccountName: data.Get("storage_account_name").(string), 255 SubscriptionID: data.Get("subscription_id").(string), 256 TenantID: data.Get("tenant_id").(string), 257 UseMsi: data.Get("use_msi").(bool), 258 UseOIDC: data.Get("use_oidc").(bool), 259 UseAzureADAuthentication: data.Get("use_azuread_auth").(bool), 260 } 261 262 armClient, err := buildArmClient(context.TODO(), config) 263 if err != nil { 264 return err 265 } 266 267 thingsNeededToLookupAccessKeySpecified := config.AccessKey == "" && config.SasToken == "" && config.ResourceGroupName == "" 268 if thingsNeededToLookupAccessKeySpecified && !config.UseAzureADAuthentication { 269 return fmt.Errorf("Either an Access Key / SAS Token or the Resource Group for the Storage Account must be specified - or Azure AD Authentication must be enabled") 270 } 271 272 b.armClient = armClient 273 return nil 274 }