[← Back to Reviews Index](../Stewards%20Reviews%20Index.md)

# Infra Deployment Review — Azure-AI-RAG-CSharp-Semantic-Kernel-Functions

| Field | Value |
|---|---|
| **Project** | Azure-AI-RAG-CSharp-Semantic-Kernel-Functions |
| **Date** | 2026-03-22 |
| **Steward** | Infra Deployment Steward |
| **Critical** | 3 |
| **Notable** | 4 |
| **Minor** | 3 |
| **Info** | 2 |
| **Total** | 12 |

---

## 1. Deployment Architecture Overview

The project deploys a multi-component Azure solution consisting of:

- **ChatAPI** — .NET 8 App Service (Windows)
- **DocumentLoaderFunction** — Python 3.11 Azure Function App (Linux)
- **Web** — React/Node.js App Service (Linux)
- **Supporting resources** — Azure CosmosDB, Azure AI Search, Azure OpenAI, Azure Storage, Application Insights, Log Analytics, Managed Identity

Infrastructure is defined in Bicep (`infra/main.bicep` + modules under `infra/core/` and `infra/app/`). Deployment is executed via a single PowerShell orchestration script (`infra/deploy.ps1`) that wraps `az deployment sub create` and three subsidiary scripts in `infra/scripts/`.

There is no CI/CD pipeline, no `azure.yaml` (azd), and no GitHub Actions or Azure DevOps pipeline configuration present anywhere in the repository. All deployment is fully manual and executed from a developer workstation.

---

## 2. CI/CD Pipeline Assessment

No CI/CD pipeline exists in this repository. There is no `.github/workflows/` directory and no Azure DevOps pipeline YAML. Deployment is entirely manual via the PowerShell scripts described in the README.

The `deploy.ps1` script performs a sequential orchestration:
1. `az login` (interactive login — workstation-only)
2. `az deployment sub create` (Bicep infra)
3. `deploy_functionapp.ps1` — zip-deploy the Python Function App
4. `deploy_api.ps1` — dotnet publish + zip-deploy the .NET API
5. `deploy_web.ps1` — npm install + npm build + zip-deploy the React app

This is entirely local-machine automation. It has no pipeline triggers, no approval gates, no environment promotion, and no separation of build from deploy.

**Notable gaps:**
- `az login` runs interactively — not usable in automation.
- `Start-Sleep -Seconds 120` is a fragile mechanism waiting for infrastructure readiness.
- Python helper (`directory_zipper.py`) is required on the executing machine with no dependency verification.
- No test stage in the pipeline.

---

## 3. Environment Promotion Assessment

Only one environment (`demo`) is hardcoded in `deploy.ps1`:

```powershell
$environmentName = "demo"
```

There is no staging environment, no dev/test/prod separation, and no promotion gates. All deployments go directly to the single `demo` environment. The README describes only a single deployment path for production use.

---

## 4. Secrets in Pipelines Assessment

There is no CI/CD pipeline, so pipeline-level secrets management does not apply directly. However, several secrets-related concerns exist at the deployment configuration level:

**Positive findings:**
- Managed Identity is used for all service-to-service authentication (OpenAI, Azure AI Search, Storage, CosmosDB data plane) — no API keys in Bicep.
- `disableLocalAuth: true` is set on Azure AI Search.
- Azure Functions use `AzureWebJobsStorage__credential: managedidentity` — no storage connection string.

**Concerns:**
- `CosmosDb_ConnectionString` is configured as an empty string in `api-app.bicep` (line 66) and the README explicitly instructs developers to manually add the Cosmos DB connection string via the Azure Portal after deployment. This means a credential is handled ad-hoc outside any automated or auditable process.
- `key-vault.bicep` is an empty file — Key Vault is provisioned as a resource in the module list, but the file has no content. This means Key Vault is referenced but non-functional.
- `KeyVaultUri` is set to an empty string in `loader-function.bicep` (line 103) — the Function App has a placeholder for Key Vault integration that is not wired up.
- The `deploy_web.ps1` script writes `REACT_APP_API_HOST` directly into a `.env` file on the developer's local filesystem during deployment (line 13), bypassing any secrets management.

---

## 5. Deployment Validation Assessment

There is no `az deployment what-if` pre-validation step in any of the deployment scripts. Bicep is submitted directly to `az deployment sub create` without a dry-run.

There are no post-deployment health checks. After each `az webapp deploy` or function deployment, the scripts proceed immediately or sleep for a fixed period (120 seconds) with no verification of application availability or health endpoint polling.

The README notes a manual post-deployment validation step (step 6: navigate to the web app and submit a test question), but this is entirely manual.

---

## 6. Rollback Assessment

No rollback mechanism exists. The deployment scripts do not:
- Create deployment snapshots
- Tag resource group states
- Use App Service deployment slots for zero-downtime swap and rollback
- Preserve previous zip artifacts for re-deployment

Because deployment replaces the running application with `az webapp deploy` directly, there is no mechanism to revert to a known-good state without manually re-running the entire deployment from source.

The `Start-Sleep -Seconds 120` buffer after Bicep deployment and the `--track-status false` flag on `az webapp deploy` (in `deploy_api.ps1`, line 26) means failures may not be detected at all during deployment.

---

## 7. Azure Developer CLI (azd) Assessment

No `azure.yaml` file exists in the repository. The project does not use the Azure Developer CLI. The deployment is entirely custom PowerShell + Azure CLI. There are no azd hooks, no service definitions, and no azd environment management.

The absence of azd is not itself a finding, but it represents a missed opportunity to benefit from standardized environment management, environment variable promotion, and pipeline integration that azd provides out of the box.

---

## 8. Findings

| Severity | ID | Title | File |
|---|---|---|---|
| 🔴 Critical | IDEP-CICD-001 | No CI/CD pipeline — deployment is fully manual | _(none)_ |
| 🔴 Critical | IDEP-CICD-002 | No deployment validation (`what-if`) before Bicep execution | `infra/deploy.ps1` |
| 🔴 Critical | IDEP-SECRETS-001 | CosmosDB connection string managed manually outside automation | `infra/app/api-app.bicep`, `README.md` |
| 🟡 Notable | IDEP-ENV-001 | Single hardcoded environment (`demo`) — no staging or promotion gates | `infra/deploy.ps1` |
| 🟡 Notable | IDEP-ROLLBACK-001 | No rollback mechanism — no deployment slots or artifact preservation | `infra/scripts/deploy_api.ps1` |
| 🟡 Notable | IDEP-VALIDATE-001 | No post-deployment health check after application deployment | `infra/scripts/deploy_api.ps1`, `deploy_web.ps1`, `deploy_functionapp.ps1` |
| 🟡 Notable | IDEP-SECRETS-002 | Key Vault provisioned but `key-vault.bicep` is empty and `KeyVaultUri` is unset | `infra/core/security/key-vault.bicep`, `infra/app/loader-function.bicep` |
| 🟢 Minor | IDEP-CICD-003 | Interactive `az login` in deploy script prevents pipeline automation | `infra/deploy.ps1` |
| 🟢 Minor | IDEP-CICD-004 | Fixed `Start-Sleep -Seconds 120` used as infra readiness wait | `infra/deploy.ps1` |
| 🟢 Minor | IDEP-CICD-005 | `--track-status false` suppresses deployment error detection | `infra/scripts/deploy_api.ps1` |
| ℹ️ Info | IDEP-SECRETS-003 | Managed Identity used for all service authentication — good practice | `infra/core/security/managed-identity.bicep`, all app bicep modules |
| ℹ️ Info | IDEP-AZD-001 | No `azure.yaml` — project does not use Azure Developer CLI | _(none)_ |

---

## 9. Recommended Improvements

| Finding | Recommended Action | Priority |
|---|---|---|
| IDEP-CICD-001 | Implement a GitHub Actions or Azure DevOps pipeline with build, test, and deploy stages. Use workload identity federation to replace `az login`. | High |
| IDEP-CICD-002 | Add `az deployment sub create --what-if` as a mandatory pre-deployment step to detect Bicep changes before applying them. | High |
| IDEP-SECRETS-001 | Wire CosmosDB connection retrieval into the deployment script using `az cosmosdb keys list` and set it as an App Setting programmatically, or use Key Vault with a Key Vault reference in App Settings. | High |
| IDEP-ENV-001 | Define at least two environments (e.g., `staging` and `production`) with promotion gates. Parameterise `$environmentName` as a pipeline variable and require approval before production deployment. | Medium |
| IDEP-ROLLBACK-001 | Configure App Service deployment slots (staging slot → swap to production). Preserve zip artifacts in Storage so prior versions can be re-deployed without rebuilding. | Medium |
| IDEP-VALIDATE-001 | After each `az webapp deploy`, poll the application's health endpoint or use `az webapp show --query state` to confirm the app is running before proceeding. | Medium |
| IDEP-SECRETS-002 | Implement `key-vault.bicep` to provision a Key Vault with access policies for the Managed Identity. Migrate `CosmosDb_ConnectionString` and any other runtime secrets to Key Vault references. Set `KeyVaultUri` in `loader-function.bicep`. | Medium |
| IDEP-CICD-003 | Replace `az login` with a service principal or workload identity federation credential passed via environment variable or pipeline secret. | Low |
| IDEP-CICD-004 | Replace `Start-Sleep -Seconds 120` with a polling loop that checks for Azure resource provisioning state (`az deployment sub show --query properties.provisioningState`). | Low |
| IDEP-CICD-005 | Remove `--track-status false` from `az webapp deploy` calls so that deployment failures surface as script errors and halt execution. | Low |
| IDEP-AZD-001 | Consider adopting Azure Developer CLI (`azure.yaml`) to standardise environment management, pipeline templates, and deployment hooks. This is optional but reduces boilerplate for multi-environment promotion. | Low |

---

## Footer

> This review is based on static analysis of IaC files, deployment scripts, and project configuration. No runtime environment was inspected. Findings reflect the state of the repository at the time of review and may not capture runtime-only conditions.
>
> Generated by: **Infra Deployment Steward** (`infra-deployment-steward.md`) on 2026-03-22.
