Skip to content
Merged
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
12 changes: 0 additions & 12 deletions 03-Azure/01-04-AI/05_AI_Governance/challenges/azure.yaml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -133,19 +133,53 @@ Each challenge follows the same anatomy, so you always know where to look:
## Repository layout

```
05_AI_Governance/
README.md This file
challenges/ Challenge instructions (challenge-01.md … challenge-09.md)
labautomation/ Platform entry point — provisions the Hub + Spoke per attendee
deploy-lab.ps1 Called once per attendee by the MicroHack platform
06_AI_Governance/
README.md This file
challenges/ Participant-facing content
challenge-01.md … challenge-09.md The challenge instructions
finish.md Wrap-up and cleanup
azure.yaml azd project-root marker — makes the notebooks'
`azd env get-value` calls resolve. Not used to deploy.
workshop/ The 9 unchanged Citadel workshop notebooks this
MicroHack is built on (plus their agent/MCP sources)
shared/ utils.py / apimtools.py — imported by the notebooks
via `sys.path.insert(1, '../shared')`
bicep/ Per-challenge Bicep the notebooks deploy themselves
(`../bicep/infra/llm-backend-onboarding`,
`../bicep/infra/citadel-access-contracts`)
walkthrough/ Coach solutions — challenge-0N/solution-0N.md
labautomation/ Platform entry point — provisions the Hub + Spoke per attendee
deploy-lab.ps1 Called once per attendee by the MicroHack platform
shared-deploy-lab.ps1 Called once per subscription (resource-provider registration)
infra/resources.bicep The hub + spoke template, resource-group scoped
run-local.ps1 Local-only dry-run wrapper (not used by the platform)
setup-notebook-env.ps1 Bridges dashboard credentials into a local azd environment
README.md Full provisioning + notebook-environment-setup reference
reference/
ai-hub-gateway-solution-accelerator/
workshop/ The 9 unchanged Citadel workshop notebooks this MicroHack is built on
```

> [!NOTE]
> `challenges/workshop/`, `challenges/shared/`, `challenges/bicep/` and `challenges/azure.yaml`
> reproduce the upstream repository's own layout. That nesting is deliberate: the workshop
> notebooks are **unmodified**, and they resolve `../shared` and `../bicep/infra` relative to
> their own folder. Moving or flattening these breaks challenges 1, 2, 3, 5, 6, 8 and 9.

## Solutions — spoiler warning

Coach-facing walkthroughs live in [`walkthrough/`](walkthrough/), one folder per challenge.
Participants should attempt each challenge first.

* [Solution 1 — Onboard a New LLM Backend](walkthrough/challenge-01/solution-01.md)
* [Solution 2 — Universal LLM API Across Every Model](walkthrough/challenge-02/solution-02.md)
* [Solution 3 — Access Contracts: Model RBAC & Capacity](walkthrough/challenge-03/solution-03.md)
* [Solution 4 — Drive Access Contracts from Agent Frameworks](walkthrough/challenge-04/solution-04.md)
* [Solution 5 — PII Anonymization, Blocking & Analytics](walkthrough/challenge-05/solution-05.md)
* [Solution 6 — Unified AI API Across Providers](walkthrough/challenge-06/solution-06.md)
* [Solution 7 — Build a Governed Hosted Agent with AGT](walkthrough/challenge-07/solution-07.md)
* [Solution 8 — Publish a Foundry Agent as an A2A Endpoint](walkthrough/challenge-08/solution-08.md)
* [Solution 9 — HR MCP via APIM](walkthrough/challenge-09/solution-09.md)

➡️ [Finish](challenges/finish.md)

## Contributors

| Name | Role |
Expand Down
21 changes: 21 additions & 0 deletions 03-Azure/01-04-AI/06_AI_Governance/challenges/azure.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Application name
#
# In the MicroHack this file is NOT used to deploy anything — the lab is provisioned by
# labautomation/deploy-lab.ps1 (Bicep, resource-group scoped), not by `azd up`.
#
# It is kept because it is azd's project-root marker: the unchanged workshop notebooks read
# their configuration with `azd env get-value <KEY>`, and azd locates the current environment
# by searching upward from the working directory for the nearest azure.yaml — exactly as git
# searches for .git. Notebooks run from ./workshop/, so this file one level up is what makes
# those calls resolve. labautomation/setup-notebook-env.ps1 depends on it and fails fast if
# it is missing. Do not move or delete it.
name: ai-citadel-governance-hub
metadata:
template: ai-citadel-governance-hub
infra:
path: bicep/infra
provider: bicep
# NOTE: the upstream workshop declares a `services:` block here for the usage-ingestion
# Logic App (./src/usage-ingestion-logicapp). That source folder is not vendored into this
# MicroHack module, and the lab never runs `azd deploy`, so the block is intentionally
# omitted — leaving it in place made `azd` fail on a path that does not exist.
54 changes: 54 additions & 0 deletions 03-Azure/01-04-AI/06_AI_Governance/challenges/finish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Finish

[← Solution 09](../walkthrough/challenge-09/solution-09.md) · **[Home](../README.md)**

## Congratulations 🎉

You finished the **AI Governance** MicroHack.

Along the way you turned an ungoverned pile of models into a governed AI platform:

- **Challenge 1–2** — onboarded an LLM backend behind a single gateway and proved one
Universal LLM API works across every deployed model.
- **Challenge 3–4** — enforced *access contracts*: which caller may use which model, at what
capacity — then drove those same contracts from three different agent frameworks.
- **Challenge 5** — masked, blocked, and analysed PII **at the gateway**, so governance holds
no matter which client calls.
- **Challenge 6** — unified Azure OpenAI, Foundry inference, and Responses-style APIs behind
one contract.
- **Challenge 7–9** *(follow-on)* — built a governed hosted agent with the Agent Governance
Toolkit, published it as an A2A endpoint, and exposed an MCP server through APIM.

The governance idea worth taking home: **policy belongs at the gateway, not in each app.**
Every control you configured applies identically to a notebook, an SDK agent, and a
third-party tool — because none of them can reach a model any other way.

## Clean up

Your MicroHack resource group is deleted by the platform when the lab expires — no action
needed. If you deployed the workshop yourself outside the MicroHack, delete the resource
group and then purge the soft-deleted resources:

```bash
az group delete --name <your-resource-group> --yes --no-wait
az cognitiveservices account purge --name <foundry-account> --resource-group <rg> --location <region>
az keyvault purge --name <key-vault> --location <region>
```

> [!NOTE]
> API Management and Azure AI Foundry accounts are **soft-deleted**. Without the purge
> commands their names stay reserved and a redeploy in the same subscription fails.

## Keep going

- [Azure API Management — AI gateway capabilities](https://learn.microsoft.com/azure/api-management/genai-gateway-capabilities)
- [Azure AI Foundry documentation](https://learn.microsoft.com/azure/ai-foundry/)
- [Responsible AI in Azure AI Foundry](https://learn.microsoft.com/azure/ai-foundry/responsible-use-of-ai-overview)
- [AI Hub Gateway Solution Accelerator](https://github.com/mohamedsaif/ai-hub-gateway-solution-accelerator) — the upstream project this MicroHack is built on

## Feedback

Found a bug, or something that could be explained better? Please open an issue on this
repository or reach out to the contributors listed in the [module README](../README.md).

Thank you for investing the time — see you at the next MicroHack!
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Citadel AI Governance Hub - Hands-On Workshop Lab Guide 🏛️

> [!CAUTION]
> **MicroHack participants: do NOT follow the deployment steps in this file.**
>
> This is the **unmodified upstream workshop guide**, kept here because the notebooks are
> unchanged and reference it. It assumes you provision everything yourself with `azd up`
> plus `scripts/deploy-spoke-foundry.*`, into **two** resource groups.
>
> In the MicroHack, **your hub and spoke are already deployed for you**, both inside the
> **single** resource group listed on your lab dashboard. Running `azd up` or
> `deploy-spoke-foundry.*` here will fail or create duplicate resources you will be billed
> for, and it will not match the credentials on your dashboard.
>
> **Start at [`../challenge-01.md`](../challenge-01.md) instead**, and use
> `labautomation/setup-notebook-env.ps1` to wire the notebooks to your environment.
> Sections 4 onward of this file (notebook walkthroughs, architecture, concepts) are still
> accurate and worth reading.

**Duration:** ~3 hours
**Level:** Intermediate
**Last updated:** June 2026
Expand All @@ -11,6 +28,7 @@ This hands-on lab deploys a governed AI hub-and-spoke architecture into your Azu
- **Lab environment** — your local machine or the included Devcontainer, where deployment commands, notebooks, and validation traffic originate.

You deploy the hub with `azd up`, then deploy the sample spoke with `workshop/scripts/deploy-spoke-foundry.*`.
*(MicroHack: both are already deployed into your single resource group — see the caution above.)*

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,44 @@ param(
[string]$FoundryAccountName = $env:FOUNDRY_ACCOUNT_NAME,
[string]$FoundryProjectName = $env:FOUNDRY_PROJECT_NAME,
[string]$KeyVaultName = $env:KEY_VAULT_NAME,
[string]$KeyVaultEnablePurgeProtection = $(if ($env:KEY_VAULT_ENABLE_PURGE_PROTECTION) { $env:KEY_VAULT_ENABLE_PURGE_PROTECTION } else { 'false' })
[string]$KeyVaultEnablePurgeProtection = $(if ($env:KEY_VAULT_ENABLE_PURGE_PROTECTION) { $env:KEY_VAULT_ENABLE_PURGE_PROTECTION } else { 'false' }),

# MicroHack guard. Not part of the upstream workshop script.
[switch]$Force
)

# ---------------------------------------------------------------------------
# MicroHack guard (added for the MicroHack port — not in the upstream workshop)
#
# Upstream, this script is step 3.7: it creates a SEPARATE spoke resource group after
# `azd up` has built the hub. In the MicroHack each participant gets exactly ONE resource
# group, and the spoke is already provisioned into it by
# labautomation/infra/resources.bicep. Running this script here would create a second,
# unbudgeted resource group whose resource names do not match the lab dashboard
# credentials, and would silently diverge from the environment the notebooks are wired to.
#
# The script is kept (rather than deleted) because notebook 7 and the workshop guide refer
# to it, and because it is still the correct tool outside the MicroHack. Pass -Force to run
# it deliberately.
# ---------------------------------------------------------------------------
if (-not $Force) {
Write-Host ''
Write-Host 'This script is NOT used in the MicroHack.' -ForegroundColor Yellow
Write-Host ''
Write-Host 'Your Citadel spoke (Foundry account + project, Key Vault, Log Analytics,'
Write-Host 'Application Insights, and ACR) is already deployed into the single resource'
Write-Host 'group shown on your lab dashboard. There is nothing to run here.'
Write-Host ''
Write-Host 'To connect the notebooks to it, run from the labautomation folder:'
Write-Host ' ./setup-notebook-env.ps1 -ResourceGroup <ResourceGroup> ...' -ForegroundColor Cyan
Write-Host 'See labautomation/README.md -> "Notebook Environment Setup".'
Write-Host ''
Write-Host 'If you are running the ORIGINAL workshop outside the MicroHack and really do'
Write-Host 'want a separate spoke resource group, re-run with -Force.'
Write-Host ''
exit 0
}

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
if ($PSVersionTable.PSVersion.Major -ge 7) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,55 @@
#!/usr/bin/env bash
set -euo pipefail

# ---------------------------------------------------------------------------
# MicroHack guard (added for the MicroHack port — not in the upstream workshop)
#
# Upstream, this script is step 3.7: it creates a SEPARATE spoke resource group after
# `azd up` has built the hub. In the MicroHack each participant gets exactly ONE resource
# group, and the spoke is already provisioned into it by
# labautomation/infra/resources.bicep. Running this script here would create a second,
# unbudgeted resource group whose resource names do not match the lab dashboard
# credentials, and would silently diverge from the environment the notebooks are wired to.
#
# The script is kept (rather than deleted) because notebook 7 and the workshop guide refer
# to it, and because it is still the correct tool outside the MicroHack. Pass --force to
# run it deliberately.
# ---------------------------------------------------------------------------
microhack_force="false"
for _arg in "$@"; do
if [[ "$_arg" == "--force" ]]; then
microhack_force="true"
fi
done

if [[ "$microhack_force" != "true" ]]; then
cat <<'MICROHACK_GUARD'

This script is NOT used in the MicroHack.

Your Citadel spoke (Foundry account + project, Key Vault, Log Analytics,
Application Insights, and ACR) is already deployed into the single resource
group shown on your lab dashboard. There is nothing to run here.

To connect the notebooks to it, run from the labautomation folder:
./setup-notebook-env.ps1 -ResourceGroup <ResourceGroup> ...
See labautomation/README.md -> "Notebook Environment Setup".

If you are running the ORIGINAL workshop outside the MicroHack and really do
want a separate spoke resource group, re-run with --force.

MICROHACK_GUARD
exit 0
fi

# Drop the MicroHack-only --force flag so the upstream argument parser below never sees it.
_filtered_args=()
for _arg in "$@"; do
[[ "$_arg" == "--force" ]] && continue
_filtered_args+=("$_arg")
done
set -- ${_filtered_args[@]+"${_filtered_args[@]}"}

# Silence a known Azure CLI Python SyntaxWarning from an upstream dependency.
export PYTHONWARNINGS="${PYTHONWARNINGS:+$PYTHONWARNINGS,}ignore::SyntaxWarning"

Expand Down
Loading