{
  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.44.1.10279",
      "templateHash": "11808818605748866327"
    }
  },
  "parameters": {
    "location": {
      "type": "string",
      "allowedValues": [
        "centralus"
      ],
      "metadata": {
        "description": "Central US is the only region qualified for the v0.5.0 installer. The one-click wizard supplies it from the deployment-location picker."
      }
    },
    "resourceGroupName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 90,
      "metadata": {
        "description": "Name of the resource group to create for the app stack."
      }
    },
    "namePrefix": {
      "type": "string",
      "defaultValue": "gv",
      "minLength": 2,
      "maxLength": 11,
      "metadata": {
        "description": "Short prefix for resource names: 2-11 lowercase letters or digits, starting with a letter."
      }
    },
    "tagsByResource": {
      "type": "object",
      "defaultValue": {},
      "metadata": {
        "description": "Customer governance tags from the portal Tags control. The resource-group entry is applied to every taggable install-owned resource; immutable publisher ownership tags take precedence."
      }
    },
    "enableGeoRedundancy": {
      "type": "bool",
      "defaultValue": true,
      "metadata": {
        "description": "Regional data-recovery posture. true (default): geo-redundant DB backups + GZRS results storage support recovery after a regional failure; false: local DB backups + ZRS storage keep data in one region. This does not provide automatic cross-region application failover. DB geo-redundant backup is CREATE-TIME ONLY."
      }
    },
    "enableDatabaseHighAvailability": {
      "type": "bool",
      "defaultValue": true,
      "metadata": {
        "description": "Enable General Purpose PostgreSQL with zone-redundant high availability. true is the production default. Set false only when accepting a lower-cost Burstable database with no standby or in-region database failover."
      }
    },
    "image": {
      "type": "string",
      "defaultValue": "gvacrgv01.azurecr.io/general-validation-release@sha256:d8caa24da690117d5844bdade7470071eb495c166ab7b8b59823461303d80766",
      "allowedValues": [
        "gvacrgv01.azurecr.io/general-validation-release@sha256:d8caa24da690117d5844bdade7470071eb495c166ab7b8b59823461303d80766",
        "gvacrgv01.azurecr.io/general-validation-validation@sha256:d8caa24da690117d5844bdade7470071eb495c166ab7b8b59823461303d80766"
      ],
      "metadata": {
        "description": "Container image, digest-pinned and constrained to the qualified release inputs. Publisher-set per release; hidden from the one-click wizard."
      }
    },
    "registryUsername": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "Registry token NAME for cross-tenant pull from the publisher private registry (a per-recipient repository-scoped pull-only token)."
      }
    },
    "registryPassword": {
      "type": "securestring",
      "minLength": 1,
      "metadata": {
        "description": "Password for the per-recipient registry pull token."
      }
    },
    "oidcClientId": {
      "type": "string",
      "defaultValue": ""
    },
    "oidcDiscoveryUrl": {
      "type": "string",
      "defaultValue": ""
    },
    "bootstrapOwner": {
      "type": "string",
      "defaultValue": "",
      "metadata": {
        "description": "Entra oid granted the owner role on first login when OIDC is configured at deploy time."
      }
    },
    "grantSubscriptionFactoryRead": {
      "type": "bool",
      "defaultValue": false,
      "metadata": {
        "description": "false (default): grant the built-in Reader role only on each selected source factory after install. true: grant the app identity a custom metadata-read role at subscription scope so every current and future Data Factory is browse-ready."
      }
    },
    "grantSubscriptionStorageRead": {
      "type": "bool",
      "defaultValue": false,
      "metadata": {
        "description": "false (default): grant the execution Data Factory Storage Blob Data Reader only on each selected source storage account through the customer-approved Azure portal workflow. true: explicitly grant that factory data-plane read access to every current and future Blob / ADLS Gen2 account in this subscription."
      }
    },
    "bootstrapSecret": {
      "type": "securestring",
      "minLength": 16,
      "maxLength": 256,
      "metadata": {
        "description": "One-time bootstrap secret gating /auth/setup before the first owner exists. Required for the one-click button path (OIDC configured at runtime)."
      }
    }
  },
  "variables": {
    "registryServer": "gvacrgv01.azurecr.io",
    "releaseVersion": "v0.5.0",
    "installerContractVersion": 5,
    "customerTags": "[coalesce(tryGet(parameters('tagsByResource'), 'Microsoft.Resources/resourceGroups'), createObject())]",
    "tags": "[union(variables('customerTags'), createObject('ApplicationName', 'General Validation', 'ManagedBy', 'General Validation (installed product)', 'Publisher', 'General Validation', 'SupportContact', 'support@generalvalidation.com', 'SupportUrl', 'https://www.generalvalidation.com'))]"
  },
  "resources": [
    {
      "type": "Microsoft.Resources/resourceGroups",
      "apiVersion": "2023-07-01",
      "name": "[parameters('resourceGroupName')]",
      "location": "[parameters('location')]",
      "tags": "[variables('tags')]"
    },
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2025-04-01",
      "name": "gvStack",
      "resourceGroup": "[parameters('resourceGroupName')]",
      "properties": {
        "expressionEvaluationOptions": {
          "scope": "inner"
        },
        "mode": "Incremental",
        "parameters": {
          "location": {
            "value": "[parameters('location')]"
          },
          "tags": {
            "value": "[variables('tags')]"
          },
          "namePrefix": {
            "value": "[parameters('namePrefix')]"
          },
          "enableGeoRedundancy": {
            "value": "[parameters('enableGeoRedundancy')]"
          },
          "dbSkuName": "[if(parameters('enableDatabaseHighAvailability'), createObject('value', 'Standard_D2ds_v5'), createObject('value', 'Standard_B1ms'))]",
          "dbSkuTier": "[if(parameters('enableDatabaseHighAvailability'), createObject('value', 'GeneralPurpose'), createObject('value', 'Burstable'))]",
          "dbHighAvailability": "[if(parameters('enableDatabaseHighAvailability'), createObject('value', 'ZoneRedundant'), createObject('value', 'Disabled'))]",
          "image": {
            "value": "[parameters('image')]"
          },
          "appVersion": {
            "value": "[variables('releaseVersion')]"
          },
          "registryServer": {
            "value": "[variables('registryServer')]"
          },
          "acrName": {
            "value": ""
          },
          "acrResourceGroup": {
            "value": ""
          },
          "oidcClientId": {
            "value": "[parameters('oidcClientId')]"
          },
          "oidcDiscoveryUrl": {
            "value": "[parameters('oidcDiscoveryUrl')]"
          },
          "bootstrapOwner": {
            "value": "[parameters('bootstrapOwner')]"
          },
          "bootstrapSecret": {
            "value": "[parameters('bootstrapSecret')]"
          },
          "registryUsername": {
            "value": "[parameters('registryUsername')]"
          },
          "registryPassword": {
            "value": "[parameters('registryPassword')]"
          },
          "grantAcrPull": {
            "value": false
          }
        },
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "metadata": {
            "_generator": {
              "name": "bicep",
              "version": "0.44.1.10279",
              "templateHash": "13037760452940521612"
            }
          },
          "parameters": {
            "location": {
              "type": "string",
              "defaultValue": "[resourceGroup().location]",
              "metadata": {
                "description": "Azure region for all provisioned resources. Defaults to the resource group location so the deploy follows wherever the customer created the RG — no surprising cross-region default."
              }
            },
            "namePrefix": {
              "type": "string",
              "defaultValue": "gv",
              "minLength": 2,
              "maxLength": 11,
              "metadata": {
                "description": "Short prefix for resource names: 2-11 lowercase letters or digits, starting with a letter."
              }
            },
            "enableGeoRedundancy": {
              "type": "bool",
              "defaultValue": true,
              "metadata": {
                "description": "Region-level recovery posture. true (default): the DB uses geo-redundant backup and the results ADLS uses GZRS, supporting customer-operated restoration after a regional failure. This does not provide automatic application failover. false: DB backup stays local and ADLS uses ZRS for strict data residency. DB geo-redundant backup is CREATE-TIME ONLY. GZRS and ZRS require an availability-zone-enabled region; lower-level deployments can select Standard_GRS or Standard_LRS through adlsSkuOverride when necessary."
              }
            },
            "adlsSkuOverride": {
              "type": "string",
              "defaultValue": "",
              "allowedValues": [
                "",
                "Standard_GZRS",
                "Standard_ZRS",
                "Standard_GRS",
                "Standard_LRS"
              ],
              "metadata": {
                "description": "Escape hatch for regions WITHOUT availability zones, where both enableGeoRedundancy mappings (GZRS/ZRS) fail at storage creation: set Standard_GRS (geo, no zones) or Standard_LRS (local only) to override the results-ADLS redundancy directly. Empty (default) = derive from enableGeoRedundancy."
              }
            },
            "dbSkuName": {
              "type": "string",
              "defaultValue": "Standard_B1ms",
              "metadata": {
                "description": "PostgreSQL compute SKU name. The lower-level default is Burstable Standard_B1ms; the public subscription installer passes Standard_D2ds_v5 by default. High Availability requires a General Purpose / Memory Optimized SKU."
              }
            },
            "dbSkuTier": {
              "type": "string",
              "defaultValue": "Burstable",
              "metadata": {
                "description": "PostgreSQL compute tier. The lower-level Burstable default cannot do HA; the public subscription installer passes GeneralPurpose by default."
              }
            },
            "dbHighAvailability": {
              "type": "string",
              "defaultValue": "Disabled",
              "metadata": {
                "description": "PostgreSQL high-availability mode. The lower-level default is Disabled; the public subscription installer passes ZoneRedundant by default. ZoneRedundant requires General Purpose or Memory Optimized compute and an availability-zone-enabled region."
              }
            },
            "dbBackupRetentionDays": {
              "type": "int",
              "defaultValue": 14,
              "metadata": {
                "description": "PostgreSQL point-in-time-restore retention in days (7-35)."
              }
            },
            "workerMinReplicas": {
              "type": "int",
              "defaultValue": 2,
              "metadata": {
                "description": "Minimum worker replicas. Default 2 provides continuous background processing during a single replica restart."
              }
            },
            "workerMaxReplicas": {
              "type": "int",
              "defaultValue": 2,
              "metadata": {
                "description": "Maximum worker replicas. Defaults to the minimum for predictable database capacity."
              }
            },
            "apiDbPoolSize": {
              "type": "int",
              "defaultValue": 5,
              "metadata": {
                "description": "API SQLAlchemy pool size: persistent DB connections per replica."
              }
            },
            "apiDbMaxOverflow": {
              "type": "int",
              "defaultValue": 10,
              "metadata": {
                "description": "API SQLAlchemy max overflow: burst DB connections per replica above the pool."
              }
            },
            "workerDbPoolSize": {
              "type": "int",
              "defaultValue": 3,
              "metadata": {
                "description": "Worker SQLAlchemy pool size per replica. Small — a worker processes one job at a time."
              }
            },
            "workerDbMaxOverflow": {
              "type": "int",
              "defaultValue": 2,
              "metadata": {
                "description": "Worker SQLAlchemy max overflow per replica."
              }
            },
            "image": {
              "type": "string",
              "metadata": {
                "description": "Digest-pinned General Validation container image reference."
              }
            },
            "imageTag": {
              "type": "string",
              "defaultValue": "[parameters('image')]",
              "metadata": {
                "description": "Image tag/sha surfaced at /ready as a drift signal. Defaults to the image reference if unset."
              }
            },
            "appVersion": {
              "type": "string",
              "defaultValue": "",
              "metadata": {
                "description": "Publisher-assigned product version shown in the application. The subscription installer always supplies it from VERSION."
              }
            },
            "buildSha": {
              "type": "string",
              "defaultValue": "",
              "metadata": {
                "description": "Git commit SHA of the deployed build, shown on the admin version card (GV_BUILD_SHA). CI supplies it; empty renders as \"Not set\"."
              }
            },
            "buildDate": {
              "type": "string",
              "defaultValue": "",
              "metadata": {
                "description": "UTC timestamp of the deployed build, shown on the admin version card (GV_BUILD_DATE). CI supplies it; empty renders as \"Not set\"."
              }
            },
            "registryServer": {
              "type": "string",
              "metadata": {
                "description": "ACR login server, e.g. contoso.azurecr.io"
              }
            },
            "acrName": {
              "type": "string",
              "defaultValue": "",
              "metadata": {
                "description": "ACR name for the AcrPull grant (same-tenant / customer-owned ACR pull). Leave empty for the cross-tenant publisher-token pull path."
              }
            },
            "acrResourceGroup": {
              "type": "string",
              "defaultValue": "",
              "metadata": {
                "description": "Resource group that contains the ACR (only when acrName is set)."
              }
            },
            "registryUsername": {
              "type": "string",
              "defaultValue": "",
              "metadata": {
                "description": "Registry token NAME for cross-tenant pull from the publisher private registry — a per-recipient repository-scoped pull-only token. Empty = pull via the managed identity from a same-tenant / customer-owned ACR."
              }
            },
            "registryPassword": {
              "type": "securestring",
              "defaultValue": "",
              "metadata": {
                "description": "Password for the registry token (when registryUsername is set). Stored as a Container App secret; never in the params file."
              }
            },
            "adlsFileSystem": {
              "type": "string",
              "defaultValue": "generalvalidation",
              "metadata": {
                "description": "Blob container (ADLS file system) holding the GV control/result/bad-record Delta tables. Safe to rename: the generalvalidation/Tables/... table paths are folders INSIDE this container, independent of its name."
              }
            },
            "adfNamespace": {
              "type": "string",
              "defaultValue": "default",
              "metadata": {
                "description": "Namespace that scopes the ADF artifacts this stack publishes (gv_*_<namespace>). Keep \"default\" for a single stack per factory; give a non-default value (e.g. \"staging\") so a second stack can share one factory without its artifacts colliding with the production stack."
              }
            },
            "oidcClientId": {
              "type": "string",
              "defaultValue": ""
            },
            "oidcDiscoveryUrl": {
              "type": "string",
              "defaultValue": ""
            },
            "appOrigin": {
              "type": "string",
              "defaultValue": "",
              "metadata": {
                "description": "Public app origin, e.g. https://gv-api.<env>.centralus.azurecontainerapps.io."
              }
            },
            "bootstrapOwner": {
              "type": "string",
              "defaultValue": "",
              "metadata": {
                "description": "Subject value (Entra oid with AUTH_OIDC_SUBJECT_CLAIM=oid) granted the owner role on first login."
              }
            },
            "existingIdentityName": {
              "type": "string",
              "defaultValue": "",
              "metadata": {
                "description": "When set, reuse an existing user-assigned identity of this name in the target RG instead of creating one. Empty = create a new MI. The CI path sets this to a pre-authorized identity."
              }
            },
            "grantAcrPull": {
              "type": "bool",
              "defaultValue": true,
              "metadata": {
                "description": "When true, the template grants the MI AcrPull on the registry. Set false when the identity was pre-authorized out-of-band (CI path — the deploy principal then needs no role-write rights in the ACR resource group)."
              }
            },
            "bootstrapSecret": {
              "type": "securestring",
              "defaultValue": "",
              "metadata": {
                "description": "One-time bootstrap secret gating /auth/setup before the first owner exists. Required for the button / pending-setup path; unused when OIDC is supplied via the oidc* params."
              }
            },
            "tags": {
              "type": "object",
              "defaultValue": {},
              "metadata": {
                "description": "Resource tags applied to every provisioned resource. subscription.bicep passes these in; this default covers direct resource-group validation."
              }
            }
          },
          "variables": {
            "miName": "[if(empty(parameters('existingIdentityName')), format('{0}-mi', parameters('namePrefix')), parameters('existingIdentityName'))]",
            "suffix": "[uniqueString(resourceGroup().id)]",
            "sanitizedPrefix": "[toLower(replace(replace(parameters('namePrefix'), '-', ''), '_', ''))]",
            "shortPrefix": "[take(if(empty(variables('sanitizedPrefix')), 'gv', variables('sanitizedPrefix')), 11)]",
            "factoryName": "[format('{0}-adf-{1}', variables('shortPrefix'), variables('suffix'))]",
            "adlsAccountName": "[format('{0}{1}', variables('shortPrefix'), variables('suffix'))]",
            "effectiveTags": "[if(empty(parameters('tags')), createObject('ApplicationName', 'General Validation', 'ManagedBy', 'General Validation (installed product)', 'Publisher', 'General Validation', 'SupportContact', 'support@generalvalidation.com', 'SupportUrl', 'https://www.generalvalidation.com'), parameters('tags'))]",
            "oidcConfigured": "[and(and(not(empty(parameters('oidcClientId'))), not(empty(parameters('oidcDiscoveryUrl')))), not(empty(parameters('bootstrapOwner'))))]"
          },
          "resources": [
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "logAnalytics",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "name": {
                    "value": "[format('{0}-law', parameters('namePrefix'))]"
                  },
                  "location": {
                    "value": "[parameters('location')]"
                  },
                  "tags": {
                    "value": "[variables('effectiveTags')]"
                  }
                },
                "template": {
                  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                  "contentVersion": "1.0.0.0",
                  "metadata": {
                    "_generator": {
                      "name": "bicep",
                      "version": "0.44.1.10279",
                      "templateHash": "5868731705615910676"
                    }
                  },
                  "parameters": {
                    "name": {
                      "type": "string",
                      "metadata": {
                        "description": "Log Analytics workspace backing the Container Apps environment."
                      }
                    },
                    "location": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "object",
                      "defaultValue": {}
                    }
                  },
                  "resources": [
                    {
                      "type": "Microsoft.OperationalInsights/workspaces",
                      "apiVersion": "2022-10-01",
                      "name": "[parameters('name')]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]",
                      "properties": {
                        "sku": {
                          "name": "PerGB2018"
                        },
                        "features": {
                          "disableLocalAuth": true,
                          "enableLogAccessUsingOnlyResourcePermissions": true
                        },
                        "publicNetworkAccessForIngestion": "Enabled",
                        "publicNetworkAccessForQuery": "Enabled",
                        "retentionInDays": 90
                      }
                    }
                  ],
                  "outputs": {
                    "id": {
                      "type": "string",
                      "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]"
                    }
                  }
                }
              }
            },
            {
              "condition": "[empty(parameters('existingIdentityName'))]",
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "identity",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "name": {
                    "value": "[format('{0}-mi', parameters('namePrefix'))]"
                  },
                  "location": {
                    "value": "[parameters('location')]"
                  },
                  "tags": {
                    "value": "[variables('effectiveTags')]"
                  }
                },
                "template": {
                  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                  "contentVersion": "1.0.0.0",
                  "metadata": {
                    "_generator": {
                      "name": "bicep",
                      "version": "0.44.1.10279",
                      "templateHash": "234161127543163569"
                    }
                  },
                  "parameters": {
                    "name": {
                      "type": "string",
                      "metadata": {
                        "description": "User-assigned managed identity. Its lifecycle and permissions are assigned by the caller."
                      }
                    },
                    "location": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "object",
                      "defaultValue": {}
                    }
                  },
                  "resources": [
                    {
                      "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
                      "apiVersion": "2023-01-31",
                      "name": "[parameters('name')]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]"
                    }
                  ],
                  "outputs": {
                    "id": {
                      "type": "string",
                      "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]"
                    },
                    "clientId": {
                      "type": "string",
                      "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2023-01-31').clientId]"
                    },
                    "principalId": {
                      "type": "string",
                      "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2023-01-31').principalId]"
                    }
                  }
                }
              }
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "containerEnv",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "name": {
                    "value": "[format('{0}-env', parameters('namePrefix'))]"
                  },
                  "location": {
                    "value": "[parameters('location')]"
                  },
                  "tags": {
                    "value": "[variables('effectiveTags')]"
                  },
                  "logAnalyticsWorkspaceId": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.id.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.44.1.10279",
                      "templateHash": "6916532746403125227"
                    }
                  },
                  "parameters": {
                    "name": {
                      "type": "string",
                      "metadata": {
                        "description": "Azure Container Apps managed environment (Consumption)."
                      }
                    },
                    "location": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "object",
                      "defaultValue": {}
                    },
                    "logAnalyticsWorkspaceId": {
                      "type": "string",
                      "metadata": {
                        "description": "Resource id of the Log Analytics workspace for console/system logs."
                      }
                    }
                  },
                  "resources": [
                    {
                      "type": "Microsoft.App/managedEnvironments",
                      "apiVersion": "2025-01-01",
                      "name": "[parameters('name')]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]",
                      "properties": {
                        "appLogsConfiguration": {
                          "destination": "azure-monitor"
                        }
                      }
                    },
                    {
                      "type": "Microsoft.Insights/diagnosticSettings",
                      "apiVersion": "2021-05-01-preview",
                      "scope": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]",
                      "name": "gv-container-env-diag",
                      "properties": {
                        "workspaceId": "[parameters('logAnalyticsWorkspaceId')]",
                        "logAnalyticsDestinationType": "Dedicated",
                        "logs": [
                          {
                            "categoryGroup": "allLogs",
                            "enabled": true
                          }
                        ],
                        "metrics": [
                          {
                            "category": "AllMetrics",
                            "enabled": true
                          }
                        ]
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]"
                      ]
                    }
                  ],
                  "outputs": {
                    "id": {
                      "type": "string",
                      "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]"
                    },
                    "defaultDomain": {
                      "type": "string",
                      "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('name')), '2025-01-01').defaultDomain]"
                    }
                  }
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'logAnalytics')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "postgres",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "name": {
                    "value": "[format('{0}pg{1}', parameters('namePrefix'), variables('suffix'))]"
                  },
                  "location": {
                    "value": "[parameters('location')]"
                  },
                  "tags": {
                    "value": "[variables('effectiveTags')]"
                  },
                  "logAnalyticsWorkspaceId": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.id.value]"
                  },
                  "managedIdentityName": {
                    "value": "[variables('miName')]"
                  },
                  "managedIdentityPrincipalId": "[if(empty(parameters('existingIdentityName')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.principalId.value), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName')), '2023-01-31').principalId))]",
                  "skuName": {
                    "value": "[parameters('dbSkuName')]"
                  },
                  "skuTier": {
                    "value": "[parameters('dbSkuTier')]"
                  },
                  "highAvailabilityMode": {
                    "value": "[parameters('dbHighAvailability')]"
                  },
                  "backupRetentionDays": {
                    "value": "[parameters('dbBackupRetentionDays')]"
                  },
                  "geoRedundantBackup": "[if(parameters('enableGeoRedundancy'), createObject('value', 'Enabled'), createObject('value', 'Disabled'))]"
                },
                "template": {
                  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                  "contentVersion": "1.0.0.0",
                  "metadata": {
                    "_generator": {
                      "name": "bicep",
                      "version": "0.44.1.10279",
                      "templateHash": "5557571692709032492"
                    }
                  },
                  "parameters": {
                    "name": {
                      "type": "string",
                      "metadata": {
                        "description": "Azure Database for PostgreSQL Flexible Server + the app database. The public subscription installer selects the production General Purpose + zone-redundant-HA posture by default; lower-level deployments can select a lower-cost Burstable tier explicitly."
                      }
                    },
                    "location": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "object",
                      "defaultValue": {}
                    },
                    "managedIdentityName": {
                      "type": "string",
                      "metadata": {
                        "description": "Name of the application managed identity registered as the PostgreSQL Entra administrator."
                      }
                    },
                    "managedIdentityPrincipalId": {
                      "type": "string",
                      "metadata": {
                        "description": "Object (principal) id of the application managed identity used as the Entra admin object id."
                      }
                    },
                    "databaseName": {
                      "type": "string",
                      "defaultValue": "generalvalidation"
                    },
                    "logAnalyticsWorkspaceId": {
                      "type": "string",
                      "metadata": {
                        "description": "Log Analytics workspace resource id for the PostgreSQL diagnostic (logs + metrics) setting."
                      }
                    },
                    "skuName": {
                      "type": "string",
                      "defaultValue": "Standard_B1ms",
                      "metadata": {
                        "description": "Compute SKU name. The module default is Burstable Standard_B1ms; the public subscription installer passes Standard_D2ds_v5 by default. High Availability requires General Purpose or Memory Optimized compute."
                      }
                    },
                    "skuTier": {
                      "type": "string",
                      "defaultValue": "Burstable",
                      "allowedValues": [
                        "Burstable",
                        "GeneralPurpose",
                        "MemoryOptimized"
                      ],
                      "metadata": {
                        "description": "Compute tier. The module default is Burstable, which cannot do High Availability; the public subscription installer passes GeneralPurpose by default."
                      }
                    },
                    "storageSizeGB": {
                      "type": "int",
                      "defaultValue": 32,
                      "metadata": {
                        "description": "Provisioned storage (GB). Auto-grow is enabled, so this is a floor, not a cap."
                      }
                    },
                    "backupRetentionDays": {
                      "type": "int",
                      "defaultValue": 14,
                      "minValue": 7,
                      "maxValue": 35,
                      "metadata": {
                        "description": "Point-in-time-restore retention in days."
                      }
                    },
                    "geoRedundantBackup": {
                      "type": "string",
                      "defaultValue": "Enabled",
                      "allowedValues": [
                        "Enabled",
                        "Disabled"
                      ],
                      "metadata": {
                        "description": "Geo-redundant backup — replicates backups to the Azure paired region for region-loss recovery. CREATE-TIME ONLY: it cannot be enabled after the server exists. Metadata only (no customer rows), but backups do leave the primary region, so set Disabled for single-region data-residency requirements."
                      }
                    },
                    "highAvailabilityMode": {
                      "type": "string",
                      "defaultValue": "Disabled",
                      "allowedValues": [
                        "Disabled",
                        "ZoneRedundant",
                        "SameZone"
                      ],
                      "metadata": {
                        "description": "High-availability mode. The module default is Disabled; the public subscription installer passes ZoneRedundant by default. ZoneRedundant runs a hot standby in another availability zone and requires General Purpose or Memory Optimized compute."
                      }
                    }
                  },
                  "resources": [
                    {
                      "type": "Microsoft.DBforPostgreSQL/flexibleServers",
                      "apiVersion": "2024-08-01",
                      "name": "[parameters('name')]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]",
                      "sku": {
                        "name": "[parameters('skuName')]",
                        "tier": "[parameters('skuTier')]"
                      },
                      "properties": {
                        "version": "16",
                        "authConfig": {
                          "activeDirectoryAuth": "Enabled",
                          "passwordAuth": "Disabled",
                          "tenantId": "[tenant().tenantId]"
                        },
                        "storage": {
                          "storageSizeGB": "[parameters('storageSizeGB')]",
                          "autoGrow": "Enabled"
                        },
                        "backup": {
                          "backupRetentionDays": "[parameters('backupRetentionDays')]",
                          "geoRedundantBackup": "[parameters('geoRedundantBackup')]"
                        },
                        "highAvailability": {
                          "mode": "[parameters('highAvailabilityMode')]"
                        },
                        "network": {
                          "publicNetworkAccess": "Enabled"
                        }
                      }
                    },
                    {
                      "type": "Microsoft.DBforPostgreSQL/flexibleServers/configurations",
                      "apiVersion": "2024-08-01",
                      "name": "[format('{0}/{1}', parameters('name'), 'require_secure_transport')]",
                      "properties": {
                        "value": "ON",
                        "source": "user-override"
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name'))]"
                      ]
                    },
                    {
                      "type": "Microsoft.DBforPostgreSQL/flexibleServers/configurations",
                      "apiVersion": "2024-08-01",
                      "name": "[format('{0}/{1}', parameters('name'), 'ssl_min_protocol_version')]",
                      "properties": {
                        "value": "TLSv1.2",
                        "source": "user-override"
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name'))]",
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers/configurations', parameters('name'), 'require_secure_transport')]"
                      ]
                    },
                    {
                      "type": "Microsoft.DBforPostgreSQL/flexibleServers/configurations",
                      "apiVersion": "2024-08-01",
                      "name": "[format('{0}/{1}', parameters('name'), 'pg_qs.query_capture_mode')]",
                      "properties": {
                        "value": "none",
                        "source": "user-override"
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name'))]",
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers/configurations', parameters('name'), 'ssl_min_protocol_version')]"
                      ]
                    },
                    {
                      "type": "Microsoft.DBforPostgreSQL/flexibleServers/administrators",
                      "apiVersion": "2024-08-01",
                      "name": "[format('{0}/{1}', parameters('name'), parameters('managedIdentityPrincipalId'))]",
                      "properties": {
                        "principalType": "ServicePrincipal",
                        "principalName": "[parameters('managedIdentityName')]",
                        "tenantId": "[tenant().tenantId]"
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name'))]",
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers/configurations', parameters('name'), 'pg_qs.query_capture_mode')]"
                      ]
                    },
                    {
                      "type": "Microsoft.DBforPostgreSQL/flexibleServers/databases",
                      "apiVersion": "2024-08-01",
                      "name": "[format('{0}/{1}', parameters('name'), parameters('databaseName'))]",
                      "dependsOn": [
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name'))]",
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers/administrators', parameters('name'), parameters('managedIdentityPrincipalId'))]"
                      ]
                    },
                    {
                      "type": "Microsoft.DBforPostgreSQL/flexibleServers/firewallRules",
                      "apiVersion": "2024-08-01",
                      "name": "[format('{0}/{1}', parameters('name'), 'AllowAzureServices')]",
                      "properties": {
                        "startIpAddress": "0.0.0.0",
                        "endIpAddress": "0.0.0.0"
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers/databases', parameters('name'), parameters('databaseName'))]",
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name'))]"
                      ]
                    },
                    {
                      "type": "Microsoft.Insights/diagnosticSettings",
                      "apiVersion": "2021-05-01-preview",
                      "scope": "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name'))]",
                      "name": "gv-pg-diag",
                      "properties": {
                        "workspaceId": "[parameters('logAnalyticsWorkspaceId')]",
                        "logAnalyticsDestinationType": "Dedicated",
                        "logs": [
                          {
                            "categoryGroup": "allLogs",
                            "enabled": true
                          }
                        ],
                        "metrics": [
                          {
                            "category": "AllMetrics",
                            "enabled": true
                          }
                        ]
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers/firewallRules', parameters('name'), 'AllowAzureServices')]",
                        "[resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name'))]"
                      ]
                    }
                  ],
                  "outputs": {
                    "fqdn": {
                      "type": "string",
                      "value": "[reference(resourceId('Microsoft.DBforPostgreSQL/flexibleServers', parameters('name')), '2024-08-01').fullyQualifiedDomainName]"
                    },
                    "databaseName": {
                      "type": "string",
                      "value": "[parameters('databaseName')]"
                    }
                  }
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'logAnalytics')]",
                "[resourceId('Microsoft.Resources/deployments', 'identity')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "dataFactory",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "name": {
                    "value": "[variables('factoryName')]"
                  },
                  "location": {
                    "value": "[parameters('location')]"
                  },
                  "tags": {
                    "value": "[variables('effectiveTags')]"
                  },
                  "logAnalyticsWorkspaceId": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.id.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.44.1.10279",
                      "templateHash": "10371591123671206194"
                    }
                  },
                  "parameters": {
                    "name": {
                      "type": "string",
                      "metadata": {
                        "description": "Data Factory name. Globally unique, 3-63 chars, letters/digits/hyphens, starts with a letter or digit."
                      }
                    },
                    "location": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "object",
                      "defaultValue": {}
                    },
                    "logAnalyticsWorkspaceId": {
                      "type": "string",
                      "metadata": {
                        "description": "Log Analytics workspace resource ID for factory run diagnostics."
                      }
                    }
                  },
                  "resources": [
                    {
                      "type": "Microsoft.DataFactory/factories",
                      "apiVersion": "2018-06-01",
                      "name": "[parameters('name')]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]",
                      "identity": {
                        "type": "SystemAssigned"
                      }
                    },
                    {
                      "type": "Microsoft.Insights/diagnosticSettings",
                      "apiVersion": "2021-05-01-preview",
                      "scope": "[resourceId('Microsoft.DataFactory/factories', parameters('name'))]",
                      "name": "gv-adf-diag",
                      "properties": {
                        "workspaceId": "[parameters('logAnalyticsWorkspaceId')]",
                        "logAnalyticsDestinationType": "Dedicated",
                        "logs": [
                          {
                            "category": "PipelineRuns",
                            "enabled": true
                          },
                          {
                            "category": "ActivityRuns",
                            "enabled": true
                          },
                          {
                            "category": "TriggerRuns",
                            "enabled": true
                          }
                        ],
                        "metrics": [
                          {
                            "category": "AllMetrics",
                            "enabled": true
                          }
                        ]
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.DataFactory/factories', parameters('name'))]"
                      ]
                    }
                  ],
                  "outputs": {
                    "name": {
                      "type": "string",
                      "value": "[parameters('name')]"
                    },
                    "id": {
                      "type": "string",
                      "value": "[resourceId('Microsoft.DataFactory/factories', parameters('name'))]"
                    },
                    "principalId": {
                      "type": "string",
                      "value": "[reference(resourceId('Microsoft.DataFactory/factories', parameters('name')), '2018-06-01', 'full').identity.principalId]"
                    }
                  }
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'logAnalytics')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "adlsGen2",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "name": {
                    "value": "[variables('adlsAccountName')]"
                  },
                  "location": {
                    "value": "[parameters('location')]"
                  },
                  "tags": {
                    "value": "[variables('effectiveTags')]"
                  },
                  "fileSystem": {
                    "value": "[parameters('adlsFileSystem')]"
                  },
                  "logAnalyticsWorkspaceId": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'logAnalytics'), '2025-04-01').outputs.id.value]"
                  },
                  "sku": "[if(not(empty(parameters('adlsSkuOverride'))), createObject('value', parameters('adlsSkuOverride')), if(parameters('enableGeoRedundancy'), createObject('value', 'Standard_GZRS'), createObject('value', 'Standard_ZRS')))]"
                },
                "template": {
                  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                  "contentVersion": "1.0.0.0",
                  "metadata": {
                    "_generator": {
                      "name": "bicep",
                      "version": "0.44.1.10279",
                      "templateHash": "3535274192865423252"
                    }
                  },
                  "parameters": {
                    "name": {
                      "type": "string",
                      "metadata": {
                        "description": "Storage account name. Lowercase letters/digits only, 3-24 chars, globally unique."
                      }
                    },
                    "location": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "object",
                      "defaultValue": {}
                    },
                    "logAnalyticsWorkspaceId": {
                      "type": "string",
                      "metadata": {
                        "description": "Log Analytics workspace resource ID for Blob/ADLS data-plane diagnostics."
                      }
                    },
                    "fileSystem": {
                      "type": "string",
                      "defaultValue": "generalvalidation",
                      "metadata": {
                        "description": "Blob container (ADLS file system) holding the GV shared control/result Delta tables and submission-scoped bad-record artifacts. Table paths are folders inside this container and do not depend on its name."
                      }
                    },
                    "sku": {
                      "type": "string",
                      "defaultValue": "Standard_GZRS",
                      "allowedValues": [
                        "Standard_GZRS",
                        "Standard_ZRS",
                        "Standard_GRS",
                        "Standard_LRS"
                      ],
                      "metadata": {
                        "description": "Storage recovery posture. GZRS (default) is zone-redundant in-region and geo-replicated to the paired region to support recovery after a regional failure; it does not fail the application over automatically. ZRS keeps data zone-redundant within one region for strict data residency."
                      }
                    }
                  },
                  "resources": [
                    {
                      "type": "Microsoft.Storage/storageAccounts",
                      "apiVersion": "2023-01-01",
                      "name": "[parameters('name')]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]",
                      "sku": {
                        "name": "[parameters('sku')]"
                      },
                      "kind": "StorageV2",
                      "properties": {
                        "isHnsEnabled": true,
                        "minimumTlsVersion": "TLS1_2",
                        "supportsHttpsTrafficOnly": true,
                        "allowBlobPublicAccess": false,
                        "defaultToOAuthAuthentication": true,
                        "allowCrossTenantReplication": false,
                        "allowSharedKeyAccess": false,
                        "publicNetworkAccess": "Enabled"
                      }
                    },
                    {
                      "type": "Microsoft.Storage/storageAccounts/blobServices",
                      "apiVersion": "2023-01-01",
                      "name": "[format('{0}/{1}', parameters('name'), 'default')]",
                      "properties": {
                        "deleteRetentionPolicy": {
                          "enabled": true,
                          "days": 30,
                          "allowPermanentDelete": false
                        },
                        "containerDeleteRetentionPolicy": {
                          "enabled": true,
                          "days": 30,
                          "allowPermanentDelete": false
                        }
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]"
                      ]
                    },
                    {
                      "type": "Microsoft.Insights/diagnosticSettings",
                      "apiVersion": "2021-05-01-preview",
                      "scope": "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('name'), 'default')]",
                      "name": "gv-blob-diag",
                      "properties": {
                        "workspaceId": "[parameters('logAnalyticsWorkspaceId')]",
                        "logAnalyticsDestinationType": "Dedicated",
                        "logs": [
                          {
                            "category": "StorageRead",
                            "enabled": true
                          },
                          {
                            "category": "StorageWrite",
                            "enabled": true
                          },
                          {
                            "category": "StorageDelete",
                            "enabled": true
                          }
                        ],
                        "metrics": [
                          {
                            "category": "Transaction",
                            "enabled": true
                          }
                        ]
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('name'), 'default')]"
                      ]
                    },
                    {
                      "type": "Microsoft.Storage/storageAccounts/blobServices/containers",
                      "apiVersion": "2023-01-01",
                      "name": "[format('{0}/{1}/{2}', parameters('name'), 'default', parameters('fileSystem'))]",
                      "dependsOn": [
                        "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('name'), 'default')]"
                      ]
                    }
                  ],
                  "outputs": {
                    "name": {
                      "type": "string",
                      "value": "[parameters('name')]"
                    },
                    "fileSystem": {
                      "type": "string",
                      "value": "[parameters('fileSystem')]"
                    }
                  }
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'logAnalytics')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "storageAccessGrantTemplateSpec",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "name": {
                    "value": "[format('{0}-storage-access-grant', variables('shortPrefix'))]"
                  },
                  "location": {
                    "value": "[parameters('location')]"
                  },
                  "tags": {
                    "value": "[variables('effectiveTags')]"
                  },
                  "executionFactoryPrincipalId": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'dataFactory'), '2025-04-01').outputs.principalId.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.44.1.10279",
                      "templateHash": "10617019542819217441"
                    }
                  },
                  "parameters": {
                    "name": {
                      "type": "string",
                      "metadata": {
                        "description": "Template Spec name created in the General Validation resource group."
                      }
                    },
                    "location": {
                      "type": "string",
                      "metadata": {
                        "description": "Azure region in which the Template Spec metadata is stored."
                      }
                    },
                    "tags": {
                      "type": "object",
                      "metadata": {
                        "description": "Tags applied to the Template Spec and its version."
                      }
                    },
                    "executionFactoryPrincipalId": {
                      "type": "string",
                      "metadata": {
                        "description": "Object ID of the execution Data Factory system-assigned managed identity."
                      }
                    }
                  },
                  "variables": {
                    "templateSpecVersion": "1.0.0",
                    "storageBlobDataReaderRoleId": "2a2b9908-6ea1-4ae2-8e65-a410df84e7d1"
                  },
                  "resources": [
                    {
                      "type": "Microsoft.Resources/templateSpecs",
                      "apiVersion": "2022-02-01",
                      "name": "[parameters('name')]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]",
                      "properties": {
                        "displayName": "General Validation source storage access",
                        "description": "Grants the General Validation execution factory Storage Blob Data Reader on one customer-selected storage account."
                      }
                    },
                    {
                      "type": "Microsoft.Resources/templateSpecs/versions",
                      "apiVersion": "2022-02-01",
                      "name": "[format('{0}/{1}', parameters('name'), variables('templateSpecVersion'))]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]",
                      "properties": {
                        "description": "Least-privilege source storage access for the General Validation execution factory.",
                        "mainTemplate": {
                          "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
                          "contentVersion": "1.0.0.0",
                          "parameters": {
                            "storageAccountResourceId": {
                              "type": "string",
                              "metadata": {
                                "description": "Resource ID of the existing storage account that the execution factory may read."
                              }
                            },
                            "roleAssignmentName": {
                              "type": "string",
                              "defaultValue": "[format('[guid(parameters(''storageAccountResourceId''), ''{0}'', ''{1}'')]', parameters('executionFactoryPrincipalId'), variables('storageBlobDataReaderRoleId'))]",
                              "metadata": {
                                "description": "Deterministic role-assignment name. Release validation may provide a pre-recorded UUID so cleanup ownership exists before deployment."
                              }
                            }
                          },
                          "variables": {
                            "storageAccountName": "[[last(split(parameters('storageAccountResourceId'), '/'))]",
                            "storageAccountResourceGroupName": "[[split(parameters('storageAccountResourceId'), '/')[4]]",
                            "roleDefinitionId": "[format('[subscriptionResourceId(''Microsoft.Authorization/roleDefinitions'', ''{0}'')]', variables('storageBlobDataReaderRoleId'))]"
                          },
                          "resources": [
                            {
                              "type": "Microsoft.Resources/deployments",
                              "apiVersion": "2025-04-01",
                              "name": "[[format('gvStorageRead-{0}', uniqueString(parameters('storageAccountResourceId')))]",
                              "resourceGroup": "[[variables('storageAccountResourceGroupName')]",
                              "properties": {
                                "mode": "Incremental",
                                "expressionEvaluationOptions": {
                                  "scope": "inner"
                                },
                                "parameters": {
                                  "storageAccountName": {
                                    "value": "[[variables('storageAccountName')]"
                                  },
                                  "roleAssignmentName": {
                                    "value": "[[parameters('roleAssignmentName')]"
                                  }
                                },
                                "template": {
                                  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                                  "contentVersion": "1.0.0.0",
                                  "parameters": {
                                    "storageAccountName": {
                                      "type": "string"
                                    },
                                    "roleAssignmentName": {
                                      "type": "string"
                                    }
                                  },
                                  "resources": [
                                    {
                                      "type": "Microsoft.Authorization/roleAssignments",
                                      "apiVersion": "2022-04-01",
                                      "name": "[[parameters('roleAssignmentName')]",
                                      "scope": "[[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]",
                                      "properties": {
                                        "roleDefinitionId": "[format('[subscriptionResourceId(''Microsoft.Authorization/roleDefinitions'', ''{0}'')]', variables('storageBlobDataReaderRoleId'))]",
                                        "principalId": "[parameters('executionFactoryPrincipalId')]",
                                        "principalType": "ServicePrincipal",
                                        "description": "General Validation execution factory read access to this storage account."
                                      }
                                    }
                                  ]
                                }
                              }
                            }
                          ],
                          "outputs": {
                            "roleAssignmentId": {
                              "type": "string",
                              "value": "[[extensionResourceId(parameters('storageAccountResourceId'), 'Microsoft.Authorization/roleAssignments', parameters('roleAssignmentName'))]"
                            },
                            "storageAccountResourceId": {
                              "type": "string",
                              "value": "[[parameters('storageAccountResourceId')]"
                            },
                            "principalId": {
                              "type": "string",
                              "value": "[parameters('executionFactoryPrincipalId')]"
                            },
                            "roleDefinitionId": {
                              "type": "string",
                              "value": "[[variables('roleDefinitionId')]"
                            }
                          }
                        },
                        "uiFormDefinition": {
                          "$schema": "https://schema.management.azure.com/schemas/2021-09-09/uiFormDefinition.schema.json#",
                          "view": {
                            "kind": "Form",
                            "properties": {
                              "title": "Grant General Validation access to one storage account",
                              "steps": [
                                {
                                  "name": "access",
                                  "label": "Storage access",
                                  "elements": [
                                    {
                                      "name": "accessInfo",
                                      "type": "Microsoft.Common.InfoBox",
                                      "options": {
                                        "style": "Info",
                                        "text": "Azure will grant the General Validation execution factory the built-in Storage Blob Data Reader role on only the storage account you select. Review the assignment, then choose Create. The approving account needs both deployment-write and role-assignment-write permission; Owner has both. General Validation cannot approve this grant for you."
                                      }
                                    },
                                    {
                                      "name": "subscription",
                                      "type": "Microsoft.Common.SubscriptionSelector",
                                      "resourceProviders": [
                                        "Microsoft.Storage"
                                      ],
                                      "constraints": {
                                        "validations": [
                                          {
                                            "isValid": "[[not(empty(steps('access').subscription.subscriptionId))]",
                                            "message": "Select the subscription that contains the storage account."
                                          }
                                        ]
                                      },
                                      "visible": true
                                    },
                                    {
                                      "name": "storageAccount",
                                      "type": "Microsoft.Solutions.ResourceSelector",
                                      "label": "Storage account",
                                      "resourceType": "Microsoft.Storage/storageAccounts",
                                      "scope": {
                                        "subscriptionId": "[[steps('access').subscription.subscriptionId]"
                                      },
                                      "constraints": {
                                        "required": true
                                      },
                                      "visible": true
                                    }
                                  ]
                                }
                              ]
                            },
                            "outputs": {
                              "kind": "Subscription",
                              "subscriptionId": "[[steps('access').subscription.subscriptionId]",
                              "location": "[[steps('access').storageAccount.location]",
                              "parameters": {
                                "storageAccountResourceId": "[[steps('access').storageAccount.id]"
                              }
                            }
                          }
                        }
                      },
                      "dependsOn": [
                        "[resourceId('Microsoft.Resources/templateSpecs', parameters('name'))]"
                      ]
                    }
                  ],
                  "outputs": {
                    "versionResourceId": {
                      "type": "string",
                      "value": "[resourceId('Microsoft.Resources/templateSpecs/versions', parameters('name'), variables('templateSpecVersion'))]"
                    }
                  }
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'dataFactory')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "dbUrlSecret",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "managedIdentityName": {
                    "value": "[variables('miName')]"
                  },
                  "fqdn": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'postgres'), '2025-04-01').outputs.fqdn.value]"
                  },
                  "databaseName": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'postgres'), '2025-04-01').outputs.databaseName.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.44.1.10279",
                      "templateHash": "8018008888467747628"
                    }
                  },
                  "parameters": {
                    "managedIdentityName": {
                      "type": "string",
                      "metadata": {
                        "description": "Managed-identity name used as the Postgres (Entra) connection user."
                      }
                    },
                    "fqdn": {
                      "type": "string",
                      "metadata": {
                        "description": "Postgres flexible-server FQDN."
                      }
                    },
                    "databaseName": {
                      "type": "string",
                      "metadata": {
                        "description": "Database name."
                      }
                    }
                  },
                  "resources": [],
                  "outputs": {
                    "dbUrl": {
                      "type": "string",
                      "value": "[format('postgresql+psycopg://{0}@{1}:5432/{2}?sslmode=verify-full&sslrootcert=system', parameters('managedIdentityName'), parameters('fqdn'), parameters('databaseName'))]"
                    }
                  }
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'postgres')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "apiApp",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "name": {
                    "value": "[format('{0}-api', parameters('namePrefix'))]"
                  },
                  "location": {
                    "value": "[parameters('location')]"
                  },
                  "tags": {
                    "value": "[variables('effectiveTags')]"
                  },
                  "environmentId": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'containerEnv'), '2025-04-01').outputs.id.value]"
                  },
                  "identityId": "[if(empty(parameters('existingIdentityName')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.id.value), createObject('value', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName'))))]",
                  "image": {
                    "value": "[parameters('image')]"
                  },
                  "registryServer": {
                    "value": "[parameters('registryServer')]"
                  },
                  "registryUsername": {
                    "value": "[parameters('registryUsername')]"
                  },
                  "cpu": {
                    "value": "1"
                  },
                  "memory": {
                    "value": "2Gi"
                  },
                  "minReplicas": {
                    "value": 1
                  },
                  "maxReplicas": {
                    "value": 1
                  },
                  "enableExternalIngress": {
                    "value": true
                  },
                  "targetPort": {
                    "value": 8000
                  },
                  "probes": {
                    "value": [
                      {
                        "type": "Startup",
                        "httpGet": {
                          "path": "/live",
                          "port": 8000,
                          "scheme": "HTTP"
                        },
                        "periodSeconds": 5,
                        "timeoutSeconds": 3,
                        "failureThreshold": 12
                      },
                      {
                        "type": "Liveness",
                        "httpGet": {
                          "path": "/live",
                          "port": 8000,
                          "scheme": "HTTP"
                        },
                        "periodSeconds": 15,
                        "timeoutSeconds": 3,
                        "failureThreshold": 3
                      },
                      {
                        "type": "Readiness",
                        "httpGet": {
                          "path": "/ready",
                          "port": 8000,
                          "scheme": "HTTP"
                        },
                        "periodSeconds": 10,
                        "timeoutSeconds": 5,
                        "failureThreshold": 3,
                        "successThreshold": 1
                      }
                    ]
                  },
                  "initContainers": {
                    "value": [
                      {
                        "name": "migrate",
                        "image": "[parameters('image')]",
                        "args": [
                          "migrate"
                        ],
                        "resources": {
                          "cpu": "[json('0.25')]",
                          "memory": "0.5Gi"
                        },
                        "env": [
                          {
                            "name": "DATABASE_URL",
                            "value": "[reference(resourceId('Microsoft.Resources/deployments', 'dbUrlSecret'), '2025-04-01').outputs.dbUrl.value]"
                          },
                          {
                            "name": "GV_DB_AUTH",
                            "value": "entra"
                          },
                          {
                            "name": "AZURE_CLIENT_ID",
                            "value": "[if(empty(parameters('existingIdentityName')), reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.clientId.value, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName')), '2023-01-31').clientId)]"
                          }
                        ]
                      }
                    ]
                  },
                  "envVars": {
                    "value": "[concat(concat(createArray(createObject('name', 'DATABASE_URL', 'value', reference(resourceId('Microsoft.Resources/deployments', 'dbUrlSecret'), '2025-04-01').outputs.dbUrl.value), createObject('name', 'GV_DB_AUTH', 'value', 'entra'), createObject('name', 'AZURE_CLIENT_ID', 'value', if(empty(parameters('existingIdentityName')), reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.clientId.value, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName')), '2023-01-31').clientId)), createObject('name', 'AZURE_SUBSCRIPTION_ID', 'value', subscription().subscriptionId), createObject('name', 'ADF_RESOURCE_GROUP', 'value', resourceGroup().name), createObject('name', 'ADF_FACTORY_NAME', 'value', reference(resourceId('Microsoft.Resources/deployments', 'dataFactory'), '2025-04-01').outputs.name.value), createObject('name', 'ADF_FACTORY_PRINCIPAL_ID', 'value', reference(resourceId('Microsoft.Resources/deployments', 'dataFactory'), '2025-04-01').outputs.principalId.value), createObject('name', 'ADF_INTEGRATION_RUNTIME', 'value', 'AutoResolveIntegrationRuntime'), createObject('name', 'GV_ADF_NAMESPACE', 'value', parameters('adfNamespace')), createObject('name', 'ADF_PIPELINE_PREFIX', 'value', 'gv_validation'), createObject('name', 'ADF_DATAFLOW_PREFIX', 'value', 'gv_flow'), createObject('name', 'ADF_VALIDATION_SINK_STORE', 'value', 'azureBlobFS'), createObject('name', 'ADF_VALIDATION_SINK_FILE_SYSTEM', 'value', reference(resourceId('Microsoft.Resources/deployments', 'adlsGen2'), '2025-04-01').outputs.fileSystem.value), createObject('name', 'ADF_VALIDATION_SINK_TABLE_PATH', 'value', 'generalvalidation/Tables/reporting/validation_results'), createObject('name', 'ADF_VALIDATION_CONTROL_FILE_SYSTEM', 'value', reference(resourceId('Microsoft.Resources/deployments', 'adlsGen2'), '2025-04-01').outputs.fileSystem.value), createObject('name', 'ADF_VALIDATION_CONTROL_TABLE_PATH', 'value', 'generalvalidation/Tables/control/runtime_control'), createObject('name', 'ADF_VALIDATION_BAD_RECORD_TABLE_PATH', 'value', 'generalvalidation/Tables/evidence/bad_records'), createObject('name', 'GV_ADLS_GRANT_ACCOUNT', 'value', reference(resourceId('Microsoft.Resources/deployments', 'adlsGen2'), '2025-04-01').outputs.name.value), createObject('name', 'GV_STORAGE_ACCESS_TEMPLATE_SPEC_ID', 'value', reference(resourceId('Microsoft.Resources/deployments', 'storageAccessGrantTemplateSpec'), '2025-04-01').outputs.versionResourceId.value), createObject('name', 'GV_APP_VERSION', 'value', parameters('appVersion')), createObject('name', 'GV_IMAGE_TAG', 'value', parameters('imageTag')), createObject('name', 'GV_BUILD_SHA', 'value', parameters('buildSha')), createObject('name', 'GV_BUILD_DATE', 'value', parameters('buildDate'))), concat(createArray(createObject('name', 'GV_DEPLOYMENT', 'value', 'enterprise'), createObject('name', 'AUTH_APP_ORIGIN', 'value', if(not(empty(parameters('appOrigin'))), parameters('appOrigin'), format('https://{0}-api.{1}', parameters('namePrefix'), reference(resourceId('Microsoft.Resources/deployments', 'containerEnv'), '2025-04-01').outputs.defaultDomain.value))), createObject('name', 'AUTH_ROLE_SOURCE', 'value', 'db'), createObject('name', 'AUTH_BOOTSTRAP_OWNER', 'value', parameters('bootstrapOwner'))), if(variables('oidcConfigured'), createArray(createObject('name', 'AUTH_OIDC_DISCOVERY_URL', 'value', parameters('oidcDiscoveryUrl')), createObject('name', 'AUTH_OIDC_CLIENT_ID', 'value', parameters('oidcClientId')), createObject('name', 'AUTH_OIDC_AUDIENCE', 'value', parameters('oidcClientId')), createObject('name', 'AUTH_OIDC_SUBJECT_CLAIM', 'value', 'oid'), createObject('name', 'AUTH_OIDC_CREDENTIAL_MODE', 'value', 'managed_identity_federation'), createObject('name', 'AUTH_REDIRECT_URI', 'value', format('{0}/auth/callback', if(not(empty(parameters('appOrigin'))), parameters('appOrigin'), format('https://{0}-api.{1}', parameters('namePrefix'), reference(resourceId('Microsoft.Resources/deployments', 'containerEnv'), '2025-04-01').outputs.defaultDomain.value))))), createArray())), if(not(empty(parameters('bootstrapSecret'))), createArray(createObject('name', 'GV_BOOTSTRAP_SECRET', 'secretRef', 'bootstrapsecret')), createArray())), createArray(createObject('name', 'GV_DB_POOL_SIZE', 'value', string(parameters('apiDbPoolSize'))), createObject('name', 'GV_DB_MAX_OVERFLOW', 'value', string(parameters('apiDbMaxOverflow')))))]"
                  },
                  "registryPassword": {
                    "value": "[parameters('registryPassword')]"
                  },
                  "bootstrapSecret": {
                    "value": "[parameters('bootstrapSecret')]"
                  }
                },
                "template": {
                  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                  "contentVersion": "1.0.0.0",
                  "metadata": {
                    "_generator": {
                      "name": "bicep",
                      "version": "0.44.1.10279",
                      "templateHash": "16779964177190957416"
                    }
                  },
                  "parameters": {
                    "name": {
                      "type": "string",
                      "metadata": {
                        "description": "API Container App. The separate worker module has no bootstrap-secret capability."
                      }
                    },
                    "location": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "object",
                      "defaultValue": {}
                    },
                    "environmentId": {
                      "type": "string"
                    },
                    "identityId": {
                      "type": "string",
                      "metadata": {
                        "description": "Managed identity used by the application and any init containers."
                      }
                    },
                    "image": {
                      "type": "string"
                    },
                    "registryServer": {
                      "type": "string"
                    },
                    "registryUsername": {
                      "type": "string",
                      "defaultValue": "",
                      "metadata": {
                        "description": "Registry token name for username/password pull (cross-tenant publisher registry). Empty = pull via the managed identity (same-tenant / customer-owned ACR)."
                      }
                    },
                    "registryPassword": {
                      "type": "securestring",
                      "defaultValue": "",
                      "metadata": {
                        "description": "Registry token password. Stored as a Container App secret only when registryUsername is set."
                      }
                    },
                    "bootstrapSecret": {
                      "type": "securestring",
                      "defaultValue": "",
                      "metadata": {
                        "description": "One-time application bootstrap secret. Supply only to the API app; never to workers or migration containers."
                      }
                    },
                    "cpu": {
                      "type": "string",
                      "defaultValue": "0.5"
                    },
                    "memory": {
                      "type": "string",
                      "defaultValue": "1Gi"
                    },
                    "minReplicas": {
                      "type": "int",
                      "defaultValue": 1
                    },
                    "maxReplicas": {
                      "type": "int",
                      "defaultValue": 1
                    },
                    "enableExternalIngress": {
                      "type": "bool",
                      "defaultValue": false
                    },
                    "targetPort": {
                      "type": "int",
                      "defaultValue": 8000
                    },
                    "args": {
                      "type": "array",
                      "defaultValue": [],
                      "metadata": {
                        "description": "Container args, e.g. [\"worker\"]. Empty = use the image CMD (api)."
                      }
                    },
                    "initContainers": {
                      "type": "array",
                      "defaultValue": [],
                      "metadata": {
                        "description": "Init containers that must exit 0 before the main container starts (e.g. a migrate step). Empty = none."
                      }
                    },
                    "envVars": {
                      "type": "array",
                      "metadata": {
                        "description": "Env vars: array of { name, value } and/or { name, secretRef }."
                      }
                    },
                    "probes": {
                      "type": "array",
                      "defaultValue": [],
                      "metadata": {
                        "description": "ACA container probes (Liveness/Readiness/Startup). Empty = platform default."
                      }
                    }
                  },
                  "variables": {
                    "configuredSecrets": "[concat(if(not(empty(parameters('registryUsername'))), createArray(createObject('name', 'publisherpull', 'value', parameters('registryPassword'))), createArray()), if(not(empty(parameters('bootstrapSecret'))), createArray(createObject('name', 'bootstrapsecret', 'value', parameters('bootstrapSecret'))), createArray()))]"
                  },
                  "resources": [
                    {
                      "type": "Microsoft.App/containerApps",
                      "apiVersion": "2025-01-01",
                      "name": "[parameters('name')]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]",
                      "identity": {
                        "type": "UserAssigned",
                        "userAssignedIdentities": {
                          "[format('{0}', parameters('identityId'))]": {}
                        }
                      },
                      "properties": {
                        "managedEnvironmentId": "[parameters('environmentId')]",
                        "configuration": {
                          "activeRevisionsMode": "Single",
                          "ingress": "[if(parameters('enableExternalIngress'), createObject('external', true(), 'targetPort', parameters('targetPort'), 'transport', 'auto', 'allowInsecure', false()), null())]",
                          "secrets": "[variables('configuredSecrets')]",
                          "registries": [
                            "[if(empty(parameters('registryUsername')), createObject('server', parameters('registryServer'), 'identity', parameters('identityId')), createObject('server', parameters('registryServer'), 'username', parameters('registryUsername'), 'passwordSecretRef', 'publisherpull'))]"
                          ]
                        },
                        "template": {
                          "initContainers": "[if(empty(parameters('initContainers')), null(), parameters('initContainers'))]",
                          "containers": [
                            {
                              "name": "[parameters('name')]",
                              "image": "[parameters('image')]",
                              "args": "[if(empty(parameters('args')), null(), parameters('args'))]",
                              "resources": {
                                "cpu": "[json(parameters('cpu'))]",
                                "memory": "[parameters('memory')]"
                              },
                              "env": "[parameters('envVars')]",
                              "probes": "[if(empty(parameters('probes')), null(), parameters('probes'))]"
                            }
                          ],
                          "scale": {
                            "minReplicas": "[parameters('minReplicas')]",
                            "maxReplicas": "[parameters('maxReplicas')]"
                          }
                        }
                      }
                    }
                  ],
                  "outputs": {
                    "fqdn": {
                      "type": "string",
                      "value": "[if(parameters('enableExternalIngress'), reference(resourceId('Microsoft.App/containerApps', parameters('name')), '2025-01-01').configuration.ingress.fqdn, '')]"
                    }
                  }
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'adlsGen2')]",
                "[resourceId('Microsoft.Resources/deployments', 'dataFactory')]",
                "[resourceId('Microsoft.Resources/deployments', 'dbUrlSecret')]",
                "[resourceId('Microsoft.Resources/deployments', 'containerEnv')]",
                "[resourceId('Microsoft.Resources/deployments', 'identity')]",
                "[resourceId('Microsoft.Resources/deployments', 'storageAccessGrantTemplateSpec')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "workerApp",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "name": {
                    "value": "[format('{0}-worker', parameters('namePrefix'))]"
                  },
                  "location": {
                    "value": "[parameters('location')]"
                  },
                  "tags": {
                    "value": "[variables('effectiveTags')]"
                  },
                  "environmentId": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'containerEnv'), '2025-04-01').outputs.id.value]"
                  },
                  "identityId": "[if(empty(parameters('existingIdentityName')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.id.value), createObject('value', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName'))))]",
                  "image": {
                    "value": "[parameters('image')]"
                  },
                  "registryServer": {
                    "value": "[parameters('registryServer')]"
                  },
                  "registryUsername": {
                    "value": "[parameters('registryUsername')]"
                  },
                  "minReplicas": {
                    "value": "[parameters('workerMinReplicas')]"
                  },
                  "maxReplicas": {
                    "value": "[parameters('workerMaxReplicas')]"
                  },
                  "envVars": {
                    "value": "[concat(concat(createArray(createObject('name', 'DATABASE_URL', 'value', reference(resourceId('Microsoft.Resources/deployments', 'dbUrlSecret'), '2025-04-01').outputs.dbUrl.value), createObject('name', 'GV_DB_AUTH', 'value', 'entra'), createObject('name', 'AZURE_CLIENT_ID', 'value', if(empty(parameters('existingIdentityName')), reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.clientId.value, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName')), '2023-01-31').clientId)), createObject('name', 'AZURE_SUBSCRIPTION_ID', 'value', subscription().subscriptionId), createObject('name', 'ADF_RESOURCE_GROUP', 'value', resourceGroup().name), createObject('name', 'ADF_FACTORY_NAME', 'value', reference(resourceId('Microsoft.Resources/deployments', 'dataFactory'), '2025-04-01').outputs.name.value), createObject('name', 'ADF_FACTORY_PRINCIPAL_ID', 'value', reference(resourceId('Microsoft.Resources/deployments', 'dataFactory'), '2025-04-01').outputs.principalId.value), createObject('name', 'ADF_INTEGRATION_RUNTIME', 'value', 'AutoResolveIntegrationRuntime'), createObject('name', 'GV_ADF_NAMESPACE', 'value', parameters('adfNamespace')), createObject('name', 'ADF_PIPELINE_PREFIX', 'value', 'gv_validation'), createObject('name', 'ADF_DATAFLOW_PREFIX', 'value', 'gv_flow'), createObject('name', 'ADF_VALIDATION_SINK_STORE', 'value', 'azureBlobFS'), createObject('name', 'ADF_VALIDATION_SINK_FILE_SYSTEM', 'value', reference(resourceId('Microsoft.Resources/deployments', 'adlsGen2'), '2025-04-01').outputs.fileSystem.value), createObject('name', 'ADF_VALIDATION_SINK_TABLE_PATH', 'value', 'generalvalidation/Tables/reporting/validation_results'), createObject('name', 'ADF_VALIDATION_CONTROL_FILE_SYSTEM', 'value', reference(resourceId('Microsoft.Resources/deployments', 'adlsGen2'), '2025-04-01').outputs.fileSystem.value), createObject('name', 'ADF_VALIDATION_CONTROL_TABLE_PATH', 'value', 'generalvalidation/Tables/control/runtime_control'), createObject('name', 'ADF_VALIDATION_BAD_RECORD_TABLE_PATH', 'value', 'generalvalidation/Tables/evidence/bad_records'), createObject('name', 'GV_ADLS_GRANT_ACCOUNT', 'value', reference(resourceId('Microsoft.Resources/deployments', 'adlsGen2'), '2025-04-01').outputs.name.value), createObject('name', 'GV_STORAGE_ACCESS_TEMPLATE_SPEC_ID', 'value', reference(resourceId('Microsoft.Resources/deployments', 'storageAccessGrantTemplateSpec'), '2025-04-01').outputs.versionResourceId.value), createObject('name', 'GV_APP_VERSION', 'value', parameters('appVersion')), createObject('name', 'GV_IMAGE_TAG', 'value', parameters('imageTag')), createObject('name', 'GV_BUILD_SHA', 'value', parameters('buildSha')), createObject('name', 'GV_BUILD_DATE', 'value', parameters('buildDate'))), concat(createArray(createObject('name', 'GV_DEPLOYMENT', 'value', 'enterprise'), createObject('name', 'AUTH_APP_ORIGIN', 'value', if(not(empty(parameters('appOrigin'))), parameters('appOrigin'), format('https://{0}-api.{1}', parameters('namePrefix'), reference(resourceId('Microsoft.Resources/deployments', 'containerEnv'), '2025-04-01').outputs.defaultDomain.value))), createObject('name', 'AUTH_ROLE_SOURCE', 'value', 'db'), createObject('name', 'AUTH_BOOTSTRAP_OWNER', 'value', parameters('bootstrapOwner'))), if(variables('oidcConfigured'), createArray(createObject('name', 'AUTH_OIDC_DISCOVERY_URL', 'value', parameters('oidcDiscoveryUrl')), createObject('name', 'AUTH_OIDC_CLIENT_ID', 'value', parameters('oidcClientId')), createObject('name', 'AUTH_OIDC_AUDIENCE', 'value', parameters('oidcClientId')), createObject('name', 'AUTH_OIDC_SUBJECT_CLAIM', 'value', 'oid'), createObject('name', 'AUTH_OIDC_CREDENTIAL_MODE', 'value', 'managed_identity_federation'), createObject('name', 'AUTH_REDIRECT_URI', 'value', format('{0}/auth/callback', if(not(empty(parameters('appOrigin'))), parameters('appOrigin'), format('https://{0}-api.{1}', parameters('namePrefix'), reference(resourceId('Microsoft.Resources/deployments', 'containerEnv'), '2025-04-01').outputs.defaultDomain.value))))), createArray()))), createArray(createObject('name', 'GV_DB_POOL_SIZE', 'value', string(parameters('workerDbPoolSize'))), createObject('name', 'GV_DB_MAX_OVERFLOW', 'value', string(parameters('workerDbMaxOverflow')))))]"
                  },
                  "registryPassword": {
                    "value": "[parameters('registryPassword')]"
                  }
                },
                "template": {
                  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                  "contentVersion": "1.0.0.0",
                  "metadata": {
                    "_generator": {
                      "name": "bicep",
                      "version": "0.44.1.10279",
                      "templateHash": "8243693613246144857"
                    }
                  },
                  "parameters": {
                    "name": {
                      "type": "string",
                      "metadata": {
                        "description": "Worker Container App. Its only possible secret is the registry pull credential; it has no bootstrap-secret capability."
                      }
                    },
                    "location": {
                      "type": "string"
                    },
                    "tags": {
                      "type": "object",
                      "defaultValue": {}
                    },
                    "environmentId": {
                      "type": "string"
                    },
                    "identityId": {
                      "type": "string",
                      "metadata": {
                        "description": "Managed identity used by the worker."
                      }
                    },
                    "image": {
                      "type": "string"
                    },
                    "registryServer": {
                      "type": "string"
                    },
                    "registryUsername": {
                      "type": "string",
                      "defaultValue": "",
                      "metadata": {
                        "description": "Registry token name for username/password pull (cross-tenant publisher registry). Empty = pull via the managed identity (same-tenant / customer-owned ACR)."
                      }
                    },
                    "registryPassword": {
                      "type": "securestring",
                      "defaultValue": "",
                      "metadata": {
                        "description": "Registry token password. Stored as a Container App secret only when registryUsername is set."
                      }
                    },
                    "minReplicas": {
                      "type": "int",
                      "defaultValue": 1
                    },
                    "maxReplicas": {
                      "type": "int",
                      "defaultValue": 1
                    },
                    "envVars": {
                      "type": "array",
                      "metadata": {
                        "description": "Worker environment variables. Secret references are not accepted by the caller contract."
                      }
                    }
                  },
                  "variables": {
                    "configuredSecrets": "[if(not(empty(parameters('registryUsername'))), createArray(createObject('name', 'publisherpull', 'value', parameters('registryPassword'))), createArray())]"
                  },
                  "resources": [
                    {
                      "type": "Microsoft.App/containerApps",
                      "apiVersion": "2025-01-01",
                      "name": "[parameters('name')]",
                      "location": "[parameters('location')]",
                      "tags": "[parameters('tags')]",
                      "identity": {
                        "type": "UserAssigned",
                        "userAssignedIdentities": {
                          "[format('{0}', parameters('identityId'))]": {}
                        }
                      },
                      "properties": {
                        "managedEnvironmentId": "[parameters('environmentId')]",
                        "configuration": {
                          "activeRevisionsMode": "Single",
                          "secrets": "[variables('configuredSecrets')]",
                          "registries": [
                            "[if(empty(parameters('registryUsername')), createObject('server', parameters('registryServer'), 'identity', parameters('identityId')), createObject('server', parameters('registryServer'), 'username', parameters('registryUsername'), 'passwordSecretRef', 'publisherpull'))]"
                          ]
                        },
                        "template": {
                          "containers": [
                            {
                              "name": "[parameters('name')]",
                              "image": "[parameters('image')]",
                              "args": [
                                "worker"
                              ],
                              "resources": {
                                "cpu": "[json('0.5')]",
                                "memory": "1Gi"
                              },
                              "env": "[parameters('envVars')]"
                            }
                          ],
                          "scale": {
                            "minReplicas": "[parameters('minReplicas')]",
                            "maxReplicas": "[parameters('maxReplicas')]"
                          }
                        }
                      }
                    }
                  ]
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'adlsGen2')]",
                "[resourceId('Microsoft.Resources/deployments', 'apiApp')]",
                "[resourceId('Microsoft.Resources/deployments', 'dataFactory')]",
                "[resourceId('Microsoft.Resources/deployments', 'dbUrlSecret')]",
                "[resourceId('Microsoft.Resources/deployments', 'containerEnv')]",
                "[resourceId('Microsoft.Resources/deployments', 'identity')]",
                "[resourceId('Microsoft.Resources/deployments', 'storageAccessGrantTemplateSpec')]"
              ]
            },
            {
              "condition": "[and(parameters('grantAcrPull'), not(empty(parameters('acrName'))))]",
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "acrPullRoleAssignment",
              "resourceGroup": "[parameters('acrResourceGroup')]",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "acrName": {
                    "value": "[parameters('acrName')]"
                  },
                  "principalId": "[if(empty(parameters('existingIdentityName')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.principalId.value), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName')), '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.44.1.10279",
                      "templateHash": "16243139434281861691"
                    }
                  },
                  "parameters": {
                    "acrName": {
                      "type": "string",
                      "metadata": {
                        "description": "Existing container registry name."
                      }
                    },
                    "principalId": {
                      "type": "string"
                    }
                  },
                  "variables": {
                    "acrPull": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]"
                  },
                  "resources": [
                    {
                      "type": "Microsoft.Authorization/roleAssignments",
                      "apiVersion": "2022-04-01",
                      "scope": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName'))]",
                      "name": "[guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('acrName')), parameters('principalId'), variables('acrPull'))]",
                      "properties": {
                        "roleDefinitionId": "[variables('acrPull')]",
                        "principalId": "[parameters('principalId')]",
                        "principalType": "ServicePrincipal"
                      }
                    }
                  ]
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'identity')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "adfRoleAssignment",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "factoryName": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'dataFactory'), '2025-04-01').outputs.name.value]"
                  },
                  "principalId": "[if(empty(parameters('existingIdentityName')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.principalId.value), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName')), '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.44.1.10279",
                      "templateHash": "14635549297836435948"
                    }
                  },
                  "parameters": {
                    "factoryName": {
                      "type": "string",
                      "metadata": {
                        "description": "GV-provisioned ADF factory name."
                      }
                    },
                    "principalId": {
                      "type": "string",
                      "metadata": {
                        "description": "Object id (principalId) of the user-assigned managed identity to grant."
                      }
                    }
                  },
                  "variables": {
                    "dataFactoryContributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]"
                  },
                  "resources": [
                    {
                      "type": "Microsoft.Authorization/roleAssignments",
                      "apiVersion": "2022-04-01",
                      "scope": "[resourceId('Microsoft.DataFactory/factories', parameters('factoryName'))]",
                      "name": "[guid(resourceId('Microsoft.DataFactory/factories', parameters('factoryName')), parameters('principalId'), variables('dataFactoryContributor'))]",
                      "properties": {
                        "roleDefinitionId": "[variables('dataFactoryContributor')]",
                        "principalId": "[parameters('principalId')]",
                        "principalType": "ServicePrincipal"
                      }
                    }
                  ]
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'dataFactory')]",
                "[resourceId('Microsoft.Resources/deployments', 'identity')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "adlsRoleAssignment",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "storageAccountName": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'adlsGen2'), '2025-04-01').outputs.name.value]"
                  },
                  "principalId": "[if(empty(parameters('existingIdentityName')), createObject('value', reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.principalId.value), createObject('value', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName')), '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.44.1.10279",
                      "templateHash": "11026718489411796678"
                    }
                  },
                  "parameters": {
                    "storageAccountName": {
                      "type": "string",
                      "metadata": {
                        "description": "GV-provisioned ADLS Gen2 storage account name."
                      }
                    },
                    "principalId": {
                      "type": "string",
                      "metadata": {
                        "description": "Object id (principalId) of the user-assigned managed identity to grant."
                      }
                    }
                  },
                  "variables": {
                    "storageBlobDataContributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
                    "reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]"
                  },
                  "resources": [
                    {
                      "type": "Microsoft.Authorization/roleAssignments",
                      "apiVersion": "2022-04-01",
                      "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
                      "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('principalId'), variables('storageBlobDataContributor'))]",
                      "properties": {
                        "roleDefinitionId": "[variables('storageBlobDataContributor')]",
                        "principalId": "[parameters('principalId')]",
                        "principalType": "ServicePrincipal"
                      }
                    },
                    {
                      "type": "Microsoft.Authorization/roleAssignments",
                      "apiVersion": "2022-04-01",
                      "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
                      "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('principalId'), variables('reader'))]",
                      "properties": {
                        "roleDefinitionId": "[variables('reader')]",
                        "principalId": "[parameters('principalId')]",
                        "principalType": "ServicePrincipal"
                      }
                    }
                  ]
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'adlsGen2')]",
                "[resourceId('Microsoft.Resources/deployments', 'identity')]"
              ]
            },
            {
              "type": "Microsoft.Resources/deployments",
              "apiVersion": "2025-04-01",
              "name": "adlsFactoryRoleAssignment",
              "properties": {
                "expressionEvaluationOptions": {
                  "scope": "inner"
                },
                "mode": "Incremental",
                "parameters": {
                  "storageAccountName": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'adlsGen2'), '2025-04-01').outputs.name.value]"
                  },
                  "principalId": {
                    "value": "[reference(resourceId('Microsoft.Resources/deployments', 'dataFactory'), '2025-04-01').outputs.principalId.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.44.1.10279",
                      "templateHash": "12302349504298858515"
                    }
                  },
                  "parameters": {
                    "storageAccountName": {
                      "type": "string",
                      "metadata": {
                        "description": "GV-provisioned ADLS Gen2 storage account name."
                      }
                    },
                    "principalId": {
                      "type": "string",
                      "metadata": {
                        "description": "Object id (principalId) of the ADF factory system-assigned managed identity to grant."
                      }
                    }
                  },
                  "variables": {
                    "storageBlobDataContributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]"
                  },
                  "resources": [
                    {
                      "type": "Microsoft.Authorization/roleAssignments",
                      "apiVersion": "2022-04-01",
                      "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
                      "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('principalId'), variables('storageBlobDataContributor'))]",
                      "properties": {
                        "roleDefinitionId": "[variables('storageBlobDataContributor')]",
                        "principalId": "[parameters('principalId')]",
                        "principalType": "ServicePrincipal"
                      }
                    }
                  ]
                }
              },
              "dependsOn": [
                "[resourceId('Microsoft.Resources/deployments', 'adlsGen2')]",
                "[resourceId('Microsoft.Resources/deployments', 'dataFactory')]"
              ]
            }
          ],
          "outputs": {
            "apiUrl": {
              "type": "string",
              "value": "[format('https://{0}', reference(resourceId('Microsoft.Resources/deployments', 'apiApp'), '2025-04-01').outputs.fqdn.value)]"
            },
            "setupUrl": {
              "type": "string",
              "value": "[format('{0}/auth/setup', format('https://{0}', reference(resourceId('Microsoft.Resources/deployments', 'apiApp'), '2025-04-01').outputs.fqdn.value))]"
            },
            "managedIdentityPrincipalId": {
              "type": "string",
              "value": "[if(empty(parameters('existingIdentityName')), reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.principalId.value, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName')), '2023-01-31').principalId)]"
            },
            "managedIdentityClientId": {
              "type": "string",
              "value": "[if(empty(parameters('existingIdentityName')), reference(resourceId('Microsoft.Resources/deployments', 'identity'), '2025-04-01').outputs.clientId.value, reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('existingIdentityName')), '2023-01-31').clientId)]"
            },
            "postgresServerName": {
              "type": "string",
              "value": "[format('{0}pg{1}', parameters('namePrefix'), variables('suffix'))]"
            },
            "executionFactoryName": {
              "type": "string",
              "value": "[reference(resourceId('Microsoft.Resources/deployments', 'dataFactory'), '2025-04-01').outputs.name.value]"
            },
            "executionFactoryPrincipalId": {
              "type": "string",
              "value": "[reference(resourceId('Microsoft.Resources/deployments', 'dataFactory'), '2025-04-01').outputs.principalId.value]"
            },
            "resultsStorageAccountName": {
              "type": "string",
              "value": "[reference(resourceId('Microsoft.Resources/deployments', 'adlsGen2'), '2025-04-01').outputs.name.value]"
            },
            "storageAccessTemplateSpecVersionId": {
              "type": "string",
              "value": "[reference(resourceId('Microsoft.Resources/deployments', 'storageAccessGrantTemplateSpec'), '2025-04-01').outputs.versionResourceId.value]"
            }
          }
        }
      },
      "dependsOn": [
        "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]"
      ]
    },
    {
      "condition": "[parameters('grantSubscriptionFactoryRead')]",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2025-04-01",
      "name": "subscriptionFactoryRead",
      "location": "[deployment().location]",
      "properties": {
        "expressionEvaluationOptions": {
          "scope": "inner"
        },
        "mode": "Incremental",
        "parameters": {
          "principalId": {
            "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.managedIdentityPrincipalId.value]"
          }
        },
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "metadata": {
            "_generator": {
              "name": "bicep",
              "version": "0.44.1.10279",
              "templateHash": "17969049510447384569"
            }
          },
          "parameters": {
            "principalId": {
              "type": "string",
              "metadata": {
                "description": "Object id (principalId) of the app user-assigned managed identity to grant."
              }
            }
          },
          "variables": {
            "roleName": "[format('Data Factory Reader (General Validation {0})', subscription().subscriptionId)]"
          },
          "resources": [
            {
              "type": "Microsoft.Authorization/roleDefinitions",
              "apiVersion": "2022-04-01",
              "name": "[guid(subscription().id, 'gv-data-factory-reader')]",
              "properties": {
                "roleName": "[variables('roleName')]",
                "description": "Read-only access to Data Factory factories and their sub-resources (datasets, linked services, pipelines) in this subscription. Lets the General Validation application identity discover factory metadata without any broader read rights. Created by the General Validation install; safe to re-assert on template re-runs.",
                "type": "CustomRole",
                "permissions": [
                  {
                    "actions": [
                      "Microsoft.DataFactory/factories/read",
                      "Microsoft.DataFactory/factories/*/read"
                    ],
                    "notActions": []
                  }
                ],
                "assignableScopes": [
                  "[subscription().id]"
                ]
              }
            },
            {
              "type": "Microsoft.Authorization/roleAssignments",
              "apiVersion": "2022-04-01",
              "name": "[guid(subscription().id, 'gv-data-factory-reader', parameters('principalId'))]",
              "properties": {
                "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', guid(subscription().id, 'gv-data-factory-reader'))]",
                "principalId": "[parameters('principalId')]",
                "principalType": "ServicePrincipal"
              },
              "dependsOn": [
                "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', guid(subscription().id, 'gv-data-factory-reader'))]"
              ]
            }
          ],
          "outputs": {
            "roleDefinitionId": {
              "type": "string",
              "value": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', guid(subscription().id, 'gv-data-factory-reader'))]"
            },
            "roleAssignmentId": {
              "type": "string",
              "value": "[subscriptionResourceId('Microsoft.Authorization/roleAssignments', guid(subscription().id, 'gv-data-factory-reader', parameters('principalId')))]"
            }
          }
        }
      },
      "dependsOn": [
        "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack')]"
      ]
    },
    {
      "condition": "[parameters('grantSubscriptionStorageRead')]",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2025-04-01",
      "name": "subscriptionStorageRead",
      "location": "[deployment().location]",
      "properties": {
        "expressionEvaluationOptions": {
          "scope": "inner"
        },
        "mode": "Incremental",
        "parameters": {
          "principalId": {
            "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.executionFactoryPrincipalId.value]"
          }
        },
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "metadata": {
            "_generator": {
              "name": "bicep",
              "version": "0.44.1.10279",
              "templateHash": "16261384737153081194"
            }
          },
          "parameters": {
            "principalId": {
              "type": "string",
              "metadata": {
                "description": "Object id (principalId) of the execution Data Factory system-assigned managed identity to grant."
              }
            }
          },
          "variables": {
            "storageBlobDataReader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]"
          },
          "resources": [
            {
              "type": "Microsoft.Authorization/roleAssignments",
              "apiVersion": "2022-04-01",
              "name": "[guid(subscription().id, 'gv-execution-storage-read', parameters('principalId'))]",
              "properties": {
                "roleDefinitionId": "[variables('storageBlobDataReader')]",
                "principalId": "[parameters('principalId')]",
                "principalType": "ServicePrincipal"
              }
            }
          ],
          "outputs": {
            "roleAssignmentId": {
              "type": "string",
              "value": "[subscriptionResourceId('Microsoft.Authorization/roleAssignments', guid(subscription().id, 'gv-execution-storage-read', parameters('principalId')))]"
            },
            "roleDefinitionId": {
              "type": "string",
              "value": "[variables('storageBlobDataReader')]"
            }
          }
        }
      },
      "dependsOn": [
        "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack')]"
      ]
    }
  ],
  "outputs": {
    "installerContractVersion": {
      "type": "int",
      "metadata": {
        "description": "Publisher release-validation contract version for this exact installer resource shape."
      },
      "value": "[variables('installerContractVersion')]"
    },
    "apiUrl": {
      "type": "string",
      "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.apiUrl.value]"
    },
    "setupUrl": {
      "type": "string",
      "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.setupUrl.value]"
    },
    "managedIdentityPrincipalId": {
      "type": "string",
      "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.managedIdentityPrincipalId.value]"
    },
    "managedIdentityClientId": {
      "type": "string",
      "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.managedIdentityClientId.value]"
    },
    "postgresServerName": {
      "type": "string",
      "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.postgresServerName.value]"
    },
    "executionFactoryName": {
      "type": "string",
      "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.executionFactoryName.value]"
    },
    "resultsStorageAccountName": {
      "type": "string",
      "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.resultsStorageAccountName.value]"
    },
    "storageAccessTemplateSpecVersionId": {
      "type": "string",
      "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.storageAccessTemplateSpecVersionId.value]"
    },
    "cleanupManifest": {
      "type": "object",
      "value": {
        "schemaVersion": 2,
        "resourceGroupId": "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]",
        "subscriptionDeploymentIds": "[concat(createArray(format('{0}/providers/Microsoft.Resources/deployments/{1}', subscription().id, deployment().name)), if(parameters('grantSubscriptionFactoryRead'), createArray(format('{0}/providers/Microsoft.Resources/deployments/subscriptionFactoryRead', subscription().id)), createArray()), if(parameters('grantSubscriptionStorageRead'), createArray(format('{0}/providers/Microsoft.Resources/deployments/subscriptionStorageRead', subscription().id)), createArray()))]",
        "subscriptionRoleAssignments": "[concat(if(parameters('grantSubscriptionFactoryRead'), createArray(createObject('id', reference(subscriptionResourceId('Microsoft.Resources/deployments', 'subscriptionFactoryRead'), '2025-04-01').outputs.roleAssignmentId.value, 'roleDefinitionId', reference(subscriptionResourceId('Microsoft.Resources/deployments', 'subscriptionFactoryRead'), '2025-04-01').outputs.roleDefinitionId.value, 'principalId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.managedIdentityPrincipalId.value, 'scope', subscription().id, 'deleteRoleDefinition', true())), createArray()), if(parameters('grantSubscriptionStorageRead'), createArray(createObject('id', reference(subscriptionResourceId('Microsoft.Resources/deployments', 'subscriptionStorageRead'), '2025-04-01').outputs.roleAssignmentId.value, 'roleDefinitionId', reference(subscriptionResourceId('Microsoft.Resources/deployments', 'subscriptionStorageRead'), '2025-04-01').outputs.roleDefinitionId.value, 'principalId', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'gvStack'), '2025-04-01').outputs.executionFactoryPrincipalId.value, 'scope', subscription().id, 'deleteRoleDefinition', false())), createArray()))]"
      }
    }
  }
}