From 10efba5d6cf45c5690a1d4ff60775ce7d69b8e51 Mon Sep 17 00:00:00 2001 From: Justin Linenkohl <83235841+jlinenkohl@users.noreply.github.com> Date: Wed, 14 Oct 2026 22:17:00 -0400 Subject: [PATCH] fix(export): do not enable a connector whose data source could not be resolved Both exporters infer the AppInsights and LogAnalytics toggles from the presence of a connector of that type, then read its resource id from properties.dataSource or extendedProperties.armResourceId. ARM does not return dataSource on read, and on an agent whose connector predates the current validation rules extendedProperties is null too, so the id comes back empty while the toggle is still set to true. The deployment then fails, every time, on a resource the clone did not need: InvalidRequestParameter: The object validation failed: Agent DataConnector 'log-analytics': DataSource is required. is invalid. Both the bash and the PowerShell clone path failed this way against the same source agent, which stops the whole deployment rather than skipping one connector. The toggle is now set only when a resource id was actually resolved, and a warning names the connector that was left out. The same root cause affects connectors carried in the connectors array rather than through a toggle, and there it cannot be fixed by inference: ARM simply does not return dataSource, so every exported Kusto, IcM or similar connector has an empty one and the deployment fails on each in turn, from inside the ARM operation and long after validation passed. clone-agent.sh now reports those connectors by name during validation and points at the --override flag it already supports, so the operator can supply the values before anything is created instead of discovering them one failed deployment at a time. --- sreagent-templates/bin/clone-agent.sh | 10 +++++++- sreagent-templates/bin/export-agent.sh | 27 ++++++++++++++++------ sreagent-templates/bin/ps/Export-Agent.ps1 | 24 +++++++++++++++---- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/sreagent-templates/bin/clone-agent.sh b/sreagent-templates/bin/clone-agent.sh index b863b265b..682c6dbcb 100755 --- a/sreagent-templates/bin/clone-agent.sh +++ b/sreagent-templates/bin/clone-agent.sh @@ -298,8 +298,15 @@ for i in $(seq 0 $((CONNECTOR_COUNT - 1))); do else _ok "Connector '${cname}' (${ctype}): ${dsrc}" fi + elif [[ -z "$dsrc" ]]; then + # ARM does not return dataSource on read -- neither does the data plane -- so an + # exported connector of a type that requires one always arrives empty. Deploying it + # fails from inside the ARM operation, one connector at a time, long after validation + # passed. Naming it here lets the operator supply it with --override first. + _warn "Connector '${cname}' (${ctype}): no dataSource; supply one before deploying, e.g." + _warn " --override connectors.${i}.properties.dataSource=" else - _ok "Connector '${cname}' (${ctype})" + _ok "Connector '${cname}' (${ctype}): ${dsrc}" fi done @@ -544,6 +551,7 @@ if [[ -n "$EXTRAS" ]]; then _warn "${MCP_COUNT} MCP connector(s) need data-plane overlay (ARM strips bearer tokens)" fi + # Knowledge & memories KNOWLEDGE_COUNT=$(jq '(.knowledge // []) | length' "$EXTRAS") KI_COUNT=$(jq '(.knowledgeItems // []) | length' "$EXTRAS") diff --git a/sreagent-templates/bin/export-agent.sh b/sreagent-templates/bin/export-agent.sh index 39a1f78ef..94e8fa3a4 100755 --- a/sreagent-templates/bin/export-agent.sh +++ b/sreagent-templates/bin/export-agent.sh @@ -1041,18 +1041,31 @@ for i in $(seq 0 $((CONNECTOR_COUNT - 1))); do ctype=$(echo "$CONNECTORS" | jq -r --argjson i "$i" '.[$i].properties.dataConnectorType') case "$ctype" in AppInsights) - # Only capture the FIRST AppInsights connector for the toggle + # Only capture the FIRST AppInsights connector for the toggle. + # ARM does not return dataSource on read, so this can come back empty. Enabling the + # connector without one makes the deployment fail with "DataSource is required", so + # the toggle is only set when a resource id was actually resolved. if [[ "$ENABLE_AI" == "false" ]]; then - ENABLE_AI=true - AI_RESOURCE_ID=$(echo "$CONNECTORS" | jq -r --argjson i "$i" '.[$i].properties.dataSource // .[$i].properties.extendedProperties.armResourceId // ""') - AI_APP_ID=$(echo "$CONNECTORS" | jq -r --argjson i "$i" '.[$i].properties.extendedProperties.appId // ""') + _ai_id=$(echo "$CONNECTORS" | jq -r --argjson i "$i" '.[$i].properties.dataSource // .[$i].properties.extendedProperties.armResourceId // ""') + if [[ -n "$_ai_id" ]]; then + ENABLE_AI=true + AI_RESOURCE_ID="$_ai_id" + AI_APP_ID=$(echo "$CONNECTORS" | jq -r --argjson i "$i" '.[$i].properties.extendedProperties.appId // ""') + else + _warn "AppInsights connector has no resolvable resource id — not enabling it on the clone" + fi fi ;; LogAnalytics) - # Only capture the FIRST LogAnalytics connector for the toggle + # Only capture the FIRST LogAnalytics connector for the toggle. See the note above. if [[ "$ENABLE_LAW" == "false" ]]; then - ENABLE_LAW=true - LAW_RESOURCE_ID=$(echo "$CONNECTORS" | jq -r --argjson i "$i" '.[$i].properties.dataSource // .[$i].properties.extendedProperties.armResourceId // ""') + _law_id=$(echo "$CONNECTORS" | jq -r --argjson i "$i" '.[$i].properties.dataSource // .[$i].properties.extendedProperties.armResourceId // ""') + if [[ -n "$_law_id" ]]; then + ENABLE_LAW=true + LAW_RESOURCE_ID="$_law_id" + else + _warn "LogAnalytics connector has no resolvable workspace id — not enabling it on the clone" + fi fi ;; AzureMonitor) diff --git a/sreagent-templates/bin/ps/Export-Agent.ps1 b/sreagent-templates/bin/ps/Export-Agent.ps1 index 1aa2eb35d..cb5a4e94a 100644 --- a/sreagent-templates/bin/ps/Export-Agent.ps1 +++ b/sreagent-templates/bin/ps/Export-Agent.ps1 @@ -1206,13 +1206,27 @@ for ($i = 0; $i -lt $CONNECTOR_COUNT; $i++) { $ctype = $CONNECTORS | jq -r --argjson i $i '.[$i].properties.dataConnectorType' switch ($ctype) { 'AppInsights' { - $ENABLE_AI = $true - $AI_RESOURCE_ID = $CONNECTORS | Invoke-Jq -Raw -Filter '.[$i].properties.dataSource // .[$i].properties.extendedProperties.armResourceId // ""' -ExtraArgs @('--argjson', 'i', "$i") - $AI_APP_ID = $CONNECTORS | Invoke-Jq -Raw -Filter '.[$i].properties.extendedProperties.appId // ""' -ExtraArgs @('--argjson', 'i', "$i") + # ARM does not return dataSource on read, so this can come back empty. Enabling + # the connector without one makes the deployment fail with "DataSource is + # required", so the toggle is only set when a resource id was actually resolved. + $aiId = $CONNECTORS | Invoke-Jq -Raw -Filter '.[$i].properties.dataSource // .[$i].properties.extendedProperties.armResourceId // ""' -ExtraArgs @('--argjson', 'i', "$i") + if (-not [string]::IsNullOrWhiteSpace($aiId)) { + $ENABLE_AI = $true + $AI_RESOURCE_ID = $aiId + $AI_APP_ID = $CONNECTORS | Invoke-Jq -Raw -Filter '.[$i].properties.extendedProperties.appId // ""' -ExtraArgs @('--argjson', 'i', "$i") + } else { + _warn 'AppInsights connector has no resolvable resource id - not enabling it on the clone' + } } 'LogAnalytics' { - $ENABLE_LAW = $true - $LAW_RESOURCE_ID = $CONNECTORS | Invoke-Jq -Raw -Filter '.[$i].properties.dataSource // .[$i].properties.extendedProperties.armResourceId // ""' -ExtraArgs @('--argjson', 'i', "$i") + # See the note above. + $lawId = $CONNECTORS | Invoke-Jq -Raw -Filter '.[$i].properties.dataSource // .[$i].properties.extendedProperties.armResourceId // ""' -ExtraArgs @('--argjson', 'i', "$i") + if (-not [string]::IsNullOrWhiteSpace($lawId)) { + $ENABLE_LAW = $true + $LAW_RESOURCE_ID = $lawId + } else { + _warn 'LogAnalytics connector has no resolvable workspace id - not enabling it on the clone' + } } 'AzureMonitor' { $ENABLE_AZMON = $true