Skip to content

Commit a08aff0

Browse files
authored
Merge pull request #769 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents e336e88 + 2c3bc5b commit a08aff0

File tree

8 files changed

+197
-70
lines changed

8 files changed

+197
-70
lines changed

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

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,26 @@ function Push-ExecJITAdminListAllTenants {
1313
# Get schema extensions
1414
$Schema = Get-CIPPSchemaExtensions | Where-Object { $_.id -match '_cippUser' } | Select-Object -First 1
1515

16-
# Query users with JIT Admin enabled
17-
$Query = @{
18-
TenantFilter = $DomainName # Use $DomainName for the current tenant
19-
Endpoint = 'users'
20-
Parameters = @{
21-
'$count' = 'true'
22-
'$select' = "id,accountEnabled,displayName,userPrincipalName,$($Schema.id)"
23-
'$filter' = "$($Schema.id)/jitAdminEnabled eq true or $($Schema.id)/jitAdminEnabled eq false" # Fetches both states to cache current status
24-
}
25-
}
26-
$Users = Get-GraphRequestList @Query | Where-Object { $_.id }
16+
# Query users with JIT Admin enabled using bulk request
17+
$BulkRequests = [System.Collections.Generic.List[object]]::new()
18+
$BulkRequests.Add(@{
19+
id = 'users'
20+
method = 'GET'
21+
url = "users?`$count=true&`$select=id,accountEnabled,displayName,userPrincipalName,$($Schema.id)&`$filter=$($Schema.id)/jitAdminEnabled eq true or $($Schema.id)/jitAdminEnabled eq false&`$top=999"
22+
})
23+
24+
$BulkResults = New-GraphBulkRequest -tenantid $DomainName -Requests $BulkRequests
25+
$Users = ($BulkResults | Where-Object { $_.id -eq 'users' }).body.value | Where-Object { $_.id }
2726

2827
if ($Users) {
2928
# Get role memberships
30-
$BulkRequests = $Users | ForEach-Object { @(
31-
@{
32-
id = $_.id
29+
$BulkRequests.Clear()
30+
foreach ($User in $Users) {
31+
$BulkRequests.Add(@{
32+
id = $User.id
3333
method = 'GET'
34-
url = "users/$($_.id)/memberOf/microsoft.graph.directoryRole/?`$select=id,displayName"
35-
}
36-
)
34+
url = "users/$($User.id)/memberOf/microsoft.graph.directoryRole/?`$select=id,displayName"
35+
})
3736
}
3837
# Ensure $BulkRequests is not empty or null before making the bulk request
3938
if ($BulkRequests -and $BulkRequests.Count -gt 0) {

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

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ function Push-ExecOnboardTenantQueue {
4747
@{ Name = 'SharePoint Administrator'; Id = 'f28a1f50-f6e7-4571-818b-6a12f2af6b6c' },
4848
@{ Name = 'Authentication Policy Administrator'; Id = '0526716b-113d-4c15-b2c8-68e3c22b9f80' },
4949
@{ Name = 'Privileged Role Administrator'; Id = 'e8611ab8-c189-46e8-94e1-60213ab1f814' },
50-
@{ Name = 'Privileged Authentication Administrator'; Id = '7be44c8a-adaf-4e2a-84d6-ab2649e08a13' }
50+
@{ Name = 'Privileged Authentication Administrator'; Id = '7be44c8a-adaf-4e2a-84d6-ab2649e08a13' },
51+
@{ Name = 'Billing Administrator'; Id = 'b0f54661-2d74-4c50-afa3-1ec803f12efe'; Optional = $true },
52+
@{ Name = 'Global Reader'; Id = 'f2ef992c-3afb-46b9-b7cf-a126ee74c451'; Optional = $true },
53+
@{ Name = 'Domain Name Administrator'; Id = '8329153b-31d0-4727-b945-745eb3bc5f31'; Optional = $true }
5154
)
5255

5356
if ($OnboardingSteps.Step1.Status -ne 'succeeded') {
@@ -99,14 +102,16 @@ function Push-ExecOnboardTenantQueue {
99102
}
100103
if (($MissingRoles | Measure-Object).Count -gt 0) {
101104
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = 'Missing roles for relationship' })
102-
if ($Item.IgnoreMissingRoles -ne $true) {
105+
$RequiredMissingRoles = $ExpectedRoles | Where-Object { $_.Optional -ne $true -and $MissingRoles -contains $_.Name }
106+
if ($Item.IgnoreMissingRoles -ne $true -and ($RequiredMissingRoles | Measure-Object).Count -gt 0) {
107+
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = "Missing the following required roles: $($MissingRoles -join ', ')" })
103108
$TenantOnboarding.Status = 'failed'
104109
$OnboardingSteps.Step2.Status = 'failed'
105110
$OnboardingSteps.Step2.Message = "Your GDAP relationship is missing the following roles: $($MissingRoles -join ', ')"
106111
} else {
107112
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = 'Ignoring missing roles' })
108113
$OnboardingSteps.Step2.Status = 'succeeded'
109-
$OnboardingSteps.Step2.Message = 'Your GDAP relationship is missing some roles, but the onboarding will continue'
114+
$OnboardingSteps.Step2.Message = "Your GDAP relationship is missing some roles, but the onboarding will continue. Missing roles: $($MissingRoles -join ', ')"
110115
}
111116
} else {
112117
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = 'Required roles found' })
@@ -121,6 +126,7 @@ function Push-ExecOnboardTenantQueue {
121126
if ($OnboardingSteps.Step2.Status -eq 'succeeded') {
122127
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = 'Checking group mapping' })
123128
$AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id/accessAssignments"
129+
$AccessAssignments = $AccessAssignments | Where-Object { $_.status -notin @('deleted', 'deleting') }
124130
if ($AccessAssignments.id -and $Item.AutoMapRoles -ne $true) {
125131
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = 'Groups mapped' })
126132
$OnboardingSteps.Step3.Status = 'succeeded'
@@ -223,6 +229,7 @@ function Push-ExecOnboardTenantQueue {
223229

224230
do {
225231
$AccessAssignments = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships/$Id/accessAssignments"
232+
$AccessAssignments = $AccessAssignments | Where-Object { $_.status -notin @('deleted', 'deleting') }
226233
Start-Sleep -Seconds 15
227234
} while ($AccessAssignments.status -contains 'pending' -and (Get-Date) -lt $Start.AddMinutes(8))
228235

@@ -231,19 +238,58 @@ function Push-ExecOnboardTenantQueue {
231238
$OnboardingSteps.Step3.Status = 'succeeded'
232239

233240
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = 'Checking for missing groups for SAM user' })
234-
$SamUserId = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/me?`$select=id" -NoAuthCheck $true).id
235-
$CurrentMemberships = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/me/transitiveMemberOf?`$select=id,displayName" -NoAuthCheck $true
236-
$ExpectedCippRoles = $Item.Roles | Where-Object { $_.roleDefinitionId -in $ExpectedRoles.roleDefinitionId }
241+
$BulkRequests = @(
242+
@{
243+
id = 'samUserId'
244+
method = 'GET'
245+
url = "/me?`$select=id"
246+
},
247+
@{
248+
id = 'currentMemberships'
249+
method = 'GET'
250+
url = "/me/transitiveMemberOf?`$select=id,displayName"
251+
}
252+
)
253+
$BulkResults = New-GraphBulkRequest -Requests $BulkRequests -NoAuthCheck $true
254+
$SamUserId = ($BulkResults | Where-Object { $_.id -eq 'samUserId' }).body.id
255+
$CurrentMemberships = ($BulkResults | Where-Object { $_.id -eq 'currentMemberships' }).body.value
256+
$ExpectedCippRoles = $Item.Roles | Where-Object { $_.roleDefinitionId -in $ExpectedRoles.Id }
257+
258+
# Build bulk requests for missing group memberships
259+
$GroupMembershipRequests = [System.Collections.Generic.List[object]]::new()
260+
$GroupMembershipLogs = [System.Collections.Generic.List[object]]::new()
261+
237262
foreach ($Role in $ExpectedCippRoles) {
238263
if ($CurrentMemberships.id -notcontains $Role.GroupId) {
239-
$PostBody = @{
240-
'@odata.id' = 'https://graph.microsoft.com/v1.0/directoryObjects/{0}' -f $SamUserId
241-
} | ConvertTo-Json -Compress
242-
try {
243-
New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($Role.GroupId)/members/`$ref" -body $PostBody -AsApp $true -NoAuthCheck $true
244-
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = "Added SAM user to $($Role.GroupName)" })
245-
} catch {
246-
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = "Failed to add SAM user to $($Role.GroupName) - $($_.Exception.Message)" })
264+
$GroupMembershipRequests.Add(@{
265+
id = "addSamUser-$($Role.GroupId)"
266+
method = 'POST'
267+
url = "groups/$($Role.GroupId)/members/`$ref"
268+
body = @{
269+
'@odata.id' = 'https://graph.microsoft.com/v1.0/directoryObjects/{0}' -f $SamUserId
270+
}
271+
headers = @{
272+
'Content-Type' = 'application/json'
273+
}
274+
})
275+
$GroupMembershipLogs.Add(@{
276+
id = "addSamUser-$($Role.GroupId)"
277+
GroupName = $Role.GroupName
278+
})
279+
}
280+
}
281+
282+
# Execute bulk group membership additions if any are needed
283+
if ($GroupMembershipRequests.Count -gt 0) {
284+
$GroupMembershipResults = New-GraphBulkRequest -Requests $GroupMembershipRequests -AsApp $true -NoAuthCheck $true
285+
286+
foreach ($LogEntry in $GroupMembershipLogs) {
287+
$Result = $GroupMembershipResults | Where-Object { $_.id -eq $LogEntry.id }
288+
if ($Result.status -match '^2[0-9]+') {
289+
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = "Added SAM user to $($LogEntry.GroupName)" })
290+
} else {
291+
$ErrorMessage = if ($Result.body.error.message) { $Result.body.error.message } else { 'Unknown error' }
292+
$Logs.Add([PSCustomObject]@{ Date = (Get-Date).ToUniversalTime(); Log = "Failed to add SAM user to $($LogEntry.GroupName) - $ErrorMessage" })
247293
}
248294
}
249295
}

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

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,23 @@
1717

1818
if ($TenantFilter -ne 'AllTenants') {
1919
# Single tenant logic
20-
$Query = @{
21-
TenantFilter = $TenantFilter
22-
Endpoint = 'users'
23-
Parameters = @{
24-
'$count' = 'true'
25-
'$select' = "id,accountEnabled,displayName,userPrincipalName,$($Schema.id)"
26-
'$filter' = "$($Schema.id)/jitAdminEnabled eq true or $($Schema.id)/jitAdminEnabled eq false"
27-
}
28-
}
29-
$Users = Get-GraphRequestList @Query | Where-Object { $_.id }
30-
$BulkRequests = $Users | ForEach-Object { @(
31-
@{
32-
id = $_.id
20+
$BulkRequests = [System.Collections.Generic.List[object]]::new()
21+
$BulkRequests.Add(@{
22+
id = 'users'
23+
method = 'GET'
24+
url = "users?`$count=true&`$select=id,accountEnabled,displayName,userPrincipalName,$($Schema.id)&`$filter=$($Schema.id)/jitAdminEnabled eq true or $($Schema.id)/jitAdminEnabled eq false&`$top=999"
25+
})
26+
27+
$BulkResults = New-GraphBulkRequest -tenantid $TenantFilter -Requests $BulkRequests
28+
$Users = ($BulkResults | Where-Object { $_.id -eq 'users' }).body.value | Where-Object { $_.id }
29+
30+
$BulkRequests.Clear()
31+
foreach ($User in $Users) {
32+
$BulkRequests.Add(@{
33+
id = $User.id
3334
method = 'GET'
34-
url = "users/$($_.id)/memberOf/microsoft.graph.directoryRole/?`$select=id,displayName"
35-
}
36-
)
35+
url = "users/$($User.id)/memberOf/microsoft.graph.directoryRole/?`$select=id,displayName"
36+
})
3737
}
3838
$RoleResults = New-GraphBulkRequest -tenantid $TenantFilter -Requests @($BulkRequests)
3939
# Write-Information ($RoleResults | ConvertTo-Json -Depth 10 )
@@ -54,7 +54,7 @@
5454
}
5555

5656
# Write-Information ($Results | ConvertTo-Json -Depth 10)
57-
$Metadata = [PSCustomObject]@{Parameters = $Query.Parameters }
57+
$Metadata = [PSCustomObject]@{Method = 'BulkRequest' }
5858
} else {
5959
# AllTenants logic
6060
$Results = [System.Collections.Generic.List[object]]::new()

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecAddGDAPRole.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ function Invoke-ExecAddGDAPRole {
7171
@{ label = 'SharePoint Administrator'; value = 'f28a1f50-f6e7-4571-818b-6a12f2af6b6c' },
7272
@{ label = 'Authentication Policy Administrator'; value = '0526716b-113d-4c15-b2c8-68e3c22b9f80' },
7373
@{ label = 'Privileged Role Administrator'; value = 'e8611ab8-c189-46e8-94e1-60213ab1f814' },
74-
@{ label = 'Privileged Authentication Administrator'; value = '7be44c8a-adaf-4e2a-84d6-ab2649e08a13' }
74+
@{ label = 'Privileged Authentication Administrator'; value = '7be44c8a-adaf-4e2a-84d6-ab2649e08a13' },
75+
@{ label = 'Billing Administrator'; value = 'b0f54661-2d74-4c50-afa3-1ec803f12efe' },
76+
@{ label = 'Global Reader'; value = 'f2ef992c-3afb-46b9-b7cf-a126ee74c451' },
77+
@{ label = 'Domain Name Administrator'; value = '8329153b-31d0-4727-b945-745eb3bc5f31' }
7578
)
7679

7780
$Groups = $Request.Body.gdapRoles ?? $CippDefaults

Modules/CIPPCore/Public/Set-CIPPDBCacheManagedDevices.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ function Set-CIPPDBCacheManagedDevices {
1818

1919
try {
2020
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching managed devices' -sev Debug
21-
$ManagedDevices = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/managedDevices?$top=999&$select=id,deviceName,operatingSystem,osVersion,complianceState,managedDeviceOwnerType,enrolledDateTime,lastSyncDateTime' -tenantid $TenantFilter
21+
$ManagedDevices = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/managedDevices?$top=999' -tenantid $TenantFilter
2222
if (!$ManagedDevices) { $ManagedDevices = @() }
2323
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'ManagedDevices' -Data $ManagedDevices
2424
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'ManagedDevices' -Data $ManagedDevices -Count

Modules/CIPPCore/Public/Set-CIPPDBCacheUsers.ps1

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,75 @@ function Set-CIPPDBCacheUsers {
1919
try {
2020
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching users' -sev Debug
2121

22+
$SignInLogsCapable = Test-CIPPStandardLicense -StandardName 'UserSignInLogsCapable' -TenantFilter $TenantFilter -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') -SkipLog
23+
24+
# Base properties needed by tests, standards, reports, UI, and integrations (Hudu, NinjaOne)
25+
$BaseSelect = @(
26+
# Core identity
27+
'id'
28+
'displayName'
29+
'userPrincipalName'
30+
'givenName'
31+
'surname'
32+
'mailNickname'
33+
34+
# Account status
35+
'accountEnabled'
36+
'userType'
37+
'isResourceAccount'
38+
'createdDateTime'
39+
40+
# Security & policies
41+
'passwordPolicies'
42+
'perUserMfaState'
43+
44+
# Contact information
45+
'mail'
46+
'otherMails'
47+
'mobilePhone'
48+
'businessPhones'
49+
'faxNumber'
50+
'proxyAddresses'
51+
52+
# Location & organization
53+
'jobTitle'
54+
'department'
55+
'companyName'
56+
'officeLocation'
57+
'city'
58+
'state'
59+
'country'
60+
'postalCode'
61+
'streetAddress'
62+
63+
# Settings
64+
'preferredLanguage'
65+
'usageLocation'
66+
'preferredDataLocation'
67+
'showInAddressList'
68+
69+
# Licenses
70+
'assignedLicenses'
71+
'assignedPlans'
72+
'licenseAssignmentStates'
73+
74+
# On-premises sync
75+
'onPremisesSyncEnabled'
76+
'onPremisesImmutableId'
77+
'onPremisesLastSyncDateTime'
78+
'onPremisesDistinguishedName'
79+
)
80+
81+
if ($SignInLogsCapable) {
82+
$Select = ($BaseSelect + 'signInActivity') -join ','
83+
$Top = 500
84+
} else {
85+
$Select = $BaseSelect -join ','
86+
$Top = 999
87+
}
88+
2289
# Stream users directly from Graph API to batch processor
23-
# Using $top=500 due to signInActivity limitation
24-
New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=500&$select=signInActivity' -tenantid $TenantFilter |
90+
New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=$Top&`$select=$Select&`$count=true" -ComplexFilter -tenantid $TenantFilter |
2591
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'Users' -AddCount
2692

2793
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Cached users successfully' -sev Debug

Modules/CIPPCore/Public/Test-CIPPAccessTenant.ps1

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ function Test-CIPPAccessTenant {
1717
@{ Name = 'SharePoint Administrator'; Id = 'f28a1f50-f6e7-4571-818b-6a12f2af6b6c' },
1818
@{ Name = 'Authentication Policy Administrator'; Id = '0526716b-113d-4c15-b2c8-68e3c22b9f80' },
1919
@{ Name = 'Privileged Role Administrator'; Id = 'e8611ab8-c189-46e8-94e1-60213ab1f814' },
20-
@{ Name = 'Privileged Authentication Administrator'; Id = '7be44c8a-adaf-4e2a-84d6-ab2649e08a13' }
20+
@{ Name = 'Privileged Authentication Administrator'; Id = '7be44c8a-adaf-4e2a-84d6-ab2649e08a13' },
21+
@{ Name = 'Billing Administrator'; Id = 'b0f54661-2d74-4c50-afa3-1ec803f12efe'; Optional = $true },
22+
@{ Name = 'Global Reader'; Id = 'f2ef992c-3afb-46b9-b7cf-a126ee74c451'; Optional = $true },
23+
@{ Name = 'Domain Name Administrator'; Id = '8329153b-31d0-4727-b945-745eb3bc5f31'; Optional = $true }
2124
)
2225

2326
$TenantParams = @{
@@ -82,11 +85,11 @@ function Test-CIPPAccessTenant {
8285
if (!$Role) {
8386
$MissingRoles.Add(
8487
[PSCustomObject]@{
85-
Name = $RoleId.Name
86-
Type = 'Tenant'
88+
Name = $RoleId.Name
89+
Type = 'Tenant'
90+
Optional = $RoleId.Optional
8791
}
8892
)
89-
$AddedText = 'but missing GDAP roles'
9093
} else {
9194
$GDAPRoles.Add([PSCustomObject]@{
9295
Role = $RoleId.Name
@@ -95,6 +98,13 @@ function Test-CIPPAccessTenant {
9598
}
9699
}
97100

101+
$RequiredMissingRoles = $MissingRoles | Where-Object { $_.Optional -ne $true }
102+
if (($RequiredMissingRoles | Measure-Object).Count -gt 0) {
103+
$AddedText = 'but missing required GDAP roles'
104+
} elseif (($MissingRoles | Measure-Object).Count -gt 0) {
105+
$AddedText = 'but missing optional GDAP roles'
106+
}
107+
98108
$GraphTest = "Successfully connected to Graph $($AddedText)"
99109
$GraphStatus = $true
100110
} catch {

0 commit comments

Comments
 (0)