diff --git a/api/v1alpha1/terraform/terraform_config.go b/api/v1alpha1/terraform/terraform_config.go index 41a5d4c29..0945cb8b0 100644 --- a/api/v1alpha1/terraform/terraform_config.go +++ b/api/v1alpha1/terraform/terraform_config.go @@ -11,6 +11,7 @@ type BackendConfig struct { S3 *S3Backend `yaml:"s3,omitempty"` Kubernetes *KubernetesBackend `yaml:"kubernetes,omitempty"` Local *LocalBackend `yaml:"local,omitempty"` + AzureRM *AzureRMBackend `yaml:"azurerm,omitempty"` Prefix *string `yaml:"prefix,omitempty"` } @@ -90,6 +91,40 @@ type ExecConfig struct { Env *map[string]string `yaml:"env,omitempty"` } +// AzureRMBackend represents the configuration for the AzureRM backend +type AzureRMBackend struct { + StorageAccountName *string `yaml:"storage_account_name,omitempty"` + ContainerName *string `yaml:"container_name,omitempty"` + Key *string `yaml:"key,omitempty"` + Environment *string `yaml:"environment,omitempty"` + MetadataHost *string `yaml:"metadata_host,omitempty"` + UseAzureAD *bool `yaml:"use_azuread,omitempty"` + UseOIDC *bool `yaml:"use_oidc,omitempty"` + UseCLI *bool `yaml:"use_cli,omitempty"` + UseMSI *bool `yaml:"use_msi,omitempty"` + UseAksWorkloadIdentity *bool `yaml:"use_aks_workload_identity,omitempty"` + TenantID *string `yaml:"tenant_id,omitempty"` + SubscriptionID *string `yaml:"subscription_id,omitempty"` + ClientID *string `yaml:"client_id,omitempty"` + ClientSecret *string `yaml:"client_secret,omitempty"` + ClientCertificate *string `yaml:"client_certificate,omitempty"` + ClientCertPassword *string `yaml:"client_certificate_password,omitempty"` + ClientCertPath *string `yaml:"client_certificate_path,omitempty"` + ClientIdFilePath *string `yaml:"client_id_file_path,omitempty"` + ClientSecretFilePath *string `yaml:"client_secret_file_path,omitempty"` + MSIEndpoint *string `yaml:"msi_endpoint,omitempty"` + ResourceGroupName *string `yaml:"resource_group_name,omitempty"` + UseDNSZoneEndpoint *bool `yaml:"use_dns_zone_endpoint,omitempty"` + Snapshot *bool `yaml:"snapshot,omitempty"` + AccessKey *string `yaml:"access_key,omitempty"` + SasToken *string `yaml:"sas_token,omitempty"` + AdoPipelineServiceConnectionId *string `yaml:"ado_pipeline_service_connection_id,omitempty"` + OidcRequestUrl *string `yaml:"oidc_request_url,omitempty"` + OidcRequestToken *string `yaml:"oidc_request_token,omitempty"` + OidcToken *string `yaml:"oidc_token,omitempty"` + OidcTokenFilePath *string `yaml:"oidc_token_file_path,omitempty"` +} + // Merge performs a simple merge of the current TerraformConfig with another TerraformConfig. func (base *TerraformConfig) Merge(overlay *TerraformConfig) { if overlay.Enabled != nil { diff --git a/pkg/env/terraform_env.go b/pkg/env/terraform_env.go index 1e89740af..9974e9869 100644 --- a/pkg/env/terraform_env.go +++ b/pkg/env/terraform_env.go @@ -190,6 +190,10 @@ func (e *TerraformEnvPrinter) generateBackendOverrideTf() error { case "kubernetes": backendConfig = fmt.Sprintf(`terraform { backend "kubernetes" {} +}`) + case "azurerm": + backendConfig = fmt.Sprintf(`terraform { + backend "azurerm" {} }`) default: return fmt.Errorf("unsupported backend: %s", backend) @@ -255,6 +259,14 @@ func (e *TerraformEnvPrinter) generateBackendConfigArgs(projectPath, configRoot return nil, fmt.Errorf("error processing Kubernetes backend config: %w", err) } } + case "azurerm": + keyPath := fmt.Sprintf("%s%s", prefix, filepath.ToSlash(filepath.Join(projectPath, "terraform.tfstate"))) + addBackendConfigArg("key", keyPath) + if backend := e.configHandler.GetConfig().Terraform.Backend.AzureRM; backend != nil { + if err := e.processBackendConfig(backend, addBackendConfigArg); err != nil { + return nil, fmt.Errorf("error processing AzureRM backend config: %w", err) + } + } default: return nil, fmt.Errorf("unsupported backend: %s", backend) } diff --git a/pkg/env/terraform_env_test.go b/pkg/env/terraform_env_test.go index f71e53870..3239bf51b 100644 --- a/pkg/env/terraform_env_test.go +++ b/pkg/env/terraform_env_test.go @@ -773,6 +773,33 @@ func TestTerraformEnv_generateBackendOverrideTf(t *testing.T) { } }) + t.Run("AzureRMBackend", func(t *testing.T) { + // Given a TerraformEnvPrinter with AzureRM backend configuration + printer, mocks := setup(t) + mocks.ConfigHandler.SetContextValue("terraform.backend.type", "azurerm") + + var writtenData []byte + mocks.Shims.WriteFile = func(filename string, data []byte, perm os.FileMode) error { + writtenData = data + return nil + } + + // When generateBackendOverrideTf is called + err := printer.generateBackendOverrideTf() + + // Then no error should occur and the expected backend config should be written + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + expectedContent := `terraform { + backend "azurerm" {} +}` + if string(writtenData) != expectedContent { + t.Errorf("Expected backend config %q, got %q", expectedContent, string(writtenData)) + } + }) + t.Run("UnsupportedBackend", func(t *testing.T) { // Given a TerraformEnvPrinter with unsupported backend configuration printer, mocks := setup(t) @@ -1122,6 +1149,37 @@ func TestTerraformEnv_generateBackendConfigArgs(t *testing.T) { } }) + t.Run("AzureRMBackendWithPrefix", func(t *testing.T) { + // Given a TerraformEnvPrinter with AzureRM backend and prefix configuration + printer, mocks := setup(t) + mocks.ConfigHandler.SetContextValue("terraform.backend.type", "azurerm") + mocks.ConfigHandler.SetContextValue("terraform.backend.prefix", "mock-prefix/") + mocks.ConfigHandler.SetContextValue("terraform.backend.azurerm.storage_account_name", "mock-storage") + mocks.ConfigHandler.SetContextValue("terraform.backend.azurerm.container_name", "mock-container") + mocks.ConfigHandler.SetContextValue("terraform.backend.azurerm.use_azuread", true) + projectPath := "project/path" + configRoot := "/mock/config/root" + + // When generateBackendConfigArgs is called + backendConfigArgs, err := printer.generateBackendConfigArgs(projectPath, configRoot) + + // Then no error should occur and the expected arguments should be returned + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + expectedArgs := []string{ + `-backend-config="key=mock-prefix/project/path/terraform.tfstate"`, + `-backend-config="container_name=mock-container"`, + `-backend-config="storage_account_name=mock-storage"`, + `-backend-config="use_azuread=true"`, + } + + if !reflect.DeepEqual(backendConfigArgs, expectedArgs) { + t.Errorf("expected %v, got %v", expectedArgs, backendConfigArgs) + } + }) + t.Run("UnsupportedBackendType", func(t *testing.T) { // Given a TerraformEnvPrinter with unsupported backend configuration printer, mocks := setup(t)