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  }