Skip to content

Commit 69e32b0

Browse files
authored
Merge pull request #316 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 5183ad2 + 6b842b5 commit 69e32b0

File tree

5 files changed

+208
-60
lines changed

5 files changed

+208
-60
lines changed

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecAccessChecks.ps1

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,19 @@ function Invoke-ExecAccessChecks {
5050
$Results = foreach ($Tenant in $Tenants) {
5151
$TenantCheck = $AccessChecks | Where-Object -Property RowKey -EQ $Tenant.customerId | Select-Object -Property Data
5252
$TenantResult = [PSCustomObject]@{
53-
TenantId = $Tenant.customerId
54-
TenantName = $Tenant.displayName
55-
DefaultDomainName = $Tenant.defaultDomainName
56-
GraphStatus = 'Not run yet'
57-
ExchangeStatus = 'Not run yet'
58-
GDAPRoles = ''
59-
MissingRoles = ''
60-
LastRun = ''
61-
GraphTest = ''
62-
ExchangeTest = ''
63-
OrgManagementRoles = @()
53+
TenantId = $Tenant.customerId
54+
TenantName = $Tenant.displayName
55+
DefaultDomainName = $Tenant.defaultDomainName
56+
GraphStatus = 'Not run yet'
57+
ExchangeStatus = 'Not run yet'
58+
GDAPRoles = ''
59+
MissingRoles = ''
60+
LastRun = ''
61+
GraphTest = ''
62+
ExchangeTest = ''
63+
OrgManagementRoles = @()
64+
OrgManagementRolesMissing = @()
65+
OrgManagementRepairNeeded = $false
6466
}
6567
if ($TenantCheck) {
6668
$Data = @($TenantCheck.Data | ConvertFrom-Json -ErrorAction Stop)
@@ -71,7 +73,9 @@ function Invoke-ExecAccessChecks {
7173
$TenantResult.LastRun = $Data.LastRun
7274
$TenantResult.GraphTest = $Data.GraphTest
7375
$TenantResult.ExchangeTest = $Data.ExchangeTest
74-
$TenantResult.OrgManagementRoles = $Data.OrgManagementRoles
76+
$TenantResult.OrgManagementRoles = $Data.OrgManagementRoles ? @($Data.OrgManagementRoles) : @()
77+
$TenantResult.OrgManagementRolesMissing = $Data.OrgManagementRolesMissing ? @($Data.OrgManagementRolesMissing) : @()
78+
$TenantResult.OrgManagementRepairNeeded = $Data.OrgManagementRolesMissing.Count -gt 0
7579
}
7680
$TenantResult
7781
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
function Invoke-ExecExchangeRoleRepair {
2+
<#
3+
.FUNCTIONALITY
4+
Entrypoint
5+
.ROLE
6+
CIPP.AppSettings.ReadWrite
7+
#>
8+
[CmdletBinding()]
9+
param($Request, $TriggerMetadata)
10+
11+
$Headers = $Request.Headers
12+
13+
$TenantId = $Request.Query.tenantId ?? $Request.Body.tenantId
14+
$Tenant = Get-Tenants -TenantFilter $TenantId
15+
16+
try {
17+
Write-Information "Starting Exchange Organization Management role repair for tenant: $($Tenant.defaultDomainName)"
18+
$OrgManagementRoles = New-ExoRequest -tenantid $Tenant.customerId -cmdlet 'Get-ManagementRoleAssignment' -cmdParams @{ RoleAssignee = 'Organization Management'; Delegating = $false } | Select-Object -Property Role, Guid
19+
Write-Information "Found $($OrgManagementRoles.Count) Organization Management roles in Exchange"
20+
21+
$RoleDefinitions = New-GraphGetRequest -tenantid $Tenant.customerId -uri 'https://graph.microsoft.com/beta/roleManagement/exchange/roleDefinitions'
22+
Write-Information "Found $($RoleDefinitions.Count) Exchange role definitions"
23+
24+
$BasePath = Get-Module -Name 'CIPPCore' | Select-Object -ExpandProperty ModuleBase
25+
$AllOrgManagementRoles = Get-Content -Path "$BasePath\Public\OrganizationManagementRoles.json" -ErrorAction Stop | ConvertFrom-Json
26+
27+
$AvailableRoles = $RoleDefinitions | Where-Object -Property displayName -In $AllOrgManagementRoles | Select-Object -Property displayName, id, description
28+
Write-Information "Found $($AvailableRoles.Count) available Organization Management roles in Exchange"
29+
$MissingOrgMgmtRoles = $AvailableRoles | Where-Object { $OrgManagementRoles.Role -notcontains $_.displayName }
30+
31+
if ($MissingOrgMgmtRoles.Count -gt 0) {
32+
$Requests = foreach ($Role in $MissingOrgMgmtRoles) {
33+
[PSCustomObject]@{
34+
id = $Role.id
35+
method = 'POST'
36+
url = 'roleManagement/exchange/roleAssignments'
37+
body = @{
38+
principalId = '/RoleGroups/Organization Management'
39+
roleDefinitionId = $Role.id
40+
directoryScopeId = '/'
41+
appScopeId = $null
42+
}
43+
headers = @{
44+
'Content-Type' = 'application/json'
45+
}
46+
}
47+
}
48+
49+
$RepairResults = New-GraphBulkRequest -tenantid $Tenant.customerId -Requests @($Requests) -asapp $true
50+
$RepairSuccess = $RepairResults.status -eq 201
51+
if ($RepairSuccess) {
52+
$Results = @{
53+
state = 'success'
54+
resultText = "Successfully repaired the missing Organization Management roles: $($MissingOrgMgmtRoles.displayName -join ', ')"
55+
}
56+
Write-LogMessage -headers $Headers -tenant $Tenant.defaultDomainName -tenantid $Tenant.customerId -Message "Successfully repaired the missing Organization Management roles: $($MissingOrgMgmtRoles.displayName -join ', '). Run another Tenant Access check after waiting a bit for replication." -sev 'Info'
57+
} else {
58+
# Get roles that failed to repair
59+
$FailedRoles = $RepairResults | Where-Object { $_.status -ne 201 } | ForEach-Object {
60+
$RoleId = $_.id
61+
$Role = $MissingOrgMgmtRoles | Where-Object { $_.id -eq $RoleId }
62+
$Role.displayName
63+
}
64+
$PermissionError = $false
65+
if ($RepairResults.status -in (401, 403, 500)) {
66+
$PermissionError = $true
67+
}
68+
$Results = @{
69+
state = 'error'
70+
resultText = "Failed to repair the missing Organization Management roles: $($FailedRoles -join ', ').$(if ($PermissionError) { " This may be due to insufficient permissions. The required Graph Permission is 'Application - RoleManagement.ReadWrite.Exchange'" })"
71+
}
72+
Write-LogMessage -headers $Headers -tenant $Tenant.defaultDomainName -tenantid $Tenant.customerId -Message "Failed to repair the missing Organization Management roles: $($FailedRoles -join ', ')" -sev 'Error'
73+
}
74+
} else {
75+
$Results = @{
76+
state = 'success'
77+
resultText = 'No missing Organization Management roles found.'
78+
}
79+
}
80+
} catch {
81+
$ErrorMessage = Get-CippException -Exception $_
82+
Write-Warning "Exception during Exchange Organization Management role repair: $($ErrorMessage.NormalizedError)"
83+
Write-LogMessage -headers $Headers -tenant $Tenant.defaultDomainName -tenantid $Tenant.customerId -Message "Exchange Organization Management role repair failed: $($ErrorMessage.NormalizedError)" -sev 'Error' -LogData $ErrorMessage
84+
$Results = @{
85+
state = 'error'
86+
resultText = "Exchange Organization Management role repair failed: $($ErrorMessage.NormalizedError)"
87+
}
88+
}
89+
90+
Push-OutputBinding -Name 'Response' -Value ([HttpResponseContext]@{
91+
StatusCode = [System.Net.HttpStatusCode]::OK
92+
Body = $Results
93+
})
94+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
[
2+
"Audit Logs",
3+
"Communication Compliance Admin",
4+
"Communication Compliance Investigation",
5+
"Compliance Admin",
6+
"Data Loss Prevention",
7+
"Distribution Groups",
8+
"E-Mail Address Policies",
9+
"Federated Sharing",
10+
"Information Protection Admin",
11+
"Information Protection Analyst",
12+
"Information Protection Investigator",
13+
"Information Protection Reader",
14+
"Information Rights Management",
15+
"Insider Risk Management Admin",
16+
"Insider Risk Management Investigation",
17+
"Journaling",
18+
"Legal Hold",
19+
"Mail Enabled Public Folders",
20+
"Mail Recipient Creation",
21+
"Mail Recipients",
22+
"Mail Tips",
23+
"Message Tracking",
24+
"Migration",
25+
"Move Mailboxes",
26+
"Org Custom Apps",
27+
"Org Marketplace Apps",
28+
"Organization Client Access",
29+
"Organization Configuration",
30+
"Organization Transport Settings",
31+
"PlacesBuildingManagement",
32+
"PlacesDeskManagement",
33+
"Privacy Management Admin",
34+
"Privacy Management Investigation",
35+
"Public Folders",
36+
"Recipient Policies",
37+
"Remote and Accepted Domains",
38+
"Reset Password",
39+
"Retention Management",
40+
"Role Management",
41+
"Security Admin",
42+
"Security Group Creation and Membership",
43+
"Security Reader",
44+
"TenantPlacesManagement",
45+
"Transport Hygiene",
46+
"Transport Rules",
47+
"User Options",
48+
"View-Only Audit Logs",
49+
"View-Only Configuration",
50+
"View-Only Recipients"
51+
]

Modules/CIPPCore/Public/SAMManifest.json

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@
143143
"id": "292d869f-3427-49a8-9dab-8c70152b74e9",
144144
"type": "Role"
145145
},
146+
{
147+
"id": "2cb92fee-97a3-4034-8702-24a6f5d0d1e9",
148+
"type": "Role"
149+
},
146150
{
147151
"id": "b6890674-9dd5-4e42-bb15-5af07f541ae1",
148152
"type": "Role"
@@ -191,6 +195,10 @@
191195
"id": "2a60023f-3219-47ad-baa4-40e17cd02a1d",
192196
"type": "Role"
193197
},
198+
{
199+
"id": "025d3225-3f02-4882-b4c0-cd5b541a4e80",
200+
"type": "Role"
201+
},
194202
{
195203
"id": "04c55753-2244-4c25-87fc-704ab82a4f69",
196204
"type": "Role"
@@ -399,6 +407,10 @@
399407
"id": "46ca0847-7e6b-426e-9775-ea810a948356",
400408
"type": "Scope"
401409
},
410+
{
411+
"id": "346c19ff-3fb2-4e81-87a0-bac9e33990c1",
412+
"type": "Scope"
413+
},
402414
{
403415
"id": "e67e6727-c080-415e-b521-e3f35d5248e9",
404416
"type": "Scope"
@@ -558,14 +570,6 @@
558570
{
559571
"id": "b7887744-6746-4312-813d-72daeaee7e2d",
560572
"type": "Scope"
561-
},
562-
{
563-
"id": "346c19ff-3fb2-4e81-87a0-bac9e33990c1",
564-
"type": "Scope"
565-
},
566-
{
567-
"id": "2cb92fee-97a3-4034-8702-24a6f5d0d1e9",
568-
"type": "Role"
569573
}
570574
]
571575
},
@@ -639,4 +643,4 @@
639643
]
640644
}
641645
]
642-
}
646+
}

Modules/CIPPCore/Public/Test-CIPPAccessTenant.ps1

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,16 @@ function Test-CIPPAccessTenant {
4848
$ExchangeStatus = $false
4949

5050
$Results = [PSCustomObject]@{
51-
TenantName = $Tenant.defaultDomainName
52-
GraphStatus = $false
53-
GraphTest = ''
54-
ExchangeStatus = $false
55-
ExchangeTest = ''
56-
GDAPRoles = ''
57-
MissingRoles = ''
58-
OrgManagementRoles = @()
59-
LastRun = (Get-Date).ToUniversalTime()
51+
TenantName = $Tenant.defaultDomainName
52+
GraphStatus = $false
53+
GraphTest = ''
54+
ExchangeStatus = $false
55+
ExchangeTest = ''
56+
GDAPRoles = ''
57+
MissingRoles = ''
58+
OrgManagementRoles = @()
59+
OrgManagementRolesMissing = @()
60+
LastRun = (Get-Date).ToUniversalTime()
6061
}
6162

6263
$AddedText = ''
@@ -104,39 +105,32 @@ function Test-CIPPAccessTenant {
104105

105106
try {
106107
$null = New-ExoRequest -tenantid $Tenant.customerId -cmdlet 'Get-OrganizationConfig' -ErrorAction Stop
107-
$ExchangeStatus = $true
108-
$ExchangeTest = 'Successfully connected to Exchange'
109-
110-
# Get the Exchange role definitions and assignments for the Organization Management role group
111-
$Requests = @(
112-
@{
113-
id = 'roleDefinitions'
114-
method = 'GET'
115-
url = 'roleManagement/exchange/roleDefinitions?$top=999'
116-
}
117-
@{
118-
id = 'roleAssignments'
119-
method = 'GET'
120-
url = "roleManagement/exchange/roleAssignments?`$filter=principalId eq '/RoleGroups/Organization Management'&`$top=999"
121-
}
122-
)
123-
124-
$ExchangeRoles = New-GraphBulkRequest -tenantid $Tenant.customerId -Requests $Requests
125-
126-
# Get results and expand assigments with role definitions
127-
$RoleDefinitions = ($ExchangeRoles | Where-Object -Property id -EQ 'roleDefinitions').body.value | Select-Object -Property id, displayName, description, isBuiltIn, isEnabled
128-
$RoleAssignments = ($ExchangeRoles | Where-Object -Property id -EQ 'roleAssignments').body.value
129-
$OrgManagementAssignments = $RoleAssignments | Where-Object -Property principalId -EQ '/RoleGroups/Organization Management' | Sort-Object -Property roleDefinitionId -Unique
130-
$OrgManagementRoles = $OrgManagementAssignments | ForEach-Object {
131-
$RoleDefinitions | Where-Object -Property id -EQ $_.roleDefinitionId
132-
} | Sort-Object -Property displayName
133108

134-
Write-Warning "Found $($OrgManagementRoles.Count) Organization Management role assignments in Exchange"
109+
$OrgManagementRoles = New-ExoRequest -tenantid $Tenant.customerId -cmdlet 'Get-ManagementRoleAssignment' -cmdParams @{ RoleAssignee = 'Organization Management'; Delegating = $false } | Select-Object -Property Role, Guid
110+
Write-Information "Found $($OrgManagementRoles.Count) Organization Management roles in Exchange"
135111
$Results.OrgManagementRoles = $OrgManagementRoles
136112

137-
# TODO: Get list of known good roles and compare against the found roles
138-
139-
113+
$RoleDefinitions = New-GraphGetRequest -tenantid $Tenant.customerId -uri 'https://graph.microsoft.com/beta/roleManagement/exchange/roleDefinitions'
114+
Write-Information "Found $($RoleDefinitions.Count) Exchange role definitions"
115+
116+
$BasePath = Get-Module -Name 'CIPPCore' | Select-Object -ExpandProperty ModuleBase
117+
$AllOrgManagementRoles = Get-Content -Path "$BasePath\Public\OrganizationManagementRoles.json" -ErrorAction Stop | ConvertFrom-Json
118+
Write-Information "Loaded all Organization Management roles from $BasePath\Public\OrganizationManagementRoles.json"
119+
120+
$AvailableRoles = $RoleDefinitions | Where-Object -Property displayName -In $AllOrgManagementRoles | Select-Object -Property displayName, id, description
121+
Write-Information "Found $($AvailableRoles.Count) available Organization Management roles in Exchange"
122+
$MissingOrgMgmtRoles = $AvailableRoles | Where-Object { $OrgManagementRoles.Role -notcontains $_.displayName }
123+
if (($MissingOrgMgmtRoles | Measure-Object).Count -gt 0) {
124+
$Results.OrgManagementRolesMissing = $MissingOrgMgmtRoles
125+
Write-Warning "Found $($MissingRoles.Count) missing Organization Management roles in Exchange"
126+
$ExchangeStatus = $false
127+
$ExchangeTest = 'Connected to Exchange but missing permissions in Organization Management. This may impact the ability to manage Exchange features'
128+
Write-LogMessage -headers $Headers -API $APINAME -tenant $tenant.defaultDomainName -message 'Tenant access check for Exchange failed: Missing Organization Management roles' -Sev 'Warning' -LogData $MissingOrgMgmtRoles
129+
} else {
130+
Write-Warning 'All available Organization Management roles are present in Exchange'
131+
$ExchangeStatus = $true
132+
$ExchangeTest = 'Successfully connected to Exchange'
133+
}
140134
} catch {
141135
$ErrorMessage = Get-CippException -Exception $_
142136
$ReportedError = ($_.ErrorDetails | ConvertFrom-Json -ErrorAction SilentlyContinue)
@@ -145,6 +139,7 @@ function Test-CIPPAccessTenant {
145139

146140
$ExchangeTest = "Failed to connect to Exchange: $($ErrorMessage.NormalizedError)"
147141
Write-LogMessage -headers $Headers -API $APINAME -tenant $tenant.defaultDomainName -message "Tenant access check for Exchange failed: $($ErrorMessage.NormalizedError) " -Sev 'Error' -LogData $ErrorMessage
142+
Write-Warning "Failed to connect to Exchange: $($_.Exception.Message)"
148143
}
149144

150145
if ($GraphStatus -and $ExchangeStatus) {

0 commit comments

Comments
 (0)