This directory contains scripts and Terraform configurations for the one-time setup of Azure infrastructure and GitHub Actions OIDC authentication.
This setup MUST be run from a local development machine, NOT from GitHub Actions. Remember to login using incognito mode in browser with device code to avoid token expired weird issues
-
Chicken-and-egg problem: GitHub Actions needs OIDC credentials to authenticate to Azure, but we need to create those credentials first. The
initial-azure-setup.shscript creates the managed identity and federated credentials that enable GitHub Actions to work. -
Security group membership: The script adds the managed identity to the appropriate Entra ID security group. This requires owner permissions on the security group, which are typically held by project leads, not service principals.
-
Interactive confirmation: The Terraform deployment prompts for confirmation before creating resources, ensuring human oversight for foundational infrastructure.
-
One-time setup: This is a bootstrap process that only needs to run once per environment and adds the self hosted runner in
toolsenvironment. After completion, GitHub Actions handles all subsequent deployments. -
VM-Secret-Key: The VM access is done if needed at all via bastion using ssh private key (or Entra ID SSH login with
az network bastion ssh --auth-type AAD), which is another reason to run from dev machine who is owner of the subscription.
The initial-azure-setup.sh script orchestrates the entire setup process:
initial-azure-setup.sh
│
├── 1. Create User-Assigned Managed Identity
├── 2. Configure OIDC Federated Credentials
├── 3. Add Identity to Security Group
├── 4. Create Terraform State Storage Account
├── 5. Create GitHub Environment & Secrets (optional)
---
## Quick Start (Recommended)
Run the complete setup with a single command:
```bash
# Navigate to initial-setup directory
cd initial-setup
# Run the setup script for tools environment
./initial-azure-setup.sh \
-g "ABCD-tools-networking" \
-n "myapp-tools-identity" \
-r "myorg/myrepo" \
-e "tools" \
-s "12345678-1234-1234-1234-123456789012" \
--create-storage \
--create-github-secrets
This will:
- Create the managed identity and OIDC credentials
- Set up the Terraform state storage account
- Create GitHub environment and secrets
- Operating System - Linux or macOS (Windows not supported)
- Access to Azure Landing Zone - VNet must exist
- Security Group Ownership - Must be owner of
DO_PuC_Azure_Live_{LicensePlate}_Contributor - terraform.tfvars - Configuration file with your values (in
infra/folder)
The initial-azure-setup.sh script automatically detects and installs missing tools on Linux and macOS:
| Tool | Required | Installation Method |
|---|---|---|
| Azure CLI | ✅ Yes | apt/yum/brew (depending on OS) |
| Terraform >= 1.12.0 | ✅ Yes | apt/yum/brew (depending on OS) |
| GitHub CLI | ❌ Optional | apt/yum/brew (for automatic secret creation) |
When you run initial-azure-setup.sh:
- Detection: Script checks if each tool is already installed
- Prompting: If a required tool is missing, you'll be prompted:
Azure CLI not found. Install Azure CLI? (yes/no) - Auto-Installation: Responds to
yeswith automatic installation for your OS - Graceful Fallback: On unsupported systems, provides manual installation guidance
Linux:
apt(Debian/Ubuntu)yum(RedHat/CentOS/Fedora)pacman(Arch Linux)
macOS:
brew(Homebrew)
If your system uses a different package manager, the script will exit with a link to manual installation instructions.
# -----------------------------------------------------------------------------
# Terraform Variables Configuration
# -----------------------------------------------------------------------------
# Application Configuration
app_env = "tools"
app_name = "ai-hub-deploy-utils"
# Azure Resource Configuration
location = "Canada Central"
resource_group_name = "ai-hub-deploy-utils-tools"
# Virtual Network Configuration (existing VNet from platform team)
vnet_name = "$licenseplate-tools-vwan-spoke" # Set via TF_VAR_vnet_name or GitHub secret
vnet_resource_group_name = "$licenseplate-tools-networking" # Set via TF_VAR_vnet_resource_group_name or GitHub secret
vnet_address_space = "$address-space" # Set via TF_VAR_vnet_address_space or GitHub secret
# Common Tags
common_tags = {
Environment = "tools"
Project = "ai-hub-deploy-utils"
ManagedBy = "Terraform"
}
subscription_id = "$subscription_id"
tenant_id = "$tenant_id$"
client_id = "" , just set it to blank, since it will take user authz
use_oidc = false # keep it false, since it is user authz
# -----------------------------------------------------------------------------
# GitHub Runners on Azure Container Apps
# -----------------------------------------------------------------------------
# These are set via TF_VAR_* environment variables in GitHub Actions
github_runners_aca_enabled = true # Enable in GitHub Actions workflow
github_organization = "bcgov" # Set via TF_VAR_github_organization
github_repository = "ai-hub-tracking" # Set via TF_VAR_github_repository
github_runner_pat = "$github_pat" # Set via TF_VAR_github_runner_pat (sensitive)If you skipped the infrastructure deployment during setup, or need to run it separately:
# 1. Navigate to infra directory
cd initial-setup/infra
# 2. Ensure terraform.tfvars is configured
# Edit terraform.tfvars with your values
# 3. Login to Azure (if not already)
az login
# 4. Initialize Terraform
./deploy-terraform.sh init
# 5. Preview changes
./deploy-terraform.sh plan
# 6. Apply changes
./deploy-terraform.sh apply# Deploy only the network module
./deploy-terraform.sh apply -target=module.network
# Deploy only the GitHub runners module
./deploy-terraform.sh apply -target=module.github_runners_acaAzure Bastion + jumpbox are managed by the bcgov action, not this root. Use
.github/workflows/add-or-remove-module.yml(apply/plan/destroy) to manage the Bastion stack.
The legacy chisel azure-proxy App Service and the in-repo bastion/jumpbox modules (and their
AzureBastionSubnet/jumpbox subnets) have been removed in favour of the
bcgov/action-deployer-vm-bastion-alz
action. Before the action runs against the shared tools VNet for the first time:
- New GitHub secrets (tools environment):
TOOLS_SUBSCRIPTION_ID,BASTION_SUBNET_ADDRESS_PREFIX(≥ /26),JUMPBOX_SUBNET_ADDRESS_PREFIX,VM_ADMIN_LOGIN_PRINCIPAL_IDS— the last must include the CI service principal's object id soaz network bastion ssh --auth-type AADworks. Reuse the CIDRs previously allocated by the network module (.144/28jumpbox,.192/26bastion) to avoid re-IP. - If the old in-repo bastion/jumpbox/subnets were ever applied, remove them from this root's state
first (e.g.
terraform state rmthe oldmodule.bastion/module.jumpbox/subnet resources, then delete the orphaned Azure subnets) so the action can create its own without anAzureBastionSubnetcollision. If they were never applied (dormant), there is nothing to migrate. - Run
.github/workflows/.deployer.yml(viaadd-or-remove-module.yml→ apply) once. It applies the initial-setup infra (resource group, network, monitoring) first, then creates the Bastion + jumpbox + subnets fresh inai-hub-bastion-tools, reusing the Log Analytics workspace from the initial-setup stage.
initial-setup/infra/
├── deploy-terraform.sh # Deployment wrapper script
├── main.tf # Root module and resource group
├── variables.tf # Input variable definitions
├── outputs.tf # Output values
├── providers.tf # Provider configuration
├── backend.tf # Remote state configuration
├── terraform.tfvars # Variable values (not committed)
└── modules/
├── github-runners-aca/ # Self-hosted GitHub runners
└── network/ # Subnets and NSGs
Azure Bastion + jumpbox (the private-endpoint tunnel) are no longer in-repo modules. They are provisioned by the
bcgov/action-deployer-vm-bastion-alzaction via.github/workflows/.deployer.yml. The legacy chiselazure-proxymodule has been removed.
Terraform state is stored in Azure Storage:
- Resource Group: Configured via
BACKEND_RESOURCE_GROUP - Storage Account: Configured via
BACKEND_STORAGE_ACCOUNT - Container:
tfstate - Key:
ai-hub-deploy-utils/tools/terraform.tfstate
Backend values are injected via the deployment script using -backend-config flags.
The deployment script supports these environment variables:
| Variable | Description | Required |
|---|---|---|
TF_VAR_subscription_id |
Azure Subscription ID | If no tfvars |
TF_VAR_tenant_id |
Azure Tenant ID | If no tfvars |
TF_VAR_client_id |
Azure Client ID (OIDC) | If no tfvars |
ARM_USE_OIDC |
Use OIDC authentication | Optional |
CI |
Enable CI mode (auto-approve) | Optional |
- Terraform Reference - Detailed module documentation
- Workflows - GitHub Actions workflows
- OIDC Setup - Azure authentication setup