The Advisory AI platform — governance assessment, artifact generation, code scanning, compliance gates — packaged as a corporate-ready standalone repository. Kiro generated the entire deployment layer: 4 Bicep modules, two GitHub Actions pipelines, a blast radius audit tool, a unified seed runner replacing 18 numbered scripts, smoke tests, and environment validation. Zero lines of existing business logic were modified.
The starting state: a working advisory platform deployed manually — no IaC, no CI/CD, 18+ numbered seed scripts, business logic scattered at the root. The goal: package it as a corporate-ready repository another team could clone and deploy from scratch. Kiro generated the entire transformation from a design spec.
function_app.py, governance_routes.py, and code_scanner.py
are relocated from the flat root into src/functions/ but never touched otherwise —
not a single line changed. All new code is infrastructure: Bicep templates, pipeline YAML,
Python tooling (audit + seed runner), and shell scripts. The design spec explicitly called this
out as a constraint, and Kiro honored it. The blast radius audit tool was generated to verify
this guarantee: it checks file presence, sizes, SHA-256 hashes, Python syntax, and import
resolution across the entire repository after restructuring.
Orchestrator main.bicep composing compute, data, storage, and monitoring modules. No state file. Consumption Plan for dev, Premium EP1 for prod. System-assigned Managed Identity on the Function App.
deploy.yml: build → deploy-functions → deploy-web → verify. infra.yml: triggers on Bicep/config changes, runs az deployment group create with environment parameter files.
Standalone scripts/audit.py that generates SHA-256 manifests, checks presence/size of every expected file, validates Python syntax, and checks import resolution. Runnable via make audit. Exit code 0 = all clear.
18+ numbered migration scripts collapsed into a single SeedRunner class with 4 ordered phases: verify containers, seed frameworks (NIST, ISO, EU AI Act, etc.), seed scan patterns, optional demo data. Idempotent via deterministic IDs + upsert.
Old path → new path mapping for every file: business logic into src/functions/, web assets into src/web/, seed scripts into scripts/seed/, docs into docs/reference/ and docs/archive/.
3 .bicepparam files for dev/staging/prod — controlling Function App SKU, Cosmos DB billing mode (serverless vs provisioned autoscale), storage redundancy (LRS vs GRS), and throughput limits.
7 containers with partition keys and TTL configured. Managed Identity assigned Cosmos DB Built-in Data Contributor role — no connection strings in app settings. Storage similarly uses Storage Blob Data Contributor.
smoke-test.sh checks health endpoint, static website, governance API, and Cosmos DB connectivity with retry logic. validate-env.sh checks all required variables before any deployment step runs.
Hypothesis tests covering: audit manifest completeness + SHA-256 correctness, import resolution accuracy, and seed document ID determinism. 100+ random iterations each, covering the pure Python logic layers.
All resources in a single resource group per environment. Managed Identity eliminates every connection string from app settings.
How a fresh clone goes from zero to running in a new Azure subscription.
bash scripts/validate-env.sh checks all required variables: Cosmos connection, AI Foundry endpoint/key, storage account, deployment name. Exits with a clear list of anything missing before any Azure API is called.
az deployment group create --template-file infra/main.bicep --parameters config/dev.bicepparam. Bicep creates resource group, Function App + plan, Cosmos DB + 7 containers, Storage Account + static hosting, Application Insights — and assigns Managed Identity RBAC roles atomically.
make seed-data runs the unified SeedRunner: verifies all 7 containers exist, upserts 7 framework documents (NIST, ISO, FinOps, ModelOps, AIOps, XDR, EU AI Act), upserts multi-language scan patterns, optionally seeds demo project. Idempotent — safe to re-run.
func azure functionapp publish deploys the Python 3.11 app from src/functions/. az storage blob upload-batch syncs src/web/ to the $web container with correct MIME type metadata for each extension.
Post-deploy: smoke-test.sh hits health, static root, governance API, and Cosmos DB connectivity with 3-retry logic. Optionally run make audit to generate SHA-256 manifest of the deployed repo and verify every expected file is present and intact.
All containers use /id as partition key — matching the existing application's data model exactly.
No data migration required when promoting from prototype to this deployment.
After restructuring 40+ files from a flat prototype layout into the new repository structure, the audit tool provides a machine-verifiable guarantee that nothing was accidentally dropped, truncated, or broken.
Every file in the expected inventory is checked for existence on disk. Missing files → AuditResult.missing[]. Unknown files (on disk but not in inventory) → AuditResult.unknown[].
Source files under 100 bytes are flagged as unexpected_size — a sign of a placeholder or partial write. The 8,600-line governance-app.js was a specific concern after the file loss event that prompted this tool.
Every file gets a SHA-256 hash recorded in the audit manifest. Output is a JSON document with per-file entries including size, hash, and category — fully diff-able between runs.
py_compile runs on all .py files in src/functions/. Syntax errors are recorded in compile_results and cause exit code 1 if they affect critical path files.
Unresolved imports in governance_routes.py and code_scanner.py (the critical path) cause exit code 1. Non-critical import warnings are recorded but non-blocking.
Exit code 0 = all present, all syntax valid, all critical imports resolved. Exit code 1 = at least one critical gap. The CI/CD pipeline gates deployment on exit code 0 from make audit.
Bicep .bicepparam files control all per-environment differences.
The Bicep modules contain no hard-coded environment conditionals — only parameterized resource properties.
The core security decision: system-assigned Managed Identity on the Function App eliminates Cosmos DB and Storage connection strings from every environment configuration.
Cosmos DB Built-in Data Contributor on the Cosmos accountStorage Blob Data Contributor on the storage accountKEY_VAULT_URL optional — when set, app settings use @Microsoft.KeyVault() references.env.example lists all vars with placeholders — never committed with real valuesvalidate-env.sh pre-checks all required variables before any deployment step@allowed(['dev', 'staging', 'prod']) on environment parameter — prevents accidental deployments to unknown environmentsgenerated-documents blob container — served via Function App pre-signed URLs onlyIaC and pipelines are tested via Bicep validation and actionlint. The two pure Python components — the blast radius audit tool and seed ID generator — are the right target for property-based testing: pure functions with complex input spaces.
For any directory tree — the manifest generator records every file with a SHA-256 that matches the actual content and a size that matches the actual file size. Inventory comparison correctly categorizes as present (≥100 bytes), missing, unexpected_size, or unknown.
Validates: 1.1, 1.2, 1.4For any Python file with import statements and a set of module files — the resolver reports an import as resolved if and only if a corresponding .py file exists containing the expected exported symbols. No false positives, no false negatives.
Validates: 1.6For any (type, name) string pair — generate_document_id() returns the same ID on every call, and two different (type, name) pairs never produce the same ID. Tested with arbitrary text() strategies, 100+ iterations.
az bicep build on all 5 Bicep files catches syntax errors before deployment. az deployment group what-if against a test resource group validates resource definitions without provisioning.actionlint validates deploy.yml and infra.yml syntax and GitHub Actions expression references.seed_all.py runs twice against a Cosmos DB emulator; document counts are identical after both runs.py_compile on all .py files in src/functions/ runs as the first build stage — catches syntax regressions before packaging.Bicep is Azure-native, produces significantly cleaner templates than ARM JSON (no nested dependsOn boilerplate, real parameter types, module composition), and requires no state file. The team already uses Azure CLI — no new tooling needed. Terraform would add state file management complexity and a HashiCorp layer. ARM JSON is too verbose to maintain. Bicep's module decomposition mirrors the CloudFormation nested stack approach used in the AWS version, making the two deployments structurally symmetrical.
The 18+ numbered scripts accumulated organically — 01_create_containers.py, 02_seed_frameworks.py, 07_fix_pattern_ids.py, etc. They had overlapping responsibilities, inconsistent error handling, and had to be run in a specific order that wasn't encoded anywhere. The SeedRunner class consolidates everything: ordered phases, idempotent upserts with deterministic IDs, a SeedReport return value, and a single CLI entrypoint. Running it from scratch or re-running it on an existing database produces identical state either way.
Connection strings are secrets that rotate, get committed accidentally, appear in logs, and require manual distribution across environments. Managed Identity eliminates all of that: the Function App's system identity is assigned RBAC roles directly on the Cosmos DB account and Storage Account in Bicep, with no key material involved. Rotation is automatic. The only secrets that remain are third-party API keys (NewsAPI, Alpha Vantage, etc.) which have no Managed Identity equivalent — those go in Function App settings or Key Vault references.
Consumption Plan (Y1) costs nothing when idle — ideal for dev/staging where the app sits unused most of the day. Its cold start behavior (1–3 second delays) is acceptable during testing. Premium EP1 eliminates cold starts entirely via always-warm instances, supports VNet integration for private endpoint access, and provides predictable latency under production load. The trade-off (always-on billing) is justified for prod. The Bicep parameter file makes the switch a single line change: functionAppSku = 'EP1'.
During an earlier development session, a file restructuring operation (moving the flat prototype into the new directory layout) resulted in some files being partially written or silently truncated — specifically large JS files like governance-app.js (8,600 lines). The corruption wasn't immediately visible in the terminal output. The audit tool was designed as a post-restructuring verification step: generate a SHA-256 manifest of the expected inventory, check every file is present and above the minimum size threshold, validate Python syntax, and check import resolution. It runs in under a second and provides a machine-readable exit code that CI/CD can gate on.
The Azure version is the original — the platform existed on Azure before the AWS deployment was designed. The key structural difference: the Azure version required no cloud adapter layer because the business logic was already written against Azure SDKs. The AWS deployment added the full adapter abstraction to keep those 7,500 lines of business logic unchanged while switching the cloud backend. Both use the same shared seed data (frameworks.py, patterns.py), deterministic document IDs, and idempotent upsert strategy. The Bicep module structure was intentionally mirrored in CloudFormation nested stacks for the AWS version.