Skip to content

Commit b8c663a

Browse files
authored
Merge pull request #612 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 528a8d5 + e3931e6 commit b8c663a

File tree

15 files changed

+447
-16
lines changed

15 files changed

+447
-16
lines changed

CIPPTimers.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,5 +203,14 @@
203203
"Priority": 20,
204204
"RunOnProcessor": true,
205205
"IsSystem": true
206+
},
207+
{
208+
"Id": "b8f3c2e1-5d4a-4f7b-9a2c-1e6d8f3b5a7c",
209+
"Command": "Start-BackupRetentionCleanup",
210+
"Description": "Timer to cleanup old backups based on retention policy",
211+
"Cron": "0 0 2 * * *",
212+
"Priority": 21,
213+
"RunOnProcessor": true,
214+
"IsSystem": true
206215
}
207216
]
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
function Get-CIPPAlertGlobalAdminAllowList {
2+
<#
3+
.FUNCTIONALITY
4+
Entrypoint
5+
#>
6+
[CmdletBinding()]
7+
param (
8+
[Parameter(Mandatory = $false)]
9+
[Alias('input')]
10+
$InputValue,
11+
$TenantFilter
12+
)
13+
try {
14+
$AllowedAdmins = @()
15+
$AlertEachAdmin = $false
16+
if ($InputValue -is [hashtable] -or $InputValue -is [pscustomobject]) {
17+
$AlertEachAdmin = [bool]($InputValue['AlertEachAdmin'])
18+
$ApprovedValue = if ($InputValue.ContainsKey('ApprovedGlobalAdmins') -or ($InputValue.PSObject.Properties.Name -contains 'ApprovedGlobalAdmins')) {
19+
$InputValue['ApprovedGlobalAdmins']
20+
} else {
21+
$null
22+
}
23+
$InputValue = $ApprovedValue
24+
}
25+
if ($null -ne $InputValue) {
26+
if ($InputValue -is [string]) {
27+
$AllowedAdmins = $InputValue -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ }
28+
} elseif ($InputValue -is [System.Collections.IEnumerable]) {
29+
$AllowedAdmins = $InputValue | ForEach-Object { $_.ToString().Trim() } | Where-Object { $_ }
30+
} else {
31+
$AllowedAdmins = @("$InputValue")
32+
}
33+
}
34+
$AllowedLookup = $AllowedAdmins | ForEach-Object { $_.ToLowerInvariant() } | Select-Object -Unique
35+
36+
if (-not $AllowedLookup -or $AllowedLookup.Count -eq 0) {
37+
return
38+
}
39+
40+
$GlobalAdmins = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/directoryRoles/roleTemplateId=62e90394-69f5-4237-9190-012177145e10/members?`$select=id,displayName,userPrincipalName" -tenantid $TenantFilter -AsApp $true -ErrorAction Stop | Where-Object {
41+
$_.'@odata.type' -eq '#microsoft.graph.user' -and $_.displayName -ne 'On-Premises Directory Synchronization Service Account'
42+
}
43+
44+
$UnapprovedAdmins = foreach ($admin in $GlobalAdmins) {
45+
if ([string]::IsNullOrWhiteSpace($admin.userPrincipalName)) { continue }
46+
$UpnPrefix = ($admin.userPrincipalName -split '@')[0].ToLowerInvariant()
47+
if ($AllowedLookup -notcontains $UpnPrefix) {
48+
[PSCustomObject]@{
49+
Admin = $admin
50+
UpnPrefix = $UpnPrefix
51+
}
52+
}
53+
}
54+
55+
if ($UnapprovedAdmins) {
56+
if ($AlertEachAdmin) {
57+
$AlertData = foreach ($item in $UnapprovedAdmins) {
58+
$admin = $item.Admin
59+
$UpnPrefix = $item.UpnPrefix
60+
[PSCustomObject]@{
61+
Message = "$($admin.userPrincipalName) has Global Administrator role but is not in the approved allow list (prefix '$UpnPrefix')."
62+
DisplayName = $admin.displayName
63+
UserPrincipalName = $admin.userPrincipalName
64+
Id = $admin.id
65+
AllowedList = if ($AllowedAdmins) { $AllowedAdmins -join ', ' } else { 'Not provided' }
66+
Tenant = $TenantFilter
67+
}
68+
}
69+
} else {
70+
$NonCompliantUpns = @($UnapprovedAdmins.Admin.userPrincipalName)
71+
$AlertData = @([PSCustomObject]@{
72+
Message = "Found $($NonCompliantUpns.Count) Global Administrator account(s) not in the approved allow list."
73+
NonCompliantUsers = $NonCompliantUpns
74+
ApprovedPrefixes = if ($AllowedAdmins) { $AllowedAdmins -join ', ' } else { 'Not provided' }
75+
Tenant = $TenantFilter
76+
})
77+
}
78+
79+
Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $AlertData
80+
}
81+
} catch {
82+
Write-AlertMessage -tenant $TenantFilter -message "Failed to check approved Global Admins: $(Get-NormalizedError -message $_.Exception.Message)"
83+
}
84+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ function Push-SchedulerCIPPNotifications {
115115
}
116116

117117
if ($CurrentStandardsLogs) {
118+
$Data = $CurrentStandardsLogs
118119
$JSONContent = New-CIPPAlertTemplate -Data $Data -Format 'json' -InputObject 'table' -CIPPURL $CIPPURL
119120
$CurrentStandardsLogs | ConvertTo-Json -Compress
120121
Send-CIPPAlert -Type 'webhook' -JSONContent $JSONContent -TenantFilter $Tenant -APIName 'Alerts'

Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPStandardsApplyBatch.ps1

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,29 @@ function Push-CIPPStandardsApplyBatch {
77

88
try {
99
# Aggregate all standards from all tenants
10-
$AllStandards = @($Item.Results | Where-Object { $_ -and $_.FunctionName -eq 'CIPPStandard' })
10+
$AllStandards = $Item.Results | ForEach-Object {
11+
foreach ($Standard in $_) {
12+
if ($Standard -and $Standard.FunctionName -eq 'CIPPStandard') {
13+
$Standard
14+
}
15+
}
16+
}
1117

1218
if ($AllStandards.Count -eq 0) {
1319
Write-Information 'No standards to apply across all tenants'
1420
return
1521
}
1622

17-
Write-Information "Aggregated $($AllStandards.Count) standards from all tenants"
23+
Write-Information "Aggregated $($AllStandards.Count) standards from all tenants: $($AllStandards | ConvertTo-Json -Depth 5 -Compress)"
1824

1925
# Start orchestrator to apply standards
2026
$InputObject = [PSCustomObject]@{
2127
OrchestratorName = 'StandardsApply'
2228
Batch = @($AllStandards)
2329
SkipLog = $true
24-
}
25-
26-
$InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress)
30+
} | ConvertTo-Json -Depth 25 -Compress
31+
Write-Host "Standards InputObject: $InputObject"
32+
$InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject $InputObject
2733
Write-Information "Started standards apply orchestrator with ID = '$InstanceId'"
2834

2935
} catch {

Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPStandardsList.ps1

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ function Push-CIPPStandardsList {
170170

171171
# Remove if both unchanged
172172
if (-not $PolicyChanged -and -not $StandardTemplateChanged) {
173-
[void]$ComputedStandards.Remove($Key)
173+
x [void]$ComputedStandards.Remove($Key)
174174
}
175175
}
176176
} catch {
@@ -181,7 +181,7 @@ function Push-CIPPStandardsList {
181181

182182
Write-Host "Returning $($ComputedStandards.Count) standards for tenant $TenantFilter after filtering."
183183
# Return filtered standards
184-
$ComputedStandards.Values | ForEach-Object {
184+
$FilteredStandards = $ComputedStandards.Values | ForEach-Object {
185185
[PSCustomObject]@{
186186
Tenant = $_.Tenant
187187
Standard = $_.Standard
@@ -190,6 +190,8 @@ function Push-CIPPStandardsList {
190190
FunctionName = 'CIPPStandard'
191191
}
192192
}
193+
Write-Host "Sending back $($FilteredStandards.Count) standards: $($FilteredStandards | ConvertTo-Json -Depth 5 -Compress)"
194+
return $FilteredStandards
193195

194196
} catch {
195197
Write-Warning "Error listing standards for $TenantFilter : $($_.Exception.Message)"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
function Invoke-ExecBackupRetentionConfig {
2+
<#
3+
.FUNCTIONALITY
4+
Entrypoint
5+
.ROLE
6+
CIPP.AppSettings.ReadWrite
7+
#>
8+
[CmdletBinding()]
9+
param($Request, $TriggerMetadata)
10+
$Table = Get-CIPPTable -TableName Config
11+
$Filter = "PartitionKey eq 'BackupRetention' and RowKey eq 'Settings'"
12+
13+
$results = try {
14+
if ($Request.Query.List) {
15+
$RetentionSettings = Get-CIPPAzDataTableEntity @Table -Filter $Filter
16+
if (!$RetentionSettings) {
17+
# Return default values if not set
18+
@{
19+
RetentionDays = 30
20+
}
21+
} else {
22+
@{
23+
RetentionDays = [int]$RetentionSettings.RetentionDays
24+
}
25+
}
26+
} else {
27+
$RetentionDays = [int]$Request.Body.RetentionDays
28+
29+
# Validate minimum value
30+
if ($RetentionDays -lt 7) {
31+
throw 'Retention days must be at least 7 days'
32+
}
33+
34+
$RetentionConfig = @{
35+
'RetentionDays' = $RetentionDays
36+
'PartitionKey' = 'BackupRetention'
37+
'RowKey' = 'Settings'
38+
}
39+
40+
Add-CIPPAzDataTableEntity @Table -Entity $RetentionConfig -Force | Out-Null
41+
Write-LogMessage -headers $Request.Headers -API $Request.Params.CIPPEndpoint -message "Set backup retention to $RetentionDays days" -Sev 'Info'
42+
"Successfully set backup retention to $RetentionDays days"
43+
}
44+
} catch {
45+
$ErrorMessage = Get-CippException -Exception $_
46+
Write-LogMessage -headers $Request.Headers -API $Request.Params.CIPPEndpoint -message "Failed to set backup retention configuration: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage
47+
"Failed to set configuration: $($ErrorMessage.NormalizedError)"
48+
}
49+
50+
$body = [pscustomobject]@{'Results' = $Results }
51+
52+
return ([HttpResponseContext]@{
53+
StatusCode = [HttpStatusCode]::OK
54+
Body = $body
55+
})
56+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
function Invoke-ExecDevicePasscodeAction {
2+
<#
3+
.FUNCTIONALITY
4+
Entrypoint
5+
.ROLE
6+
Endpoint.MEM.ReadWrite
7+
#>
8+
[CmdletBinding()]
9+
param($Request, $TriggerMetadata)
10+
11+
$APIName = $Request.Params.CIPPEndpoint
12+
$Headers = $Request.Headers
13+
14+
$Action = $Request.Body.Action
15+
$DeviceFilter = $Request.Body.GUID
16+
$TenantFilter = $Request.Body.tenantFilter
17+
18+
try {
19+
$GraphResponse = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$DeviceFilter')/$($Action)" -type POST -tenantid $TenantFilter -body '{}'
20+
21+
$Result = switch ($Action) {
22+
'resetPasscode' {
23+
if ($GraphResponse.value) {
24+
"Passcode reset successfully. New passcode: $($GraphResponse.value)"
25+
} else {
26+
"Passcode reset queued for device $DeviceFilter. The new passcode will be generated and can be retrieved from the device details."
27+
}
28+
}
29+
'removeDevicePasscode' {
30+
"Successfully removed passcode requirement from device $DeviceFilter"
31+
}
32+
default {
33+
"Successfully queued $Action on device $DeviceFilter"
34+
}
35+
}
36+
37+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message $Result -Sev Info
38+
$StatusCode = [HttpStatusCode]::OK
39+
$Results = $Result
40+
41+
} catch {
42+
$ErrorMessage = Get-CippException -Exception $_
43+
$Result = "Failed to execute $Action on device $DeviceFilter : $($ErrorMessage.NormalizedError)"
44+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message $Result -Sev Error -LogData $ErrorMessage
45+
$StatusCode = [HttpStatusCode]::InternalServerError
46+
$Results = $Result
47+
}
48+
49+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
50+
StatusCode = $StatusCode
51+
Body = @{Results = $Results }
52+
})
53+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ function Invoke-ExecJITAdmin {
3232
'UserPrincipalName' = $Username
3333
}
3434
Expiration = $Expiration
35+
StartDate = $Start
3536
Reason = $Request.Body.reason
3637
Action = 'Create'
3738
TenantFilter = $TenantFilter
@@ -152,6 +153,7 @@ function Invoke-ExecJITAdmin {
152153
Action = 'AddRoles'
153154
Reason = $Request.Body.Reason
154155
Expiration = $Expiration
156+
StartDate = $Start
155157
Headers = $Headers
156158
APIName = $APIName
157159
}
@@ -173,7 +175,7 @@ function Invoke-ExecJITAdmin {
173175
}
174176
Add-CIPPScheduledTask -Task $TaskBody -hidden $false
175177
if ($Request.Body.userAction -ne 'create') {
176-
Set-CIPPUserJITAdminProperties -TenantFilter $TenantFilter -UserId $Request.Body.existingUser.value -Expiration $Expiration -Reason $Request.Body.Reason
178+
Set-CIPPUserJITAdminProperties -TenantFilter $TenantFilter -UserId $Request.Body.existingUser.value -Expiration $Expiration -StartDate $Start -Reason $Request.Body.Reason -CreatedBy (([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Headers.'x-ms-client-principal')) | ConvertFrom-Json).userDetails)
177179
}
178180
$Results.Add("Scheduling JIT Admin enable task for $Username")
179181
} else {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
accountEnabled = $_.accountEnabled
4747
jitAdminEnabled = $_.($Schema.id).jitAdminEnabled
4848
jitAdminExpiration = $_.($Schema.id).jitAdminExpiration
49+
jitAdminStartDate = $_.($Schema.id).jitAdminStartDate
4950
jitAdminReason = $_.($Schema.id).jitAdminReason
51+
jitAdminCreatedBy = $_.($Schema.id).jitAdminCreatedBy
5052
memberOf = $MemberOf
5153
}
5254
}
@@ -109,7 +111,9 @@
109111
accountEnabled = $UserObject.accountEnabled
110112
jitAdminEnabled = $UserObject.jitAdminEnabled
111113
jitAdminExpiration = $UserObject.jitAdminExpiration
114+
jitAdminStartDate = $UserObject.jitAdminStartDate
112115
jitAdminReason = $UserObject.jitAdminReason
116+
jitAdminCreatedBy = $UserObject.jitAdminCreatedBy
113117
memberOf = $UserObject.memberOf
114118
}
115119
)

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ function Invoke-CIPPStandardsRun {
5454
}
5555

5656
# Get tenant list for batch processing
57+
write-host "Getting tenants for filter: $TenantFilter"
5758
$AllTenantsList = if ($TenantFilter -eq 'allTenants') {
5859
Get-Tenants
5960
} else {

0 commit comments

Comments
 (0)