diff --git a/samples/readme.md b/samples/readme.md
index d2a7825..858d0b5 100644
--- a/samples/readme.md
+++ b/samples/readme.md
@@ -6,6 +6,7 @@ You can use this folder to share Logic Apps sample.
- [**Sample Logic Apps Workspace**](./sample-logicapp-workspace/): This is a simple request response project, just to exemplify the required structure.
- [**AI Loan Agent**](./ai-loan-agent-sample/): AI-powered loan approval system that automates the evaluation of vehicle loan applications using Azure Logic Apps Standard and Azure OpenAI.
+- [**Transaction Repair Agent**](./transaction-repair-agent/): Conversational AI agent that helps operations teams diagnose and repair failed work orders through natural chat interaction. Built with Azure Logic Apps Standard and Azure OpenAI, featuring guided workflows, approval management, and ITSM audit logging.
## How to contribute
diff --git a/samples/shared/README.md b/samples/shared/README.md
new file mode 100644
index 0000000..45d1576
--- /dev/null
+++ b/samples/shared/README.md
@@ -0,0 +1,203 @@
+# Shared Infrastructure for Logic Apps Samples
+
+Reusable Bicep modules, templates, and scripts that provide consistent infrastructure across all Logic Apps samples.
+
+## Quick Start
+
+```powershell
+# 1. Create folder structure
+samples/your-sample-name/
+├── Deployment/ # Generated assets (empty initially)
+└── LogicApps/ # Your workflows go here
+
+# 2. Generate deployment files
+.\samples\shared\scripts\BundleAssets.ps1 -Sample "your-sample-name"
+
+# 3. Deploy infrastructure
+az group create --name rg-test --location westeurope
+az deployment group create `
+ --resource-group rg-test `
+ --template-file samples/your-sample-name/Deployment/main.bicep `
+ --parameters BaseName=test123
+
+# 4. Upload local workflows (deployment-script 404 is expected!)
+az webapp deploy `
+ --name test123-logicapp `
+ --resource-group rg-test `
+ --src-path samples/your-sample-name/Deployment/workflows.zip `
+ --type zip
+
+# 5. Test your workflows in Azure Portal
+```
+
+## Directory Structure
+
+```
+shared/
+├── modules/ # 6 reusable Bicep modules
+│ ├── storage.bicep
+│ ├── openai.bicep
+│ ├── logicapp.bicep
+│ ├── storage-rbac.bicep
+│ ├── openai-rbac.bicep
+│ └── deployment-script.bicep
+├── scripts/
+│ └── BundleAssets.ps1 # Generates deployment artifacts
+└── templates/
+ └── main.bicep.template # Infrastructure template
+```
+
+## Bicep Modules
+
+Six reusable modules provide complete Logic Apps infrastructure:
+
+| Module | Purpose |
+|--------|---------|
+| **storage.bicep** | Storage Account for Logic App runtime (managed identity only, HTTPS/TLS 1.2, no shared keys) |
+| **openai.bicep** | Azure OpenAI S0 with gpt-4o-mini model (GlobalStandard, 150K tokens) |
+| **logicapp.bicep** | Logic App Standard with App Service Plan (WS1 SKU, system + user-assigned identities) |
+| **storage-rbac.bicep** | Storage Blob/Queue/Table Data Contributor roles for Logic App |
+| **openai-rbac.bicep** | Cognitive Services OpenAI User role for Logic App |
+| **deployment-script.bicep** | Downloads and deploys workflows.zip using deployment script |
+
+## BundleAssets.ps1 Script
+
+Generates all deployment artifacts for a sample:
+
+```powershell
+.\samples\shared\scripts\BundleAssets.ps1 -Sample "your-sample-name"
+```
+
+**Generates:**
+1. `main.bicep` - From template (only if doesn't exist, preserves customizations)
+2. `sample-arm.json` - Compiled ARM template
+3. `workflows.zip` - Bundled LogicApps folder
+
+**How it works:**
+- Uses hardcoded `Azure/logicapps-labs` main branch for upstream URLs
+- Never overwrites existing `main.bicep` (delete to regenerate)
+- Replaces `{{WORKFLOWS_ZIP_URL}}` template placeholder with upstream URL
+- Requires Bicep CLI and PowerShell 5.1+
+
+**Why upstream URLs?** The generated `workflowsZipUrl` parameter points to the upstream main branch so the "Deploy to Azure" button works for end users. The deployment-script module downloads and deploys workflows.zip from this URL automatically. For local testing before merge, use `az webapp deploy` to upload your local zip file instead.
+
+## Creating a New Sample
+
+### 1. Create Folder Structure
+
+```
+samples/your-sample-name/
+├── Deployment/ # Empty (generated by script)
+└── LogicApps/ # Your workflows
+ ├── host.json
+ ├── connections.json
+ └── YourWorkflow/
+ └── workflow.json
+```
+
+### 2. Generate Deployment Files
+
+```powershell
+.\samples\shared\scripts\BundleAssets.ps1 -Sample "your-sample-name"
+```
+
+Creates `Deployment/main.bicep` with:
+- 6 shared module references
+- `workflowsZipUrl` parameter pointing to upstream main (for "Deploy to Azure" button)
+- Standard BaseName and location parameters
+
+The upstream URL enables the deployment-script module to automatically download and deploy workflows.zip when users click "Deploy to Azure".
+
+### 3. Customize (Optional)
+
+Edit `Deployment/main.bicep` to add sample-specific resources or configurations. The script won't overwrite your changes.
+
+### 4. Test Locally
+
+```powershell
+# Create resource group (use supported region)
+az group create --name rg-test --location westeurope
+
+# Deploy infrastructure
+az deployment group create `
+ --resource-group rg-test `
+ --template-file samples/your-sample-name/Deployment/main.bicep `
+ --parameters BaseName=test123
+
+# Upload local workflows (deployment-script 404 is normal!)
+az webapp deploy `
+ --name test123-logicapp `
+ --resource-group rg-test `
+ --src-path samples/your-sample-name/Deployment/workflows.zip `
+ --type zip
+
+# Test workflows in Azure Portal
+```
+
+### 5. Commit After Testing
+
+```powershell
+git add samples/your-sample-name/
+git commit -m "Add your-sample-name sample"
+git push
+```
+
+## Local Testing Details
+
+### Prerequisites
+
+- Azure CLI and Bicep CLI installed
+- Azure subscription with:
+ - Azure OpenAI access ([request here](https://aka.ms/oai/access))
+ - Logic Apps Standard quota
+- Supported regions: **eastus2**, **westeurope**, **australiaeast**
+
+### Expected Deployment Behavior
+
+**Infrastructure deployment:**
+- ✅ Storage Account deploys successfully
+- ✅ Azure OpenAI deploys successfully
+- ✅ Logic App deploys successfully
+- ✅ RBAC roles assigned successfully
+- ❌ **deployment-script fails with 404** - This is **expected**! workflows.zip isn't on main yet.
+
+**Solution:** Use `az webapp deploy` to upload your local workflows.zip directly.
+
+### Validation Checklist
+
+After deployment:
+- ✅ All resources visible in Azure Portal
+- ✅ Logic App has system + user-assigned managed identities
+- ✅ Storage uses managed identity only (no keys)
+- ✅ Workflows deployed and visible in Logic App
+- ✅ Workflows execute successfully
+- ✅ OpenAI connections work
+
+### Troubleshooting
+
+| Issue | Solution |
+|-------|----------|
+| **Deployment-script 404** | Normal for local testing - use `az webapp deploy` instead |
+| **Logic Apps quota error** | Try different region: eastus2, westeurope, or australiaeast |
+| **OpenAI access denied** | Request access at https://aka.ms/oai/access |
+| **Region not supported** | Use eastus2, westeurope, or australiaeast only |
+
+### Cleanup
+
+```powershell
+az group delete --name rg-test --yes --no-wait
+```
+
+## Benefits
+
+✅ **Consistency** - All samples use identical infrastructure patterns
+✅ **Security** - Managed identity only, RBAC-based, no shared keys
+✅ **Simplicity** - One script, three files, ready to deploy
+✅ **Testable** - Local testing workflow with `az webapp deploy`
+✅ **Maintainable** - Fix modules once, all samples benefit
+
+## Examples
+
+See existing samples:
+- [product-return-agent-sample](../product-return-agent-sample/)
+- [ai-loan-agent-sample](../ai-loan-agent-sample/)
diff --git a/samples/shared/modules/deployment-script.bicep b/samples/shared/modules/deployment-script.bicep
new file mode 100644
index 0000000..1b8e5dd
--- /dev/null
+++ b/samples/shared/modules/deployment-script.bicep
@@ -0,0 +1,89 @@
+// Deployment Script Module - Deploys workflows.zip to Logic App
+// Includes RBAC assignment for deployment identity
+
+@description('Location for the deployment script resource')
+param location string
+
+@description('Name for the deployment script resource')
+param deploymentScriptName string
+
+@description('User-assigned managed identity ID for deployment')
+param userAssignedIdentityId string
+
+@description('Principal ID of the user-assigned managed identity used for deployment')
+param deploymentIdentityPrincipalId string
+
+@description('Name of the Logic App to deploy to')
+param logicAppName string
+
+@description('Resource group name')
+param resourceGroupName string
+
+@description('URL to the workflows.zip file')
+param workflowsZipUrl string
+
+// Grant Website Contributor role at resource group level to deployment identity
+// This allows the deployment script to deploy code to the Logic App and read the App Service Plan
+resource websiteContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid(resourceGroup().id, deploymentIdentityPrincipalId, 'de139f84-1756-47ae-9be6-808fbbe84772')
+ scope: resourceGroup()
+ properties: {
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') // Website Contributor
+ principalId: deploymentIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+}
+
+// Deploy workflows.zip to Logic App using Azure CLI
+resource workflowDeploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
+ name: deploymentScriptName
+ location: location
+ kind: 'AzureCLI'
+ identity: {
+ type: 'UserAssigned'
+ userAssignedIdentities: {
+ '${userAssignedIdentityId}': {}
+ }
+ }
+ properties: {
+ azCliVersion: '2.59.0'
+ retentionInterval: 'PT1H'
+ timeout: 'PT30M'
+ cleanupPreference: 'OnSuccess'
+ environmentVariables: [
+ {
+ name: 'LOGIC_APP_NAME'
+ value: logicAppName
+ }
+ {
+ name: 'RESOURCE_GROUP'
+ value: resourceGroupName
+ }
+ {
+ name: 'WORKFLOWS_ZIP_URL'
+ value: workflowsZipUrl
+ }
+ ]
+ scriptContent: '''
+ #!/bin/bash
+ set -e
+
+ echo "Downloading workflows.zip..."
+ wget -O workflows.zip "$WORKFLOWS_ZIP_URL"
+
+ echo "Deploying workflows to Logic App: $LOGIC_APP_NAME"
+ az functionapp deployment source config-zip \
+ --resource-group "$RESOURCE_GROUP" \
+ --name "$LOGIC_APP_NAME" \
+ --src workflows.zip
+
+ echo "Waiting 60 seconds for workflow registration and RBAC propagation..."
+ sleep 60
+
+ echo "Deployment completed successfully"
+ '''
+ }
+ dependsOn: [
+ websiteContributorRoleAssignment
+ ]
+}
diff --git a/samples/shared/modules/logicapp.bicep b/samples/shared/modules/logicapp.bicep
new file mode 100644
index 0000000..2a4a2cf
--- /dev/null
+++ b/samples/shared/modules/logicapp.bicep
@@ -0,0 +1,122 @@
+// Logic App Standard Module
+
+@description('Logic App name')
+param logicAppName string
+
+@description('Location for Logic App')
+param location string
+
+@description('Storage account name')
+param storageAccountName string
+
+@description('OpenAI endpoint')
+param openAIEndpoint string
+
+@description('OpenAI resource ID')
+param openAIResourceId string
+
+@description('User-assigned managed identity resource ID for storage authentication')
+param managedIdentityId string
+
+resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
+ name: '${logicAppName}-plan'
+ location: location
+ sku: {
+ name: 'WS1'
+ tier: 'WorkflowStandard'
+ }
+ kind: 'elastic'
+ properties: {
+ maximumElasticWorkerCount: 20
+ }
+}
+
+resource logicApp 'Microsoft.Web/sites@2023-12-01' = {
+ name: '${logicAppName}-logicapp'
+ location: location
+ kind: 'functionapp,workflowapp'
+ identity: {
+ type: 'SystemAssigned, UserAssigned'
+ userAssignedIdentities: {
+ '${managedIdentityId}': {}
+ }
+ }
+ properties: {
+ serverFarmId: appServicePlan.id
+ siteConfig: {
+ netFrameworkVersion: 'v8.0'
+ functionsRuntimeScaleMonitoringEnabled: true
+ appSettings: [
+ {
+ name: 'FUNCTIONS_EXTENSION_VERSION'
+ value: '~4'
+ }
+ {
+ name: 'FUNCTIONS_WORKER_RUNTIME'
+ value: 'dotnet'
+ }
+ {
+ name: 'AzureWebJobsStorage__managedIdentityResourceId'
+ value: managedIdentityId
+ }
+ {
+ name: 'AzureWebJobsStorage__credential'
+ value: 'managedIdentity'
+ }
+ {
+ name: 'AzureWebJobsStorage__blobServiceUri'
+ value: 'https://${storageAccountName}.blob.${environment().suffixes.storage}'
+ }
+ {
+ name: 'AzureWebJobsStorage__queueServiceUri'
+ value: 'https://${storageAccountName}.queue.${environment().suffixes.storage}'
+ }
+ {
+ name: 'AzureWebJobsStorage__tableServiceUri'
+ value: 'https://${storageAccountName}.table.${environment().suffixes.storage}'
+ }
+ {
+ name: 'WEBSITES_ENABLE_APP_SERVICE_STORAGE'
+ value: 'false'
+ }
+ {
+ name: 'AzureFunctionsJobHost__extensionBundle__id'
+ value: 'Microsoft.Azure.Functions.ExtensionBundle.Workflows'
+ }
+ {
+ name: 'AzureFunctionsJobHost__extensionBundle__version'
+ value: '[1.*, 2.0.0)'
+ }
+ {
+ name: 'APP_KIND'
+ value: 'workflowApp'
+ }
+ {
+ name: 'WORKFLOWS_SUBSCRIPTION_ID'
+ value: subscription().subscriptionId
+ }
+ {
+ name: 'WORKFLOWS_LOCATION_NAME'
+ value: location
+ }
+ {
+ name: 'WORKFLOWS_RESOURCE_GROUP_NAME'
+ value: resourceGroup().name
+ }
+ {
+ name: 'agent_openAIEndpoint'
+ value: openAIEndpoint
+ }
+ {
+ name: 'agent_ResourceID'
+ value: openAIResourceId
+ }
+ ]
+ }
+ httpsOnly: true
+ }
+}
+
+output name string = logicApp.name
+output systemAssignedPrincipalId string = logicApp.identity.principalId
+output quickTestUrl string = 'https://${logicApp.properties.defaultHostName}/api/ProductReturnAgent/triggers/When_a_HTTP_request_is_received/invoke?api-version=2022-05-01&sp=%2Ftriggers%2FWhen_a_HTTP_request_is_received%2Frun&sv=1.0&sig='
diff --git a/samples/shared/modules/openai-rbac.bicep b/samples/shared/modules/openai-rbac.bicep
new file mode 100644
index 0000000..fb9a60b
--- /dev/null
+++ b/samples/shared/modules/openai-rbac.bicep
@@ -0,0 +1,26 @@
+// OpenAI RBAC Module - Grants Logic App access to OpenAI
+
+@description('OpenAI account name')
+param openAIName string
+
+@description('Logic App managed identity principal ID')
+param logicAppPrincipalId string
+
+resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = {
+ name: openAIName
+}
+
+// Cognitive Services OpenAI User role
+var cognitiveServicesOpenAIUserRoleId = '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'
+
+resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid(openAI.id, logicAppPrincipalId, cognitiveServicesOpenAIUserRoleId)
+ scope: openAI
+ properties: {
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', cognitiveServicesOpenAIUserRoleId)
+ principalId: logicAppPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+}
+
+output roleAssignmentId string = roleAssignment.id
diff --git a/samples/shared/modules/openai.bicep b/samples/shared/modules/openai.bicep
new file mode 100644
index 0000000..e501a93
--- /dev/null
+++ b/samples/shared/modules/openai.bicep
@@ -0,0 +1,40 @@
+// Azure OpenAI Module
+
+@description('Azure OpenAI account name')
+param openAIName string
+
+@description('Location for Azure OpenAI')
+param location string
+
+resource openAI 'Microsoft.CognitiveServices/accounts@2024-10-01' = {
+ name: openAIName
+ location: location
+ kind: 'OpenAI'
+ sku: {
+ name: 'S0'
+ }
+ properties: {
+ customSubDomainName: openAIName
+ publicNetworkAccess: 'Enabled'
+ }
+}
+
+resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = {
+ parent: openAI
+ name: 'gpt-4o-mini'
+ sku: {
+ name: 'GlobalStandard'
+ capacity: 50
+ }
+ properties: {
+ model: {
+ format: 'OpenAI'
+ name: 'gpt-4o-mini'
+ version: '2024-07-18'
+ }
+ }
+}
+
+output name string = openAI.name
+output endpoint string = openAI.properties.endpoint
+output resourceId string = openAI.id
diff --git a/samples/shared/modules/storage-rbac.bicep b/samples/shared/modules/storage-rbac.bicep
new file mode 100644
index 0000000..1ed0ed8
--- /dev/null
+++ b/samples/shared/modules/storage-rbac.bicep
@@ -0,0 +1,45 @@
+// Storage RBAC Module - Assigns required roles to Logic App managed identity
+
+@description('Storage account name')
+param storageAccountName string
+
+@description('Principal ID of the Logic App managed identity')
+param logicAppPrincipalId string
+
+// Storage account reference
+resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
+ name: storageAccountName
+}
+
+// Role assignment: Storage Blob Data Owner
+resource storageBlobDataOwnerAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid(storageAccount.id, logicAppPrincipalId, 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')
+ scope: storageAccount
+ properties: {
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')
+ principalId: logicAppPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+}
+
+// Role assignment: Storage Queue Data Contributor
+resource storageQueueDataContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid(storageAccount.id, logicAppPrincipalId, '974c5e8b-45b9-4653-ba55-5f855dd0fb88')
+ scope: storageAccount
+ properties: {
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')
+ principalId: logicAppPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+}
+
+// Role assignment: Storage Table Data Contributor
+resource storageTableDataContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
+ name: guid(storageAccount.id, logicAppPrincipalId, '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')
+ scope: storageAccount
+ properties: {
+ roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')
+ principalId: logicAppPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+}
diff --git a/samples/shared/modules/storage.bicep b/samples/shared/modules/storage.bicep
new file mode 100644
index 0000000..449f211
--- /dev/null
+++ b/samples/shared/modules/storage.bicep
@@ -0,0 +1,28 @@
+// Storage Account Module - For Logic App runtime only
+
+@description('Storage account name')
+param storageAccountName string
+
+@description('Location for the storage account')
+param location string
+
+resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
+ name: storageAccountName
+ location: location
+ sku: {
+ name: 'Standard_LRS'
+ }
+ kind: 'StorageV2'
+ properties: {
+ supportsHttpsTrafficOnly: true
+ minimumTlsVersion: 'TLS1_2'
+ allowBlobPublicAccess: false
+ allowSharedKeyAccess: false // Enforce managed identity only - no connection strings or keys
+ }
+}
+
+output storageAccountName string = storageAccount.name
+output storageAccountId string = storageAccount.id
+output blobServiceUri string = storageAccount.properties.primaryEndpoints.blob
+output queueServiceUri string = storageAccount.properties.primaryEndpoints.queue
+output tableServiceUri string = storageAccount.properties.primaryEndpoints.table
diff --git a/samples/shared/scripts/BundleAssets.ps1 b/samples/shared/scripts/BundleAssets.ps1
new file mode 100644
index 0000000..b39c290
--- /dev/null
+++ b/samples/shared/scripts/BundleAssets.ps1
@@ -0,0 +1,279 @@
+#!/usr/bin/env powershell
+<#
+.SYNOPSIS
+ Create ARM template and bundle LogicApps folder for 1-click deployment.
+
+.DESCRIPTION
+ This script prepares all necessary assets for 1-click deployment by performing two key tasks:
+
+ 1. Build ARM Template: Compiles the Bicep infrastructure file into an ARM template using the Bicep CLI.
+ 2. Bundle Workflows: Creates a deployment-ready workflows.zip containing all Logic App workflows.
+
+ Automatically excludes development artifacts:
+ - Version control (.git)
+ - Editor settings (.vscode)
+ - Dependencies (node_modules)
+ - Local storage (__azurite*, __blobstorage__*, __queuestorage__*)
+ - Existing zip files
+
+.PARAMETER Sample
+ Required. Name of the sample folder (e.g., "product-return-agent-sample").
+ All paths are built from this parameter.
+
+.EXAMPLE
+ # From anywhere in the repository:
+ .\samples\shared\scripts\BundleAssets.ps1 -Sample "product-return-agent-sample"
+
+.EXAMPLE
+ # From samples folder:
+ .\shared\scripts\BundleAssets.ps1 -Sample "ai-loan-agent-sample"
+
+.NOTES
+ Requirements:
+ - Bicep CLI must be installed
+ - PowerShell 5.1 or later
+
+ Expected folder structure:
+ samples/your-sample/
+ ├── Deployment/
+ │ ├── main.bicep
+ │ ├── sample-arm.json # Generated
+ │ └── workflows.zip # Generated
+ └── LogicApps/
+#>
+
+[CmdletBinding()]
+param(
+ [Parameter(Mandatory = $true)]
+ [string]$Sample
+)
+
+$ErrorActionPreference = "Stop"
+
+# ============================================================================
+# CONSTANTS
+# ============================================================================
+
+# Hardcoded upstream repository for URL generation
+$upstreamRepo = "Azure/logicapps-labs"
+$upstreamBranch = "main"
+
+# ============================================================================
+# HELPER FUNCTIONS
+# ============================================================================
+
+Function New-MainBicepFromTemplate {
+ <#
+ .SYNOPSIS
+ Generates main.bicep from template with placeholder replacement
+ #>
+ param(
+ [Parameter(Mandatory = $true)]
+ [string]$TemplatePath,
+
+ [Parameter(Mandatory = $true)]
+ [string]$OutputPath,
+
+ [Parameter(Mandatory = $true)]
+ [string]$WorkflowsZipUrl
+ )
+
+ if (-not (Test-Path $TemplatePath)) {
+ throw "Template file not found: $TemplatePath"
+ }
+
+ $template = Get-Content $TemplatePath -Raw
+ $content = $template -replace '\{\{WORKFLOWS_ZIP_URL\}\}', $WorkflowsZipUrl
+
+ $outputDir = Split-Path $OutputPath
+ if (-not (Test-Path $outputDir)) {
+ New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
+ }
+
+ Set-Content -Path $OutputPath -Value $content -NoNewline
+}
+
+# ============================================================================
+# BUILD PATHS FROM SAMPLE NAME
+# ============================================================================
+
+# Find repository root (contains samples/ folder)
+$scriptDir = $PSScriptRoot
+$repoRoot = $scriptDir
+while ($repoRoot -and -not (Test-Path (Join-Path $repoRoot "samples"))) {
+ $repoRoot = Split-Path $repoRoot -Parent
+}
+
+if (-not $repoRoot) {
+ Write-Host "✗ Could not find repository root (looking for samples/ folder)" -ForegroundColor Red
+ exit 1
+}
+
+# Build all paths from sample name
+$sampleFolder = Join-Path $repoRoot "samples\$Sample"
+$deploymentFolder = Join-Path $sampleFolder "Deployment"
+$logicAppsFolder = Join-Path $sampleFolder "LogicApps"
+$bicepPath = Join-Path $deploymentFolder "main.bicep"
+$armTemplatePath = Join-Path $deploymentFolder "sample-arm.json"
+$zipPath = Join-Path $deploymentFolder "workflows.zip"
+
+# Get sample display name (convert folder name to title case)
+$sampleDisplayName = ($Sample -replace '-sample$', '' -replace '-', ' ' |
+ ForEach-Object { (Get-Culture).TextInfo.ToTitleCase($_) })
+
+Write-Host "`n=== Bundling Assets for $sampleDisplayName ===" -ForegroundColor Cyan
+Write-Host "Sample Folder: $sampleFolder" -ForegroundColor Gray
+Write-Host "Deployment Folder: $deploymentFolder" -ForegroundColor Gray
+Write-Host "LogicApps Folder: $logicAppsFolder" -ForegroundColor Gray
+
+# Validate paths
+if (-not (Test-Path $sampleFolder)) {
+ Write-Host "✗ Sample folder not found: $sampleFolder" -ForegroundColor Red
+ exit 1
+}
+
+if (-not (Test-Path $logicAppsFolder)) {
+ Write-Host "✗ LogicApps folder not found: $logicAppsFolder" -ForegroundColor Red
+ exit 1
+}
+
+# Ensure deployment directory exists
+if (-not (Test-Path $deploymentFolder)) {
+ New-Item -Path $deploymentFolder -ItemType Directory -Force | Out-Null
+ Write-Host "✓ Created Deployment folder" -ForegroundColor Green
+}
+
+# ============================================================================
+# TEMPLATE GENERATION: Create main.bicep if it doesn't exist
+# ============================================================================
+
+if (-not (Test-Path $bicepPath)) {
+ Write-Host "`nGenerating main.bicep from template..." -ForegroundColor Cyan
+
+ # Use hardcoded upstream repo for URL generation
+ $workflowsUrl = "https://raw.githubusercontent.com/$upstreamRepo/$upstreamBranch/samples/$Sample/Deployment/workflows.zip"
+ Write-Host " Using: $upstreamRepo / $upstreamBranch" -ForegroundColor Gray
+
+ # Use template from shared
+ $templatePath = Join-Path $repoRoot "samples\shared\templates\main.bicep.template"
+
+ if (-not (Test-Path $templatePath)) {
+ Write-Host "✗ Template file not found: $templatePath" -ForegroundColor Red
+ Write-Host " Expected at: samples/shared/templates/main.bicep.template" -ForegroundColor Yellow
+ exit 1
+ }
+
+ try {
+ New-MainBicepFromTemplate -TemplatePath $templatePath -OutputPath $bicepPath -WorkflowsZipUrl $workflowsUrl
+ Write-Host " ✓ Created: main.bicep" -ForegroundColor Green
+ Write-Host " Location: $bicepPath" -ForegroundColor Gray
+ } catch {
+ Write-Host "✗ Failed to generate main.bicep: $($_.Exception.Message)" -ForegroundColor Red
+ exit 1
+ }
+} else {
+ Write-Host "`nUsing existing main.bicep (not overwriting)" -ForegroundColor Cyan
+ Write-Host " Location: $bicepPath" -ForegroundColor Gray
+}
+
+# ============================================================================
+# BUILD BICEP TO ARM TEMPLATE
+# ============================================================================
+
+Write-Host "`nBuilding ARM template from Bicep..." -ForegroundColor Cyan
+
+# Check for Bicep CLI
+$bicepAvailable = $null -ne (Get-Command bicep -ErrorAction SilentlyContinue)
+
+if (-not $bicepAvailable) {
+ Write-Host "✗ Bicep CLI not found. Please install it first." -ForegroundColor Red
+ Write-Host "Install: https://learn.microsoft.com/azure/azure-resource-manager/bicep/install" -ForegroundColor Yellow
+ exit 1
+}
+
+try {
+ bicep build $bicepPath --outfile $armTemplatePath
+
+ if (Test-Path $armTemplatePath) {
+ $armSize = (Get-Item $armTemplatePath).Length / 1KB
+ Write-Host "✓ Successfully created sample-arm.json ($("{0:N2}" -f $armSize) KB)" -ForegroundColor Green
+ } else {
+ throw "ARM template file was not created"
+ }
+} catch {
+ Write-Host "✗ Failed to build ARM template: $($_.Exception.Message)" -ForegroundColor Red
+ exit 1
+}
+
+# ============================================================================
+# BUNDLE WORKFLOWS ZIP
+# ============================================================================
+
+Write-Host "`nBundling workflows.zip..." -ForegroundColor Cyan
+
+# Remove existing zip if present
+if (Test-Path $zipPath) {
+ Remove-Item $zipPath -Force
+ Write-Host "✓ Removed existing workflows.zip" -ForegroundColor Green
+}
+
+# Get all items except those we want to exclude
+$itemsToZip = Get-ChildItem -Path $logicAppsFolder | Where-Object {
+ $_.Name -notin @('.git', '.vscode', 'node_modules') -and
+ $_.Name -notlike '__azurite*' -and
+ $_.Name -notlike '__blobstorage__*' -and
+ $_.Name -notlike '__queuestorage__*' -and
+ $_.Extension -ne '.zip'
+}
+
+Write-Host "`nIncluding files:"
+$itemsToZip | ForEach-Object { Write-Host " - $($_.Name)" -ForegroundColor Gray }
+
+# Create zip
+Push-Location $logicAppsFolder
+try {
+ Compress-Archive -Path $itemsToZip.Name -DestinationPath $zipPath -Force
+} catch {
+ Pop-Location
+ Write-Host "`n✗ Failed to create workflows.zip: $($_.Exception.Message)" -ForegroundColor Red
+ exit 1
+}
+Pop-Location
+
+if (Test-Path $zipPath) {
+ $zipSize = (Get-Item $zipPath).Length / 1MB
+ Write-Host "`n✓ Successfully created workflows.zip ($("{0:N2}" -f $zipSize) MB)" -ForegroundColor Green
+ Write-Host "Location: $zipPath" -ForegroundColor Cyan
+} else {
+ Write-Host "`n✗ Failed to create workflows.zip" -ForegroundColor Red
+ exit 1
+}
+
+# ============================================================================
+# DEPLOY TO AZURE BUTTON
+# ============================================================================
+
+Write-Host "`n=== Deploy to Azure Button ===" -ForegroundColor Cyan
+
+# Construct the URL to sample-arm.json using hardcoded upstream repo
+$armUrl = "https://raw.githubusercontent.com/$upstreamRepo/$upstreamBranch/samples/$Sample/Deployment/sample-arm.json"
+
+# URL encode for Azure Portal
+$encodedUrl = [System.Uri]::EscapeDataString($armUrl)
+$portalUrl = "https://portal.azure.com/#create/Microsoft.Template/uri/$encodedUrl"
+$badgeUrl = "https://aka.ms/deploytoazurebutton"
+
+Write-Host "Repository: $upstreamRepo" -ForegroundColor Gray
+Write-Host "Branch: $upstreamBranch" -ForegroundColor Gray
+Write-Host "ARM URL: $armUrl" -ForegroundColor Gray
+Write-Host "`nAdd this to your README.md:" -ForegroundColor Cyan
+Write-Host "[]($portalUrl)" -ForegroundColor Green
+
+# ============================================================================
+# SUMMARY
+# ============================================================================
+
+Write-Host "`n=== Bundling Complete ===" -ForegroundColor Cyan
+Write-Host "Sample: $sampleDisplayName" -ForegroundColor Gray
+Write-Host "ARM Template: $armTemplatePath" -ForegroundColor Gray
+Write-Host "Workflows Zip: $zipPath" -ForegroundColor Gray
diff --git a/samples/shared/templates/main.bicep.template b/samples/shared/templates/main.bicep.template
new file mode 100644
index 0000000..16718b9
--- /dev/null
+++ b/samples/shared/templates/main.bicep.template
@@ -0,0 +1,99 @@
+// Auto-generated from shared/templates/main.bicep.template
+// To customize: edit this file directly or delete to regenerate from template
+//
+// AI Product Return Agent - Azure Infrastructure as Code
+// Deploys Logic Apps Standard with Azure OpenAI for autonomous product return decisions
+// Uses managed identity exclusively (no secrets/connection strings)
+
+targetScope = 'resourceGroup'
+
+@description('Base name used for the resources that will be deployed (alphanumerics and hyphens only)')
+@minLength(3)
+@maxLength(60)
+param BaseName string
+
+// uniqueSuffix for when we need unique values
+var uniqueSuffix = uniqueString(resourceGroup().id)
+
+// URL to workflows.zip (replaced by BundleAssets.ps1 with {{WORKFLOWS_ZIP_URL}})
+var workflowsZipUrl = '{{WORKFLOWS_ZIP_URL}}'
+
+// User-Assigned Managed Identity for Logic App → Storage authentication
+resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: '${take(BaseName, 60)}-managedidentity'
+ location: resourceGroup().location
+}
+
+// Storage Account for workflow runtime
+module storage '../../shared/modules/storage.bicep' = {
+ name: '${take(BaseName, 43)}-storage-deployment'
+ params: {
+ storageAccountName: toLower(take(replace('${take(BaseName, 16)}${uniqueSuffix}', '-', ''), 24))
+ location: resourceGroup().location
+ }
+}
+
+// Azure OpenAI with gpt-4o-mini model
+module openai '../../shared/modules/openai.bicep' = {
+ name: '${take(BaseName, 44)}-openai-deployment'
+ params: {
+ openAIName: '${take(BaseName, 54)}-openai'
+ location: resourceGroup().location
+ }
+}
+
+// Logic Apps Standard with dual managed identities
+module logicApp '../../shared/modules/logicapp.bicep' = {
+ name: '${take(BaseName, 42)}-logicapp-deployment'
+ params: {
+ logicAppName: '${take(BaseName, 22)}${uniqueSuffix}'
+ location: resourceGroup().location
+ storageAccountName: storage.outputs.storageAccountName
+ openAIEndpoint: openai.outputs.endpoint
+ openAIResourceId: openai.outputs.resourceId
+ managedIdentityId: userAssignedIdentity.id
+ }
+}
+
+// RBAC: Logic App → Storage (Blob, Queue, Table Contributor roles)
+module storageRbac '../../shared/modules/storage-rbac.bicep' = {
+ name: '${take(BaseName, 38)}-storage-rbac-deployment'
+ params: {
+ storageAccountName: storage.outputs.storageAccountName
+ logicAppPrincipalId: userAssignedIdentity.properties.principalId
+ }
+ dependsOn: [
+ logicApp
+ ]
+}
+
+// RBAC: Logic App → Azure OpenAI (Cognitive Services User role)
+module openaiRbac '../../shared/modules/openai-rbac.bicep' = {
+ name: '${take(BaseName, 39)}-openai-rbac-deployment'
+ params: {
+ openAIName: openai.outputs.name
+ logicAppPrincipalId: logicApp.outputs.systemAssignedPrincipalId
+ }
+}
+
+// Deploy workflows using deployment script with RBAC
+module workflowDeployment '../../shared/modules/deployment-script.bicep' = {
+ name: '${take(BaseName, 42)}-workflow-deployment'
+ params: {
+ deploymentScriptName: '${BaseName}-deploy-workflows'
+ location: resourceGroup().location
+ userAssignedIdentityId: userAssignedIdentity.id
+ deploymentIdentityPrincipalId: userAssignedIdentity.properties.principalId
+ logicAppName: logicApp.outputs.name
+ resourceGroupName: resourceGroup().name
+ workflowsZipUrl: workflowsZipUrl
+ }
+ dependsOn: [
+ storageRbac
+ openaiRbac
+ ]
+}
+
+// Outputs
+output logicAppName string = logicApp.outputs.name
+output openAIEndpoint string = openai.outputs.endpoint
diff --git a/samples/transaction-repair-agent/Deployment/main.bicep b/samples/transaction-repair-agent/Deployment/main.bicep
new file mode 100644
index 0000000..cab0011
--- /dev/null
+++ b/samples/transaction-repair-agent/Deployment/main.bicep
@@ -0,0 +1,99 @@
+// Auto-generated from shared/templates/main.bicep.template
+// To customize: edit this file directly or delete to regenerate from template
+//
+// Transaction Repair Agent - Azure Infrastructure as Code
+// Deploys Logic Apps Standard with Azure OpenAI for conversational operations agent
+// Uses managed identity exclusively (no secrets/connection strings)
+
+targetScope = 'resourceGroup'
+
+@description('Base name used for the resources that will be deployed (alphanumerics and hyphens only)')
+@minLength(3)
+@maxLength(60)
+param BaseName string
+
+// uniqueSuffix for when we need unique values
+var uniqueSuffix = uniqueString(resourceGroup().id)
+
+// URL to workflows.zip (replaced by BundleAssets.ps1 with actual GitHub URL)
+var workflowsZipUrl = 'https://raw.githubusercontent.com/modularity/logicapps-labs/transaction-repair-agent/samples/transaction-repair-agent/Deployment/workflows.zip'
+
+// User-Assigned Managed Identity for Logic App → Storage authentication
+resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: '${take(BaseName, 60)}-managedidentity'
+ location: resourceGroup().location
+}
+
+// Storage Account for workflow runtime
+module storage '../../shared/modules/storage.bicep' = {
+ name: '${take(BaseName, 43)}-storage-deployment'
+ params: {
+ storageAccountName: toLower(take(replace('${take(BaseName, 16)}${uniqueSuffix}', '-', ''), 24))
+ location: resourceGroup().location
+ }
+}
+
+// Azure OpenAI with gpt-4o-mini model
+module openai '../../shared/modules/openai.bicep' = {
+ name: '${take(BaseName, 44)}-openai-deployment'
+ params: {
+ openAIName: '${take(BaseName, 54)}-openai'
+ location: resourceGroup().location
+ }
+}
+
+// Logic Apps Standard with dual managed identities
+module logicApp '../../shared/modules/logicapp.bicep' = {
+ name: '${take(BaseName, 42)}-logicapp-deployment'
+ params: {
+ logicAppName: '${take(BaseName, 22)}${uniqueSuffix}'
+ location: resourceGroup().location
+ storageAccountName: storage.outputs.storageAccountName
+ openAIEndpoint: openai.outputs.endpoint
+ openAIResourceId: openai.outputs.resourceId
+ managedIdentityId: userAssignedIdentity.id
+ }
+}
+
+// RBAC: Logic App → Storage (Blob, Queue, Table Contributor roles)
+module storageRbac '../../shared/modules/storage-rbac.bicep' = {
+ name: '${take(BaseName, 38)}-storage-rbac-deployment'
+ params: {
+ storageAccountName: storage.outputs.storageAccountName
+ logicAppPrincipalId: userAssignedIdentity.properties.principalId
+ }
+ dependsOn: [
+ logicApp
+ ]
+}
+
+// RBAC: Logic App → Azure OpenAI (Cognitive Services User role)
+module openaiRbac '../../shared/modules/openai-rbac.bicep' = {
+ name: '${take(BaseName, 39)}-openai-rbac-deployment'
+ params: {
+ openAIName: openai.outputs.name
+ logicAppPrincipalId: logicApp.outputs.systemAssignedPrincipalId
+ }
+}
+
+// Deploy workflows using deployment script with RBAC
+module workflowDeployment '../../shared/modules/deployment-script.bicep' = {
+ name: '${take(BaseName, 42)}-workflow-deployment'
+ params: {
+ deploymentScriptName: '${BaseName}-deploy-workflows'
+ location: resourceGroup().location
+ userAssignedIdentityId: userAssignedIdentity.id
+ deploymentIdentityPrincipalId: userAssignedIdentity.properties.principalId
+ logicAppName: logicApp.outputs.name
+ resourceGroupName: resourceGroup().name
+ workflowsZipUrl: workflowsZipUrl
+ }
+ dependsOn: [
+ storageRbac
+ openaiRbac
+ ]
+}
+
+// Outputs
+output logicAppName string = logicApp.outputs.name
+output openAIEndpoint string = openai.outputs.endpoint
diff --git a/samples/transaction-repair-agent/Deployment/sample-arm.json b/samples/transaction-repair-agent/Deployment/sample-arm.json
new file mode 100644
index 0000000..c17d9e2
--- /dev/null
+++ b/samples/transaction-repair-agent/Deployment/sample-arm.json
@@ -0,0 +1,723 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.34.44.8038",
+ "templateHash": "5815924213456264593"
+ }
+ },
+ "parameters": {
+ "BaseName": {
+ "type": "string",
+ "minLength": 3,
+ "maxLength": 60,
+ "metadata": {
+ "description": "Base name used for the resources that will be deployed (alphanumerics and hyphens only)"
+ }
+ }
+ },
+ "variables": {
+ "uniqueSuffix": "[uniqueString(resourceGroup().id)]",
+ "workflowsZipUrl": "https://raw.githubusercontent.com/modularity/logicapps-labs/transaction-repair-agent/samples/transaction-repair-agent/Deployment/workflows.zip"
+ },
+ "resources": [
+ {
+ "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
+ "apiVersion": "2023-01-31",
+ "name": "[format('{0}-managedidentity', take(parameters('BaseName'), 60))]",
+ "location": "[resourceGroup().location]"
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-storage-deployment', take(parameters('BaseName'), 43))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "storageAccountName": {
+ "value": "[toLower(take(replace(format('{0}{1}', take(parameters('BaseName'), 16), variables('uniqueSuffix')), '-', ''), 24))]"
+ },
+ "location": {
+ "value": "[resourceGroup().location]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.34.44.8038",
+ "templateHash": "6666359930464991611"
+ }
+ },
+ "parameters": {
+ "storageAccountName": {
+ "type": "string",
+ "metadata": {
+ "description": "Storage account name"
+ }
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "Location for the storage account"
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Storage/storageAccounts",
+ "apiVersion": "2023-05-01",
+ "name": "[parameters('storageAccountName')]",
+ "location": "[parameters('location')]",
+ "sku": {
+ "name": "Standard_LRS"
+ },
+ "kind": "StorageV2",
+ "properties": {
+ "supportsHttpsTrafficOnly": true,
+ "minimumTlsVersion": "TLS1_2",
+ "allowBlobPublicAccess": false,
+ "allowSharedKeyAccess": false
+ }
+ }
+ ],
+ "outputs": {
+ "storageAccountName": {
+ "type": "string",
+ "value": "[parameters('storageAccountName')]"
+ },
+ "storageAccountId": {
+ "type": "string",
+ "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
+ },
+ "blobServiceUri": {
+ "type": "string",
+ "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2023-05-01').primaryEndpoints.blob]"
+ },
+ "queueServiceUri": {
+ "type": "string",
+ "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2023-05-01').primaryEndpoints.queue]"
+ },
+ "tableServiceUri": {
+ "type": "string",
+ "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2023-05-01').primaryEndpoints.table]"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-openai-deployment', take(parameters('BaseName'), 44))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "openAIName": {
+ "value": "[format('{0}-openai', take(parameters('BaseName'), 54))]"
+ },
+ "location": {
+ "value": "[resourceGroup().location]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.34.44.8038",
+ "templateHash": "5370603553016721570"
+ }
+ },
+ "parameters": {
+ "openAIName": {
+ "type": "string",
+ "metadata": {
+ "description": "Azure OpenAI account name"
+ }
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "Location for Azure OpenAI"
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.CognitiveServices/accounts",
+ "apiVersion": "2024-10-01",
+ "name": "[parameters('openAIName')]",
+ "location": "[parameters('location')]",
+ "kind": "OpenAI",
+ "sku": {
+ "name": "S0"
+ },
+ "properties": {
+ "customSubDomainName": "[parameters('openAIName')]",
+ "publicNetworkAccess": "Enabled"
+ }
+ },
+ {
+ "type": "Microsoft.CognitiveServices/accounts/deployments",
+ "apiVersion": "2024-10-01",
+ "name": "[format('{0}/{1}', parameters('openAIName'), 'gpt-4o-mini')]",
+ "sku": {
+ "name": "GlobalStandard",
+ "capacity": 50
+ },
+ "properties": {
+ "model": {
+ "format": "OpenAI",
+ "name": "gpt-4o-mini",
+ "version": "2024-07-18"
+ }
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName'))]"
+ ]
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "value": "[parameters('openAIName')]"
+ },
+ "endpoint": {
+ "type": "string",
+ "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName')), '2024-10-01').endpoint]"
+ },
+ "resourceId": {
+ "type": "string",
+ "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName'))]"
+ }
+ }
+ }
+ }
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-logicapp-deployment', take(parameters('BaseName'), 42))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "logicAppName": {
+ "value": "[format('{0}{1}', take(parameters('BaseName'), 22), variables('uniqueSuffix'))]"
+ },
+ "location": {
+ "value": "[resourceGroup().location]"
+ },
+ "storageAccountName": {
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-storage-deployment', take(parameters('BaseName'), 43))), '2022-09-01').outputs.storageAccountName.value]"
+ },
+ "openAIEndpoint": {
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-openai-deployment', take(parameters('BaseName'), 44))), '2022-09-01').outputs.endpoint.value]"
+ },
+ "openAIResourceId": {
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-openai-deployment', take(parameters('BaseName'), 44))), '2022-09-01').outputs.resourceId.value]"
+ },
+ "managedIdentityId": {
+ "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-managedidentity', take(parameters('BaseName'), 60)))]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.34.44.8038",
+ "templateHash": "2259432651138452624"
+ }
+ },
+ "parameters": {
+ "logicAppName": {
+ "type": "string",
+ "metadata": {
+ "description": "Logic App name"
+ }
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "Location for Logic App"
+ }
+ },
+ "storageAccountName": {
+ "type": "string",
+ "metadata": {
+ "description": "Storage account name"
+ }
+ },
+ "openAIEndpoint": {
+ "type": "string",
+ "metadata": {
+ "description": "OpenAI endpoint"
+ }
+ },
+ "openAIResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "OpenAI resource ID"
+ }
+ },
+ "managedIdentityId": {
+ "type": "string",
+ "metadata": {
+ "description": "User-assigned managed identity resource ID for storage authentication"
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Web/serverfarms",
+ "apiVersion": "2023-12-01",
+ "name": "[format('{0}-plan', parameters('logicAppName'))]",
+ "location": "[parameters('location')]",
+ "sku": {
+ "name": "WS1",
+ "tier": "WorkflowStandard"
+ },
+ "kind": "elastic",
+ "properties": {
+ "maximumElasticWorkerCount": 20
+ }
+ },
+ {
+ "type": "Microsoft.Web/sites",
+ "apiVersion": "2023-12-01",
+ "name": "[format('{0}-logicapp', parameters('logicAppName'))]",
+ "location": "[parameters('location')]",
+ "kind": "functionapp,workflowapp",
+ "identity": {
+ "type": "SystemAssigned, UserAssigned",
+ "userAssignedIdentities": {
+ "[format('{0}', parameters('managedIdentityId'))]": {}
+ }
+ },
+ "properties": {
+ "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', format('{0}-plan', parameters('logicAppName')))]",
+ "siteConfig": {
+ "netFrameworkVersion": "v8.0",
+ "functionsRuntimeScaleMonitoringEnabled": true,
+ "appSettings": [
+ {
+ "name": "FUNCTIONS_EXTENSION_VERSION",
+ "value": "~4"
+ },
+ {
+ "name": "FUNCTIONS_WORKER_RUNTIME",
+ "value": "dotnet"
+ },
+ {
+ "name": "AzureWebJobsStorage__managedIdentityResourceId",
+ "value": "[parameters('managedIdentityId')]"
+ },
+ {
+ "name": "AzureWebJobsStorage__credential",
+ "value": "managedIdentity"
+ },
+ {
+ "name": "AzureWebJobsStorage__blobServiceUri",
+ "value": "[format('https://{0}.blob.{1}', parameters('storageAccountName'), environment().suffixes.storage)]"
+ },
+ {
+ "name": "AzureWebJobsStorage__queueServiceUri",
+ "value": "[format('https://{0}.queue.{1}', parameters('storageAccountName'), environment().suffixes.storage)]"
+ },
+ {
+ "name": "AzureWebJobsStorage__tableServiceUri",
+ "value": "[format('https://{0}.table.{1}', parameters('storageAccountName'), environment().suffixes.storage)]"
+ },
+ {
+ "name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE",
+ "value": "false"
+ },
+ {
+ "name": "AzureFunctionsJobHost__extensionBundle__id",
+ "value": "Microsoft.Azure.Functions.ExtensionBundle.Workflows"
+ },
+ {
+ "name": "AzureFunctionsJobHost__extensionBundle__version",
+ "value": "[1.*, 2.0.0)"
+ },
+ {
+ "name": "APP_KIND",
+ "value": "workflowApp"
+ },
+ {
+ "name": "WORKFLOWS_SUBSCRIPTION_ID",
+ "value": "[subscription().subscriptionId]"
+ },
+ {
+ "name": "WORKFLOWS_LOCATION_NAME",
+ "value": "[parameters('location')]"
+ },
+ {
+ "name": "WORKFLOWS_RESOURCE_GROUP_NAME",
+ "value": "[resourceGroup().name]"
+ },
+ {
+ "name": "agent_openAIEndpoint",
+ "value": "[parameters('openAIEndpoint')]"
+ },
+ {
+ "name": "agent_ResourceID",
+ "value": "[parameters('openAIResourceId')]"
+ }
+ ]
+ },
+ "httpsOnly": true
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/serverfarms', format('{0}-plan', parameters('logicAppName')))]"
+ ]
+ }
+ ],
+ "outputs": {
+ "name": {
+ "type": "string",
+ "value": "[format('{0}-logicapp', parameters('logicAppName'))]"
+ },
+ "systemAssignedPrincipalId": {
+ "type": "string",
+ "value": "[reference(resourceId('Microsoft.Web/sites', format('{0}-logicapp', parameters('logicAppName'))), '2023-12-01', 'full').identity.principalId]"
+ },
+ "quickTestUrl": {
+ "type": "string",
+ "value": "[format('https://{0}/api/ProductReturnAgent/triggers/When_a_HTTP_request_is_received/invoke?api-version=2022-05-01&sp=%2Ftriggers%2FWhen_a_HTTP_request_is_received%2Frun&sv=1.0&sig=', reference(resourceId('Microsoft.Web/sites', format('{0}-logicapp', parameters('logicAppName'))), '2023-12-01').defaultHostName)]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.Resources/deployments', format('{0}-openai-deployment', take(parameters('BaseName'), 44)))]",
+ "[resourceId('Microsoft.Resources/deployments', format('{0}-storage-deployment', take(parameters('BaseName'), 43)))]",
+ "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-managedidentity', take(parameters('BaseName'), 60)))]"
+ ]
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-storage-rbac-deployment', take(parameters('BaseName'), 38))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "storageAccountName": {
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-storage-deployment', take(parameters('BaseName'), 43))), '2022-09-01').outputs.storageAccountName.value]"
+ },
+ "logicAppPrincipalId": {
+ "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-managedidentity', take(parameters('BaseName'), 60))), '2023-01-31').principalId]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.34.44.8038",
+ "templateHash": "13372547588259048703"
+ }
+ },
+ "parameters": {
+ "storageAccountName": {
+ "type": "string",
+ "metadata": {
+ "description": "Storage account name"
+ }
+ },
+ "logicAppPrincipalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Principal ID of the Logic App managed identity"
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]",
+ "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('logicAppPrincipalId'), 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]",
+ "properties": {
+ "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]",
+ "principalId": "[parameters('logicAppPrincipalId')]",
+ "principalType": "ServicePrincipal"
+ }
+ },
+ {
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]",
+ "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('logicAppPrincipalId'), '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]",
+ "properties": {
+ "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]",
+ "principalId": "[parameters('logicAppPrincipalId')]",
+ "principalType": "ServicePrincipal"
+ }
+ },
+ {
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]",
+ "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('logicAppPrincipalId'), '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]",
+ "properties": {
+ "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]",
+ "principalId": "[parameters('logicAppPrincipalId')]",
+ "principalType": "ServicePrincipal"
+ }
+ }
+ ]
+ }
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.Resources/deployments', format('{0}-logicapp-deployment', take(parameters('BaseName'), 42)))]",
+ "[resourceId('Microsoft.Resources/deployments', format('{0}-storage-deployment', take(parameters('BaseName'), 43)))]",
+ "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-managedidentity', take(parameters('BaseName'), 60)))]"
+ ]
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-openai-rbac-deployment', take(parameters('BaseName'), 39))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "openAIName": {
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-openai-deployment', take(parameters('BaseName'), 44))), '2022-09-01').outputs.name.value]"
+ },
+ "logicAppPrincipalId": {
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-logicapp-deployment', take(parameters('BaseName'), 42))), '2022-09-01').outputs.systemAssignedPrincipalId.value]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.34.44.8038",
+ "templateHash": "3822718305786172483"
+ }
+ },
+ "parameters": {
+ "openAIName": {
+ "type": "string",
+ "metadata": {
+ "description": "OpenAI account name"
+ }
+ },
+ "logicAppPrincipalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Logic App managed identity principal ID"
+ }
+ }
+ },
+ "variables": {
+ "cognitiveServicesOpenAIUserRoleId": "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd"
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('openAIName'))]",
+ "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName')), parameters('logicAppPrincipalId'), variables('cognitiveServicesOpenAIUserRoleId'))]",
+ "properties": {
+ "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', variables('cognitiveServicesOpenAIUserRoleId'))]",
+ "principalId": "[parameters('logicAppPrincipalId')]",
+ "principalType": "ServicePrincipal"
+ }
+ }
+ ],
+ "outputs": {
+ "roleAssignmentId": {
+ "type": "string",
+ "value": "[extensionResourceId(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName')), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('openAIName')), parameters('logicAppPrincipalId'), variables('cognitiveServicesOpenAIUserRoleId')))]"
+ }
+ }
+ }
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.Resources/deployments', format('{0}-logicapp-deployment', take(parameters('BaseName'), 42)))]",
+ "[resourceId('Microsoft.Resources/deployments', format('{0}-openai-deployment', take(parameters('BaseName'), 44)))]"
+ ]
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2022-09-01",
+ "name": "[format('{0}-workflow-deployment', take(parameters('BaseName'), 42))]",
+ "properties": {
+ "expressionEvaluationOptions": {
+ "scope": "inner"
+ },
+ "mode": "Incremental",
+ "parameters": {
+ "deploymentScriptName": {
+ "value": "[format('{0}-deploy-workflows', parameters('BaseName'))]"
+ },
+ "location": {
+ "value": "[resourceGroup().location]"
+ },
+ "userAssignedIdentityId": {
+ "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-managedidentity', take(parameters('BaseName'), 60)))]"
+ },
+ "deploymentIdentityPrincipalId": {
+ "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-managedidentity', take(parameters('BaseName'), 60))), '2023-01-31').principalId]"
+ },
+ "logicAppName": {
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-logicapp-deployment', take(parameters('BaseName'), 42))), '2022-09-01').outputs.name.value]"
+ },
+ "resourceGroupName": {
+ "value": "[resourceGroup().name]"
+ },
+ "workflowsZipUrl": {
+ "value": "[variables('workflowsZipUrl')]"
+ }
+ },
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.34.44.8038",
+ "templateHash": "6479226689635161201"
+ }
+ },
+ "parameters": {
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "Location for the deployment script resource"
+ }
+ },
+ "deploymentScriptName": {
+ "type": "string",
+ "metadata": {
+ "description": "Name for the deployment script resource"
+ }
+ },
+ "userAssignedIdentityId": {
+ "type": "string",
+ "metadata": {
+ "description": "User-assigned managed identity ID for deployment"
+ }
+ },
+ "deploymentIdentityPrincipalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Principal ID of the user-assigned managed identity used for deployment"
+ }
+ },
+ "logicAppName": {
+ "type": "string",
+ "metadata": {
+ "description": "Name of the Logic App to deploy to"
+ }
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "Resource group name"
+ }
+ },
+ "workflowsZipUrl": {
+ "type": "string",
+ "metadata": {
+ "description": "URL to the workflows.zip file"
+ }
+ }
+ },
+ "resources": [
+ {
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "name": "[guid(resourceGroup().id, parameters('deploymentIdentityPrincipalId'), 'de139f84-1756-47ae-9be6-808fbbe84772')]",
+ "properties": {
+ "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]",
+ "principalId": "[parameters('deploymentIdentityPrincipalId')]",
+ "principalType": "ServicePrincipal"
+ }
+ },
+ {
+ "type": "Microsoft.Resources/deploymentScripts",
+ "apiVersion": "2023-08-01",
+ "name": "[parameters('deploymentScriptName')]",
+ "location": "[parameters('location')]",
+ "kind": "AzureCLI",
+ "identity": {
+ "type": "UserAssigned",
+ "userAssignedIdentities": {
+ "[format('{0}', parameters('userAssignedIdentityId'))]": {}
+ }
+ },
+ "properties": {
+ "azCliVersion": "2.59.0",
+ "retentionInterval": "PT1H",
+ "timeout": "PT30M",
+ "cleanupPreference": "OnSuccess",
+ "environmentVariables": [
+ {
+ "name": "LOGIC_APP_NAME",
+ "value": "[parameters('logicAppName')]"
+ },
+ {
+ "name": "RESOURCE_GROUP",
+ "value": "[parameters('resourceGroupName')]"
+ },
+ {
+ "name": "WORKFLOWS_ZIP_URL",
+ "value": "[parameters('workflowsZipUrl')]"
+ }
+ ],
+ "scriptContent": " #!/bin/bash\r\n set -e\r\n\r\n echo \"Downloading workflows.zip...\"\r\n wget -O workflows.zip \"$WORKFLOWS_ZIP_URL\"\r\n\r\n echo \"Deploying workflows to Logic App: $LOGIC_APP_NAME\"\r\n az functionapp deployment source config-zip \\\r\n --resource-group \"$RESOURCE_GROUP\" \\\r\n --name \"$LOGIC_APP_NAME\" \\\r\n --src workflows.zip\r\n\r\n echo \"Waiting 60 seconds for workflow registration and RBAC propagation...\"\r\n sleep 60\r\n\r\n echo \"Deployment completed successfully\"\r\n "
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.Authorization/roleAssignments', guid(resourceGroup().id, parameters('deploymentIdentityPrincipalId'), 'de139f84-1756-47ae-9be6-808fbbe84772'))]"
+ ]
+ }
+ ]
+ }
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.Resources/deployments', format('{0}-logicapp-deployment', take(parameters('BaseName'), 42)))]",
+ "[resourceId('Microsoft.Resources/deployments', format('{0}-openai-rbac-deployment', take(parameters('BaseName'), 39)))]",
+ "[resourceId('Microsoft.Resources/deployments', format('{0}-storage-rbac-deployment', take(parameters('BaseName'), 38)))]",
+ "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-managedidentity', take(parameters('BaseName'), 60)))]"
+ ]
+ }
+ ],
+ "outputs": {
+ "logicAppName": {
+ "type": "string",
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-logicapp-deployment', take(parameters('BaseName'), 42))), '2022-09-01').outputs.name.value]"
+ },
+ "openAIEndpoint": {
+ "type": "string",
+ "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-openai-deployment', take(parameters('BaseName'), 44))), '2022-09-01').outputs.endpoint.value]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/transaction-repair-agent/Deployment/workflows.zip b/samples/transaction-repair-agent/Deployment/workflows.zip
new file mode 100644
index 0000000..4379654
Binary files /dev/null and b/samples/transaction-repair-agent/Deployment/workflows.zip differ
diff --git a/samples/transaction-repair-agent/LogicApps/GetApproval/workflow.json b/samples/transaction-repair-agent/LogicApps/GetApproval/workflow.json
new file mode 100644
index 0000000..f18db26
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/GetApproval/workflow.json
@@ -0,0 +1,55 @@
+{
+ "definition": {
+ "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
+ "contentVersion": "1.0.0.0",
+ "actions": {
+ "Initialize_Approvals": {
+ "type": "InitializeVariable",
+ "inputs": {
+ "variables": [
+ {
+ "name": "ApprovalResponses",
+ "type": "object",
+ "value": {
+ "WO-12345": "APPROVED",
+ "WO-67890": "DENIED",
+ "WO-11111": "APPROVED"
+ }
+ }
+ ]
+ },
+ "runAfter": {}
+ },
+ "Response": {
+ "type": "Response",
+ "kind": "Http",
+ "inputs": {
+ "statusCode": 200,
+ "body": "@variables('ApprovalResponses')[triggerBody()?['trackingId']]"
+ },
+ "runAfter": {
+ "Initialize_Approvals": ["Succeeded"]
+ }
+ }
+ },
+ "contentVersion": "1.0.0.0",
+ "outputs": {},
+ "triggers": {
+ "manual": {
+ "type": "Request",
+ "kind": "Http",
+ "inputs": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "trackingId": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "kind": "Stateful"
+}
diff --git a/samples/transaction-repair-agent/LogicApps/GetFixPlan/workflow.json b/samples/transaction-repair-agent/LogicApps/GetFixPlan/workflow.json
new file mode 100644
index 0000000..065bc11
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/GetFixPlan/workflow.json
@@ -0,0 +1,61 @@
+{
+ "definition": {
+ "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
+ "contentVersion": "1.0.0.0",
+ "actions": {
+ "Initialize_Catalog": {
+ "type": "InitializeVariable",
+ "inputs": {
+ "variables": [
+ {
+ "name": "RepairCatalog",
+ "type": "object",
+ "value": {
+ "schema_validation_error": {
+ "fixSteps": ["Convert amount field from string to number", "Validate against order schema", "Resubmit to processing queue"]
+ },
+ "timeout_config": {
+ "fixSteps": ["Increase API timeout from 30s to 300s", "Update retry policy", "Redeploy configuration"]
+ },
+ "missing_required_field": {
+ "fixSteps": ["Add missing required field with default value", "Apply field mapping transformation"]
+ }
+ }
+ }
+ ]
+ },
+ "runAfter": {}
+ },
+ "Response": {
+ "type": "Response",
+ "kind": "Http",
+ "inputs": {
+ "statusCode": 200,
+ "body": "@variables('RepairCatalog')[triggerBody()?['failureType']]"
+ },
+ "runAfter": {
+ "Initialize_Catalog": ["Succeeded"]
+ }
+ }
+ },
+ "contentVersion": "1.0.0.0",
+ "outputs": {},
+ "triggers": {
+ "manual": {
+ "type": "Request",
+ "kind": "Http",
+ "inputs": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "failureType": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "kind": "Stateful"
+}
diff --git a/samples/transaction-repair-agent/LogicApps/GetWorkOrder/workflow.json b/samples/transaction-repair-agent/LogicApps/GetWorkOrder/workflow.json
new file mode 100644
index 0000000..52494a5
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/GetWorkOrder/workflow.json
@@ -0,0 +1,83 @@
+{
+ "definition": {
+ "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
+ "contentVersion": "1.0.0.0",
+ "actions": {
+ "Find_WorkOrder": {
+ "type": "Query",
+ "inputs": {
+ "from": "@variables('WorkOrderQueue')",
+ "where": "@equals(item()?['trackingId'], triggerBody()?['trackingId'])"
+ },
+ "runAfter": {
+ "Initialize_Queue": [
+ "Succeeded"
+ ]
+ }
+ },
+ "Response": {
+ "type": "Response",
+ "kind": "Http",
+ "inputs": {
+ "statusCode": 200,
+ "body": "@first(body('Find_WorkOrder'))"
+ },
+ "runAfter": {
+ "Find_WorkOrder": [
+ "Succeeded"
+ ]
+ }
+ },
+ "Initialize_Queue": {
+ "type": "InitializeVariable",
+ "inputs": {
+ "variables": [
+ {
+ "name": "WorkOrderQueue",
+ "type": "array",
+ "value": [
+ {
+ "trackingId": "WO-12345",
+ "failureType": "schema_validation_error",
+ "payload": {"orderId": "ORD-9876", "amount": "250.00", "status": "failed"},
+ "timestamp": "2025-12-15T10:30:00Z"
+ },
+ {
+ "trackingId": "WO-67890",
+ "failureType": "timeout_config",
+ "payload": {"apiEndpoint": "/api/process", "timeout": 30, "error": "timeout_exceeded"},
+ "timestamp": "2025-12-15T11:15:00Z"
+ },
+ {
+ "trackingId": "WO-11111",
+ "failureType": "missing_required_field",
+ "payload": {"customerId": "CUST-555", "productId": "PROD-123"},
+ "timestamp": "2025-12-15T09:45:00Z"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "contentVersion": "1.0.0.0",
+ "outputs": {},
+ "triggers": {
+ "manual": {
+ "type": "Request",
+ "kind": "Http",
+ "inputs": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "trackingId": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "kind": "Stateful"
+}
diff --git a/samples/transaction-repair-agent/LogicApps/LogToITSM/workflow.json b/samples/transaction-repair-agent/LogicApps/LogToITSM/workflow.json
new file mode 100644
index 0000000..a6276e1
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/LogToITSM/workflow.json
@@ -0,0 +1,42 @@
+{
+ "definition": {
+ "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
+ "contentVersion": "1.0.0.0",
+ "actions": {
+ "Response": {
+ "type": "Response",
+ "kind": "Http",
+ "inputs": {
+ "statusCode": 200,
+ "body": {
+ "incidentId": "@concat('INC', string(rand(100000, 999999)))",
+ "trackingId": "@triggerBody()?['trackingId']",
+ "timestamp": "@utcNow()"
+ }
+ }
+ }
+ },
+ "contentVersion": "1.0.0.0",
+ "outputs": {},
+ "triggers": {
+ "manual": {
+ "type": "Request",
+ "kind": "Http",
+ "inputs": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "trackingId": {
+ "type": "string"
+ },
+ "action": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "kind": "Stateful"
+}
diff --git a/samples/transaction-repair-agent/LogicApps/ResubmitWorkOrder/workflow.json b/samples/transaction-repair-agent/LogicApps/ResubmitWorkOrder/workflow.json
new file mode 100644
index 0000000..a50dddf
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/ResubmitWorkOrder/workflow.json
@@ -0,0 +1,42 @@
+{
+ "definition": {
+ "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
+ "contentVersion": "1.0.0.0",
+ "actions": {
+ "Response": {
+ "type": "Response",
+ "kind": "Http",
+ "inputs": {
+ "statusCode": 200,
+ "body": {
+ "status": "resubmitted",
+ "trackingId": "@triggerBody()?['trackingId']",
+ "timestamp": "@utcNow()"
+ }
+ }
+ }
+ },
+ "contentVersion": "1.0.0.0",
+ "outputs": {},
+ "triggers": {
+ "manual": {
+ "type": "Request",
+ "kind": "Http",
+ "inputs": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "trackingId": {
+ "type": "string"
+ },
+ "payload": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "kind": "Stateful"
+}
diff --git a/samples/transaction-repair-agent/LogicApps/TransactionRepairAgent/workflow.json b/samples/transaction-repair-agent/LogicApps/TransactionRepairAgent/workflow.json
new file mode 100644
index 0000000..158ee23
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/TransactionRepairAgent/workflow.json
@@ -0,0 +1,208 @@
+{
+ "definition": {
+ "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
+ "actions": {
+ "Transaction_Repair_Agent": {
+ "type": "Agent",
+ "inputs": {
+ "parameters": {
+ "deploymentId": "gpt-4o-mini",
+ "messages": [
+ {
+ "role": "system",
+ "content": "You're a friendly transaction repair assistant helping operations teams fix failed work orders. When a user provides a work order tracking ID, follow these steps:\n\n1) Retrieve the failed work order details\n2) Analyze the failure and propose a repair plan\n3) Check if the repair requires approval\n4) If approved, resubmit the corrected work order. If denied, skip resubmission\n5) ALWAYS log the action (approved/denied) to ITSM for audit trail\n\nBe conversational and explain what you're doing at each step. Call ONE tool per turn. After logging to ITSM, confirm completion and ask if they need help with another work order."
+ }
+ ],
+ "agentModelType": "AzureOpenAI",
+ "agentModelSettings": {
+ "deploymentModelProperties": {
+ "name": "gpt-4o-mini",
+ "format": "OpenAI",
+ "version": "2024-07-18"
+ },
+ "agentHistoryReductionSettings": {
+ "agentHistoryReductionType": "maximumTokenCountReduction",
+ "maximumTokenCount": 128000
+ }
+ }
+ },
+ "modelConfigurations": {
+ "model1": {
+ "referenceName": "agent"
+ }
+ }
+ },
+ "tools": {
+ "Get_failed_workorder": {
+ "actions": {
+ "Call_GetWorkOrder": {
+ "type": "Workflow",
+ "inputs": {
+ "host": {
+ "workflow": {
+ "id": "GetWorkOrder"
+ }
+ },
+ "body": {
+ "trackingId": "@agentParameters('trackingId')"
+ }
+ }
+ }
+ },
+ "description": "Get work order details by tracking ID",
+ "agentParameterSchema": {
+ "type": "object",
+ "properties": {
+ "trackingId": {
+ "type": "string",
+ "description": "Work order tracking ID (e.g., WO-12345)"
+ }
+ },
+ "required": ["trackingId"]
+ }
+ },
+ "Propose_fix": {
+ "actions": {
+ "Call_GetFixPlan": {
+ "type": "Workflow",
+ "inputs": {
+ "host": {
+ "workflow": {
+ "id": "GetFixPlan"
+ }
+ },
+ "body": {
+ "failureType": "@agentParameters('failureType')"
+ }
+ }
+ }
+ },
+ "description": "Get repair plan for the failure type",
+ "agentParameterSchema": {
+ "type": "object",
+ "properties": {
+ "failureType": {
+ "type": "string",
+ "description": "Type of failure (e.g., 'Schema validation error', 'Timeout error')"
+ }
+ },
+ "required": ["failureType"]
+ }
+ },
+ "Get_approval": {
+ "actions": {
+ "Call_GetApproval": {
+ "type": "Workflow",
+ "inputs": {
+ "host": {
+ "workflow": {
+ "id": "GetApproval"
+ }
+ },
+ "body": {
+ "trackingId": "@agentParameters('trackingId')"
+ }
+ }
+ }
+ },
+ "description": "Check if repair is approved for this work order",
+ "agentParameterSchema": {
+ "type": "object",
+ "properties": {
+ "trackingId": {
+ "type": "string",
+ "description": "Work order tracking ID"
+ }
+ },
+ "required": ["trackingId"]
+ }
+ },
+ "Resubmit_workorder": {
+ "actions": {
+ "Call_ResubmitWorkOrder": {
+ "type": "Workflow",
+ "inputs": {
+ "host": {
+ "workflow": {
+ "id": "ResubmitWorkOrder"
+ }
+ },
+ "body": {
+ "trackingId": "@agentParameters('trackingId')",
+ "payload": "@json(agentParameters('payload'))"
+ }
+ }
+ }
+ },
+ "description": "Resubmit the corrected work order",
+ "agentParameterSchema": {
+ "type": "object",
+ "properties": {
+ "trackingId": {
+ "type": "string",
+ "description": "Work order tracking ID"
+ },
+ "payload": {
+ "type": "string",
+ "description": "Corrected JSON payload as string"
+ }
+ },
+ "required": ["trackingId", "payload"]
+ }
+ },
+ "Log_to_itsm": {
+ "actions": {
+ "Call_LogToITSM": {
+ "type": "Workflow",
+ "inputs": {
+ "host": {
+ "workflow": {
+ "id": "LogToITSM"
+ }
+ },
+ "body": {
+ "trackingId": "@agentParameters('trackingId')",
+ "action": "@agentParameters('action')"
+ }
+ }
+ }
+ },
+ "description": "Log repair action to ITSM system",
+ "agentParameterSchema": {
+ "type": "object",
+ "properties": {
+ "trackingId": {
+ "type": "string",
+ "description": "Work order tracking ID"
+ },
+ "action": {
+ "type": "string",
+ "description": "Action taken (e.g., 'Resubmitted' or 'Denied')"
+ }
+ },
+ "required": ["trackingId", "action"]
+ }
+ }
+ },
+ "runAfter": {
+ "When_a_new_chat_session_starts": [
+ "Succeeded"
+ ]
+ }
+ }
+ },
+ "contentVersion": "1.0.0.0",
+ "outputs": {},
+ "triggers": {
+ "When_a_new_chat_session_starts": {
+ "type": "Request",
+ "kind": "Agent",
+ "inputs": {
+ "agentName": "TransactionRepairAgent",
+ "agentDescription": "Analyzes failed work orders and orchestrates repair with approval workflow"
+ }
+ }
+ }
+ },
+ "kind": "Agent"
+}
diff --git a/samples/transaction-repair-agent/LogicApps/connections.json b/samples/transaction-repair-agent/LogicApps/connections.json
new file mode 100644
index 0000000..806d743
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/connections.json
@@ -0,0 +1,14 @@
+{
+ "agentConnections": {
+ "agent": {
+ "displayName": "Azure OpenAI Connection",
+ "authentication": {
+ "type": "ManagedServiceIdentity"
+ },
+ "endpoint": "@appsetting('agent_openAIEndpoint')",
+ "resourceId": "@appsetting('agent_ResourceID')",
+ "type": "model"
+ }
+ },
+ "managedApiConnections": {}
+}
diff --git a/samples/transaction-repair-agent/LogicApps/data/approvals/approve-resubmit.json b/samples/transaction-repair-agent/LogicApps/data/approvals/approve-resubmit.json
new file mode 100644
index 0000000..f15a170
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/data/approvals/approve-resubmit.json
@@ -0,0 +1,21 @@
+{
+ "WO-12345": {
+ "approved": true,
+ "approver": "ops-manager",
+ "timestamp": "2025-12-15T10:30:00Z",
+ "notes": "Schema fix is safe - proceed with resubmit"
+ },
+ "WO-67890": {
+ "approved": false,
+ "approver": "ops-manager",
+ "timestamp": "2025-12-15T10:35:00Z",
+ "notes": "Timeout issue requires infrastructure review before resubmit",
+ "reason": "requires-manual-review"
+ },
+ "WO-11111": {
+ "approved": true,
+ "approver": "ops-supervisor",
+ "timestamp": "2025-12-15T10:40:00Z",
+ "notes": "Default shipping address can be applied - proceed"
+ }
+}
diff --git a/samples/transaction-repair-agent/LogicApps/data/itsm/incidents-example.json b/samples/transaction-repair-agent/LogicApps/data/itsm/incidents-example.json
new file mode 100644
index 0000000..d1cf3ac
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/data/itsm/incidents-example.json
@@ -0,0 +1,10 @@
+[
+ {
+ "incidentId": "INC000123",
+ "trackingId": "WO-12345",
+ "activity": "Diagnosed schema-validation-failed, proposed fix, received approval, resubmitted successfully",
+ "operator": "ops-agent",
+ "timestamp": "2025-12-15T10:30:15Z",
+ "status": "logged"
+ }
+]
diff --git a/samples/transaction-repair-agent/LogicApps/data/queues/failed-workorders.json b/samples/transaction-repair-agent/LogicApps/data/queues/failed-workorders.json
new file mode 100644
index 0000000..7696d47
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/data/queues/failed-workorders.json
@@ -0,0 +1,41 @@
+[
+ {
+ "trackingId": "WO-12345",
+ "failureReason": "schema-validation-failed",
+ "failureDetails": "Field 'amount' has type string but should be number",
+ "payload": {
+ "orderId": "ORD-9001",
+ "customerId": "CUST-456",
+ "amount": "invalid",
+ "timestamp": "2025-12-15T09:00:00Z"
+ },
+ "queueName": "order-processing",
+ "retries": 3
+ },
+ {
+ "trackingId": "WO-67890",
+ "failureReason": "timeout-retry-exhausted",
+ "failureDetails": "Downstream service did not respond within 30s after 5 retries",
+ "payload": {
+ "orderId": "ORD-9002",
+ "customerId": "CUST-789",
+ "amount": 150.00,
+ "timestamp": "2025-12-15T09:15:00Z"
+ },
+ "queueName": "order-processing",
+ "retries": 5
+ },
+ {
+ "trackingId": "WO-11111",
+ "failureReason": "transform-patch-required",
+ "failureDetails": "Missing required field 'shippingAddress' in payload",
+ "payload": {
+ "orderId": "ORD-9003",
+ "customerId": "CUST-101",
+ "amount": 200.00,
+ "timestamp": "2025-12-15T09:30:00Z"
+ },
+ "queueName": "order-processing",
+ "retries": 0
+ }
+]
diff --git a/samples/transaction-repair-agent/LogicApps/data/repair-catalog.json b/samples/transaction-repair-agent/LogicApps/data/repair-catalog.json
new file mode 100644
index 0000000..e94403c
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/data/repair-catalog.json
@@ -0,0 +1,35 @@
+{
+ "schema-validation-failed": {
+ "steps": [
+ "Convert amount field from string to number",
+ "Validate against order schema",
+ "Resubmit to processing queue"
+ ],
+ "estimatedDuration": "2 minutes",
+ "risk": "low",
+ "category": "data-quality",
+ "documentation": "https://docs.example.com/schema-validation"
+ },
+ "timeout-retry-exhausted": {
+ "steps": [
+ "Increase timeout threshold to 60s",
+ "Reset retry counter",
+ "Resubmit with priority flag"
+ ],
+ "estimatedDuration": "5 minutes",
+ "risk": "medium",
+ "category": "connectivity",
+ "documentation": "https://docs.example.com/timeout-handling"
+ },
+ "transform-patch-required": {
+ "steps": [
+ "Add missing required field with default value",
+ "Apply field mapping transformation",
+ "Resubmit to processing queue"
+ ],
+ "estimatedDuration": "3 minutes",
+ "risk": "low",
+ "category": "transformation",
+ "documentation": "https://docs.example.com/field-mapping"
+ }
+}
diff --git a/samples/transaction-repair-agent/LogicApps/host.json b/samples/transaction-repair-agent/LogicApps/host.json
new file mode 100644
index 0000000..5814d25
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/host.json
@@ -0,0 +1,15 @@
+{
+ "version": "2.0",
+ "logging": {
+ "applicationInsights": {
+ "samplingSettings": {
+ "isEnabled": true,
+ "excludedTypes": "Request"
+ }
+ }
+ },
+ "extensionBundle": {
+ "id": "Microsoft.Azure.Functions.ExtensionBundle.Workflows",
+ "version": "[1.*, 2.0.0)"
+ }
+}
diff --git a/samples/transaction-repair-agent/LogicApps/local.settings.json b/samples/transaction-repair-agent/LogicApps/local.settings.json
new file mode 100644
index 0000000..45aac4b
--- /dev/null
+++ b/samples/transaction-repair-agent/LogicApps/local.settings.json
@@ -0,0 +1,12 @@
+{
+ "IsEncrypted": false,
+ "Values": {
+ "AzureWebJobsStorage": "UseDevelopmentStorage=true",
+ "agent_openAIEndpoint": "",
+ "WORKFLOWS_LOCATION_NAME": "",
+ "WORKFLOWS_RESOURCE_GROUP_NAME": "",
+ "WORKFLOWS_SUBSCRIPTION_ID": "",
+ "agent_ResourceID": "",
+ "FUNCTIONS_WORKER_RUNTIME": "dotnet"
+ }
+}
diff --git a/samples/transaction-repair-agent/README.md b/samples/transaction-repair-agent/README.md
new file mode 100644
index 0000000..675721e
--- /dev/null
+++ b/samples/transaction-repair-agent/README.md
@@ -0,0 +1,381 @@
+# Transaction Repair Agent
+
+A conversational AI agent that helps operations teams diagnose and repair failed work orders through natural chat interaction. Built with Azure Logic Apps Standard and Azure OpenAI, the agent guides users through work order analysis, proposes fixes, manages approvals, resubmits transactions, and logs all activities to ITSM for audit compliance.
+
+**[Watch Demo Video](https://youtu.be/-V4n9VMcN0k)** | **[Agent Workflow Blog](https://techcommunity.microsoft.com/blog/integrationsonazureblog/%F0%9F%A4%96-agent-loop-demos-%F0%9F%A4%96/4414770)**
+
+---
+
+## Deploy
+
+**Prerequisites:**
+- Azure subscription with contributor access
+- Region supporting Azure OpenAI (gpt-4o-mini) and Logic Apps Standard - see [region selection](#region-selection)
+
+**Deploy to your Azure subscription:**
+
+[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmodularity%2Flogicapps-labs%2Ftransaction-repair-agent%2Fsamples%2Ftransaction-repair-agent%2FDeployment%2Fsample-arm.json)
+
+
+What happens when you deploy
+
+1. Opens Azure Portal and prompts for subscription, [resource group](https://learn.microsoft.com/azure/azure-resource-manager/management/manage-resource-groups-portal) (create new recommended: `rg-transactionrepair`)
+2. Provisions Azure resources (Logic App, OpenAI, Storage, App Service Plan, Managed Identity)
+3. Configures [RBAC (Role-Based Access Control)](https://learn.microsoft.com/azure/role-based-access-control/overview) permissions for passwordless authentication
+4. Deploys conversational agent workflows with built-in test data
+
+
+
+
+What gets deployed
+
+| Resource | Purpose |
+|----------|----------|
+| Logic App Standard | Hosts conversational agent workflows |
+| Azure OpenAI | gpt-4o-mini model for agent reasoning |
+| Storage Account | Workflow state and run history |
+| App Service Plan | Compute resources |
+| Managed Identity | Passwordless authentication |
+
+See [Deployment automation](#learn-more) and [Sample data approach](#learn-more) for technical details.
+
+
+
+
+Region selection
+
+Recommended regions: East US 2, West Europe, Sweden Central, Australia East
+
+See regional availability:
+- [Azure OpenAI models](https://learn.microsoft.com/azure/ai-services/openai/concepts/models#model-summary-table-and-region-availability)
+- [Logic Apps Standard](https://azure.microsoft.com/explore/global-infrastructure/products-by-region/)
+
+
+
+
+Resource naming
+
+Resources use `{projectName}` for subscription-scoped resources and `{projectName}{uniqueId}` for globally-unique resources:
+
+| Resource | Example (projectName = "txrepair") |
+|----------|------------------------------------|
+| Resource Group | `rg-txrepair` |
+| Logic App | `txrepairxyz123-logicapp` |
+| Azure OpenAI | `txrepair-openai` |
+| Storage Account | `txrepairxyz123` |
+
+
+
+---
+
+## Explore
+
+After deployment, test the conversational agent to see how it guides you through work order repair workflows
+
+
+
+### Run a test
+
+1. Open [Azure Portal](https://portal.azure.com) > your resource group > Logic App > **Workflows** > **TransactionRepairAgent**
+2. Click **Overview** > **Chat** button to [start a conversation with the agent](https://learn.microsoft.com/azure/logic-apps/create-conversational-agent-workflows#run-and-test-your-conversational-agent)
+3. Type or paste one of the test prompts below
+4. Watch the agent retrieve work order details, propose fixes, check approvals, and log to ITSM
+5. (Optional) Check [**Run history**](https://learn.microsoft.com/azure/logic-apps/monitor-logic-apps#review-runs-history) to see nested workflow calls
+
+**Test these scenarios to see different decision paths:**
+
+
+Test scenario 1: Schema validation failure (approved)
+
+Chat prompt:
+```
+Process work order WO-12345
+```
+
+**Expected behavior:**
+- Agent retrieves work order (schema_validation_error, $250 amount)
+- Proposes fix: Convert amount from string to number, validate schema
+- Checks approval → **APPROVED**
+- Resubmits corrected work order
+- Logs to ITSM (generates incident ID like INC416577)
+- Confirms completion and offers help with another work order
+
+**Run history shows:** 5 nested workflow calls (GetWorkOrder → GetFixPlan → GetApproval → ResubmitWorkOrder → LogToITSM)
+
+
+
+
+Test scenario 2: Timeout configuration (denied)
+
+Chat prompt:
+```
+Let's start by retrieving the details of the failed work order WO-67890
+```
+
+**Expected behavior:**
+- Agent retrieves work order (timeout_config, 30s timeout)
+- Proposes fix: Increase timeout to 300s, add retry policy
+- Checks approval → **DENIED**
+- Skips resubmission (correctly follows denial)
+- Logs to ITSM automatically for audit trail
+- Confirms completion
+
+**Run history shows:** 4 nested workflow calls (GetWorkOrder → GetFixPlan → GetApproval → LogToITSM, skips ResubmitWorkOrder)
+
+
+
+
+Test scenario 3: Missing required field (approved)
+
+Chat prompt:
+```
+Help me fix work order WO-11111
+```
+
+**Expected behavior:**
+- Agent retrieves work order (missing_required_field, customerId/productId)
+- Proposes fix: Add missing field, apply field mapping
+- Checks approval → **APPROVED**
+- Resubmits corrected work order
+- Logs to ITSM (generates incident ID like INC217995)
+- Confirms completion and offers help with another work order
+
+**Run history shows:** 5 nested workflow calls (GetWorkOrder → GetFixPlan → GetApproval → ResubmitWorkOrder → LogToITSM)
+
+
+
+**Tips:**
+
+- Agent is conversational and explains each step
+- Agent calls **one tool per turn** (retrieves info, proposes fix, checks approval, etc.)
+- Review **Run history** to see nested workflow execution
+- Check run details for token usage in **Metadata** tab
+- Conversations complete in 15-30 seconds (multiple turns)
+- [Learn more about conversational agents](https://learn.microsoft.com/azure/logic-apps/create-conversational-agent-workflows)
+
+---
+
+## Extend
+
+This sample uses **nested workflows with mock data** to eliminate external dependencies. Here's how to extend it for production use:
+
+**Note:** The Chat interface is for testing only. For production scenarios with external users, configure proper authentication. [Learn more about securing conversational agent workflows](https://learn.microsoft.com/azure/logic-apps/create-conversational-agent-workflows?tabs=standard#trigger-or-run-the-workflow).
+
+### Replace demo services
+
+| Component | Demo Implementation | Production Options |
+|-----------|----------------------|-------------------|
+| Work Order Queue | **GetWorkOrder** nested workflow with mock array (3 work orders) | Azure Service Bus, Cosmos DB, SQL Database, Event Grid, REST APIs |
+| Repair Catalog | **GetFixPlan** nested workflow with mock catalog (3 failure types) | Azure App Configuration, Cosmos DB rules engine, AI recommendations, knowledge base |
+| Approval System | **GetApproval** nested workflow with mock responses (3 approvals) | Teams Adaptive Cards, Power Automate approvals, ServiceNow, custom approval API |
+| Work Order Submission | **ResubmitWorkOrder** nested workflow (simulated) | Azure Service Bus send, REST API POST, ERP integration, queue writers |
+| ITSM Logging | **LogToITSM** nested workflow (generates incident IDs) | ServiceNow REST API, Azure Monitor, Application Insights, Jira, custom webhook |
+
+### Customize workflows
+
+**Option 1: Edit in Azure Portal**
+- Navigate to your Logic App > Workflows > select workflow > **Edit**
+- Use the visual designer to modify workflow logic
+- [Learn more about editing workflows in Azure Portal](https://learn.microsoft.com/azure/logic-apps/create-single-tenant-workflows-azure-portal)
+
+**Option 2: Edit in VS Code**
+- Install [Azure Logic Apps (Standard) extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurelogicapps)
+- In VS Code, select the Azure icon on the Activity Bar
+- Under **Resources**, expand your subscription and find your deployed Logic App
+- Expand **Workflows**, right-click a workflow, and select **Open Designer** to view/edit
+- [Learn more about editing workflows in VS Code](https://learn.microsoft.com/azure/logic-apps/manage-logic-apps-visual-studio-code)
+
+### Integrate with external systems
+
+Replace nested workflow mock data with real connectors:
+
+**For GetWorkOrder (Work Order Queue):**
+- Add [Azure Service Bus connector](https://learn.microsoft.com/azure/connectors/connectors-create-api-servicebus) to receive messages from queue
+- Or use [Cosmos DB connector](https://learn.microsoft.com/azure/connectors/connectors-create-api-cosmos-db) to query work order database
+
+**For GetApproval (Approval System):**
+- Add [Microsoft Teams connector](https://learn.microsoft.com/azure/connectors/connectors-create-api-teams) with Adaptive Cards for human approval
+- Or use [HTTP connector](https://learn.microsoft.com/azure/connectors/connectors-native-http) to call custom approval API
+
+**For LogToITSM (ITSM Logging):**
+- Add [ServiceNow connector](https://learn.microsoft.com/azure/connectors/connectors-create-api-servicenow) to create incident records
+- Or use [HTTP connector](https://learn.microsoft.com/azure/connectors/connectors-native-http) to call custom ITSM webhook
+
+**For ResubmitWorkOrder (Work Order Submission):**
+- Add [Azure Service Bus connector](https://learn.microsoft.com/azure/connectors/connectors-create-api-servicebus) to send corrected work order to queue
+- Or use [HTTP connector](https://learn.microsoft.com/azure/connectors/connectors-native-http) to POST to processing API
+
+---
+
+## Workflows
+
+Six workflows process work order repairs using conversational AI interaction:
+
+
+Workflow details
+
+### TransactionRepairAgent
+
+Conversational agent that guides users through work order repair workflows. The agent analyzes failed work orders, proposes fixes, manages approvals, resubmits transactions, and logs all activities to ITSM for audit compliance.
+
+**Agent Tools (Nested Workflows):**
+- **Get_failed_workorder** - Retrieves failed work order details by tracking ID (calls GetWorkOrder workflow)
+- **Propose_fix** - Analyzes failure type and proposes repair steps (calls GetFixPlan workflow)
+- **Get_approval** - Checks if repair requires approval and retrieves decision (calls GetApproval workflow)
+- **Resubmit_workorder** - Resubmits corrected work order to processing queue (calls ResubmitWorkOrder workflow)
+- **Log_to_itsm** - Generates ITSM incident for audit trail (calls LogToITSM workflow)
+
+**Process Flow:**
+
+```mermaid
+flowchart TD
+ A[Chat Trigger
When new chat session starts]
+ A --> B[Transaction Repair Agent
Conversational AI]
+
+ B --> C{Agent Multi-turn
Conversation}
+
+ C --> D[Get Failed Workorder
nested workflow]
+ C --> E[Propose Fix
nested workflow]
+ C --> F[Get Approval
nested workflow]
+ C --> G[Resubmit Workorder
nested workflow]
+ C --> H[Log To ITSM
nested workflow]
+
+ D --> C
+ E --> C
+ F --> C
+ G --> C
+ H --> C
+
+ C --> I[Response to user]
+```
+
+### GetWorkOrder
+
+Retrieves failed work order details by tracking ID. Uses Query action to find matching work order from mock data array.
+
+**Mock Data:** 3 work orders (WO-12345, WO-67890, WO-11111) with different failure types
+
+### GetFixPlan
+
+Analyzes failure type and returns repair steps from catalog. Looks up fix plan based on failure reason.
+
+**Mock Data:** Repair catalog with 3 failure types (schema_validation_error, timeout_config, missing_required_field)
+
+### GetApproval
+
+Checks approval status for repair request. Returns approval decision based on tracking ID.
+
+**Mock Data:** 3 approval responses (WO-12345: APPROVED, WO-67890: DENIED, WO-11111: APPROVED)
+
+### ResubmitWorkOrder
+
+Resubmits corrected work order to processing queue. Returns confirmation with tracking ID and timestamp.
+
+**Mock Data:** Simulated resubmission (returns status "resubmitted")
+
+### LogToITSM
+
+Generates ITSM incident record for audit trail. Returns incident ID with tracking information.
+
+**Mock Data:** Generates random incident IDs (INC######)
+
+
+
+
+Required Connections
+
+This sample uses Azure OpenAI with Managed Identity authentication for passwordless access.
+
+| Connection Name | Connector Name | Connector Type | Purpose |
+|-----------------|----------------|----------------|---------|
+| Azure OpenAI Connection | Azure OpenAI | Agent | Powers the conversational AI agent in TransactionRepairAgent workflow |
+
+**Authentication:** System-Assigned Managed Identity with `Cognitive Services OpenAI User` role assigned to Azure OpenAI resource during deployment.
+
+
+
+---
+
+## Learn More
+
+
+Troubleshooting
+
+| Issue | Solution |
+|-------|----------|
+| **CustomDomainInUse** | Use different project name. [Purge deleted resources](https://learn.microsoft.com/azure/ai-services/recover-purge-resources) if needed. |
+| **InsufficientQuota** | Try different [region](#region-selection) or [request quota increase](https://learn.microsoft.com/azure/ai-services/openai/how-to/quota). |
+| **Deployment timeout** | Allow 15 min. [View Activity Log](https://learn.microsoft.com/azure/azure-monitor/essentials/activity-log). Redeploy: resource group > Deployments > select > Redeploy. |
+| **Unauthorized** | Wait 2-3 min for RBAC propagation. [Verify role assignments](https://learn.microsoft.com/azure/logic-apps/authenticate-with-managed-identity?tabs=standard). |
+| **ajaxExtended call failed** | Designer: rename trigger → save → rename back > save. [Details](https://learn.microsoft.com/answers/questions/2046895). |
+| **No Chat button** | Verify TransactionRepairAgent workflow `kind` is `"Agent"` and trigger `kind` is `"Agent"`. |
+| **Chat not responding** | Wait 1-2 min, refresh. Check run history for errors. Verify OpenAI model is active. |
+| **Nested workflow error** | Verify all nested workflows have Response actions at workflow level with statusCode 200. |
+
+
+
+
+Deployment automation
+
+The Deploy to Azure button uses a two-stage process:
+
+**Build** (manual via [`BundleAssets.ps1`](../shared/scripts/BundleAssets.ps1)):
+- Compiles [Bicep modules](../shared/modules/) → [`sample-arm.json`](Deployment/sample-arm.json)
+- Bundles [workflow definitions](LogicApps/) → [`workflows.zip`](Deployment/workflows.zip)
+
+**Deploy** (automated):
+- [ARM (Azure Resource Manager)](https://learn.microsoft.com/azure/azure-resource-manager/templates/overview) template provisions Azure resources
+- Embedded deployment script configures RBAC and deploys workflows
+
+[Learn about ARM deployment scripts](https://learn.microsoft.com/azure/azure-resource-manager/bicep/deployment-script-bicep)
+
+
+
+
+Sample data approach
+
+This sample uses nested workflows with mock data to simplify exploration:
+- **Work orders:** GetWorkOrder workflow (3 mock work orders)
+- **Repair catalog:** GetFixPlan workflow (3 failure types)
+- **Approval system:** GetApproval workflow (predetermined decisions)
+- **ITSM logging:** LogToITSM workflow (generated incident IDs)
+
+**Note:** The Chat interface is for testing only. For production use with external users, configure proper authentication. [Learn more](https://learn.microsoft.com/azure/logic-apps/create-conversational-agent-workflows?tabs=standard#trigger-or-run-the-workflow).
+
+For production integration options, see [Extend](#extend).
+
+
+
+
+Architecture decisions
+
+**Why nested workflows?**
+- Demonstrates agent tool pattern with callable workflows
+- Each tool is independently testable
+- Clear separation of concerns (retrieval, analysis, actions)
+- Easy to extend with real connectors
+- Reusable workflows across multiple agents
+
+**Why conversational agent (not autonomous)?**
+- Human-in-the-loop for work order decisions
+- Operations teams prefer guided workflows
+- Natural language interaction for diagnostics
+- Multi-turn conversation pattern
+- First conversational agent sample in repository
+
+**Why deterministic mock data?**
+- Reproducible test results
+- No external dependencies to configure
+- Clear demonstration of approval workflows
+- Easy to extend to Teams/ServiceNow/etc.
+
+
+
+
+Resources
+
+**Agent workflows:** [Create conversational agents](https://learn.microsoft.com/azure/logic-apps/create-conversational-agent-workflows) | [Create autonomous agents](https://learn.microsoft.com/azure/logic-apps/create-autonomous-agent-workflows) | [Best practices](https://learn.microsoft.com/azure/logic-apps/create-autonomous-agent-workflows#best-practices-for-agents-and-tools)
+
+**Azure OpenAI:** [System messages](https://learn.microsoft.com/azure/ai-services/openai/concepts/system-message) | [Managed Identity](https://learn.microsoft.com/azure/logic-apps/authenticate-with-managed-identity)
+
+