-
Notifications
You must be signed in to change notification settings - Fork 0
Migrate engine deploy auth to Azure OIDC #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # Codeflow Azure Pipeline Setup | ||
|
|
||
| The Codeflow engine deployment workflow uses GitHub Actions OIDC with a user-assigned managed identity. Do not use the legacy `AZURE_CREDENTIALS` JSON secret for the production deploy path. | ||
|
|
||
| ## Production Environment Values | ||
|
|
||
| Create a GitHub environment named `production` and configure these environment variables: | ||
|
|
||
| | Variable | Purpose | | ||
| | --- | --- | | ||
| | `AZURE_CLIENT_ID` | Client ID of the pipeline user-assigned managed identity. | | ||
| | `AZURE_TENANT_ID` | Tenant ID for the Azure subscription. | | ||
| | `AZURE_SUBSCRIPTION_ID` | Subscription containing Codeflow production resources. | | ||
| | `AZURE_RESOURCE_GROUP` | Production resource group, expected default: `pvc-prod-codeflow-rg`. | | ||
| | `AZURE_CONTAINER_APP` | Production Container App, expected default: `pvc-prod-codeflow-api`. | | ||
| | `AZURE_CONTAINER_REGISTRY` | ACR name for production images, expected default: `pvcprodcodeflowacr`. | | ||
|
|
||
| The workflow deploys only to existing infrastructure. Provision or import the production Container App and ACR before running the deploy job. | ||
|
|
||
| Do not apply the root `orchestration/infrastructure/terraform` stack as the runtime prerequisite without reviewing it first; that stack still reflects an older AKS-shaped deployment. Use `orchestration/infrastructure/terraform/runtime` for the production Container App runtime stack. | ||
|
|
||
| ## Azure Setup | ||
|
|
||
| Run `scripts/setup-azure-auth-for-pipeline.ps1` after confirming the production resource group exists. The script creates a dedicated pipeline identity, assigns resource-group scoped RBAC, grants ACR push access, and adds a federated credential for the GitHub `production` environment. | ||
|
|
||
| The signed-in Azure CLI identity running the script must be allowed to create role assignments on the deployment resource group or subscription. Use `Owner`, `User Access Administrator`, `Role Based Access Control Administrator`, or another role that includes `Microsoft.Authorization/roleAssignments/write`; `Contributor` alone is not sufficient for the RBAC step. | ||
|
|
||
| After setup, store the script outputs in the GitHub `production` environment variables. Keep any approval gates on the environment so pushes to `master` still require explicit deployment approval. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| name: Build and Deploy CodeFlow Engine | ||
|
|
||
| on: | ||
| push: | ||
|
|
@@ -8,17 +8,20 @@ | |
| - "engine/**" | ||
| - ".github/workflows/deploy-autopr-engine.yml" | ||
| - ".github/app-manifest.yml" | ||
| - ".azure/pipeline-setup.md" | ||
| - "scripts/setup-azure-auth-for-pipeline.ps1" | ||
| pull_request: | ||
| paths: | ||
| - "engine/**" | ||
| - ".github/workflows/deploy-autopr-engine.yml" | ||
| - ".github/app-manifest.yml" | ||
| - ".codeflow.yml" | ||
| - ".azure/pipeline-setup.md" | ||
| - "scripts/setup-azure-auth-for-pipeline.ps1" | ||
| workflow_dispatch: | ||
|
|
||
| env: | ||
| REGISTRY: ghcr.io | ||
| IMAGE_NAME: ${{ github.repository }} | ||
| IMAGE_REPOSITORY: codeflow-engine | ||
|
|
||
| jobs: | ||
| # PR validation - build and validate only, no push/deploy | ||
|
|
@@ -70,28 +73,39 @@ | |
| name: Build and Push Container Image | ||
| runs-on: ubuntu-latest | ||
| if: github.event_name != 'pull_request' | ||
| environment: production | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| id-token: write | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Azure Login | ||
| uses: azure/login@v2 | ||
| with: | ||
| client-id: ${{ vars.AZURE_CLIENT_ID }} | ||
| tenant-id: ${{ vars.AZURE_TENANT_ID }} | ||
| subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} | ||
|
|
||
| - name: Validate registry configuration | ||
| run: | | ||
| if [ -z "${{ vars.AZURE_CONTAINER_REGISTRY }}" ]; then | ||
| echo "::error::Configure AZURE_CONTAINER_REGISTRY in the production environment." | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Log in to Azure Container Registry | ||
| run: az acr login --name "${{ vars.AZURE_CONTAINER_REGISTRY }}" | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Log in to Container Registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Extract metadata | ||
| id: meta | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
| images: ${{ vars.AZURE_CONTAINER_REGISTRY }}.azurecr.io/${{ env.IMAGE_REPOSITORY }} | ||
| tags: | | ||
| type=ref,event=branch | ||
| type=ref,event=pr | ||
|
|
@@ -118,20 +132,40 @@ | |
| runs-on: ubuntu-latest | ||
| needs: build-and-push | ||
| if: (github.ref == 'refs/heads/master' && github.event_name == 'push') || github.event_name == 'workflow_dispatch' | ||
| environment: production | ||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
| steps: | ||
| - name: Azure Login | ||
| uses: azure/login@v2 | ||
| with: | ||
| creds: ${{ secrets.AZURE_CREDENTIALS }} | ||
| client-id: ${{ vars.AZURE_CLIENT_ID }} | ||
| tenant-id: ${{ vars.AZURE_TENANT_ID }} | ||
| subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} | ||
|
|
||
| - name: Update existing Container App | ||
| env: | ||
| RESOURCE_GROUP: ${{ vars.AZURE_RESOURCE_GROUP }} | ||
| CONTAINER_APP: ${{ vars.AZURE_CONTAINER_APP }} | ||
| IMAGE: ${{ vars.AZURE_CONTAINER_REGISTRY }}.azurecr.io/${{ env.IMAGE_REPOSITORY }}:${{ github.ref_name }} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== target file =="
wc -l .github/workflows/deploy-autopr-engine.yml || true
echo "== relevant workflow lines =="
sed -n '1,240p' .github/workflows/deploy-autopr-engine.yml || true
echo "== refs to deploy/build jobs and metadata =="
rg -n "metadata|IMAGE_REPOSITORY|build-and-push|needs\.|github\.ref_name|branches|INPUT_REF|pull_request|push|manual|workflow_dispatch|meta\.outputs" .github/workflows/deploy-autopr-engine.ymlRepository: phoenixvc/codeflow-engine Length of output: 7210 🌐 Web query:
💡 Result: In the docker/metadata-action (v5), the type=ref configuration is used to generate tags based on Git references for events such as branch, tag, and pull_request [1][2]. Crucially, workflow_dispatch is a GitHub event, not a valid ref type for the type=ref configuration [3]. While workflow_dispatch events are sometimes referenced in documentation tables to show how they behave when they trigger a workflow (e.g., they may associate with a branch reference), you cannot use type=ref,event=workflow_dispatch [3][4]. If you are attempting to use a manual input (such as an inputs.version from a workflow_dispatch event) as a tag, you should use the type=raw configuration with a custom value, rather than type=ref [3]. Example of using a manual input for tagging: tags: | type=raw,value=${{ github.event.inputs.version }} Alternatively, if you are using semver patterns with a manual version input, you can use the value attribute [5]: tags: | type=semver,pattern={{major}}.{{minor}}.{{patch}},value=${{ github.event.inputs.version }} The outputs.tags and outputs.labels generated by the action can then be accessed in subsequent steps, such as in the docker/build-push-action, using ${{ steps.meta.outputs.tags }} [1][6]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== metadata action outputs/tags relevant docs =="
python3 - <<'PY'
from urllib.request import urlopen, Request
url = "https://raw.githubusercontent.com/docker/metadata-action/v5/README.md"
req = Request(url, headers={"User-Agent": "CodeRabbit-review-verification"})
text = urlopen(req, timeout=20).read().decode()
lines = text.splitlines()
for i, line in enumerate(lines):
if "outputs.tags" in line or "outputs.version" in line or "type=ref,event=branch" in line:
start=max(0, i-3); end=min(len(lines), i+8)
print(f"--- around line {i+1} ---")
for j in range(start,end):
print(f"{j+1:4}: {lines[j]}")
print()
PYRepository: phoenixvc/codeflow-engine Length of output: 2806 🌐 Web query:
💡 Result: When using docker/metadata-action in a GitHub Actions workflow triggered by a manual event like workflow_dispatch, you cannot directly use type=ref,event=branch (or other ref-based types) to automatically derive tags from the manual run's reference, as these types are designed for specific Git-event triggers (push, pull_request, etc.) [1][2][3]. To generate tags during a manual workflow_dispatch run, use type=raw and explicitly pass the reference name via the value attribute [4][3]. Example implementation: 1. Define an input in your workflow for the tag or branch name [3]. 2. Use type=raw in your metadata-action step to consume that input [3]. workflow configuration snippet: on: workflow_dispatch: inputs: tag_name: description: 'Tag or branch name to build' required: true default: 'main' jobs: docker: runs-on: ubuntu-latest steps: - name: Docker meta id: meta uses: docker/metadata-action@v5 with: images: name/app tags: | type=raw,value=${{ github.event.inputs.tag_name }} # Or use github.ref_name if the manual run is triggered on a specific branch # type=raw,value=${{ github.ref_name }} This approach bypasses the restrictions on ref-based tag generation by manually providing the value that the action should use for the image tag [4][3]. If you do not have a specific input, you can use ${{ github.ref_name }} directly as the value for type=raw, provided the workflow was triggered against the intended branch or tag context [1][3]. Citations:
🌐 Web query:
💡 Result: The docker/metadata-action automatically handles tag sanitization to ensure compliance with Docker image tag specifications [1][2]. When generating tags—particularly from dynamic sources like branches or pull request references (via Citations:
🌐 Web query:
💡 Result: In the Citations:
Deploy the exact tag emitted by the build job. This workflow builds with 🤖 Prompt for AI Agents |
||
| run: | | ||
| RESOURCE_GROUP="prod-rg-san-codeflow" | ||
| CONTAINER_APP="prod-codeflow-san-app" | ||
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}" | ||
| missing=0 | ||
| for name in RESOURCE_GROUP CONTAINER_APP IMAGE; do | ||
| if [ -z "${!name}" ]; then | ||
| echo "::error::Missing required deployment value: $name" | ||
| missing=1 | ||
| fi | ||
| done | ||
|
|
||
| if [ "$missing" -ne 0 ]; then | ||
| echo "::error::Configure the production environment variables documented in .azure/pipeline-setup.md." | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! az containerapp show -n "$CONTAINER_APP" -g "$RESOURCE_GROUP" >/dev/null; then | ||
| echo "::error::Container App $CONTAINER_APP does not exist in $RESOURCE_GROUP. Create infrastructure from phoenixvc/codeflow-infrastructure first." | ||
| echo "::error::Cannot verify Container App $CONTAINER_APP in $RESOURCE_GROUP." | ||
| echo "::error::Confirm the app exists and the pipeline identity has access before deploying this image." | ||
| exit 1 | ||
| fi | ||
|
|
||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # Codeflow Runtime Terraform | ||
|
|
||
| This stack owns the Azure Container Apps runtime for Codeflow Engine. | ||
|
|
||
| Names follow the ADR-0027-style convention used for live Codeflow resources: | ||
|
|
||
| - Resource group: `pvc-prod-codeflow-rg` | ||
| - Container App: `pvc-prod-codeflow-api` | ||
| - Container Apps Environment: `pvc-prod-codeflow-cae` | ||
| - Log Analytics Workspace: `pvc-prod-codeflow-law` | ||
| - Container Registry: `pvcprodcodeflowacr` | ||
|
|
||
| The stack expects the Codeflow image to exist in ACR. For a first-time bootstrap before the image is | ||
| available, override `initial_image` with a temporary public image, then switch it back to the ACR | ||
| image after the first build. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| data "azurerm_resource_group" "codeflow" { | ||
| name = var.resource_group_name | ||
| } | ||
|
|
||
| resource "azurerm_log_analytics_workspace" "runtime" { | ||
| name = var.log_analytics_workspace_name | ||
| location = var.location | ||
| resource_group_name = data.azurerm_resource_group.codeflow.name | ||
| sku = "PerGB2018" | ||
| retention_in_days = 30 | ||
|
|
||
| tags = var.tags | ||
| } | ||
|
|
||
| resource "azurerm_container_registry" "runtime" { | ||
| name = var.container_registry_name | ||
| location = var.location | ||
| resource_group_name = data.azurerm_resource_group.codeflow.name | ||
| sku = "Basic" | ||
| admin_enabled = false | ||
|
|
||
| tags = var.tags | ||
| } | ||
|
|
||
| resource "azurerm_user_assigned_identity" "container_app" { | ||
| name = var.container_app_identity_name | ||
| location = var.location | ||
| resource_group_name = data.azurerm_resource_group.codeflow.name | ||
|
|
||
| tags = var.tags | ||
| } | ||
|
|
||
| resource "azurerm_role_assignment" "container_app_acr_pull" { | ||
| scope = azurerm_container_registry.runtime.id | ||
| role_definition_name = "AcrPull" | ||
| principal_id = azurerm_user_assigned_identity.container_app.principal_id | ||
| } | ||
|
|
||
| resource "azurerm_container_app_environment" "runtime" { | ||
| name = var.container_app_environment_name | ||
| location = var.location | ||
| resource_group_name = data.azurerm_resource_group.codeflow.name | ||
| log_analytics_workspace_id = azurerm_log_analytics_workspace.runtime.id | ||
|
|
||
| tags = var.tags | ||
| } | ||
|
|
||
| resource "azurerm_container_app" "api" { | ||
| name = var.container_app_name | ||
| container_app_environment_id = azurerm_container_app_environment.runtime.id | ||
| resource_group_name = data.azurerm_resource_group.codeflow.name | ||
| revision_mode = "Single" | ||
|
|
||
| identity { | ||
| type = "UserAssigned" | ||
| identity_ids = [azurerm_user_assigned_identity.container_app.id] | ||
| } | ||
|
|
||
| registry { | ||
| server = azurerm_container_registry.runtime.login_server | ||
| identity = azurerm_user_assigned_identity.container_app.id | ||
| } | ||
|
|
||
| ingress { | ||
| external_enabled = true | ||
| target_port = 8080 | ||
|
|
||
| traffic_weight { | ||
| percentage = 100 | ||
| latest_revision = true | ||
| } | ||
| } | ||
|
|
||
| template { | ||
| min_replicas = 0 | ||
| max_replicas = 1 | ||
|
|
||
| container { | ||
| name = "codeflow-engine" | ||
| image = var.initial_image | ||
|
Comment on lines
+78
to
+80
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== file outline/size =="
wc -l orchestration/infrastructure/terraform/runtime/main.tf 2>/dev/null || true
echo "== relevant main.tf lines =="
sed -n '1,140p' orchestration/infrastructure/terraform/runtime/main.tf 2>/dev/null || true
echo "== search initial_image and workflow/image refs =="
rg -n "initial_image|workflow|image|ContainerApp|containerregistry|azure\.containerapps|azuredevops|github\.actions" orchestration/infrastructure -S || true
rg -n "initial_image|workflow-deploy|production dispatch|dispatch|ref|image" . -g '!**/.git/**' -g '!**/node_modules/**' -S | head -200
echo "== git diff stat if available =="
git diff --stat HEAD 2>/dev/null || true
git diff -- orchestration/infrastructure/terraform/runtime/main.tf 2>/dev/null | sed -n '1,220p' || trueRepository: phoenixvc/codeflow-engine Length of output: 42293 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== workflow files matching deploy-codeflow-engine or similar =="
git ls-files .github orchestration/infrastructure -z | tr '\0' '\n' | rg '(\.github/workflows/.*(deploy-codeflow-engine|deploy).*\.ya?ml|deploy-codeflow-engine)' || true
echo "== deploy workflow outline/content =="
fd -p '.github/workflows/deploy-codeflow-engine.yml' . -x sh -c 'echo "--- $1"; sed -n "1,220p" "$1"' sh {} 2>/dev/null || true
echo "== terraform lifecycle directives =="
rg -n "lifecycle|ignore_changes|azurerm_container_app|initial_image" orchestration/infrastructure/terraform -S || true
echo "== runtime variables/docs relevant =="
sed -n '1,90p' orchestration/infrastructure/terraform/runtime/variables.tf
sed -n '1,30p' orchestration/infrastructure/terraform/runtime/README.mdRepository: phoenixvc/codeflow-engine Length of output: 4235 Prevent Terraform from reverting workflow-deployed images.
🤖 Prompt for AI Agents |
||
| cpu = 0.5 | ||
| memory = "1Gi" | ||
|
|
||
| env { | ||
| name = "CODEFLOW_JSON_LOGGING" | ||
| value = "true" | ||
| } | ||
|
|
||
| env { | ||
| name = "CODEFLOW_LOG_LEVEL" | ||
| value = "INFO" | ||
| } | ||
|
|
||
| env { | ||
| name = "CODEFLOW_SKIP_DB_INIT" | ||
| value = "1" | ||
| } | ||
|
|
||
| env { | ||
| name = "CODEFLOW_STORAGE_BACKEND" | ||
| value = "memory" | ||
| } | ||
|
|
||
| env { | ||
| name = "ENVIRONMENT" | ||
| value = "staging" | ||
| } | ||
|
Comment on lines
+104
to
+107
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Set the production runtime environment correctly. The resources, tags, documentation, and deployment workflow target production, but the Container App sets 🤖 Prompt for AI Agents |
||
|
|
||
| env { | ||
| name = "PORT" | ||
| value = "8080" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| depends_on = [ | ||
| azurerm_role_assignment.container_app_acr_pull | ||
| ] | ||
|
|
||
| tags = var.tags | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: phoenixvc/codeflow-engine
Length of output: 297
🏁 Script executed:
Repository: phoenixvc/codeflow-engine
Length of output: 7357
Disable persisted checkout credentials.
Only Docker builds and Azure updates follow this checkout, with no authenticated Git access needed. Set
persist-credentials: falsesoGITHUB_TOKENis not stored in local Git config during the build.Proposed fix
- name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false📝 Committable suggestion
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 81-82: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 Prompt for AI Agents
Source: Linters/SAST tools