|
| 1 | +# Foundry Private Network Cleanup |
| 2 | + |
| 3 | +`cleanup.ps1` safely tears down Foundry deployments with VNet injection. It handles the specific deletion order required to avoid stuck resources, orphaned locks, and subnet conflicts that make manual cleanup painful. |
| 4 | + |
| 5 | +## Why This Script Exists |
| 6 | + |
| 7 | +Deleting a Foundry private network deployment is **not** as simple as `az group delete`. The deployment creates capability hosts with service association links (SALs) on VNet subnets. If you delete the resource group directly: |
| 8 | + |
| 9 | +- **Capability hosts must be deleted in order** — project-level first, then account-level. Deleting in the wrong order can leave the account in a failed state. |
| 10 | +- **SALs block subnet reuse** — subnets with active SALs cannot be re-delegated or deleted. SAL cleanup happens asynchronously after caphost deletion and can take up to 24 hours. |
| 11 | +- **Soft-deleted accounts block redeployment** — Cognitive Services accounts are soft-deleted for 48 hours. A new deployment with the same name will fail unless the old account is purged. |
| 12 | + |
| 13 | +This script handles all of this automatically: discovers resources, deletes in the correct order, waits for SAL cleanup, and purges soft-deleted accounts. |
| 14 | + |
| 15 | +## Important: Use the VNet Resource Group |
| 16 | + |
| 17 | +The `--ResourceGroup` parameter must point to the resource group containing the **AI Foundry account, project, and VNet** — not the resource group with dependent resources (Search, Cosmos, Storage). |
| 18 | + |
| 19 | +If your deployment uses multiple resource groups, the cleanup script only needs the one with the AI account and VNet. Dependent resources in other resource groups can be deleted with a simple `az group delete`. |
| 20 | + |
| 21 | +## Prerequisites |
| 22 | + |
| 23 | +- [PowerShell 7+](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell) (cross-platform) |
| 24 | +- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) logged in with access to the target subscription |
| 25 | +- Active subscription must be set: `az account set --subscription <subscription-id>` |
| 26 | + |
| 27 | +## Usage |
| 28 | + |
| 29 | +```bash |
| 30 | +cd infrastructure/infrastructure-setup-bicep/deployment-tools/cleanup |
| 31 | +``` |
| 32 | + |
| 33 | +**Always start with a dry run** to see what would be deleted before making changes: |
| 34 | + |
| 35 | +```powershell |
| 36 | +.\cleanup.ps1 -SubscriptionId "<subscription-id>" -ResourceGroup "<resource-group>" -DryRun |
| 37 | +``` |
| 38 | + |
| 39 | +> [!IMPORTANT] |
| 40 | +> Always run `-DryRun` first and review the discovered accounts/projects/caphosts before running cleanup without `-DryRun`. |
| 41 | +
|
| 42 | +When you're satisfied with the discovery output, run without `-DryRun`. The script will prompt for confirmation before deleting anything. |
| 43 | + |
| 44 | +```powershell |
| 45 | +.\cleanup.ps1 -SubscriptionId "<subscription-id>" -ResourceGroup "<resource-group>" |
| 46 | +``` |
| 47 | + |
| 48 | +## Parameters |
| 49 | + |
| 50 | +| Parameter | Required | Description | |
| 51 | +|---|---|---| |
| 52 | +| `-SubscriptionId` | Yes | Azure subscription ID | |
| 53 | +| `-ResourceGroup` | Yes | Resource group containing the AI Foundry account, project, and VNet | |
| 54 | +| `-AccountName` | No | Limit cleanup to a specific AI Services account. When omitted, all AIServices accounts in the RG are discovered and cleaned up. | |
| 55 | +| `-DryRun` | No | Show what would be cleaned up without taking any action | |
| 56 | +| `-SkipSalWait` | No | Skip waiting for SAL removal (faster but risky — subnet may not be reusable immediately) | |
| 57 | +| `-DeleteRG` | No | Delete the resource group after cleanup. Not allowed with `-AccountName` (account-scoped cleanup must not delete the whole RG). | |
| 58 | + |
| 59 | +When `-AccountName` is provided, active cleanup remains scoped to that account, while soft-deleted account purge remains RG-wide residue cleanup. |
| 60 | + |
| 61 | +## What It Does |
| 62 | + |
| 63 | +### Step 0: Discovery |
| 64 | + |
| 65 | +Auto-discovers all resources in the resource group — no need to know account or project names: |
| 66 | + |
| 67 | +- AI Foundry accounts (kind: `AIServices`) |
| 68 | +- Projects under each account |
| 69 | +- Capability hosts (project-level and account-level) |
| 70 | +- VNet subnets with active service association links |
| 71 | + |
| 72 | +After discovery, a summary is printed and you are prompted to confirm before proceeding (unless `-DryRun` is set). |
| 73 | + |
| 74 | +### Step 1: Delete Project Capability Hosts |
| 75 | + |
| 76 | +Deletes all project-level capability hosts first. This is required before account-level caphosts can be removed. |
| 77 | + |
| 78 | +### Step 2: Delete Account Capability Hosts |
| 79 | + |
| 80 | +Deletes account-level capability hosts. Handles async deletion with polling (up to 30 min timeout). |
| 81 | + |
| 82 | +### Step 3: Delete Projects and Purge AI Accounts |
| 83 | + |
| 84 | +Deletes all projects under each account first (accounts cannot be deleted while nested projects exist), then deletes and purges each AI Services account to prevent soft-delete name collisions on redeployment. Also checks for and purges any previously soft-deleted accounts in the resource group. |
| 85 | + |
| 86 | +### Step 4: Wait for SAL Cleanup |
| 87 | + |
| 88 | +Waits for service association links to be removed from subnets (up to 20 min). SAL removal happens asynchronously after caphost deletion. If SALs are still present after 20 minutes, the script warns you to check again later — backend cleanup can take up to 24 hours. |
| 89 | + |
| 90 | +SAL waiting runs only for caphost-linked subnets discovered during cleanup. If none are discovered, SAL waiting is skipped with a warning. |
| 91 | + |
| 92 | +### Step 5: Resource Group (optional) |
| 93 | + |
| 94 | +If `-DeleteRG` is specified, initiates an async deletion of the resource group. Otherwise, prints the command for manual deletion. |
| 95 | + |
| 96 | +## Examples |
| 97 | + |
| 98 | +```powershell |
| 99 | +# Dry run — see what would be cleaned up |
| 100 | +.\cleanup.ps1 -SubscriptionId "xxxx" -ResourceGroup "my-foundry-rg" -DryRun |
| 101 | +
|
| 102 | +# Clean up a specific account only |
| 103 | +.\cleanup.ps1 -SubscriptionId "xxxx" -ResourceGroup "my-foundry-rg" -AccountName "my-ai-account" |
| 104 | +
|
| 105 | +# Full cleanup including resource group deletion |
| 106 | +.\cleanup.ps1 -SubscriptionId "xxxx" -ResourceGroup "my-foundry-rg" -DeleteRG |
| 107 | +
|
| 108 | +# Fast cleanup (skip SAL wait — subnet may not be immediately reusable) |
| 109 | +.\cleanup.ps1 -SubscriptionId "xxxx" -ResourceGroup "my-foundry-rg" -SkipSalWait |
| 110 | +``` |
0 commit comments