From 38408abd5b86e4cfba720e66a8459c71ed8e220f Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Thu, 23 Jul 2026 16:02:00 +0200 Subject: [PATCH 1/3] feat(firestartr-bootstrap): add azure support --- firestartr-bootstrap/README.md | 75 +++++++++-- firestartr-bootstrap/azure.go | 116 ++++++++++++++++++ .../azure_bootstrap_secrets.tmpl | 38 ++++++ .../azure_operator_secrets.tmpl | 38 ++++++ .../external_secrets/azure_secretstore.tmpl | 16 +++ firestartr-bootstrap/kubernetes.go | 60 +++++++-- firestartr-bootstrap/main.go | 64 +++++++--- firestartr-bootstrap/push_secrets.go | 11 +- .../schemas/credentials-file.json | 102 +++++++++++---- .../templates/azure_secret.tmpl | 8 ++ firestartr-bootstrap/types.go | 11 ++ 11 files changed, 476 insertions(+), 63 deletions(-) create mode 100644 firestartr-bootstrap/azure.go create mode 100644 firestartr-bootstrap/external_secrets/azure_bootstrap_secrets.tmpl create mode 100644 firestartr-bootstrap/external_secrets/azure_operator_secrets.tmpl create mode 100644 firestartr-bootstrap/external_secrets/azure_secretstore.tmpl create mode 100644 firestartr-bootstrap/templates/azure_secret.tmpl diff --git a/firestartr-bootstrap/README.md b/firestartr-bootstrap/README.md index 1d6fd77c..abd5137f 100644 --- a/firestartr-bootstrap/README.md +++ b/firestartr-bootstrap/README.md @@ -47,6 +47,42 @@ The following AWS Parameter Store parameters are required: - `/firestartr//fs--argocd/app-id` - `/firestartr//fs--argocd//installation-id` +#### 1.3 Azure requirements + +An Azure Key Vault named according to `key_vault_name` in `Credentialsfile.yaml` (e.g. `firestartr-`) must exist and be accessible by the service principal provided in the credentials file. + +The following secrets must exist inside the Key Vault (Azure Key Vault names use alphanumeric characters and dashes only — no forward slashes): + +| Key Vault Secret Name | Description | +| :--- | :--- | +| `fs-pem` | Operator GitHub App private key | +| `fs-app-id` | Operator GitHub App ID | +| `fs-client-id` | Operator GitHub App client ID | +| `fs--installation-id` | Operator GitHub App installation ID for `` | +| `fs-admin-pem` | Admin GitHub App private key | +| `fs-admin-app-id` | Admin GitHub App ID | +| `fs-admin-client-id` | Admin GitHub App client ID | +| `fs-admin--installation-id` | Admin GitHub App installation ID for `` | +| `fs-checks-pem` | Checks GitHub App private key | +| `fs-checks-app-id` | Checks GitHub App ID | +| `fs-checks-client-id` | Checks GitHub App client ID | +| `fs-checks--installation-id` | Checks GitHub App installation ID for `` | +| `fs-state-pem` | State GitHub App private key | +| `fs-state-app-id` | State GitHub App ID | +| `fs-state-client-id` | State GitHub App client ID | +| `fs-state--installation-id` | State GitHub App installation ID for `` | +| `fs-import-pem` | Import GitHub App private key | +| `fs-import-app-id` | Import GitHub App ID | +| `fs-import-client-id` | Import GitHub App client ID | +| `fs-import--installation-id` | Import GitHub App installation ID for `` | +| `fs-argocd-pem` | ArgoCD GitHub App private key | +| `fs-argocd-app-id` | ArgoCD GitHub App ID | +| `fs-argocd-client-id` | ArgoCD GitHub App client ID | +| `fs-argocd--installation-id` | ArgoCD GitHub App installation ID for `` | +| `prefapp-bot-pat` | Prefapp Bot Personal Access Token | + +The service principal must have the **Key Vault Secrets Officer** (or **Key Vault Administrator**) role on the vault, and **Storage Blob Data Contributor** on the Blob Storage container used for Terraform state. + ### 2. Bootstrap File ```yaml @@ -210,28 +246,43 @@ The rest of the parameters of the `cloudProvider` section are the AWS S3 bucket - `github.prefappBotPat`: Personal Access Token for the Prefapp Bot user, used to download the features from the features repository. - `github.operatorPat`: Personal Access Token for the Operator user, used to commit the deployment and ArgoCD application PRs to the `firestartr-` organization. -#### 3.2 Azure terraform backend provider configuration (currently not supported) +#### 3.2 Azure terraform backend provider configuration ```yaml # Credentialsfile.yaml --- cloudProvider: - providerConfigName: backend-provider-config-name - name: azurerm + name: azure config: - use_azuread_auth: true - tenant_id: "00000000-0000-0000-0000-000000000000" - client_id: "00000000-0000-0000-0000-000000000000" - client_secret: "************************************" - storage_account_name: "abcd1234" + tenant_id: "" + subscription_id: "" + client_id: "" + client_secret: "" + storage_account_name: "tfstate" container_name: "tfstate" - source: hashicorp/aws - type: aws - version: ~> 4.0 + resource_group_name: "rg-firestartr-" + key_vault_name: "firestartr-" + source: hashicorp/azurerm + type: azurerm + version: "~> 3.0" github: - providerConfigName: github-app-provider-config-name + prefappBotPat: "" + operatorPat: "" ``` +All `` must be replaced with actual values. + +- `cloudProvider.config.tenant_id`: Azure Active Directory tenant ID. +- `cloudProvider.config.subscription_id`: Azure subscription ID. +- `cloudProvider.config.client_id`: Service principal application (client) ID. +- `cloudProvider.config.client_secret`: Service principal client secret. +- `cloudProvider.config.storage_account_name`: Azure Storage Account name used as the Terraform state backend. +- `cloudProvider.config.container_name`: Blob container within the storage account (usually `tfstate`). +- `cloudProvider.config.resource_group_name`: Resource group containing the storage account and Key Vault. +- `cloudProvider.config.key_vault_name`: Name of the Azure Key Vault that holds the GitHub App secrets (see section 1.3 for required secrets). + +The pre-flight validation step (`cmd-validate-bootstrap`) will verify that the service principal can authenticate, that the storage container is accessible, and that the Key Vault exists. + ### 4. How to launch the bootstrap ``: Replace with the port that kind is using to expose the Kubernetes API server (noted in step 1.1). diff --git a/firestartr-bootstrap/azure.go b/firestartr-bootstrap/azure.go new file mode 100644 index 00000000..5e1cff40 --- /dev/null +++ b/firestartr-bootstrap/azure.go @@ -0,0 +1,116 @@ +package main + +import ( + "context" + "fmt" + "log" + "strings" +) + +// ValidateAzureCredentials verifies that the Azure Service Principal credentials are valid +// by attempting a login with the az CLI inside a Dagger container. +func (m *FirestartrBootstrap) ValidateAzureCredentials( + ctx context.Context, +) error { + log.Println("Attempting to validate Azure credentials via service principal login...") + + cfg := m.Creds.CloudProvider.Config + + clientSecretArg := cfg.ClientSecret + + output, err := dag.Container(). + From("mcr.microsoft.com/azure-cli"). + WithExec([]string{ + "az", "login", + "--service-principal", + "-u", cfg.ClientId, + "-p", clientSecretArg, + "--tenant", cfg.TenantId, + }). + Stdout(ctx) + + if err != nil { + return fmt.Errorf("Azure credential validation failed: az login rejected the service principal credentials: %w", err) + } + + log.Printf("Azure credentials validated successfully. Output: %s", strings.TrimSpace(output)) + return nil +} + +// ValidateAzureStorageAccount verifies that the Azure Blob Storage container used for +// Terraform state exists and is accessible. +func (m *FirestartrBootstrap) ValidateAzureStorageAccount( + ctx context.Context, +) error { + cfg := m.Creds.CloudProvider.Config + + log.Printf( + "Validating Azure Storage Account '%s' (container '%s') in resource group '%s'...", + cfg.StorageAccountName, cfg.ContainerName, cfg.ResourceGroupName, + ) + + _, err := dag.Container(). + From("mcr.microsoft.com/azure-cli"). + WithExec([]string{ + "az", "login", + "--service-principal", + "-u", cfg.ClientId, + "-p", cfg.ClientSecret, + "--tenant", cfg.TenantId, + }). + WithExec([]string{ + "az", "storage", "container", "show", + "--account-name", cfg.StorageAccountName, + "--name", cfg.ContainerName, + "--auth-mode", "login", + }). + Stdout(ctx) + + if err != nil { + return fmt.Errorf( + "Azure Storage Account validation failed: container '%s' in account '%s' is not accessible: %w", + cfg.ContainerName, cfg.StorageAccountName, err, + ) + } + + log.Printf( + "Azure Storage Account '%s' (container '%s') validated successfully.", + cfg.StorageAccountName, cfg.ContainerName, + ) + return nil +} + +// ValidateAzureKeyVault verifies that the Azure Key Vault exists and is accessible +// with the provided service principal credentials. +func (m *FirestartrBootstrap) ValidateAzureKeyVault( + ctx context.Context, +) error { + cfg := m.Creds.CloudProvider.Config + + log.Printf("Validating Azure Key Vault '%s'...", cfg.KeyVaultName) + + _, err := dag.Container(). + From("mcr.microsoft.com/azure-cli"). + WithExec([]string{ + "az", "login", + "--service-principal", + "-u", cfg.ClientId, + "-p", cfg.ClientSecret, + "--tenant", cfg.TenantId, + }). + WithExec([]string{ + "az", "keyvault", "show", + "--name", cfg.KeyVaultName, + }). + Stdout(ctx) + + if err != nil { + return fmt.Errorf( + "Azure Key Vault validation failed: vault '%s' is not accessible: %w", + cfg.KeyVaultName, err, + ) + } + + log.Printf("Azure Key Vault '%s' validated successfully.", cfg.KeyVaultName) + return nil +} diff --git a/firestartr-bootstrap/external_secrets/azure_bootstrap_secrets.tmpl b/firestartr-bootstrap/external_secrets/azure_bootstrap_secrets.tmpl new file mode 100644 index 00000000..7e009b00 --- /dev/null +++ b/firestartr-bootstrap/external_secrets/azure_bootstrap_secrets.tmpl @@ -0,0 +1,38 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: bootstrap-secrets +spec: + data: + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: "fs-admin-pem" + metadataPolicy: None + secretKey: fs-admin-pem + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: "fs-admin-app-id" + metadataPolicy: None + secretKey: fs-admin-appid + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: "fs-admin-{{ $.GhOrgLowerCase }}-installation-id" + metadataPolicy: None + secretKey: fs-admin-installationid + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: "prefapp-bot-pat" + metadataPolicy: None + secretKey: prefapp-bot-pat + refreshInterval: 24h0m0s + secretStoreRef: + kind: SecretStore + name: azure + target: + creationPolicy: Owner + deletionPolicy: Delete + name: bootstrap-secrets diff --git a/firestartr-bootstrap/external_secrets/azure_operator_secrets.tmpl b/firestartr-bootstrap/external_secrets/azure_operator_secrets.tmpl new file mode 100644 index 00000000..5135aef4 --- /dev/null +++ b/firestartr-bootstrap/external_secrets/azure_operator_secrets.tmpl @@ -0,0 +1,38 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: operator-secrets +spec: + data: + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: "fs-pem" + metadataPolicy: None + secretKey: fs-pem + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: "fs-app-id" + metadataPolicy: None + secretKey: fs-appid + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: "fs-{{ $.GhOrgLowerCase }}-installation-id" + metadataPolicy: None + secretKey: fs-installationid + - remoteRef: + conversionStrategy: Default + decodingStrategy: None + key: "prefapp-bot-pat" + metadataPolicy: None + secretKey: prefapp-bot-pat + refreshInterval: 24h0m0s + secretStoreRef: + kind: SecretStore + name: azure + target: + creationPolicy: Owner + deletionPolicy: Delete + name: operator-secrets diff --git a/firestartr-bootstrap/external_secrets/azure_secretstore.tmpl b/firestartr-bootstrap/external_secrets/azure_secretstore.tmpl new file mode 100644 index 00000000..9b165ad9 --- /dev/null +++ b/firestartr-bootstrap/external_secrets/azure_secretstore.tmpl @@ -0,0 +1,16 @@ +apiVersion: external-secrets.io/v1 +kind: SecretStore +metadata: + name: azure +spec: + provider: + azurekv: + tenantId: "{{ .CloudProvider.Config.TenantId }}" + vaultUrl: "https://{{ .CloudProvider.Config.KeyVaultName }}.vault.azure.net" + authSecretRef: + clientId: + name: azure-creds + key: clientId + clientSecret: + name: azure-creds + key: clientSecret diff --git a/firestartr-bootstrap/kubernetes.go b/firestartr-bootstrap/kubernetes.go index a72b2ae0..02e3052d 100644 --- a/firestartr-bootstrap/kubernetes.go +++ b/firestartr-bootstrap/kubernetes.go @@ -27,23 +27,45 @@ var OPERATOR_CREDS_SECRET_LIST = map[string]string{ "Pem": "ref:secretsclaim:operator-secrets:fs-pem", } +// isAzureProvider returns true when the cloud provider is Azure (azure or azurerm). +func isAzureProvider(providerName string) bool { + return providerName == "azure" || providerName == "azurerm" +} + func (m *FirestartrBootstrap) CreateKubernetesSecrets( ctx context.Context, kindContainer *dagger.Container, ) (*dagger.Container, error) { + // Select the credentials secret template based on the cloud provider. + secretTemplatePath := "templates/secret.tmpl" + if isAzureProvider(m.Creds.CloudProvider.Name) { + secretTemplatePath = "templates/azure_secret.tmpl" + } + secretsTmpl, err := dag.CurrentModule(). Source(). - File("templates/secret.tmpl"). + File(secretTemplatePath). Contents(ctx) + if err != nil { + return nil, err + } secretsCr, err := renderTmpl(secretsTmpl, m.Creds) if err != nil { return nil, err } + // Select ExternalSecret templates based on the cloud provider. + bootstrapSecretsTmplPath := "external_secrets/bootstrap_secrets.tmpl" + operatorSecretsTmplPath := "external_secrets/operator_secrets.tmpl" + if isAzureProvider(m.Creds.CloudProvider.Name) { + bootstrapSecretsTmplPath = "external_secrets/azure_bootstrap_secrets.tmpl" + operatorSecretsTmplPath = "external_secrets/azure_operator_secrets.tmpl" + } + bootstrapSecretsTmpl, err := dag.CurrentModule(). Source(). - File("external_secrets/bootstrap_secrets.tmpl"). + File(bootstrapSecretsTmplPath). Contents(ctx) if err != nil { return nil, err @@ -56,7 +78,7 @@ func (m *FirestartrBootstrap) CreateKubernetesSecrets( operatorSecretsTmpl, err := dag.CurrentModule(). Source(). - File("external_secrets/operator_secrets.tmpl"). + File(operatorSecretsTmplPath). Contents(ctx) if err != nil { return nil, err @@ -67,9 +89,31 @@ func (m *FirestartrBootstrap) CreateKubernetesSecrets( return nil, err } - awsSecretStoreFile := dag.CurrentModule(). - Source(). - File("external_secrets/aws_secretstore.yaml") + // Render and mount the SecretStore manifest based on the cloud provider. + const secretStorePath = "/secret_store/secretstore.yaml" + var secretStoreCr string + if isAzureProvider(m.Creds.CloudProvider.Name) { + azureSecretStoreTmpl, err := dag.CurrentModule(). + Source(). + File("external_secrets/azure_secretstore.tmpl"). + Contents(ctx) + if err != nil { + return nil, err + } + secretStoreCr, err = renderTmpl(azureSecretStoreTmpl, m.Creds) + if err != nil { + return nil, err + } + } else { + awsSecretStoreContent, err := dag.CurrentModule(). + Source(). + File("external_secrets/aws_secretstore.yaml"). + Contents(ctx) + if err != nil { + return nil, err + } + secretStoreCr = awsSecretStoreContent + } firestartrPodName, err := kindContainer. WithExec([]string{ @@ -105,9 +149,9 @@ func (m *FirestartrBootstrap) CreateKubernetesSecrets( "--timeout=10h", "-n", "external-secrets", }). - WithFile("/secret_store/aws_secretstore.yaml", awsSecretStoreFile). + WithNewFile(secretStorePath, secretStoreCr). WithExec([]string{ - "kubectl", "apply", "-f", "/secret_store/aws_secretstore.yaml", + "kubectl", "apply", "-f", secretStorePath, }). WithExec([]string{ "kubectl", "apply", "-f", BOOTSTRAP_SECRETS_FILE_PATH, diff --git a/firestartr-bootstrap/main.go b/firestartr-bootstrap/main.go index 08c26011..3923c69f 100644 --- a/firestartr-bootstrap/main.go +++ b/firestartr-bootstrap/main.go @@ -99,17 +99,26 @@ func New( } else { bootstrap.WebhookUrl = fmt.Sprintf("https://%s.events.%s.firestartr.dev", bootstrap.Customer, bootstrap.Env) } - bootstrap.WebhookSecretRef = fmt.Sprintf("/firestartr/%s/github-webhook/secret", bootstrap.Customer) - // We need to calculate the bucket (if necessary) - if creds.CloudProvider.Config.Bucket == nil { - calculatedBucket := fmt.Sprintf("tfstate-%s", bootstrap.Customer) - creds.CloudProvider.Config.Bucket = &calculatedBucket + // Secret references differ between providers: Azure Key Vault names must be + // alphanumeric + dashes only (no forward slashes), while AWS uses hierarchical + // Parameter Store paths. + if isAzureProvider(creds.CloudProvider.Name) { + bootstrap.WebhookSecretRef = "github-webhook-secret" + bootstrap.PrefappBotPatSecretRef = "prefapp-bot-pat" + bootstrap.FirestartrCliVersionSecretRef = "firestartr-cli-version" + } else { + bootstrap.WebhookSecretRef = fmt.Sprintf("/firestartr/%s/github-webhook/secret", bootstrap.Customer) + bootstrap.PrefappBotPatSecretRef = fmt.Sprintf("/firestartr/%s/prefapp-bot-pat", bootstrap.Customer) + bootstrap.FirestartrCliVersionSecretRef = fmt.Sprintf("/firestartr/%s/firestartr-cli-version", bootstrap.Customer) + + // We need to calculate the bucket (if necessary) + if creds.CloudProvider.Config.Bucket == nil { + calculatedBucket := fmt.Sprintf("tfstate-%s", bootstrap.Customer) + creds.CloudProvider.Config.Bucket = &calculatedBucket + } } - bootstrap.PrefappBotPatSecretRef = fmt.Sprintf("/firestartr/%s/prefapp-bot-pat", bootstrap.Customer) - bootstrap.FirestartrCliVersionSecretRef = fmt.Sprintf("/firestartr/%s/firestartr-cli-version", bootstrap.Customer) - claimsDotConfigDir, err := getClaimsDotConfigDir(ctx, bootstrap) if err != nil { return nil, err @@ -221,19 +230,36 @@ func (m *FirestartrBootstrap) ValidateBootstrap( errorMsgs = append(errorMsgs, err.Error()) } - _, err = m.ValidateSTSCredentials(ctx) - if err != nil { - errorMsgs = append(errorMsgs, err.Error()) - } + if isAzureProvider(m.Creds.CloudProvider.Name) { + err = m.ValidateAzureCredentials(ctx) + if err != nil { + errorMsgs = append(errorMsgs, err.Error()) + } - err = m.ValidateBucket(ctx) - if err != nil { - errorMsgs = append(errorMsgs, err.Error()) - } + err = m.ValidateAzureStorageAccount(ctx) + if err != nil { + errorMsgs = append(errorMsgs, err.Error()) + } - err = m.ValidateParameters(ctx, fmt.Sprintf("/firestartr/%s", m.Bootstrap.Customer)) - if err != nil { - errorMsgs = append(errorMsgs, err.Error()) + err = m.ValidateAzureKeyVault(ctx) + if err != nil { + errorMsgs = append(errorMsgs, err.Error()) + } + } else { + _, err = m.ValidateSTSCredentials(ctx) + if err != nil { + errorMsgs = append(errorMsgs, err.Error()) + } + + err = m.ValidateBucket(ctx) + if err != nil { + errorMsgs = append(errorMsgs, err.Error()) + } + + err = m.ValidateParameters(ctx, fmt.Sprintf("/firestartr/%s", m.Bootstrap.Customer)) + if err != nil { + errorMsgs = append(errorMsgs, err.Error()) + } } err = m.ValidatePrefappBotPat(ctx) diff --git a/firestartr-bootstrap/push_secrets.go b/firestartr-bootstrap/push_secrets.go index 45680c0e..bde23d13 100644 --- a/firestartr-bootstrap/push_secrets.go +++ b/firestartr-bootstrap/push_secrets.go @@ -9,13 +9,18 @@ func (m *FirestartrBootstrap) GeneratePushSecrets( ctx context.Context, ) (*dagger.Directory, error) { + secretStore := "aws" + if isAzureProvider(m.Creds.CloudProvider.Name) { + secretStore = "azure" + } + webHookPushSecret := PushSecretElement{ Name: "webhook-pushsecret", KubernetesSecret: "webhook-secret", KubernetesSecretKey: "webhook-secret-key", ParameterName: m.Bootstrap.WebhookSecretRef, Value: "my-secret-secret", - SecretStore: "aws", + SecretStore: secretStore, } prefappBotPatSecret := PushSecretElement{ @@ -24,7 +29,7 @@ func (m *FirestartrBootstrap) GeneratePushSecrets( KubernetesSecretKey: "botpat-secret-key", ParameterName: m.Bootstrap.PrefappBotPatSecretRef, Value: m.Creds.GithubApp.PrefappBotPat, - SecretStore: "aws", + SecretStore: secretStore, } prefappCliVersion := PushSecretElement{ @@ -33,7 +38,7 @@ func (m *FirestartrBootstrap) GeneratePushSecrets( KubernetesSecretKey: "cli-version-key", ParameterName: m.Bootstrap.FirestartrCliVersionSecretRef, Value: m.Bootstrap.Firestartr.CliVersion, - SecretStore: "aws", + SecretStore: secretStore, } rendered, err := renderPushSecret(ctx, &webHookPushSecret, "external_secrets/push_secret.tmpl") diff --git a/firestartr-bootstrap/schemas/credentials-file.json b/firestartr-bootstrap/schemas/credentials-file.json index 8eabe4e9..36bd7e35 100644 --- a/firestartr-bootstrap/schemas/credentials-file.json +++ b/firestartr-bootstrap/schemas/credentials-file.json @@ -6,6 +6,69 @@ "type": "string", "pattern": "^\\d+$", "description": "A string containing only one or more digits." + }, + "awsConfig": { + "type": "object", + "properties": { + "bucket": { + "type": "string" + }, + "region": { + "type": "string" + }, + "access_key": { + "type": "string" + }, + "secret_key": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "required": [ + "region", + "access_key", + "secret_key" + ] + }, + "azureConfig": { + "type": "object", + "properties": { + "tenant_id": { + "type": "string" + }, + "subscription_id": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "client_secret": { + "type": "string" + }, + "storage_account_name": { + "type": "string" + }, + "container_name": { + "type": "string" + }, + "resource_group_name": { + "type": "string" + }, + "key_vault_name": { + "type": "string" + } + }, + "required": [ + "tenant_id", + "client_id", + "client_secret", + "storage_account_name", + "container_name", + "resource_group_name", + "key_vault_name" + ] } }, "type": "object", @@ -14,32 +77,14 @@ "type": "object", "properties": { "name": { - "type": "string" + "type": "string", + "enum": ["aws", "azure", "azurerm"] }, "providerConfigName": { "type": "string" }, "config": { - "type": "object", - "properties": { - "bucket": { - "type": "string" - }, - "region": { - "type": "string" - }, - "access_key": { - "type": "string" - }, - "secret_key": { - "type": "string" - } - }, - "required": [ - "region", - "access_key", - "secret_key" - ] + "type": "object" }, "source": { "type": "string" @@ -51,6 +96,21 @@ "type": "string" } }, + "if": { + "properties": { + "name": { "enum": ["azure", "azurerm"] } + } + }, + "then": { + "properties": { + "config": { "$ref": "#/$defs/azureConfig" } + } + }, + "else": { + "properties": { + "config": { "$ref": "#/$defs/awsConfig" } + } + }, "required": [ "name", "config", diff --git a/firestartr-bootstrap/templates/azure_secret.tmpl b/firestartr-bootstrap/templates/azure_secret.tmpl new file mode 100644 index 00000000..cb0cfca8 --- /dev/null +++ b/firestartr-bootstrap/templates/azure_secret.tmpl @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: azure-creds +stringData: + clientId: {{ .CloudProvider.Config.ClientId }} + clientSecret: {{ .CloudProvider.Config.ClientSecret }} + tenantId: {{ .CloudProvider.Config.TenantId }} diff --git a/firestartr-bootstrap/types.go b/firestartr-bootstrap/types.go index 3f15e0b7..75d30d79 100644 --- a/firestartr-bootstrap/types.go +++ b/firestartr-bootstrap/types.go @@ -87,11 +87,22 @@ type CloudProvider struct { } type ConfigProvider struct { + // AWS fields Bucket *string `json:"bucket" yaml:"bucket"` Region string `json:"region" yaml:"region"` AccessKey string `json:"access_key" yaml:"access_key"` SecretKey string `json:"secret_key" yaml:"secret_key"` Token string `json:"token" yaml:"token"` + + // Azure fields + TenantId string `json:"tenant_id" yaml:"tenant_id"` + SubscriptionId string `json:"subscription_id" yaml:"subscription_id"` + ClientId string `json:"client_id" yaml:"client_id"` + ClientSecret string `json:"client_secret" yaml:"client_secret"` + StorageAccountName string `json:"storage_account_name" yaml:"storage_account_name"` + ContainerName string `json:"container_name" yaml:"container_name"` + ResourceGroupName string `json:"resource_group_name" yaml:"resource_group_name"` + KeyVaultName string `json:"key_vault_name" yaml:"key_vault_name"` } type GithubApp struct { From 85872ab486b8a51d803dffdc7d414433ddc1b64e Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Fri, 24 Jul 2026 13:21:52 +0200 Subject: [PATCH 2/3] fix(firestartr-bootstrap): update reference to kv --- firestartr-bootstrap/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firestartr-bootstrap/README.md b/firestartr-bootstrap/README.md index abd5137f..d46633bd 100644 --- a/firestartr-bootstrap/README.md +++ b/firestartr-bootstrap/README.md @@ -49,7 +49,7 @@ The following AWS Parameter Store parameters are required: #### 1.3 Azure requirements -An Azure Key Vault named according to `key_vault_name` in `Credentialsfile.yaml` (e.g. `firestartr-`) must exist and be accessible by the service principal provided in the credentials file. +An Azure Key Vault named according to `key_vault_name` in `Credentialsfile.yaml` (e.g. `firestartr-kv`) must exist and be accessible by the service principal provided in the credentials file. The following secrets must exist inside the Key Vault (Azure Key Vault names use alphanumeric characters and dashes only — no forward slashes): @@ -261,7 +261,7 @@ cloudProvider: storage_account_name: "tfstate" container_name: "tfstate" resource_group_name: "rg-firestartr-" - key_vault_name: "firestartr-" + key_vault_name: "firestartr-kv" source: hashicorp/azurerm type: azurerm version: "~> 3.0" From 515937d02449722f070802a91ff0f8202f803d6c Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Fri, 24 Jul 2026 13:43:58 +0200 Subject: [PATCH 3/3] fix(firestartr-bootstrap): remove duplicated dotFirestartr configuration from README --- firestartr-bootstrap/README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/firestartr-bootstrap/README.md b/firestartr-bootstrap/README.md index d46633bd..d006100e 100644 --- a/firestartr-bootstrap/README.md +++ b/firestartr-bootstrap/README.md @@ -122,9 +122,6 @@ pushFiles: secrets: push: true # When the process finishes, the generated crs will be pushed to the crs repository. repo: "state-secrets" # Normally, the state-secrets repository will be called "state-secrets", but it is possible to change the name. - dotFirestartr: - push: true # When the process finishes, the generated crs will be pushed to the crs repository. - repo: ".firestartr" # Normally, the .firestartr repository will be called ".firestartr", but it is possible to change the name. components: - name: "dot-firestartr" # claim name