Skip to content

Commit 6633b15

Browse files
authored
Merge pull request #1020 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 34a3605 + 2b33282 commit 6633b15

17 files changed

Lines changed: 360 additions & 59 deletions

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ function Push-ExecScheduledCommand {
1111
$OrchestratorBasedCommands = @('Invoke-CIPPOffboardingJob')
1212

1313
# Initialize AsyncLocal storage for thread-safe per-invocation context
14-
if (-not $global:CippScheduledTaskIdStorage) {
15-
$global:CippScheduledTaskIdStorage = [System.Threading.AsyncLocal[string]]::new()
16-
}
17-
$global:CippScheduledTaskIdStorage.Value = $Item.TaskInfo.RowKey
14+
# Store task id in CIPPCore module-scoped AsyncLocal storage so Write-LogMessage can read it -
15+
# global vars are unreliable in Azure Functions
16+
Set-CippScheduledTaskContext -TaskId $Item.TaskInfo.RowKey
17+
18+
# Store action source + creating user identity (from stored headers) for outbound User-Agent attribution
19+
Set-CippUserAgentContext -Headers $Item.Parameters.Headers -Source 'scheduled-task' -TaskId $Item.TaskInfo.RowKey
1820

1921
$Table = Get-CippTable -tablename 'ScheduledTasks'
2022
$task = $Item.TaskInfo
@@ -179,7 +181,7 @@ function Push-ExecScheduledCommand {
179181
return
180182
}
181183

182-
if ($Command.Module -notin @('CIPPCore', 'CIPPAlerts', 'CIPPStandards', 'CIPPTests', 'CIPPDB', 'CippExtensions')) {
184+
if ($Command.Module -notin @('CIPPCore', 'CIPPAlerts', 'CIPPStandards', 'CIPPTests', 'CIPPDB', 'CippExtensions', 'CIPPActivityTriggers')) {
183185
$State = 'Failed'
184186
Write-LogMessage -headers $Headers -API 'ScheduledTask' -message "Blocked attempt to schedule command from unauthorized module: $($Command.ModuleName)\$($Item.Command)" -Sev 'Warning'
185187
$Results = "Task blocked: The command '$($Item.Command)' is not permitted to run as a scheduled task."

Modules/CIPPActivityTriggers/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPStandard.ps1

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ function Push-CIPPStandard {
4646
return
4747
}
4848

49-
# Initialize AsyncLocal storage for thread-safe per-invocation context
50-
# Uses $global: so Write-LogMessage (CIPPCore module) can read it across module boundaries
51-
if (-not $global:CippStandardInfoStorage) {
52-
$global:CippStandardInfoStorage = [System.Threading.AsyncLocal[object]]::new()
53-
}
54-
$global:CippStandardInfoStorage.Value = $StandardInfo
49+
# Store standard info in CIPPCore module-scoped AsyncLocal storage so Write-LogMessage and
50+
# Set-CIPPStandardsCompareField can read it - global vars are unreliable in Azure Functions
51+
Set-CippStandardInfoContext -StandardInfo $StandardInfo
52+
53+
# Store action source + template id for outbound User-Agent attribution
54+
Set-CippUserAgentContext -Source 'standard' -TemplateId $Item.TemplateId
5555

5656
# ---- Standard execution telemetry ----
5757
$runId = [guid]::NewGuid().ToString()
@@ -131,8 +131,6 @@ function Push-CIPPStandard {
131131
Error = $err
132132
} | ConvertTo-Json -Compress)
133133

134-
if ($global:CippStandardInfoStorage) {
135-
$global:CippStandardInfoStorage.Value = $null
136-
}
134+
Set-CippStandardInfoContext -StandardInfo $null
137135
}
138136
}

Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ function Add-CIPPScheduledTask {
7070
$ImportedModules = [System.Collections.Generic.List[string]]::new()
7171
if (-not $Command) {
7272
try {
73-
foreach ($SiblingModule in @('CIPPStandards', 'CIPPAlerts', 'CIPPTests', 'CIPPDB', 'CippExtensions')) {
73+
foreach ($SiblingModule in @('CIPPStandards', 'CIPPAlerts', 'CIPPTests', 'CIPPDB', 'CippExtensions', 'CIPPActivityTriggers')) {
7474
if (-not (Get-Module -Name $SiblingModule)) {
7575
Import-Module $SiblingModule -ErrorAction SilentlyContinue
7676
if (Get-Module -Name $SiblingModule) {
@@ -91,7 +91,7 @@ function Add-CIPPScheduledTask {
9191
return "Error - The command '$RequestedCommand' does not exist and cannot be scheduled."
9292
}
9393

94-
if ($Command.Module -notin @('CIPPCore', 'CIPPAlerts', 'CIPPStandards', 'CIPPTests', 'CIPPDB', 'CippExtensions')) {
94+
if ($Command.Module -notin @('CIPPCore', 'CIPPAlerts', 'CIPPStandards', 'CIPPTests', 'CIPPDB', 'CippExtensions', 'CIPPActivityTriggers')) {
9595
Write-LogMessage -headers $Headers -API 'ScheduledTask' -message "Blocked attempt to schedule command from unauthorized module: $($Command.ModuleName)\$RequestedCommand" -Sev 'Warning'
9696
return "Error - The command '$RequestedCommand' is not permitted to run as a scheduled task."
9797
}

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/New-CippCoreRequest.ps1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ function New-CippCoreRequest {
6161
}
6262
}
6363

64+
# Store action source + acting identity for outbound User-Agent attribution
65+
Set-CippUserAgentContext -Headers $Request.Headers
66+
6467
# Check if endpoint is disabled via feature flags
6568
$FeatureFlags = Get-CIPPFeatureFlag
6669

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

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ function Start-CIPPStatsTimer {
55
#>
66
[CmdletBinding(SupportsShouldProcess = $true)]
77
param()
8+
89
#These stats are sent to a central server to help us understand how many tenants are using the product, and how many are using the latest version, this information allows the CIPP team to make decisions about what features to support, and what features to deprecate.
910

1011

@@ -31,6 +32,7 @@ function Start-CIPPStatsTimer {
3132
$FunctionOffloading = (Get-CIPPAzDataTableEntity @ConfigTable -Filter "RowKey eq 'OffloadFunctions' and PartitionKey eq 'OffloadFunctions'").state
3233
$OffloadingEnabled = $false
3334
[bool]::TryParse($FunctionOffloading, [ref]$OffloadingEnabled) | Out-Null
35+
$CIPPNG = $env:CIPPNG -eq 'true'
3436

3537
# Get counts of various entities across all tenants
3638
$counts = Get-CIPPDbItem -TenantFilter AllTenants -CountsOnly
@@ -39,30 +41,42 @@ function Start-CIPPStatsTimer {
3941
$groupsCount = ($counts | Where-Object { $_.RowKey -eq 'Groups-Count' } | Measure-Object -Property DataCount -Sum).Sum
4042
$managedDevicesCount = ($counts | Where-Object { $_.RowKey -eq 'ManagedDevices-Count' } | Measure-Object -Property DataCount -Sum).Sum
4143
$policyCount = ($counts | Where-Object { $_.RowKey -match 'Intune' -and $_.RowKey -match 'Policies|Policy' } | Measure-Object -Property DataCount -Sum).Sum
44+
$deployedApps = ($counts | Where-Object { $_.RowKey -eq 'IntuneApplications-Count' } | Measure-Object -Property DataCount -Sum).Sum
45+
$ReportsTable = Get-CippTable -tablename 'ReportBuilderTemplates'
46+
$CustomReportCount = (Get-CIPPAzDataTableEntity @ReportsTable -Filter "PartitionKey eq 'ReportBuilderTemplates'").count
47+
$uniqueStandardsApplied = Get-CIPPStatsUniqueStandardsApplied
48+
$driftStandardsCount = Get-CIPPStatsDriftStandardsCount
49+
$mobileEnrollment = Get-CIPPStatsMobileEnrollment
4250

4351
$SendingObject = [PSCustomObject]@{
44-
rgid = $env:WEBSITE_SITE_NAME
45-
SetupComplete = $SetupComplete
46-
Hosted = $env:CIPP_HOSTED -eq 'true'
47-
OffloadingEnabled = $OffloadingEnabled
48-
RunningVersionAPI = $APIVersion.trim()
49-
CountOfTotalTenants = $TenantCount
50-
uid = $env:TenantID
51-
UserCount = $userCount
52-
DeviceCount = $deviceCount
53-
GroupsCount = $groupsCount
54-
ManagedDevicesCount = $managedDevicesCount
55-
PolicyCount = $policyCount
56-
CIPPAPI = $RawExt.CIPPAPI.Enabled
57-
Hudu = $RawExt.Hudu.Enabled
58-
Sherweb = $RawExt.Sherweb.Enabled
59-
Gradient = $RawExt.Gradient.Enabled
60-
NinjaOne = $RawExt.NinjaOne.Enabled
61-
haloPSA = $RawExt.haloPSA.Enabled
62-
HIBP = $RawExt.HIBP.Enabled
63-
PWPush = $RawExt.PWPush.Enabled
64-
CFZTNA = $RawExt.CFZTNA.Enabled
65-
GitHub = $RawExt.GitHub.Enabled
52+
rgid = $env:WEBSITE_SITE_NAME
53+
SetupComplete = $SetupComplete
54+
Hosted = $env:CIPP_HOSTED -eq 'true'
55+
CIPPNG = $CIPPNG
56+
OffloadingEnabled = $OffloadingEnabled
57+
RunningVersionAPI = $APIVersion.trim()
58+
CountOfTotalTenants = $TenantCount
59+
uid = $env:TenantID
60+
UserCount = $userCount
61+
DeviceCount = $deviceCount
62+
GroupsCount = $groupsCount
63+
ManagedDevicesCount = $managedDevicesCount
64+
PolicyCount = $policyCount
65+
UniqueStandardsApplied = $uniqueStandardsApplied
66+
DriftStandardsCount = $driftStandardsCount
67+
MobileEnrollment = $mobileEnrollment
68+
DeployedApps = $deployedApps
69+
CustomReportCount = $CustomReportCount
70+
CIPPAPI = $RawExt.CIPPAPI.Enabled
71+
Hudu = $RawExt.Hudu.Enabled
72+
Sherweb = $RawExt.Sherweb.Enabled
73+
Gradient = $RawExt.Gradient.Enabled
74+
NinjaOne = $RawExt.NinjaOne.Enabled
75+
haloPSA = $RawExt.haloPSA.Enabled
76+
HIBP = $RawExt.HIBP.Enabled
77+
PWPush = $RawExt.PWPush.Enabled
78+
CFZTNA = $RawExt.CFZTNA.Enabled
79+
GitHub = $RawExt.GitHub.Enabled
6680
} | ConvertTo-Json
6781

6882
try {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
function Get-CIPPStatsDriftStandardsCount {
2+
[CmdletBinding()]
3+
param()
4+
5+
try {
6+
$Standards = @(Get-CIPPStandards -TenantFilter allTenants)
7+
$TemplateTable = Get-CippTable -tablename 'templates'
8+
$TemplateRows = @(Get-CIPPAzDataTableEntity @TemplateTable -Filter "PartitionKey eq 'StandardsTemplateV2'")
9+
10+
$TemplateTypeByGuid = @{}
11+
foreach ($TemplateRow in $TemplateRows) {
12+
if ([string]::IsNullOrWhiteSpace($TemplateRow.JSON)) { continue }
13+
14+
try {
15+
$TemplateData = $TemplateRow.JSON | ConvertFrom-Json -Depth 30 -ErrorAction Stop
16+
} catch {
17+
continue
18+
}
19+
20+
$TemplateGuid = [string]($TemplateData.GUID ?? $TemplateRow.GUID)
21+
if ([string]::IsNullOrWhiteSpace($TemplateGuid)) { continue }
22+
23+
$TemplateTypeByGuid[$TemplateGuid] = [string]$TemplateData.type
24+
}
25+
26+
$DriftStandardIds = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
27+
foreach ($Standard in $Standards) {
28+
if ([string]::IsNullOrWhiteSpace($Standard.TemplateId)) { continue }
29+
if ([string]::IsNullOrWhiteSpace($Standard.Standard)) { continue }
30+
if (-not $TemplateTypeByGuid.ContainsKey([string]$Standard.TemplateId)) { continue }
31+
if ($TemplateTypeByGuid[[string]$Standard.TemplateId] -ne 'drift') { continue }
32+
33+
$TemplateValue = $null
34+
if ($Standard.Settings -and $Standard.Settings.PSObject.Properties['TemplateList']) {
35+
if ($Standard.Settings.TemplateList -and $Standard.Settings.TemplateList.PSObject.Properties['value']) {
36+
$TemplateValue = [string]$Standard.Settings.TemplateList.value
37+
}
38+
}
39+
40+
$Id = if ([string]::IsNullOrWhiteSpace($TemplateValue)) {
41+
[string]$Standard.Standard
42+
} else {
43+
"{0}|{1}" -f $Standard.Standard, $TemplateValue
44+
}
45+
46+
if ([string]::IsNullOrWhiteSpace($Id)) { continue }
47+
[void]$DriftStandardIds.Add($Id)
48+
}
49+
50+
return $DriftStandardIds.Count
51+
} catch {
52+
Write-LogMessage -API 'CIPPStatsTimer' -tenant $env:TenantID -message "Failed to calculate DriftStandardsCount: $($_.Exception.Message)" -sev Warning
53+
return 0
54+
}
55+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
function Get-CIPPStatsMobileEnrollment {
2+
[CmdletBinding()]
3+
param()
4+
5+
try {
6+
$MobileEnrollment = 0
7+
$ManagedDeviceRows = Get-CIPPDbItem -TenantFilter allTenants -Type 'ManagedDevices'
8+
foreach ($ManagedDeviceRow in $ManagedDeviceRows) {
9+
if (-not $ManagedDeviceRow.Data) { continue }
10+
11+
try {
12+
$ManagedDevice = $ManagedDeviceRow.Data | ConvertFrom-Json -Depth 20 -ErrorAction Stop
13+
} catch {
14+
continue
15+
}
16+
17+
$OperatingSystem = [string]$ManagedDevice.operatingSystem
18+
if ($OperatingSystem -match 'iOS|iPadOS|Android') {
19+
$MobileEnrollment++
20+
}
21+
}
22+
23+
return $MobileEnrollment
24+
} catch {
25+
Write-LogMessage -API 'CIPPStatsTimer' -tenant $env:TenantID -message "Failed to calculate MobileEnrollment: $($_.Exception.Message)" -sev Warning
26+
return 0
27+
}
28+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
function Get-CIPPStatsUniqueStandardsApplied {
2+
[CmdletBinding()]
3+
param()
4+
5+
try {
6+
$Standards = @(Get-CIPPStandards -TenantFilter allTenants)
7+
$DistinctStandards = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
8+
9+
foreach ($Standard in $Standards) {
10+
if (-not $Standard.Standard) { continue }
11+
12+
$TemplateValue = $null
13+
if ($Standard.Settings -and $Standard.Settings.PSObject.Properties['TemplateList']) {
14+
if ($Standard.Settings.TemplateList -and $Standard.Settings.TemplateList.PSObject.Properties['value']) {
15+
$TemplateValue = [string]$Standard.Settings.TemplateList.value
16+
}
17+
}
18+
19+
$Id = if ([string]::IsNullOrWhiteSpace($TemplateValue)) {
20+
[string]$Standard.Standard
21+
} else {
22+
'{0}|{1}' -f $Standard.Standard, $TemplateValue
23+
}
24+
25+
if ([string]::IsNullOrWhiteSpace($Id)) { continue }
26+
[void]$DistinctStandards.Add($Id)
27+
}
28+
29+
return $DistinctStandards.Count
30+
} catch {
31+
Write-LogMessage -API 'CIPPStatsTimer' -tenant $env:TenantID -message "Failed to calculate UniqueStandardsApplied: $($_.Exception.Message)" -sev Warning
32+
return 0
33+
}
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
function Get-CippUserAgent {
2+
<#
3+
.SYNOPSIS
4+
Builds the User-Agent string for outbound M365 API requests.
5+
.DESCRIPTION
6+
Returns 'CIPP/<version>' optionally suffixed with semicolon-delimited 'key:value' context segments
7+
set via Set-CippUserAgentContext, e.g. 'CIPP/8.2.0 (user:john@msp.com)' or
8+
'CIPP/8.2.0 (scheduled-task:<taskid>; user:john@msp.com)'.
9+
This allows MDR/security teams to attribute CIPP activity in M365 audit logs.
10+
.FUNCTIONALITY
11+
Internal
12+
#>
13+
[CmdletBinding()]
14+
param()
15+
16+
$Version = $env:CippVersion ?? $env:APP_VERSION ?? '1.0'
17+
$Context = $script:CippUserAgentContextStorage.Value
18+
19+
if ($Context.Source) {
20+
$Segments = [System.Collections.Generic.List[string]]::new()
21+
if ($Context.Source -in @('user', 'api')) {
22+
# Identity belongs to the source itself, e.g. user:john@msp.com or api:<appid>
23+
$Segments.Add($Context.Identity ? ('{0}:{1}' -f $Context.Source, $Context.Identity) : $Context.Source)
24+
} else {
25+
$Id = @($Context.TaskId, $Context.TemplateId) | Where-Object { $_ } | Select-Object -First 1
26+
$Segments.Add($Id ? ('{0}:{1}' -f $Context.Source, $Id) : $Context.Source)
27+
if ($Context.Identity) {
28+
$Segments.Add('user:{0}' -f $Context.Identity)
29+
}
30+
}
31+
return ('CIPP/{0} ({1})' -f $Version, ($Segments -join '; '))
32+
}
33+
return ('CIPP/{0}' -f $Version)
34+
}

Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function New-GraphGetRequest {
5454
}
5555

5656
if (!$headers['User-Agent']) {
57-
$headers['User-Agent'] = "CIPP/$($global:CippVersion ?? '1.0')"
57+
$headers['User-Agent'] = Get-CippUserAgent
5858
}
5959

6060

0 commit comments

Comments
 (0)