Skip to content

Commit 47b5958

Browse files
Merge pull request KelvinTegelaar#1772 from KelvinTegelaar/dev
Dev to release - CIPP X(10.0.0)
2 parents 3fbe8b3 + 6cc72db commit 47b5958

File tree

907 files changed

+29185
-2015
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

907 files changed

+29185
-2015
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ ExcludedTenants
1010
SendNotifications/config.json
1111
.env
1212
Output/
13+
node_modules/.yarn-integrity
14+
yarn.lock
1315

1416
# Cursor IDE
1517
.cursor/rules

CIPPTimers.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,5 +222,23 @@
222222
"Priority": 21,
223223
"RunOnProcessor": true,
224224
"IsSystem": true
225+
},
226+
{
227+
"Id": "9a7f8e6d-5c4b-3a2d-1e0f-9b8c7d6e5f4a",
228+
"Command": "Start-CIPPDBCacheOrchestrator",
229+
"Description": "Timer to collect and cache Microsoft Graph data for all tenants",
230+
"Cron": "0 0 3 * * *",
231+
"Priority": 22,
232+
"RunOnProcessor": true,
233+
"IsSystem": true
234+
},
235+
{
236+
"Id": "1f2e3d4c-5b6a-7c8d-9e0f-1a2b3c4d5e6f",
237+
"Command": "Start-TestsOrchestrator",
238+
"Description": "Timer to run security and compliance tests against cached data",
239+
"Cron": "0 0 4 * * *",
240+
"Priority": 23,
241+
"RunOnProcessor": true,
242+
"IsSystem": true
225243
}
226244
]

ExampleReportTemplate.ps1

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
$Table = Get-CippTable -tablename 'CippReportTemplates'
2+
3+
# Dynamically discover all ZTNA test files
4+
$TestFiles = Get-ChildItem "C:\Github\CIPP-API\Modules\CIPPCore\Public\Tests\Invoke-CippTestZTNA*.ps1" | Sort-Object Name
5+
$AllTestIds = $TestFiles.BaseName | ForEach-Object { $_ -replace 'Invoke-CippTestZTNA', 'ZTNA' }
6+
7+
Write-Host "Discovered $($AllTestIds.Count) ZTNA tests"
8+
9+
$Entity = @{
10+
RowKey = 'd5d1e123-bce0-482d-971f-be6ed820dd92'
11+
PartitionKey = 'ReportingTemplate'
12+
IdentityTests = [string]($AllTestIds | ConvertTo-Json -Compress)
13+
Description = 'Complete Zero Trust Network Assessment Report'
14+
Name = 'Full ZTNA Report'
15+
}
16+
17+
Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force
18+
19+
Write-Host "Report template created successfully with ID: $($Entity.RowKey)"
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
function Add-CIPPDbItem {
2+
<#
3+
.SYNOPSIS
4+
Add items to the CIPP Reporting database
5+
6+
.DESCRIPTION
7+
Adds items to the CippReportingDB table with support for bulk inserts and count mode
8+
9+
.PARAMETER TenantFilter
10+
The tenant domain or GUID (used as partition key)
11+
12+
.PARAMETER Type
13+
The type of data being stored (used in row key)
14+
15+
.PARAMETER Data
16+
Array of items to add to the database
17+
18+
.PARAMETER Count
19+
If specified, stores a single row with count of each object property as separate properties
20+
21+
.EXAMPLE
22+
Add-CIPPDbItem -TenantFilter 'contoso.onmicrosoft.com' -Type 'Groups' -Data $GroupsData
23+
24+
.EXAMPLE
25+
Add-CIPPDbItem -TenantFilter 'contoso.onmicrosoft.com' -Type 'Groups' -Data $GroupsData -Count
26+
#>
27+
[CmdletBinding()]
28+
param(
29+
[Parameter(Mandatory = $true)]
30+
[string]$TenantFilter,
31+
32+
[Parameter(Mandatory = $true)]
33+
[string]$Type,
34+
35+
[Parameter(Mandatory = $true)]
36+
[AllowEmptyCollection()]
37+
[array]$Data,
38+
39+
[Parameter(Mandatory = $false)]
40+
[switch]$Count
41+
)
42+
43+
try {
44+
$Table = Get-CippTable -tablename 'CippReportingDB'
45+
46+
# Helper function to format RowKey values by removing disallowed characters
47+
function Format-RowKey {
48+
param([string]$RowKey)
49+
50+
# Remove disallowed characters: / \ # ? and control characters (U+0000 to U+001F and U+007F to U+009F)
51+
$sanitized = $RowKey -replace '[/\\#?]', '_' -replace '[\u0000-\u001F\u007F-\u009F]', ''
52+
53+
return $sanitized
54+
}
55+
56+
if ($Count) {
57+
$Entity = @{
58+
PartitionKey = $TenantFilter
59+
RowKey = Format-RowKey "$Type-Count"
60+
DataCount = [int]$Data.Count
61+
}
62+
63+
Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force | Out-Null
64+
65+
} else {
66+
#Get the existing type entries and nuke them. This ensures we don't have stale data.
67+
$Filter = "PartitionKey eq '{0}' and RowKey ge '{1}-' and RowKey lt '{1}0'" -f $TenantFilter, $Type
68+
$ExistingEntities = Get-CIPPAzDataTableEntity @Table -Filter $Filter
69+
if ($ExistingEntities) {
70+
Remove-AzDataTableEntity @Table -Entity $ExistingEntities -Force | Out-Null
71+
}
72+
$Entities = foreach ($Item in $Data) {
73+
$ItemId = $Item.id ?? $Item.ExternalDirectoryObjectId ?? $Item.Identity ?? $Item.skuId
74+
@{
75+
PartitionKey = $TenantFilter
76+
RowKey = Format-RowKey "$Type-$ItemId"
77+
Data = [string]($Item | ConvertTo-Json -Depth 10 -Compress)
78+
Type = $Type
79+
}
80+
}
81+
Add-CIPPAzDataTableEntity @Table -Entity $Entities -Force | Out-Null
82+
83+
}
84+
85+
Write-LogMessage -API 'CIPPDbItem' -tenant $TenantFilter -message "Added $($Data.Count) items of type $Type$(if ($Count) { ' (count mode)' })" -sev Info
86+
87+
} catch {
88+
Write-LogMessage -API 'CIPPDbItem' -tenant $TenantFilter -message "Failed to add items of type $Type : $($_.Exception.Message)" -sev Error
89+
throw
90+
}
91+
}

Modules/CIPPCore/Public/Add-CIPPScheduledTask.ps1

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,14 @@ function Add-CIPPScheduledTask {
4646
return "Could not run task: $ErrorMessage"
4747
}
4848
} else {
49+
if (!$Task.RowKey) {
50+
$RowKey = (New-Guid).Guid
51+
} else {
52+
$RowKey = $Task.RowKey
53+
}
54+
4955
if ($DisallowDuplicateName) {
50-
$Filter = "PartitionKey eq 'ScheduledTask' and Name eq '$($Task.Name)'"
56+
$Filter = "PartitionKey eq 'ScheduledTask' and Name eq '$($Task.Name)' and TaskState ne 'Completed' and TaskState ne 'Failed'"
5157
$ExistingTask = (Get-CIPPAzDataTableEntity @Table -Filter $Filter)
5258
if ($ExistingTask) {
5359
return "Task with name $($Task.Name) already exists"
@@ -110,11 +116,7 @@ function Add-CIPPScheduledTask {
110116
}
111117
$AdditionalProperties = ([PSCustomObject]$AdditionalProperties | ConvertTo-Json -Compress)
112118
if ($Parameters -eq 'null') { $Parameters = '' }
113-
if (!$Task.RowKey) {
114-
$RowKey = (New-Guid).Guid
115-
} else {
116-
$RowKey = $Task.RowKey
117-
}
119+
118120

119121
$Recurrence = if ([string]::IsNullOrEmpty($task.Recurrence.value)) {
120122
$task.Recurrence
@@ -258,7 +260,46 @@ function Add-CIPPScheduledTask {
258260
return "Error - Could not add task: $ErrorMessage"
259261
}
260262
Write-LogMessage -headers $Headers -API 'ScheduledTask' -message "Added task $($entity.Name) with ID $($entity.RowKey)" -Sev 'Info' -Tenant $tenantFilter
261-
return "Successfully added task: $($entity.Name)"
263+
264+
# Calculate relative time for next run
265+
$scheduledEpoch = [int64]$entity.ScheduledTime
266+
$currentTime = [datetime]::UtcNow
267+
268+
if ($scheduledEpoch -eq 0 -or $scheduledEpoch -le ([int64](($currentTime) - (Get-Date '1/1/1970')).TotalSeconds)) {
269+
# Task will run at next 15-minute interval - calculate efficiently
270+
$minutesToAdd = 15 - ($currentTime.Minute % 15)
271+
$nextRunTime = $currentTime.AddMinutes($minutesToAdd).AddSeconds(-$currentTime.Second).AddMilliseconds(-$currentTime.Millisecond)
272+
$timeUntilRun = $nextRunTime - $currentTime
273+
} else {
274+
# Task is scheduled for a specific time in the future
275+
$scheduledTime = [datetime]'1/1/1970' + [TimeSpan]::FromSeconds($scheduledEpoch)
276+
$timeUntilRun = $scheduledTime - $currentTime
277+
}
278+
279+
# Format relative time
280+
$relativeTime = switch ($timeUntilRun.TotalMinutes) {
281+
{ $_ -ge 1440 } {
282+
$days = [Math]::Floor($timeUntilRun.TotalDays)
283+
$hours = $timeUntilRun.Hours
284+
$result = "$days day$(if ($days -ne 1) { 's' })"
285+
if ($hours -gt 0) { $result += " and $hours hour$(if ($hours -ne 1) { 's' })" }
286+
$result
287+
break
288+
}
289+
{ $_ -ge 60 } {
290+
$hours = [Math]::Floor($timeUntilRun.TotalHours)
291+
$minutes = $timeUntilRun.Minutes
292+
$result = "$hours hour$(if ($hours -ne 1) { 's' })"
293+
if ($minutes -gt 0) { $result += " and $minutes minute$(if ($minutes -ne 1) { 's' })" }
294+
$result
295+
break
296+
}
297+
{ $_ -ge 2 } { "about $([Math]::Round($_)) minutes"; break }
298+
{ $_ -ge 1 } { 'about 1 minute'; break }
299+
default { 'less than a minute' }
300+
}
301+
302+
return "Successfully added task: $($entity.Name). It will run in $relativeTime."
262303
}
263304
} catch {
264305
Write-Warning "Failed to add scheduled task: $($_.Exception.Message)"
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
function Add-CippTestResult {
2+
<#
3+
.SYNOPSIS
4+
Adds a test result to the CIPP test results database
5+
6+
.DESCRIPTION
7+
Stores test result data in the CippTestResults table with tenant and test ID as keys
8+
9+
.PARAMETER TenantFilter
10+
The tenant domain or GUID for the test result
11+
12+
.PARAMETER TestId
13+
Unique identifier for the test
14+
15+
.PARAMETER Status
16+
Test status (e.g., Pass, Fail, Skip)
17+
18+
.PARAMETER ResultMarkdown
19+
Markdown formatted result details
20+
21+
.PARAMETER Risk
22+
Risk level (e.g., High, Medium, Low)
23+
24+
.PARAMETER Name
25+
Display name of the test
26+
27+
.PARAMETER Pillar
28+
Security pillar category
29+
30+
.PARAMETER UserImpact
31+
Impact level on users
32+
33+
.PARAMETER ImplementationEffort
34+
Effort required for implementation
35+
36+
.PARAMETER Category
37+
Test category or classification
38+
39+
.EXAMPLE
40+
Add-CippTestResult -TenantFilter 'contoso.onmicrosoft.com' -TestId 'MFA-001' -Status 'Pass' -Name 'MFA Enabled' -Risk 'High'
41+
#>
42+
[CmdletBinding()]
43+
param(
44+
[Parameter(Mandatory = $true)]
45+
[string]$TenantFilter,
46+
47+
[Parameter(Mandatory = $true)]
48+
[string]$TestId,
49+
50+
[Parameter(Mandatory = $false)]
51+
[string]$testType = 'Identity',
52+
53+
[Parameter(Mandatory = $true)]
54+
[string]$Status,
55+
56+
[Parameter(Mandatory = $false)]
57+
[string]$ResultMarkdown,
58+
59+
[Parameter(Mandatory = $false)]
60+
[string]$Risk,
61+
62+
[Parameter(Mandatory = $false)]
63+
[string]$Name,
64+
65+
[Parameter(Mandatory = $false)]
66+
[string]$Pillar,
67+
68+
[Parameter(Mandatory = $false)]
69+
[string]$UserImpact,
70+
71+
[Parameter(Mandatory = $false)]
72+
[string]$ImplementationEffort,
73+
74+
[Parameter(Mandatory = $false)]
75+
[string]$Category
76+
)
77+
78+
try {
79+
$Table = Get-CippTable -tablename 'CippTestResults'
80+
81+
$Entity = @{
82+
PartitionKey = $TenantFilter
83+
RowKey = $TestId
84+
Status = $Status
85+
ResultMarkdown = $ResultMarkdown ?? ''
86+
Risk = $Risk ?? ''
87+
Name = $Name ?? ''
88+
Pillar = $Pillar ?? ''
89+
UserImpact = $UserImpact ?? ''
90+
ImplementationEffort = $ImplementationEffort ?? ''
91+
Category = $Category ?? ''
92+
TestType = $TestType
93+
}
94+
95+
Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force
96+
Write-LogMessage -API 'CIPPTestResults' -tenant $TenantFilter -message "Added test result: $TestId - $Status" -sev Debug
97+
} catch {
98+
Write-LogMessage -API 'CIPPTestResults' -tenant $TenantFilter -message "Failed to add test result: $($_.Exception.Message)" -sev Error
99+
throw
100+
}
101+
}

Modules/CIPPCore/Public/Alerts/Get-CIPPAlertAppSecretExpiry.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ function Get-CIPPAlertAppSecretExpiry {
3131
AppName = $App.displayName
3232
AppId = $App.appId
3333
Expires = $Credential.endDateTime
34+
SecretName = $Credential.displayName
35+
SecretID = $Credential.keyId
3436
Tenant = $TenantFilter
3537
}
3638
$AlertData.Add($Message)

0 commit comments

Comments
 (0)