Skip to content

Commit 08f0d30

Browse files
committed
feat: Support granular template resolution in alignment
Add a granular mode (query param 'granular=true') to Invoke-ListTenantAlignment that flattens ComparisonDetails into one row per tenant+standard and resolves template display names. The change builds a GUID->displayName lookup from the 'templates' table (for Intune, ConditionalAccess, and Quarantine partitions), decodes hex RowKey values for QuarantineTemplate entries, and maps template types to friendly names. Existing non-granular output is preserved. Uses Get-CIPPTenantAlignment, Get-CippTable and Get-CIPPAzDataTableEntity and includes fallbacks when JSON parsing or lookup fails. fixes KelvinTegelaar/CIPP#5894
1 parent 2a9456a commit 08f0d30

1 file changed

Lines changed: 79 additions & 11 deletions

File tree

Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantAlignment.ps1

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,89 @@ function Invoke-ListTenantAlignment {
99
param($Request, $TriggerMetadata)
1010

1111
$APIName = $Request.Params.CIPPEndpoint
12+
$Granular = $Request.Query.granular -eq 'true'
1213
try {
1314
# Use the new Get-CIPPTenantAlignment function to get alignment data
1415
$AlignmentData = Get-CIPPTenantAlignment
1516

16-
# Transform the data to match the expected API response format
17-
$Results = $AlignmentData | ForEach-Object {
18-
[PSCustomObject]@{
19-
tenantFilter = $_.TenantFilter
20-
standardName = $_.StandardName
21-
standardType = $_.StandardType ? $_.StandardType : 'Classic Standard'
22-
standardId = $_.StandardId
23-
alignmentScore = $_.AlignmentScore
24-
LicenseMissingPercentage = $_.LicenseMissingPercentage
25-
combinedAlignmentScore = $_.CombinedScore
26-
latestDataCollection = $_.LatestDataCollection
17+
# Build a GUID -> displayName lookup from the templates table for all template types
18+
$TemplateLookup = @{}
19+
if ($Granular) {
20+
$TemplateTable = Get-CippTable -tablename 'templates'
21+
$TemplatePartitions = @('IntuneTemplate', 'ConditionalAccessTemplate', 'QuarantineTemplate')
22+
foreach ($Partition in $TemplatePartitions) {
23+
Get-CIPPAzDataTableEntity @TemplateTable -Filter "PartitionKey eq '$Partition'" | ForEach-Object {
24+
try {
25+
$Parsed = $_.JSON | ConvertFrom-Json -ErrorAction Stop
26+
$DisplayName = $Parsed.displayName ?? $Parsed.Displayname ?? $Parsed.DisplayName ?? $Parsed.name ?? $_.RowKey
27+
$TemplateLookup[$_.RowKey] = $DisplayName
28+
} catch {
29+
$TemplateLookup[$_.RowKey] = $_.RowKey
30+
}
31+
}
32+
}
33+
}
34+
35+
$Results = if ($Granular) {
36+
# Flatten ComparisonResults into one row per tenant+standard
37+
$AlignmentData | ForEach-Object {
38+
$Row = $_
39+
$TemplateName = $Row.StandardName
40+
$TemplateId = $Row.StandardId
41+
$StandardType = $Row.standardType ? $Row.standardType : 'Classic Standard'
42+
$Row.ComparisonDetails | ForEach-Object {
43+
$StandardId = $_.StandardName
44+
$FriendlyType = $StandardType
45+
$ResolvedName = if ($StandardId -match '^standards\.(\w+Template)\.(.+)$') {
46+
$LookupKey = if ($Matches[1] -eq 'QuarantineTemplate') {
47+
$KeyBytes = [byte[]]::new($Matches[2].Length / 2)
48+
for ($i = 0; $i -lt $KeyBytes.Length; $i++) {
49+
$KeyBytes[$i] = [Convert]::ToByte($Matches[2].Substring($i * 2, 2), 16)
50+
}
51+
[System.Text.Encoding]::UTF8.GetString($KeyBytes)
52+
} else {
53+
$Matches[2]
54+
}
55+
$PolicyName = $TemplateLookup[$LookupKey] ?? $LookupKey
56+
$FriendlyType = switch ($Matches[1]) {
57+
'IntuneTemplate' { 'Intune Template' }
58+
'ConditionalAccessTemplate' { 'Conditional Access Template' }
59+
'QuarantineTemplate' { 'Quarantine Template' }
60+
default { $Matches[1] }
61+
}
62+
"$FriendlyType - $PolicyName"
63+
} else {
64+
$StandardId
65+
}
66+
[PSCustomObject]@{
67+
tenantFilter = $Row.TenantFilter
68+
templateName = $TemplateName
69+
templateId = $TemplateId
70+
standardType = $FriendlyType
71+
standardId = $StandardId
72+
standardName = $ResolvedName
73+
complianceStatus = $_.ComplianceStatus
74+
compliant = $_.Compliant
75+
licenseAvailable = $_.LicenseAvailable
76+
currentValue = $_.CurrentValue
77+
expectedValue = $_.ExpectedValue
78+
latestDataCollection = $Row.LatestDataCollection
79+
}
80+
}
81+
}
82+
} else {
83+
# Transform the data to match the expected API response format
84+
$AlignmentData | ForEach-Object {
85+
[PSCustomObject]@{
86+
tenantFilter = $_.TenantFilter
87+
standardName = $_.StandardName
88+
standardType = $_.StandardType ? $_.StandardType : 'Classic Standard'
89+
standardId = $_.StandardId
90+
alignmentScore = $_.AlignmentScore
91+
LicenseMissingPercentage = $_.LicenseMissingPercentage
92+
combinedAlignmentScore = $_.CombinedScore
93+
latestDataCollection = $_.LatestDataCollection
94+
}
2795
}
2896
}
2997

0 commit comments

Comments
 (0)