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