Skip to content

Commit f690b6d

Browse files
committed
More reliable RG name resolution
1 parent eac806c commit f690b6d

9 files changed

Lines changed: 126 additions & 70 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
function Get-CIPPManagedIdentityResourceId {
2+
<#
3+
.SYNOPSIS
4+
Get the Azure resource ID that the Function App's managed identity belongs to.
5+
.DESCRIPTION
6+
Reads the 'xms_mirid' claim from a managed identity access token. For a system-assigned
7+
identity (which CIPP uses), this claim is the ARM resource ID of the host resource itself
8+
- i.e. the Function App site, including its resource group:
9+
10+
/subscriptions/{sub}/resourcegroups/{rg}/providers/Microsoft.Web/sites/{site}
11+
12+
This is the most reliable in-process source for the site's resource group because it is
13+
present in every managed identity token, requires no extra ARM/Graph call, and - unlike
14+
parsing WEBSITE_OWNER_NAME - always names the site's RG rather than the App Service Plan's
15+
webspace RG.
16+
17+
Note: for a user-assigned identity, xms_mirid points at the userAssignedIdentities resource
18+
instead, which may live in a different RG. Callers that need the site's RG should validate
19+
the returned ID against the expected site (see Get-CIPPFunctionAppResourceGroup).
20+
.PARAMETER ResourceUrl
21+
The Azure resource URL to request the token for. Defaults to Azure Resource Manager.
22+
.EXAMPLE
23+
Get-CIPPManagedIdentityResourceId
24+
Returns e.g. /subscriptions/.../resourcegroups/CIPP-myinstance/providers/Microsoft.Web/sites/cippabcde
25+
#>
26+
[CmdletBinding()]
27+
param(
28+
[Parameter(Mandatory = $false)]
29+
[string]$ResourceUrl = 'https://management.azure.com/'
30+
)
31+
32+
$Token = Get-CIPPAzIdentityToken -ResourceUrl $ResourceUrl
33+
if (-not $Token) {
34+
throw 'Could not acquire a managed identity token to read the xms_mirid claim.'
35+
}
36+
37+
# JWT payload is the second dot-delimited segment, base64url-encoded.
38+
$Parts = $Token.Split('.')
39+
if ($Parts.Count -lt 2) {
40+
throw 'Managed identity token is not a well-formed JWT.'
41+
}
42+
43+
$Payload = $Parts[1].Replace('-', '+').Replace('_', '/')
44+
switch ($Payload.Length % 4) {
45+
2 { $Payload += '==' }
46+
3 { $Payload += '=' }
47+
}
48+
49+
$Claims = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Payload)) | ConvertFrom-Json
50+
return $Claims.xms_mirid
51+
}

Modules/CIPPCore/Public/Entrypoints/Timer Functions/Start-ContainerUpdateCheck.ps1

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,11 @@ function Start-ContainerUpdateCheck {
8686
# Resolve ARM site details
8787
$Subscription = Get-CIPPAzFunctionAppSubId
8888
$SiteName = $env:WEBSITE_SITE_NAME
89-
$RGName = $env:WEBSITE_RESOURCE_GROUP
90-
if (-not $RGName) {
91-
$Owner = $env:WEBSITE_OWNER_NAME
92-
if ($Owner -match '^(?<SubscriptionId>[^+]+)\+(?<RGName>[^-]+(?:-[^-]+)*?)(?:-[^-]+webspace(?:-Linux)?)?$') {
93-
$RGName = $Matches.RGName
94-
}
89+
try {
90+
$RGName = Get-CIPPFunctionAppResourceGroup -SiteName $SiteName
91+
} catch {
92+
Write-Information "Could not determine resource group: $($_.Exception.Message)"
93+
$RGName = $null
9594
}
9695

9796
$ImageTag = $env:IMAGE_TAG ?? 'unknown'

Modules/CIPPCore/Public/Functions/Request-CIPPRestart.ps1

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,7 @@ function Request-CIPPRestart {
1919
try {
2020
$Subscription = Get-CIPPAzFunctionAppSubId
2121
$SiteName = $env:WEBSITE_SITE_NAME
22-
$RGName = $env:WEBSITE_RESOURCE_GROUP
23-
if (-not $RGName) {
24-
$Owner = $env:WEBSITE_OWNER_NAME
25-
if ($Owner -match '^(?<SubscriptionId>[^+]+)\+(?<RGName>[^-]+(?:-[^-]+)*?)(?:-[^-]+webspace(?:-Linux)?)?$') {
26-
$RGName = $Matches.RGName
27-
}
28-
}
22+
$RGName = Get-CIPPFunctionAppResourceGroup -SiteName $SiteName
2923
if (-not ($Subscription -and $RGName -and $SiteName)) {
3024
throw 'Azure App Service details could not be determined from environment'
3125
}

Modules/CIPPCore/Public/Get-ApplicationInsightsQuery.ps1

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ function Get-ApplicationInsightsQuery {
1010
}
1111

1212
$SubscriptionId = Get-CIPPAzFunctionAppSubId
13-
if ($env:WEBSITE_SKU -ne 'FlexConsumption' -and $Owner -match '^(?<SubscriptionId>[^+]+)\+(?<RGName>[^-]+(?:-[^-]+)*?)(?:-[^-]+webspace(?:-Linux)?)?$') {
14-
$RGName = $Matches.RGName
15-
} else {
16-
$RGName = $env:WEBSITE_RESOURCE_GROUP
17-
}
13+
$RGName = Get-CIPPFunctionAppResourceGroup
1814
$AppInsightsName = $env:WEBSITE_SITE_NAME
1915

2016
$Body = @{
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
function Get-CIPPFunctionAppResourceGroup {
2+
<#
3+
.SYNOPSIS
4+
Resolve the resource group that the CIPP Function App site lives in.
5+
.DESCRIPTION
6+
Returns the resource group of the running Function App, using authoritative sources only:
7+
8+
1. WEBSITE_RESOURCE_GROUP - platform-injected, the site's actual RG. Free, no decode.
9+
2. xms_mirid claim from the managed identity token - the site's own ARM resource ID,
10+
present even when WEBSITE_RESOURCE_GROUP is empty, needs no extra call or permission.
11+
12+
The legacy approach of parsing WEBSITE_OWNER_NAME is intentionally NOT used: that string
13+
encodes the App Service Plan's webspace RG, which is frequently different from the site's RG
14+
(e.g. it returns 'DefaultResourceGroup-WEU' or '<rg>-m01' for sites whose plan was created
15+
in an auto-generated/other resource group). Writing auth settings, restarting, or querying
16+
the wrong RG is worse than failing, so this throws when no reliable source is available.
17+
.PARAMETER SiteName
18+
The Function App site name to resolve. Defaults to WEBSITE_SITE_NAME.
19+
.EXAMPLE
20+
Get-CIPPFunctionAppResourceGroup
21+
Returns e.g. 'CIPP-myinstance'
22+
#>
23+
[CmdletBinding()]
24+
param(
25+
[Parameter(Mandatory = $false)]
26+
[string]$SiteName = $env:WEBSITE_SITE_NAME
27+
)
28+
29+
# 1. Platform-injected site resource group - authoritative, zero cost.
30+
if ($env:WEBSITE_RESOURCE_GROUP) {
31+
return $env:WEBSITE_RESOURCE_GROUP
32+
}
33+
34+
# 2. The managed identity's own token names this site's resource ID (incl. RG). Only trust it
35+
# when it actually points at this Microsoft.Web/sites resource, so a user-assigned identity
36+
# (whose xms_mirid is a userAssignedIdentities resource) falls through rather than returning
37+
# the identity's RG.
38+
try {
39+
$MiRid = Get-CIPPManagedIdentityResourceId
40+
if ($SiteName -and $MiRid -match "(?i)/resourcegroups/(?<RG>[^/]+)/providers/Microsoft\.Web/sites/$([regex]::Escape($SiteName))(/|$)") {
41+
return $Matches.RG
42+
}
43+
Write-Information "xms_mirid did not match site '$SiteName': $MiRid"
44+
} catch {
45+
Write-Warning "Could not read resource group from managed identity token: $($_.Exception.Message)"
46+
}
47+
48+
# 3. No reliable source - fail loudly rather than guess from WEBSITE_OWNER_NAME.
49+
throw "Could not determine the function app resource group for site '$SiteName'. WEBSITE_RESOURCE_GROUP is empty and the managed identity resource ID was unavailable."
50+
}

Modules/CIPPCore/Public/GraphHelper/Set-CIPPOffloadFunctionTriggers.ps1

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,7 @@ function Set-CIPPOffloadFunctionTriggers {
3838
}
3939

4040
# Determine resource group
41-
if ($env:WEBSITE_RESOURCE_GROUP) {
42-
$ResourceGroupName = $env:WEBSITE_RESOURCE_GROUP
43-
} else {
44-
$Owner = $env:WEBSITE_OWNER_NAME
45-
if ($env:WEBSITE_SKU -ne 'FlexConsumption' -and $Owner -match '^(?<SubscriptionId>[^+]+)\+(?<RGName>[^-]+(?:-[^-]+)*?)(?:-[^-]+webspace(?:-Linux)?)?$') {
46-
$ResourceGroupName = $Matches.RGName
47-
} else {
48-
throw 'Could not determine resource group. Please provide ResourceGroupName parameter.'
49-
}
50-
}
41+
$ResourceGroupName = Get-CIPPFunctionAppResourceGroup -SiteName $FunctionAppName
5142

5243
# Define the triggers to disable when offloading is enabled
5344
$TargetedTriggers = @(

Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecApiClient.ps1

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -193,19 +193,9 @@ function Invoke-ExecApiClient {
193193
}
194194
}
195195
'GetAzureConfiguration' {
196-
if ($env:WEBSITE_RESOURCE_GROUP) {
197-
$RGName = $env:WEBSITE_RESOURCE_GROUP
198-
} else {
199-
$Owner = $env:WEBSITE_OWNER_NAME
200-
if ($env:WEBSITE_SKU -ne 'FlexConsumption' -and $Owner -match '^(?<SubscriptionId>[^+]+)\+(?<RGName>[^-]+(?:-[^-]+)*?)(?:-[^-]+webspace(?:-Linux)?)?$') {
201-
$RGName = $Matches.RGName
202-
} else {
203-
Write-Information "Could not determine resource group from environment variables. Owner: $Owner"
204-
$RGName = $null
205-
}
206-
}
207196
$FunctionAppName = $env:WEBSITE_SITE_NAME
208197
try {
198+
$RGName = Get-CIPPFunctionAppResourceGroup -SiteName $FunctionAppName
209199
$APIClients = Get-CippApiAuth -RGName $RGName -FunctionAppName $FunctionAppName
210200
$Results = $ApiClients
211201
} catch {
@@ -220,24 +210,14 @@ function Invoke-ExecApiClient {
220210
}
221211
'SaveToAzure' {
222212
$TenantId = $env:TenantID
223-
if ($env:WEBSITE_RESOURCE_GROUP) {
224-
$RGName = $env:WEBSITE_RESOURCE_GROUP
225-
} else {
226-
$Owner = $env:WEBSITE_OWNER_NAME
227-
if ($env:WEBSITE_SKU -ne 'FlexConsumption' -and $Owner -match '^(?<SubscriptionId>[^+]+)\+(?<RGName>[^-]+(?:-[^-]+)*?)(?:-[^-]+webspace(?:-Linux)?)?$') {
228-
$RGName = $Matches.RGName
229-
} else {
230-
Write-Information "Could not determine resource group from environment variables. Owner: $Owner"
231-
$RGName = $null
232-
}
233-
}
234213
$FunctionAppName = $env:WEBSITE_SITE_NAME
235214
$AllClients = Get-CIPPAzDataTableEntity @Table -Filter 'Enabled eq true' | Where-Object { ![string]::IsNullOrEmpty($_.RowKey) }
236215
$ClientIds = $AllClients.RowKey
237216
# MCPAllowed can round-trip from table storage as a bool or a string; compare on string form.
238217
$McpClientIds = @($AllClients | Where-Object { "$($_.MCPAllowed)" -eq 'True' } | ForEach-Object { $_.RowKey })
239218
Write-Information "[ExecApiClient] MCP clients resolved for audiences/scope: $($McpClientIds -join ', ')"
240219
try {
220+
$RGName = Get-CIPPFunctionAppResourceGroup -SiteName $FunctionAppName
241221
Set-CippApiAuth -RGName $RGName -FunctionAppName $FunctionAppName -TenantId $TenantId -ClientIds $ClientIds -McpClientIds $McpClientIds
242222

243223
# Advertise the MCP resource scope via App Service PRM so the Claude connector requests

Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecBackendURLs.ps1

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,11 @@ function Invoke-ExecBackendURLs {
1313
# Write to the Azure Functions log stream.
1414
Write-Host 'PowerShell HTTP trigger function processed a request.'
1515

16-
if ($env:WEBSITE_RESOURCE_GROUP) {
17-
$RGName = $env:WEBSITE_RESOURCE_GROUP
18-
} else {
19-
$Owner = $env:WEBSITE_OWNER_NAME
20-
if ($env:WEBSITE_SKU -ne 'FlexConsumption' -and $Owner -match '^(?<SubscriptionId>[^+]+)\+(?<RGName>[^-]+(?:-[^-]+)*?)(?:-[^-]+webspace(?:-Linux)?)?$') {
21-
$RGName = $Matches.RGName
22-
} else {
23-
Write-Information "Could not determine resource group from environment variables. Owner: $Owner"
24-
$RGName = $null
25-
}
16+
try {
17+
$RGName = Get-CIPPFunctionAppResourceGroup
18+
} catch {
19+
Write-Information "Could not determine resource group: $($_.Exception.Message)"
20+
$RGName = $null
2621
}
2722

2823
$results = @{

Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecContainerManagement.ps1

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ function Invoke-ExecContainerManagement {
1717

1818
# Helper: resolve ARM site details
1919
function Get-ContainerSiteInfo {
20-
$info = @{
21-
Subscription = Get-CIPPAzFunctionAppSubId
22-
SiteName = $env:WEBSITE_SITE_NAME
23-
RGName = $env:WEBSITE_RESOURCE_GROUP
20+
$SiteName = $env:WEBSITE_SITE_NAME
21+
try {
22+
$RGName = Get-CIPPFunctionAppResourceGroup -SiteName $SiteName
23+
} catch {
24+
Write-Information "Could not determine resource group: $($_.Exception.Message)"
25+
$RGName = $null
2426
}
25-
if (-not $info.RGName) {
26-
$Owner = $env:WEBSITE_OWNER_NAME
27-
if ($Owner -match '^(?<SubscriptionId>[^+]+)\+(?<RGName>[^-]+(?:-[^-]+)*?)(?:-[^-]+webspace(?:-Linux)?)?$') {
28-
$info.RGName = $Matches.RGName
29-
}
27+
return @{
28+
Subscription = Get-CIPPAzFunctionAppSubId
29+
SiteName = $SiteName
30+
RGName = $RGName
3031
}
31-
return $info
3232
}
3333

3434
# Helper: query GHCR for the image at $Tag and return its digest + version label.

0 commit comments

Comments
 (0)