Skip to content

Commit 2b4e7c4

Browse files
changes to standards
1 parent bdd3794 commit 2b4e7c4

File tree

4 files changed

+295
-21
lines changed

4 files changed

+295
-21
lines changed

Modules/CIPPCore/Public/Standards/Convert-SingleStandardObject.ps1

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
function Convert-SingleStandardObject {
2-
32
param(
43
[Parameter(Mandatory = $true)]
54
$Obj
65
)
6+
7+
# Ensure we have a PSCustomObject we can modify
78
$Obj = [pscustomobject]$Obj
9+
10+
# Extract action arrays
811
$AllActionValues = @()
912
if ($Obj.PSObject.Properties.Name -contains 'combinedActions') {
1013
$AllActionValues = $Obj.combinedActions
11-
$null = $Obj.PSObject.Properties.Remove('combinedActions')
14+
$Obj.PSObject.Properties.Remove('combinedActions') | Out-Null
1215
} elseif ($Obj.PSObject.Properties.Name -contains 'action') {
1316
if ($Obj.action -and $Obj.action.value) {
1417
$AllActionValues = $Obj.action.value
1518
}
16-
$null = $Obj.PSObject.Properties.Remove('action')
19+
$Obj.PSObject.Properties.Remove('action') | Out-Null
1720
}
1821

19-
# Convert actions to booleans
22+
# Convert to booleans
2023
$Obj | Add-Member -NotePropertyName 'remediate' -NotePropertyValue ($AllActionValues -contains 'Remediate') -Force
2124
$Obj | Add-Member -NotePropertyName 'alert' -NotePropertyValue ($AllActionValues -contains 'warn') -Force
2225
$Obj | Add-Member -NotePropertyName 'report' -NotePropertyValue ($AllActionValues -contains 'Report') -Force
@@ -31,7 +34,7 @@ function Convert-SingleStandardObject {
3134
}
3235
}
3336
}
34-
$null = $Obj.PSObject.Properties.Remove('standards')
37+
$Obj.PSObject.Properties.Remove('standards') | Out-Null
3538
}
3639

3740
return $Obj
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
function ConvertTo-CippStandardObject {
2+
23
param(
34
[Parameter(Mandatory = $true)]
45
$StandardObject
56
)
7+
# If it's an array of items, process each item
68
if ($StandardObject -is [System.Collections.IEnumerable] -and -not ($StandardObject -is [string])) {
79
$ProcessedItems = New-Object System.Collections.ArrayList
810
foreach ($Item in $StandardObject) {
911
$ProcessedItems.Add((Convert-SingleStandardObject $Item)) | Out-Null
1012
}
11-
return [System.Collections.ArrayList]$ProcessedItems
13+
return $ProcessedItems
1214
} else {
15+
# Single object
1316
return Convert-SingleStandardObject $StandardObject
1417
}
1518
}
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
function Get-CIPPStandards {
2+
param(
3+
[Parameter(Mandatory = $false)]
4+
[string]$TenantFilter = 'allTenants',
5+
6+
[Parameter(Mandatory = $false)]
7+
[switch]$ListAllTenants,
8+
9+
[Parameter(Mandatory = $false)]
10+
$TemplateId = '*',
11+
12+
[Parameter(Mandatory = $false)]
13+
$runManually = $false
14+
)
15+
16+
# 1. Get all JSON-based templates from the "templates" table
17+
$Table = Get-CippTable -tablename 'templates'
18+
$Filter = "PartitionKey eq 'StandardsTemplateV2'"
19+
$Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter | Sort-Object TimeStamp).JSON |
20+
ForEach-Object {
21+
try {
22+
# Fix old "Action" => "action"
23+
$JSON = $_ -replace '"Action":', '"action":'
24+
ConvertFrom-Json -InputObject $JSON -ErrorAction SilentlyContinue
25+
} catch {}
26+
} |
27+
Where-Object {
28+
$_.GUID -like $TemplateId -and $_.runManually -eq $runManually
29+
}
30+
31+
# 2. Get tenant list, filter if needed
32+
$AllTenantsList = Get-Tenants
33+
if ($TenantFilter -ne 'allTenants') {
34+
$AllTenantsList = $AllTenantsList | Where-Object {
35+
$_.defaultDomainName -eq $TenantFilter -or $_.customerId -eq $TenantFilter
36+
}
37+
}
38+
39+
# 3. If -ListAllTenants, build standards for "AllTenants" only
40+
if ($ListAllTenants.IsPresent) {
41+
$AllTenantsTemplates = $Templates | Where-Object {
42+
$_.tenantFilter.value -contains 'AllTenants'
43+
}
44+
45+
$ComputedStandards = [ordered]@{}
46+
47+
foreach ($Template in $AllTenantsTemplates) {
48+
$Standards = $Template.standards
49+
50+
foreach ($StandardName in $Standards.PSObject.Properties.Name) {
51+
$Value = $Standards.$StandardName
52+
$IsArray = $Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])
53+
54+
if ($IsArray) {
55+
# e.g. IntuneTemplate with 2 items
56+
foreach ($Item in $Value) {
57+
$CurrentStandard = $Item.PSObject.Copy()
58+
$CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force
59+
60+
$Actions = $CurrentStandard.action.value
61+
if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') {
62+
if (-not $ComputedStandards.Contains($StandardName)) {
63+
$ComputedStandards[$StandardName] = $CurrentStandard
64+
} else {
65+
$MergedStandard = Merge-CippStandards -Existing $ComputedStandards[$StandardName] -New $CurrentStandard -StandardName $StandardName
66+
$ComputedStandards[$StandardName] = $MergedStandard
67+
}
68+
}
69+
}
70+
} else {
71+
# single object
72+
$CurrentStandard = $Value.PSObject.Copy()
73+
$CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force
74+
75+
$Actions = $CurrentStandard.action.value
76+
if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') {
77+
if (-not $ComputedStandards.Contains($StandardName)) {
78+
$ComputedStandards[$StandardName] = $CurrentStandard
79+
} else {
80+
$MergedStandard = Merge-CippStandards -Existing $ComputedStandards[$StandardName] -New $CurrentStandard -StandardName $StandardName
81+
$ComputedStandards[$StandardName] = $MergedStandard
82+
}
83+
}
84+
}
85+
}
86+
}
87+
88+
# Output result for 'AllTenants'
89+
foreach ($Standard in $ComputedStandards.Keys) {
90+
$TempCopy = $ComputedStandards[$Standard].PSObject.Copy()
91+
92+
# Remove 'TemplateId' from final output
93+
if ($TempCopy -is [System.Collections.IEnumerable] -and -not ($TempCopy -is [string])) {
94+
foreach ($subItem in $TempCopy) {
95+
$subItem.PSObject.Properties.Remove('TemplateId') | Out-Null
96+
}
97+
} else {
98+
$TempCopy.PSObject.Properties.Remove('TemplateId') | Out-Null
99+
}
100+
101+
$Normalized = ConvertTo-CippStandardObject $TempCopy
102+
103+
[pscustomobject]@{
104+
Tenant = 'AllTenants'
105+
Standard = $Standard
106+
Settings = $Normalized
107+
TemplateId = if ($ComputedStandards[$Standard] -is [System.Collections.IEnumerable] -and -not ($ComputedStandards[$Standard] -is [string])) {
108+
# If multiple items from multiple templates, you may have multiple TemplateIds
109+
$ComputedStandards[$Standard] | ForEach-Object { $_.TemplateId }
110+
} else {
111+
$ComputedStandards[$Standard].TemplateId
112+
}
113+
}
114+
}
115+
} else {
116+
# 4. For each tenant, figure out which templates apply, merge them, and output.
117+
foreach ($Tenant in $AllTenantsList) {
118+
$TenantName = $Tenant.defaultDomainName
119+
120+
# Determine which templates apply to this tenant
121+
$ApplicableTemplates = $Templates | ForEach-Object {
122+
$template = $_
123+
$tenantFilterValues = $template.tenantFilter | ForEach-Object { $_.value }
124+
$excludedTenantValues = @()
125+
126+
if ($template.excludedTenants) {
127+
if ($template.excludedTenants -is [System.Collections.IEnumerable] -and -not ($template.excludedTenants -is [string])) {
128+
$excludedTenantValues = $template.excludedTenants | ForEach-Object { $_.value }
129+
} else {
130+
$excludedTenantValues = @($template.excludedTenants)
131+
}
132+
}
133+
134+
$AllTenantsApplicable = $false
135+
$TenantSpecificApplicable = $false
136+
137+
if ($tenantFilterValues -contains 'AllTenants' -and -not ($excludedTenantValues -contains $TenantName)) {
138+
$AllTenantsApplicable = $true
139+
}
140+
if ($tenantFilterValues -contains $TenantName) {
141+
$TenantSpecificApplicable = $true
142+
}
143+
144+
if ($AllTenantsApplicable -or $TenantSpecificApplicable) {
145+
$template
146+
}
147+
}
148+
149+
# Separate them into AllTenant vs. TenantSpecific sets
150+
$AllTenantTemplatesSet = $ApplicableTemplates | Where-Object {
151+
$_.tenantFilter.value -contains 'AllTenants'
152+
}
153+
$TenantSpecificTemplatesSet = $ApplicableTemplates | Where-Object {
154+
$_.tenantFilter.value -notcontains 'AllTenants'
155+
}
156+
157+
$ComputedStandards = [ordered]@{}
158+
159+
# 4a. Merge the AllTenantTemplatesSet
160+
foreach ($Template in $AllTenantTemplatesSet) {
161+
$Standards = $Template.standards
162+
163+
foreach ($StandardName in $Standards.PSObject.Properties.Name) {
164+
$Value = $Standards.$StandardName
165+
$IsArray = $Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])
166+
167+
if ($IsArray) {
168+
foreach ($Item in $Value) {
169+
$CurrentStandard = $Item.PSObject.Copy()
170+
$CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force
171+
172+
$Actions = $CurrentStandard.action.value
173+
if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') {
174+
if (-not $ComputedStandards.Contains($StandardName)) {
175+
$ComputedStandards[$StandardName] = $CurrentStandard
176+
} else {
177+
$MergedStandard = Merge-CippStandards -Existing $ComputedStandards[$StandardName] -New $CurrentStandard -StandardName $StandardName
178+
$ComputedStandards[$StandardName] = $MergedStandard
179+
}
180+
}
181+
}
182+
} else {
183+
$CurrentStandard = $Value.PSObject.Copy()
184+
$CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force
185+
186+
$Actions = $CurrentStandard.action.value
187+
if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') {
188+
if (-not $ComputedStandards.Contains($StandardName)) {
189+
$ComputedStandards[$StandardName] = $CurrentStandard
190+
} else {
191+
$MergedStandard = Merge-CippStandards -Existing $ComputedStandards[$StandardName] -New $CurrentStandard -StandardName $StandardName
192+
$ComputedStandards[$StandardName] = $MergedStandard
193+
}
194+
}
195+
}
196+
}
197+
}
198+
199+
# 4b. Merge the TenantSpecificTemplatesSet
200+
foreach ($Template in $TenantSpecificTemplatesSet) {
201+
$Standards = $Template.standards
202+
203+
foreach ($StandardName in $Standards.PSObject.Properties.Name) {
204+
$Value = $Standards.$StandardName
205+
$IsArray = $Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])
206+
207+
if ($IsArray) {
208+
foreach ($Item in $Value) {
209+
$CurrentStandard = $Item.PSObject.Copy()
210+
$CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force
211+
212+
# Filter actions only 'Remediate','warn','Report'
213+
$Actions = $CurrentStandard.action.value | Where-Object { $_ -in 'Remediate', 'warn', 'Report' }
214+
if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') {
215+
if (-not $ComputedStandards.Contains($StandardName)) {
216+
$ComputedStandards[$StandardName] = $CurrentStandard
217+
} else {
218+
$MergedStandard = Merge-CippStandards -Existing $ComputedStandards[$StandardName] -New $CurrentStandard -StandardName $StandardName
219+
$ComputedStandards[$StandardName] = $MergedStandard
220+
}
221+
}
222+
}
223+
} else {
224+
$CurrentStandard = $Value.PSObject.Copy()
225+
$CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force
226+
227+
$Actions = $CurrentStandard.action.value | Where-Object { $_ -in 'Remediate', 'warn', 'Report' }
228+
if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') {
229+
if (-not $ComputedStandards.Contains($StandardName)) {
230+
$ComputedStandards[$StandardName] = $CurrentStandard
231+
} else {
232+
$MergedStandard = Merge-CippStandards -Existing $ComputedStandards[$StandardName] -New $CurrentStandard -StandardName $StandardName
233+
$ComputedStandards[$StandardName] = $MergedStandard
234+
}
235+
}
236+
}
237+
}
238+
}
239+
240+
# 4c. Output each final standard for this tenant
241+
foreach ($Standard in $ComputedStandards.Keys) {
242+
$TempCopy = $ComputedStandards[$Standard].PSObject.Copy()
243+
# Remove local 'TemplateId' from final object(s)
244+
if ($TempCopy -is [System.Collections.IEnumerable] -and -not ($TempCopy -is [string])) {
245+
foreach ($subItem in $TempCopy) {
246+
$subItem.PSObject.Properties.Remove('TemplateId') | Out-Null
247+
}
248+
} else {
249+
$TempCopy.PSObject.Properties.Remove('TemplateId') | Out-Null
250+
}
251+
252+
$Normalized = ConvertTo-CippStandardObject $TempCopy
253+
254+
[pscustomobject]@{
255+
Tenant = $TenantName
256+
Standard = $Standard
257+
Settings = $Normalized
258+
TemplateId = $ComputedStandards[$Standard].TemplateId
259+
}
260+
}
261+
}
262+
}
263+
}
264+
Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
function Merge-CippStandards {
22
param(
3-
[Parameter(Mandatory = $true)]
4-
[object]$Existing,
5-
[Parameter(Mandatory = $true)]
6-
[object]$New
3+
[Parameter(Mandatory = $true)][object]$Existing,
4+
[Parameter(Mandatory = $true)][object]$New,
5+
[Parameter(Mandatory = $true)][string]$StandardName
76
)
87

9-
if (-not $Existing) {
10-
return $New
11-
}
12-
$ExistingIsArray = ($Existing -is [System.Collections.IEnumerable] -and -not ($Existing -is [string]))
13-
$NewIsArray = ($New -is [System.Collections.IEnumerable] -and -not ($New -is [string]))
8+
# If $Existing or $New is $null/empty, just return the other.
9+
if (-not $Existing) { return $New }
10+
if (-not $New) { return $Existing }
1411

15-
if (-not $ExistingIsArray) {
16-
$Existing = @($Existing)
17-
}
18-
if (-not $NewIsArray) {
19-
$New = @($New)
12+
# If the standard name ends with 'Template', we treat them as arrays to merge.
13+
if ($StandardName -like '*Template') {
14+
$ExistingIsArray = $Existing -is [System.Collections.IEnumerable] -and -not ($Existing -is [string])
15+
$NewIsArray = $New -is [System.Collections.IEnumerable] -and -not ($New -is [string])
16+
17+
# Make sure both are arrays
18+
if (-not $ExistingIsArray) { $Existing = @($Existing) }
19+
if (-not $NewIsArray) { $New = @($New) }
20+
21+
return $Existing + $New
22+
} else {
23+
# Single‐value standard: override the old with the new
24+
return $New
2025
}
21-
return $Existing + $New
2226
}

0 commit comments

Comments
 (0)