Skip to content

Commit 415b04c

Browse files
authored
Merge pull request #615 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents b1ee80a + ca27d49 commit 415b04c

File tree

9 files changed

+167
-39
lines changed

9 files changed

+167
-39
lines changed

Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ function Add-CIPPScheduledTask {
185185
ScheduledTime = [string]$task.ScheduledTime
186186
Recurrence = [string]$Recurrence
187187
PostExecution = [string]$PostExecution
188+
Reference = [string]$task.Reference
188189
AdditionalProperties = [string]$AdditionalProperties
189190
Hidden = [bool]$Hidden
190191
Results = 'Planned'
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
function Get-CIPPAzIdentityToken {
2+
<#
3+
.SYNOPSIS
4+
Get the Azure Identity token for Managed Identity
5+
.DESCRIPTION
6+
This function retrieves the Azure Identity token using the Managed Identity endpoint for the specified resource
7+
.PARAMETER ResourceUrl
8+
The Azure resource URL to get a token for. Defaults to 'https://management.azure.com/' for Azure Resource Manager.
9+
10+
Common resources:
11+
- https://management.azure.com/ (Azure Resource Manager - default)
12+
- https://vault.azure.net (Azure Key Vault)
13+
- https://api.loganalytics.io (Log Analytics / Application Insights)
14+
- https://storage.azure.com/ (Azure Storage)
15+
.EXAMPLE
16+
Get-CIPPAzIdentityToken
17+
Gets a token for Azure Resource Manager
18+
.EXAMPLE
19+
Get-CIPPAzIdentityToken -ResourceUrl 'https://vault.azure.net'
20+
Gets a token for Azure Key Vault
21+
.EXAMPLE
22+
Get-CIPPAzIdentityToken -ResourceUrl 'https://api.loganalytics.io'
23+
Gets a token for Log Analytics API
24+
#>
25+
[CmdletBinding()]
26+
param(
27+
[Parameter(Mandatory = $false)]
28+
[string]$ResourceUrl = 'https://management.azure.com/'
29+
)
30+
31+
$Endpoint = $env:IDENTITY_ENDPOINT
32+
$Secret = $env:IDENTITY_HEADER
33+
34+
if (-not $Endpoint -or -not $Secret) {
35+
throw 'Managed Identity environment variables (IDENTITY_ENDPOINT/IDENTITY_HEADER) not found. Is Managed Identity enabled on the Function App?'
36+
}
37+
38+
$EncodedResource = [System.Uri]::EscapeDataString($ResourceUrl)
39+
$TokenUri = "$($Endpoint)?resource=$EncodedResource&api-version=2019-08-01"
40+
$Headers = @{
41+
'X-IDENTITY-HEADER' = $Secret
42+
}
43+
44+
$TokenResponse = Invoke-RestMethod -Method Get -Headers $Headers -Uri $TokenUri
45+
return $TokenResponse.access_token
46+
}

Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ function Push-ExecScheduledCommand {
280280
$HTML += "<div style='background-color: transparent; border-left: 4px solid #007bff; padding: 15px; margin: 15px 0;'><h4 style='margin-top: 0; color: #007bff;'>Alert Information</h4><p style='margin-bottom: 0;'>$($task.AlertComment)</p></div>"
281281
}
282282

283-
$title = "$TaskType - $Tenant - $($task.Name)"
283+
$title = "$TaskType - $Tenant - $($task.Name)$(if ($task.Reference) { " - Reference: $($task.Reference)" })"
284284
Write-Information 'Scheduler: Sending the results to the target.'
285285
Write-Information "The content of results is: $Results"
286286
switch -wildcard ($task.PostExecution) {

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-AddUser.ps1

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ function Invoke-AddUser {
2323
value = 'New-CIPPUserTask'
2424
label = 'New-CIPPUserTask'
2525
}
26-
Parameters = [pscustomobject]@{ UserObj = $UserObj }
27-
ScheduledTime = $UserObj.Scheduled.date
28-
PostExecution = @{
26+
Parameters = [pscustomobject]@{ UserObj = $UserObj }
27+
ScheduledTime = $UserObj.Scheduled.date
28+
Reference = $UserObj.reference ?? $null
29+
PostExecution = @{
2930
Webhook = [bool]$Request.Body.PostExecution.Webhook
3031
Email = [bool]$Request.Body.PostExecution.Email
3132
PSA = [bool]$Request.Body.PostExecution.PSA

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ExecOffboardUser.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ function Invoke-ExecOffboardUser {
3535
Email = [bool]$Request.Body.PostExecution.email
3636
PSA = [bool]$Request.Body.PostExecution.psa
3737
}
38+
Reference = $Request.Body.reference
3839
}
3940
Add-CIPPScheduledTask -Task $taskObject -hidden $false -Headers $Headers
4041
} else {

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Conditional/Invoke-ExecCAExclusion.ps1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ function Invoke-ExecCAExclusion {
8484
}
8585
Parameters = [pscustomobject]$Parameters
8686
ScheduledTime = $StartDate
87+
PostExecution = $Request.Body.postExecution
88+
Reference = $Request.Body.reference
8789
}
8890

8991
Write-Information ($TaskBody | ConvertTo-Json -Depth 10)
@@ -117,6 +119,7 @@ function Invoke-ExecCAExclusion {
117119
Command = @{ value = 'Set-CIPPAuditLogUserExclusion'; label = 'Set-CIPPAuditLogUserExclusion' }
118120
Parameters = [pscustomobject]@{ Users = $AuditUsers; Action = 'Remove'; Type = 'Location' }
119121
ScheduledTime = $EndDate
122+
Reference = $Request.Body.reference
120123
}
121124
Add-CIPPScheduledTask -Task $AuditRemoveTask -hidden $true
122125
}

Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ function Get-CIPPStandards {
2323
$Table = Get-CippTable -tablename 'templates'
2424
$Filter = "PartitionKey eq 'StandardsTemplateV2'"
2525
$Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter | Sort-Object TimeStamp).JSON |
26-
ForEach-Object {
27-
try {
28-
# Fix old "Action" => "action"
29-
$JSON = $_ -replace '"Action":', '"action":' -replace '"permissionlevel":', '"permissionLevel":'
30-
ConvertFrom-Json -InputObject $JSON -ErrorAction SilentlyContinue
31-
} catch {}
32-
} |
33-
Where-Object {
34-
$_.GUID -like $TemplateId -and $_.runManually -eq $runManually
35-
}
26+
ForEach-Object {
27+
try {
28+
# Fix old "Action" => "action"
29+
$JSON = $_ -replace '"Action":', '"action":' -replace '"permissionlevel":', '"permissionLevel":'
30+
ConvertFrom-Json -InputObject $JSON -ErrorAction SilentlyContinue
31+
} catch {}
32+
} |
33+
Where-Object {
34+
$_.GUID -like $TemplateId -and $_.runManually -eq $runManually
35+
}
3636

3737
# 1.5. Expand templates that contain TemplateList-Tags into multiple standards
3838
$ExpandedTemplates = foreach ($Template in $Templates) {
@@ -243,12 +243,17 @@ function Get-CIPPStandards {
243243
}
244244
}
245245

246-
# Separate AllTenants vs TenantSpecific templates
246+
# Separate templates into three tiers: AllTenants (lowest precedence), Group (middle), Tenant-Specific (highest)
247247
$AllTenantTemplatesSet = $ApplicableTemplates | Where-Object {
248248
$_.tenantFilter.value -contains 'AllTenants'
249249
}
250+
$GroupTemplatesSet = $ApplicableTemplates | Where-Object {
251+
($_.tenantFilter.value -notcontains 'AllTenants') -and
252+
($_.tenantFilter | Where-Object { $_.type -eq 'Group' })
253+
}
250254
$TenantSpecificTemplatesSet = $ApplicableTemplates | Where-Object {
251-
$_.tenantFilter.value -notcontains 'AllTenants'
255+
($_.tenantFilter.value -notcontains 'AllTenants') -and
256+
-not ($_.tenantFilter | Where-Object { $_.type -eq 'Group' })
252257
}
253258

254259
# Build merged standards keyed by (StandardName, TemplateList.value)
@@ -323,7 +328,86 @@ function Get-CIPPStandards {
323328
}
324329
}
325330

326-
# Process TenantSpecific templates, merging with AllTenants base
331+
# Process Group templates, merging with AllTenants base
332+
foreach ($Template in $GroupTemplatesSet) {
333+
$Standards = $Template.standards
334+
335+
foreach ($StandardName in $Standards.PSObject.Properties.Name) {
336+
$Value = $Standards.$StandardName
337+
$IsArray = $Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])
338+
339+
if ($IsArray) {
340+
foreach ($Item in $Value) {
341+
$CurrentStandard = $Item.PSObject.Copy()
342+
$CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force
343+
344+
# Add Remediate if autoRemediate is true
345+
if ($CurrentStandard.autoRemediate -eq $true -and -not ($CurrentStandard.action.value -contains 'Remediate')) {
346+
$CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{
347+
label = 'Remediate'
348+
value = 'Remediate'
349+
}
350+
}
351+
352+
# Add Report if Remediate present but Report missing
353+
if ($CurrentStandard.action.value -contains 'Remediate' -and -not ($CurrentStandard.action.value -contains 'Report')) {
354+
$CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{
355+
label = 'Report'
356+
value = 'Report'
357+
}
358+
}
359+
360+
$Actions = $CurrentStandard.action.value
361+
if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') {
362+
$TemplateKey = if ($CurrentStandard.TemplateList.value) { $CurrentStandard.TemplateList.value } else { '' }
363+
$Key = "$StandardName|$TemplateKey"
364+
365+
if ($ComputedStandards.ContainsKey($Key)) {
366+
# Merge group-based over AllTenants base
367+
$MergedStandard = Merge-CippStandards -Existing $ComputedStandards[$Key] -New $CurrentStandard -StandardName $StandardName
368+
$ComputedStandards[$Key] = $MergedStandard
369+
} else {
370+
$ComputedStandards[$Key] = $CurrentStandard
371+
}
372+
}
373+
}
374+
} else {
375+
$CurrentStandard = $Value.PSObject.Copy()
376+
$CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force
377+
378+
# Add Remediate if autoRemediate is true
379+
if ($CurrentStandard.autoRemediate -eq $true -and -not ($CurrentStandard.action.value -contains 'Remediate')) {
380+
$CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{
381+
label = 'Remediate'
382+
value = 'Remediate'
383+
}
384+
}
385+
386+
# Add Report if Remediate present but Report missing
387+
if ($CurrentStandard.action.value -contains 'Remediate' -and -not ($CurrentStandard.action.value -contains 'Report')) {
388+
$CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{
389+
label = 'Report'
390+
value = 'Report'
391+
}
392+
}
393+
394+
$Actions = $CurrentStandard.action.value
395+
if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') {
396+
$TemplateKey = if ($CurrentStandard.TemplateList.value) { $CurrentStandard.TemplateList.value } else { '' }
397+
$Key = "$StandardName|$TemplateKey"
398+
399+
if ($ComputedStandards.ContainsKey($Key)) {
400+
$MergedStandard = Merge-CippStandards -Existing $ComputedStandards[$Key] -New $CurrentStandard -StandardName $StandardName
401+
$ComputedStandards[$Key] = $MergedStandard
402+
} else {
403+
$ComputedStandards[$Key] = $CurrentStandard
404+
}
405+
}
406+
}
407+
}
408+
}
409+
410+
# Process TenantSpecific templates, merging with Group and AllTenants base
327411
foreach ($Template in $TenantSpecificTemplatesSet) {
328412
$Standards = $Template.standards
329413

@@ -358,7 +442,7 @@ function Get-CIPPStandards {
358442
$Key = "$StandardName|$TemplateKey"
359443

360444
if ($ComputedStandards.ContainsKey($Key)) {
361-
# Merge tenant-specific over AllTenants base
445+
# Merge tenant-specific over Group/AllTenants base
362446
$MergedStandard = Merge-CippStandards -Existing $ComputedStandards[$Key] -New $CurrentStandard -StandardName $StandardName
363447
$ComputedStandards[$Key] = $MergedStandard
364448
} else {

profile.ps1

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,21 @@ Write-Information '#### CIPP-API Start ####'
44
Set-Location -Path $PSScriptRoot
55
try {
66
$AppInsightsDllPath = Join-Path $PSScriptRoot 'Shared\AppInsights\Microsoft.ApplicationInsights.dll'
7-
if (Test-Path $AppInsightsDllPath) {
8-
[Reflection.Assembly]::LoadFile($AppInsightsDllPath) | Out-Null
9-
Write-Information 'Application Insights SDK loaded successfully'
10-
} else {
11-
Write-Warning "Application Insights DLL not found at: $AppInsightsDllPath"
12-
}
7+
$null = [Reflection.Assembly]::LoadFile($AppInsightsDllPath)
8+
Write-Information 'Application Insights SDK loaded successfully'
139
} catch {
1410
Write-Warning "Failed to load Application Insights SDK: $($_.Exception.Message)"
1511
}
1612

1713
# Import modules
18-
@('CIPPCore', 'CippExtensions', 'Az.KeyVault', 'Az.Accounts', 'AzBobbyTables') | ForEach-Object {
14+
$ModulesPath = Join-Path $PSScriptRoot 'Modules'
15+
$Modules = @('CIPPCore', 'CippExtensions', 'Az.Accounts', 'Az.KeyVault', 'AzBobbyTables')
16+
foreach ($Module in $Modules) {
1917
try {
20-
$Module = $_
21-
Import-Module -Name $_ -ErrorAction Stop
18+
Import-Module -Name (Join-Path $ModulesPath $Module) -ErrorAction Stop
2219
} catch {
2320
Write-LogMessage -message "Failed to import module - $Module" -LogData (Get-CippException -Exception $_) -Sev 'debug'
24-
$_.Exception.Message
21+
Write-Error $_.Exception.Message
2522
}
2623
}
2724

@@ -61,7 +58,7 @@ if ($env:ExternalDurablePowerShellSDK -eq $true) {
6158
}
6259

6360
try {
64-
Disable-AzContextAutosave -Scope Process | Out-Null
61+
$null = Disable-AzContextAutosave -Scope Process
6562
} catch {}
6663

6764
try {
@@ -73,8 +70,7 @@ try {
7370
Write-LogMessage -message 'Could not retrieve keys from Keyvault' -LogData (Get-CippException -Exception $_) -Sev 'debug'
7471
}
7572

76-
Set-Location -Path $PSScriptRoot
77-
$CurrentVersion = (Get-Content .\version_latest.txt).trim()
73+
$CurrentVersion = (Get-Content -Path (Join-Path $PSScriptRoot 'version_latest.txt') -Raw).Trim()
7874
$Table = Get-CippTable -tablename 'Version'
7975
Write-Information "Function App: $($env:WEBSITE_SITE_NAME) | API Version: $CurrentVersion | PS Version: $($PSVersionTable.PSVersion)"
8076
$global:CippVersion = $CurrentVersion
@@ -84,7 +80,7 @@ if (!$LastStartup -or $CurrentVersion -ne $LastStartup.Version) {
8480
Write-Information "Version has changed from $($LastStartup.Version ?? 'None') to $CurrentVersion"
8581
if ($LastStartup) {
8682
$LastStartup.Version = $CurrentVersion
87-
$LastStartup | Add-Member -MemberType NoteProperty -Name 'PSVersion' -Value $PSVersionTable.PSVersion.ToString() -Force
83+
Add-Member -InputObject $LastStartup -MemberType NoteProperty -Name 'PSVersion' -Value $PSVersionTable.PSVersion.ToString() -Force
8884
} else {
8985
$LastStartup = [PSCustomObject]@{
9086
PartitionKey = 'Version'

requirements.psd1

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
# This file enables modules to be automatically managed by the Functions service.
22
# See https://aka.ms/functionsmanageddependency for additional information.
33
#
4-
@{
5-
# For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'.
6-
# To use the Az module in your function app, please uncomment the line below.
7-
'Az.accounts' = '2.*'
8-
'Az.Keyvault' = '3.*'
9-
'AzBobbyTables' = '2.*'
10-
}
4+
# CIPP bundles all modules locally in the Modules folder and imports them explicitly.
5+
# managedDependency is disabled in host.json - this file is intentionally empty.
6+
@{}

0 commit comments

Comments
 (0)