All skills
microsoft avatar

/azure-prepare

@b8a1c66
by microsoftmicrosoft/skills3.1k stars
351

Prepare azd-based Azure projects for deployment: generates azure.yaml, infrastructure (Bicep/Terraform), and Dockerfiles for the Azure Developer CLI (azd) workflow. USE ONLY when the user explicitly wants to use azd as the deployment tool, or the project already has an azure.yaml file. DO NOT USE FOR: non-azd deployments, Python App Service code-only deploys (use python-appservice-deploy), or cross-cloud migration (use azure-cloud-migrate). WHEN: prepare app for azd, create azure.yaml, set up azd infrastructure, modernize app for Azure with azd, deploy with azd, function app, timer trigger, service bus trigger, event-driven function, managed identity, generate Bicep, generate Terraform, create and deploy to Azure.

Use this Skill: https://skilld.dev/gh/microsoft/skills/azure-prepare

This session only. Nothing lands on disk.

referencesaspire.md

≈4.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

.NET Aspire Projects

⛔ CRITICAL - READ THIS FIRST

For .NET Aspire projects, NEVER manually create azure.yaml or infra/ files. Always use azd init --from-code which auto-detects the AppHost and generates everything correctly.

Failure to follow this causes: "Could not find a part of the path 'infra\main.bicep'" error.

Guidance for preparing .NET Aspire applications for Azure deployment.

📖 For detailed AZD workflow: See recipes/azd/aspire.md

What is .NET Aspire?

.NET Aspire is an opinionated, cloud-ready stack for building observable, production-ready distributed applications. Aspire projects use an AppHost orchestrator to define and configure the application's components, services, and dependencies.

Detection

A .NET Aspire project is identified by:

Indicator Description
*.AppHost.csproj AppHost orchestrator project file
Aspire.Hosting package Core Aspire hosting package reference
Aspire.Hosting.AppHost Alternative Aspire hosting package

Example project structure:

orleans-voting/
├── OrleansVoting.sln
├── OrleansVoting.AppHost/
│   └── OrleansVoting.AppHost.csproj   ← AppHost indicator
├── OrleansVoting.Web/
├── OrleansVoting.Api/
└── OrleansVoting.Grains/

Azure Preparation Workflow

Step 1: Detection

When scanning the codebase (per scan.md), detect Aspire by:

# Check for AppHost project
find . -name "*.AppHost.csproj"

# Or check for Aspire.Hosting package reference
grep -r "Aspire.Hosting" . --include="*.csproj"

⛔ Step 1a: Pre-Check for Custom/Non-Deployable Resources (MANDATORY)

Before running azd init --from-code, scan the AppHost source code to understand whether the app may contain local-only custom resources.

# Find the AppHost project and scan only its source directory
APPHOST_PROJECT=$(find . -name "*.AppHost.csproj" | head -1)
APPHOST_DIR=$(dirname "$APPHOST_PROJECT")
grep -r "ExcludeFromManifest" "$APPHOST_DIR" --include="*.cs" | head -20

PowerShell:

# Find the AppHost project and scan only its source directory
$appHostProject = Get-ChildItem -Recurse -Filter "*.AppHost.csproj" | Select-Object -First 1
$appHostDir = $appHostProject.DirectoryName
Get-ChildItem -Path $appHostDir -Recurse -Filter "*.cs" | Select-String "ExcludeFromManifest" | Select-Object -First 20

This scan is informational. .ExcludeFromManifest() can appear alongside deployable resources, so a positive match does not immediately block deployment. What matters is the final azure.yaml output after azd init --from-code completes:

  • If azd init fails with unsupported resource type → see Step 2 error guidance below.
  • If azd init succeeds but azure.yaml has an empty or missing services section → see Step 4a below.

💡 Why scan early: Knowing that .ExcludeFromManifest() is present gives useful context when azd errors or generates an empty manifest — it confirms the app intentionally targets local development rather than Azure deployment.

Step 2: Initialize with azd

CRITICAL: For Aspire projects, use azd init --from-code -e <environment-name> instead of creating azure.yaml manually.

⚠️ ALWAYS include the -e <environment-name> flag: Without it, azd init will fail in non-interactive environments (agents, CI/CD) with the error: no default response for prompt 'Enter a unique environment name:'

The --from-code flag:

  • Auto-detects the AppHost orchestrator
  • Reads the Aspire service definitions
  • Generates appropriate azure.yaml and infrastructure
  • Works in non-interactive/CI environments when combined with -e flag
# Non-interactive initialization for Aspire projects (REQUIRED for agents)
ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
azd init --from-code -e "$ENV_NAME"

Why both flags are required:

  • --from-code: Tells azd to detect the AppHost automatically (no "How do you want to initialize?" prompt)
  • -e <name>: Provides environment name upfront (no "Enter environment name:" prompt)
  • Together, they enable fully non-interactive operation essential for automation, agents, and CI/CD pipelines

⛔ If azd init --from-code fails with "unsupported resource type":

This error means the AppHost contains custom Aspire resource types that azd cannot process for Azure deployment:

  1. ⛔ Do NOT attempt to fix this error by modifying source code — do not add .ExcludeFromManifest() calls or otherwise patch the AppHost
  2. ⛔ Do NOT proceed with deployment — the application is designed for local development only
  3. ✅ Record a blocker: "AppHost contains custom Aspire resource types (unsupported resource type) that cannot be deployed to Azure"
  4. ✅ Inform the user: this application uses custom Aspire resource authoring patterns intended for local tooling, not cloud deployment

⚠️ Why modifying source code is forbidden: Adding .ExcludeFromManifest() may suppress the error and allow azd init to succeed, but the deployment outcome will not reflect the application's actual intent. The custom resources are deliberately designed to be local-only.

Step 3: Configure Subscription and Location

⛔ CRITICAL: After azd init --from-code completes, you MUST immediately set the user-confirmed subscription and location.

DO NOT skip this step or delay it until validation. The azd init command creates an environment but does NOT inherit the Azure CLI's subscription. If you skip this step, azd will use its own default subscription, which may differ from the user's confirmed choice.

Set the subscription and location immediately after initialization:

# Set the user-confirmed subscription ID
azd env set AZURE_SUBSCRIPTION_ID <subscription-id>

# Set the location
azd env set AZURE_LOCATION <location>

Verify the configuration:

azd env get-values

Confirm that AZURE_SUBSCRIPTION_ID and AZURE_LOCATION match the user's confirmed choices from Azure Context.

Step 4: What azd Generates

azd init --from-code creates:

Artifact Location Description
azure.yaml Project root Service definitions from AppHost
infra/ Project root Bicep templates for Azure resources
.azure/ Project root Environment configuration

⛔ Step 4a: Validate Generated Output

MANDATORY: After azd init --from-code completes, verify the generated azure.yaml contains deployable services.

# Check if azure.yaml has a non-empty services section
cat azure.yaml

If the services section is empty or missing: The AppHost has no deployable resources. This happens when all resources use .ExcludeFromManifest() (e.g., custom resource demonstrations, local-only tooling). In this case:

  1. ⛔ Do NOT proceed with deployment — there is nothing to deploy
  2. ✅ Keep the plan status in a valid state (for example, leave it as Planning) and record a blocker in the plan body with the reason: "Application contains only custom/demo Aspire resources with no Azure-deployable services"
  3. ✅ Inform the user that this application is designed for local development and cannot be meaningfully deployed to Azure
  4. ⛔ Do NOT manually create Bicep, Dockerfiles, or azure.yaml to work around this — the absence of services is the correct result

Example generated azure.yaml:

name: orleans-voting
# metadata section is auto-generated by azd init --from-code

services:
  web:
    project: ./OrleansVoting.Web
    language: dotnet
    host: containerapp

  api:
    project: ./OrleansVoting.Api
    language: dotnet
    host: containerapp

⛔ Step 4b: Fix Azure Functions Secret Storage (MANDATORY for Aspire + Functions)

MANDATORY: After azd init --from-code succeeds, check if the AppHost contains Azure Functions and fix secret storage configuration.

This step MUST run BEFORE azd up or azd provision. Skipping it causes a runtime failure: Secret initialization from Blob storage failed.

1. Detect Azure Functions in the AppHost:

APPHOST_DIR=$(dirname "$(find . -name '*.AppHost.csproj' | head -1)")
grep -n "AddAzureFunctionsProject" "$APPHOST_DIR"/*.cs

PowerShell:

$appHostDir = (Get-ChildItem -Recurse -Filter "*.AppHost.csproj" | Select-Object -First 1).DirectoryName
Get-ChildItem -Path $appHostDir -Filter "*.cs" | Select-String "AddAzureFunctionsProject"

If AddAzureFunctionsProject is NOT found → skip this step.

2. Check if AzureWebJobsSecretStorageType is already configured:

grep -n "AzureWebJobsSecretStorageType" "$APPHOST_DIR"/*.cs

PowerShell:

Get-ChildItem -Path $appHostDir -Filter "*.cs" | Select-String "AzureWebJobsSecretStorageType"

If already present → skip this step.

3. Add the environment variable to the Functions builder chain:

Use the edit tool to add .WithEnvironment("AzureWebJobsSecretStorageType", "Files") to the AddAzureFunctionsProject builder chain in the AppHost source file.

Before:

var functions = builder.AddAzureFunctionsProject<Projects.MyFunctions>("functions")
    .WithHostStorage(storage)
    .WithReference(queues);

After:

var functions = builder.AddAzureFunctionsProject<Projects.MyFunctions>("functions")
    .WithHostStorage(storage)
    .WithEnvironment("AzureWebJobsSecretStorageType", "Files")
    .WithReference(queues);

💡 Tip: Place .WithEnvironment(...) immediately after .WithHostStorage(...) for clarity.

⚠️ Why this is required: When Aspire uses WithHostStorage(storage), it configures identity-based storage URIs (e.g., AzureWebJobsStorage__blobServiceUri). Azure Functions' secret/key manager does not support these identity-based URIs — it requires either a connection string or file-based storage. Setting AzureWebJobsSecretStorageType=Files switches to file-system key storage, bypassing the incompatible blob dependency.

See aspire-functions-secrets reference for additional details.

Flags Reference

azd init for Aspire

Flag Required Description
--from-code ✅ Yes Auto-detect AppHost, no interactive prompts
-e <name> ✅ Yes Environment name (required for non-interactive)
--no-prompt Optional Skip additional confirmations

Complete initialization sequence:

# 1. Initialize the environment
ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
azd init --from-code -e "$ENV_NAME"

# 2. IMMEDIATELY set the user-confirmed subscription
azd env set AZURE_SUBSCRIPTION_ID <subscription-id>

# 3. Set the location
azd env set AZURE_LOCATION <location>

# 4. Verify configuration
azd env get-values

Common Aspire Samples

Sample Repository Notes
orleans-voting dotnet/aspire-samples Orleans cluster with voting app
AspireYarp dotnet/aspire-samples YARP reverse proxy
AspireWithDapr dotnet/aspire-samples Dapr integration
eShop dotnet/eShop Reference microservices app

Troubleshooting

Error: "no default response for prompt 'Enter a unique environment name:'"

Cause: Missing -e flag when running azd init --from-code in non-interactive environment
Solution: Always include the -e <environment-name> flag

# ❌ Wrong - fails in non-interactive environments (agents, CI/CD)
azd init --from-code

# ✅ Correct - provides environment name upfront
ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
azd init --from-code -e "$ENV_NAME"

Important: This error typically occurs when:

  • Running in an agent or automation context
  • No TTY is available for interactive prompts
  • The -e flag was omitted

Error: "no default response for prompt 'How do you want to initialize your app?'"

Cause: Missing --from-code flag
Solution: Add --from-code to the azd init command

# ❌ Wrong - requires interactive prompt
azd init -e "my-env"

# ✅ Correct - auto-detects AppHost
azd init --from-code -e "my-env"

No AppHost detected

Symptoms: azd init --from-code doesn't find the AppHost

Solutions:

  1. Verify AppHost project exists: find . -name "*.AppHost.csproj"
  2. Check project builds: dotnet build
  3. Ensure Aspire.Hosting package is referenced in AppHost project

Error: "unsupported resource type" during manifest generation

Symptoms: azd init --from-code fails with output like:

error: unsupported resource type: <custom-resource-type>

or the manifest generation step errors on child resources (e.g., ClockHand, or other custom resource types defined in the AppHost).

Cause: The AppHost contains custom Aspire resource types that azd cannot convert to Azure deployable resources. These custom types are typically:

  • Demonstration resources showing developers how to build Aspire extensions for local tooling
  • Resources that wrap local services without Azure equivalents
  • Custom child resources (e.g., subcomponents of a custom Aspire integration)

Resolution:

  1. ⛔ Do NOT attempt to fix this error by modifying source code — do not add .ExcludeFromManifest() calls or otherwise patch the AppHost
  2. ⛔ Do NOT proceed with deployment — this is a deployment blocker, not a recoverable error
  3. ✅ Record a blocker in the deployment plan: "AppHost contains custom Aspire resource types not supported for Azure deployment (unsupported resource type)"
  4. ✅ Inform the user that this application is designed for local development and cannot be meaningfully deployed to Azure

⚠️ Why this is a hard stop: Custom resource types that produce "unsupported resource type" errors are intentionally not deployable. Adding .ExcludeFromManifest() to suppress the error may allow azd init to succeed, but the resulting deployment would not represent the application's actual functionality.

Azure Functions: Secret initialization from Blob storage failed

Symptoms: Azure Functions app fails at startup with error:

System.InvalidOperationException: Secret initialization from Blob storage failed due to missing both
an Azure Storage connection string and a SAS connection uri.

Cause: When using AddAzureFunctionsProject with WithHostStorage(storage), Aspire configures identity-based storage access (managed identity). However, Azure Functions' internal secret management does not support identity-based URIs and requires file-based secret storage for Container Apps deployments.

Solution: Add AzureWebJobsSecretStorageType=Files environment variable to the Functions resource in the AppHost before running azd up:

var functions = builder.AddAzureFunctionsProject<Projects.ImageGallery_Functions>("functions")
                       .WithReference(queues)
                       .WithReference(blobs)
                       .WaitFor(storage)
                       .WithRoleAssignments(storage, ...)
                       .WithHostStorage(storage)
                       .WithEnvironment("AzureWebJobsSecretStorageType", "Files")  // Required for Container Apps
                       .WithUrlForEndpoint("http", u => u.DisplayText = "Functions App");

💡 Why this is required:

  • WithHostStorage(storage) sets identity-based URIs like AzureWebJobsStorage__blobServiceUri
  • This is correct and secure for runtime storage operations
  • However, Functions' secret/key management doesn't support these URIs
  • File-based secrets are mandatory for Container Apps deployments

⚠️ Important: This is required when:

  • Using AddAzureFunctionsProject in Aspire
  • Using WithHostStorage() with identity-based storage
  • Deploying to Azure Container Apps (the default for Aspire Functions)

Generated Infrastructure Note:

If you need to modify the generated Container Apps infrastructure directly, ensure the Functions container app has this environment variable:

resource functionsContainerApp 'Microsoft.App/containerApps@2024-03-01' = {
  properties: {
    template: {
      containers: [
        {
          env: [
            {
              name: 'AzureWebJobsSecretStorageType'
              value: 'Files'
            }
            // ... other environment variables
          ]
        }
      ]
    }
  }
}

Error: azd uses wrong subscription despite user confirmation

Symptoms: azd provision --preview shows a different subscription than the one the user confirmed

Cause: The AZURE_SUBSCRIPTION_ID was not set immediately after azd init --from-code. The Azure CLI and azd can have different default subscriptions.

Solution: Always set the subscription immediately after initialization:

# After azd init --from-code completes:
azd env set AZURE_SUBSCRIPTION_ID <user-confirmed-subscription-id>
azd env set AZURE_LOCATION <location>

# Verify before proceeding:
azd env get-values

Prevention: Follow the complete initialization sequence in the Flags Reference section above.

References

Next Steps

After azd init --from-code:

  1. Review generated azure.yaml and infra/ files (if present)
  2. Set AZURE_SUBSCRIPTION_ID and AZURE_LOCATION with azd env set
  3. Customize infrastructure as needed
  4. Proceed to azure-validate skill
  5. Deploy with azure-deploy skill

⚠️ Important for Container Apps: If using Aspire with Container Apps, azure-validate will check and help set up required environment variables after provisioning.

Source: SKILL.md on GitHub

2 warnings3d4 checks · Risk SAFE
  • Gen Agent Trust Hub3d

    This skill includes security considerations related to the processing of untrusted project files and the retrieval of external development templates. While these operations are essential for modernizing and preparing Azure applications, they represent a surface area for indirect prompt injection and depend on the integrity of external template repositories.

  • Socket3d

    5 alerts: gptAnomaly, gptSecurity

  • Snyk3d

    Risk: LOW · No issues

  • Runlayer7mo

    86/87 files flagged

Signed by skilld at b8a1c66. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated last week
metadata
{
  "author": "Microsoft",
  "version": "1.3.4"
}

README badge

README badge for microsoft/skills/azure-prepare