Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 63 additions & 15 deletions firestartr-bootstrap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,42 @@ The following AWS Parameter Store parameters are required:
- `/firestartr/<customer>/fs-<customer>-argocd/app-id`
- `/firestartr/<customer>/fs-<customer>-argocd/<org>/installation-id`

#### 1.3 Azure requirements

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):

| 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-<org>-installation-id` | Operator GitHub App installation ID for `<org>` |
| `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-<org>-installation-id` | Admin GitHub App installation ID for `<org>` |
| `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-<org>-installation-id` | Checks GitHub App installation ID for `<org>` |
| `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-<org>-installation-id` | State GitHub App installation ID for `<org>` |
| `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-<org>-installation-id` | Import GitHub App installation ID for `<org>` |
| `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-<org>-installation-id` | ArgoCD GitHub App installation ID for `<org>` |
| `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
Expand Down Expand Up @@ -86,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
Expand Down Expand Up @@ -210,28 +243,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-<env>` 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: "<azure-tenant-id>"
subscription_id: "<azure-subscription-id>"
client_id: "<azure-client-id>"
client_secret: "<azure-client-secret>"
storage_account_name: "tfstate<customer>"
container_name: "tfstate"
source: hashicorp/aws
type: aws
version: ~> 4.0
resource_group_name: "rg-firestartr-<env>"
key_vault_name: "firestartr-kv"
source: hashicorp/azurerm
type: azurerm
version: "~> 3.0"
github:
providerConfigName: github-app-provider-config-name
prefappBotPat: "<your-prefapp-bot-pat>"
operatorPat: "<your-operator-pat>"
```

All `<placeholders>` 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

`<your-kind-port>`: Replace with the port that kind is using to expose the Kubernetes API server (noted in step 1.1).
Expand Down
116 changes: 116 additions & 0 deletions firestartr-bootstrap/azure.go
Original file line number Diff line number Diff line change
@@ -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
}
38 changes: 38 additions & 0 deletions firestartr-bootstrap/external_secrets/azure_bootstrap_secrets.tmpl
Original file line number Diff line number Diff line change
@@ -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
38 changes: 38 additions & 0 deletions firestartr-bootstrap/external_secrets/azure_operator_secrets.tmpl
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions firestartr-bootstrap/external_secrets/azure_secretstore.tmpl
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading