Skip to content

Commit 3c54f58

Browse files
committed
feat: Add Exchange calendar permissions modification functionality
Add new PowerShell functions for modifying calendar permissions in Exchange: - ExecModifyCalPerms.ps1: Core implementation for calendar permission modifications - Invoke-ExecModifyCalPerms.ps1: HTTP function wrapper for the calendar permissions modification
1 parent 5321546 commit 3c54f58

2 files changed

Lines changed: 297 additions & 0 deletions

File tree

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
using namespace System.Net
2+
3+
Function Invoke-ExecModifyCalPerms {
4+
<#
5+
.FUNCTIONALITY
6+
Entrypoint
7+
.ROLE
8+
Exchange.Calendar.ReadWrite
9+
#>
10+
[CmdletBinding()]
11+
param($Request, $TriggerMetadata)
12+
13+
$APIName = $Request.Params.CIPPEndpoint
14+
Write-LogMessage -headers $Request.Headers -API $APINAME-message 'Accessed this API' -Sev 'Debug'
15+
16+
$Username = $request.body.userID
17+
$Tenantfilter = $request.body.tenantfilter
18+
$Permissions = $request.body.permissions
19+
20+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Processing request for user: $Username, tenant: $Tenantfilter" -Sev 'Debug'
21+
22+
if ($username -eq $null) {
23+
Write-LogMessage -headers $Request.Headers -API $APINAME-message 'Username is null' -Sev 'Error'
24+
$body = [pscustomobject]@{'Results' = @('Username is required') }
25+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
26+
StatusCode = [HttpStatusCode]::BadRequest
27+
Body = $Body
28+
})
29+
return
30+
}
31+
32+
try {
33+
$userid = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($username)" -tenantid $Tenantfilter).id
34+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Retrieved user ID: $userid" -Sev 'Debug'
35+
}
36+
catch {
37+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Failed to get user ID: $($_.Exception.Message)" -Sev 'Error'
38+
$body = [pscustomobject]@{'Results' = @("Failed to get user ID: $($_.Exception.Message)") }
39+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
40+
StatusCode = [HttpStatusCode]::NotFound
41+
Body = $Body
42+
})
43+
return
44+
}
45+
46+
$Results = [System.Collections.ArrayList]::new()
47+
$HasErrors = $false
48+
49+
# Convert permissions to array format if it's an object with numeric keys
50+
if ($Permissions -is [PSCustomObject]) {
51+
if ($Permissions.PSObject.Properties.Name -match '^\d+$') {
52+
$Permissions = $Permissions.PSObject.Properties.Value
53+
}
54+
else {
55+
$Permissions = @($Permissions)
56+
}
57+
}
58+
59+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Processing $($Permissions.Count) permission entries" -Sev 'Debug'
60+
61+
foreach ($Permission in $Permissions) {
62+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Processing permission: $($Permission | ConvertTo-Json)" -Sev 'Debug'
63+
64+
$PermissionLevel = if ($Permission.PermissionLevel -is [PSCustomObject]) { $Permission.PermissionLevel.value } else { $Permission.PermissionLevel }
65+
$Modification = $Permission.Modification
66+
$CanViewPrivateItems = if ($Permission.PSObject.Properties.Name -contains 'CanViewPrivateItems') { $Permission.CanViewPrivateItems } else { $false }
67+
68+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Permission Level: $PermissionLevel, Modification: $Modification, CanViewPrivateItems: $CanViewPrivateItems" -Sev 'Debug'
69+
70+
# Handle UserID as array of objects or single value
71+
$TargetUsers = if ($Permission.UserID -is [PSCustomObject]) {
72+
@($Permission.UserID.value)
73+
}
74+
elseif ($Permission.UserID -is [array]) {
75+
$Permission.UserID | ForEach-Object { $_.value }
76+
}
77+
else {
78+
@($Permission.UserID)
79+
}
80+
81+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Target Users: $($TargetUsers -join ', ')" -Sev 'Debug'
82+
83+
foreach ($TargetUser in $TargetUsers) {
84+
try {
85+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Processing target user: $TargetUser" -Sev 'Debug'
86+
87+
if ($Modification -eq 'Remove') {
88+
try {
89+
$CalPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxFolderPermission' -cmdParams @{
90+
Identity = "$($userid):\Calendar"
91+
User = $TargetUser
92+
Confirm = $false
93+
}
94+
$null = $results.Add("Removed $($TargetUser) from $($username) Calendar permissions")
95+
}
96+
catch {
97+
$null = $results.Add("No existing permissions to remove for $($TargetUser)")
98+
}
99+
}
100+
else {
101+
# Map the permission level to the correct AccessRights value
102+
$AccessRights = switch ($PermissionLevel) {
103+
'Owner' { @('Owner') }
104+
'Editor' { @('Editor') }
105+
'Reviewer' { @('Reviewer') }
106+
'AvailabilityOnly' { @('AvailabilityOnly') }
107+
default { throw "Invalid permission level: $PermissionLevel" }
108+
}
109+
110+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Setting permissions with AccessRights: $($AccessRights -join ', ')" -Sev 'Debug'
111+
112+
$cmdParams = @{
113+
Identity = "$($userid):\Calendar"
114+
User = $TargetUser
115+
AccessRights = $AccessRights
116+
Confirm = $false
117+
}
118+
119+
if ($CanViewPrivateItems) {
120+
$cmdParams['SharingPermissionFlags'] = 'Delegate,CanViewPrivateItems'
121+
}
122+
123+
try {
124+
# Try Add first
125+
$CalPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Add-MailboxFolderPermission' -cmdParams $cmdParams
126+
$null = $results.Add("Granted $($TargetUser) $($PermissionLevel) access to $($username) Calendar$($CanViewPrivateItems ? ' with access to private items' : '')")
127+
}
128+
catch {
129+
# If Add fails, try Set
130+
$CalPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Set-MailboxFolderPermission' -cmdParams $cmdParams
131+
$null = $results.Add("Updated $($TargetUser) $($PermissionLevel) access to $($username) Calendar$($CanViewPrivateItems ? ' with access to private items' : '')")
132+
}
133+
}
134+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Successfully executed $($PermissionLevel) permission modification for $($TargetUser) on $($username)" -Sev 'Info' -tenant $TenantFilter
135+
}
136+
catch {
137+
$HasErrors = $true
138+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Could not execute $($PermissionLevel) permission modification for $($TargetUser) on $($username). Error: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter
139+
$null = $results.Add("Could not execute $($PermissionLevel) permission modification for $($TargetUser) on $($username). Error: $($_.Exception.Message)")
140+
}
141+
}
142+
}
143+
144+
if ($results.Count -eq 0) {
145+
Write-LogMessage -headers $Request.Headers -API $APINAME-message 'No results were generated from the operation' -Sev 'Warning'
146+
$null = $results.Add('No results were generated from the operation. Please check the logs for more details.')
147+
$HasErrors = $true
148+
}
149+
150+
$body = [pscustomobject]@{'Results' = @($results) }
151+
152+
# Associate values to output bindings by calling 'Push-OutputBinding'.
153+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
154+
StatusCode = if ($HasErrors) { [HttpStatusCode]::InternalServerError } else { [HttpStatusCode]::OK }
155+
Body = $Body
156+
})
157+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using namespace System.Net
2+
3+
Function Invoke-ExecModifyCalPerms {
4+
<#
5+
.FUNCTIONALITY
6+
Entrypoint
7+
.ROLE
8+
Exchange.Calendar.ReadWrite
9+
#>
10+
[CmdletBinding()]
11+
param($Request, $TriggerMetadata)
12+
13+
$APIName = $Request.Params.CIPPEndpoint
14+
Write-LogMessage -headers $Request.Headers -API $APINAME-message 'Accessed this API' -Sev 'Debug'
15+
16+
$Username = $request.body.userID
17+
$Tenantfilter = $request.body.tenantfilter
18+
$Permissions = $request.body.permissions
19+
20+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Processing request for user: $Username, tenant: $Tenantfilter" -Sev 'Debug'
21+
22+
if ($username -eq $null) {
23+
Write-LogMessage -headers $Request.Headers -API $APINAME-message 'Username is null' -Sev 'Error'
24+
$body = [pscustomobject]@{'Results' = @('Username is required') }
25+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
26+
StatusCode = [HttpStatusCode]::BadRequest
27+
Body = $Body
28+
})
29+
return
30+
}
31+
32+
try {
33+
$userid = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($username)" -tenantid $Tenantfilter).id
34+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Retrieved user ID: $userid" -Sev 'Debug'
35+
}
36+
catch {
37+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Failed to get user ID: $($_.Exception.Message)" -Sev 'Error'
38+
$body = [pscustomobject]@{'Results' = @("Failed to get user ID: $($_.Exception.Message)") }
39+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
40+
StatusCode = [HttpStatusCode]::NotFound
41+
Body = $Body
42+
})
43+
return
44+
}
45+
46+
$Results = [System.Collections.ArrayList]::new()
47+
$HasErrors = $false
48+
49+
# Convert permissions to array format if it's an object with numeric keys
50+
if ($Permissions -is [PSCustomObject]) {
51+
if ($Permissions.PSObject.Properties.Name -match '^\d+$') {
52+
$Permissions = $Permissions.PSObject.Properties.Value
53+
}
54+
else {
55+
$Permissions = @($Permissions)
56+
}
57+
}
58+
59+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Processing $($Permissions.Count) permission entries" -Sev 'Debug'
60+
61+
foreach ($Permission in $Permissions) {
62+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Processing permission: $($Permission | ConvertTo-Json)" -Sev 'Debug'
63+
64+
$PermissionLevel = $Permission.PermissionLevel.value ?? $Permission.PermissionLevel
65+
$Modification = $Permission.Modification
66+
$CanViewPrivateItems = $Permission.CanViewPrivateItems ?? $false
67+
68+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Permission Level: $PermissionLevel, Modification: $Modification, CanViewPrivateItems: $CanViewPrivateItems" -Sev 'Debug'
69+
70+
# Handle UserID as array or single value
71+
$TargetUsers = @($Permission.UserID | ForEach-Object { $_.value ?? $_ })
72+
73+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Target Users: $($TargetUsers -join ', ')" -Sev 'Debug'
74+
75+
foreach ($TargetUser in $TargetUsers) {
76+
try {
77+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Processing target user: $TargetUser" -Sev 'Debug'
78+
79+
if ($Modification -eq 'Remove') {
80+
try {
81+
$CalPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxFolderPermission' -cmdParams @{
82+
Identity = "$($userid):\Calendar"
83+
User = $TargetUser
84+
Confirm = $false
85+
}
86+
$null = $results.Add("Removed $($TargetUser) from $($username) Calendar permissions")
87+
}
88+
catch {
89+
$null = $results.Add("No existing permissions to remove for $($TargetUser)")
90+
}
91+
}
92+
else {
93+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Setting permissions with AccessRights: $PermissionLevel" -Sev 'Debug'
94+
95+
$cmdParams = @{
96+
Identity = "$($userid):\Calendar"
97+
User = $TargetUser
98+
AccessRights = $PermissionLevel
99+
Confirm = $false
100+
}
101+
102+
if ($CanViewPrivateItems) {
103+
$cmdParams['SharingPermissionFlags'] = 'Delegate,CanViewPrivateItems'
104+
}
105+
106+
try {
107+
# Try Add first
108+
$CalPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Add-MailboxFolderPermission' -cmdParams $cmdParams
109+
$null = $results.Add("Granted $($TargetUser) $($PermissionLevel) access to $($username) Calendar$($CanViewPrivateItems ? ' with access to private items' : '')")
110+
}
111+
catch {
112+
# If Add fails, try Set
113+
$CalPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Set-MailboxFolderPermission' -cmdParams $cmdParams
114+
$null = $results.Add("Updated $($TargetUser) $($PermissionLevel) access to $($username) Calendar$($CanViewPrivateItems ? ' with access to private items' : '')")
115+
}
116+
}
117+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Successfully executed $($PermissionLevel) permission modification for $($TargetUser) on $($username)" -Sev 'Info' -tenant $TenantFilter
118+
}
119+
catch {
120+
$HasErrors = $true
121+
Write-LogMessage -headers $Request.Headers -API $APINAME-message "Could not execute $($PermissionLevel) permission modification for $($TargetUser) on $($username). Error: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter
122+
$null = $results.Add("Could not execute $($PermissionLevel) permission modification for $($TargetUser) on $($username). Error: $($_.Exception.Message)")
123+
}
124+
}
125+
}
126+
127+
if ($results.Count -eq 0) {
128+
Write-LogMessage -headers $Request.Headers -API $APINAME-message 'No results were generated from the operation' -Sev 'Warning'
129+
$null = $results.Add('No results were generated from the operation. Please check the logs for more details.')
130+
$HasErrors = $true
131+
}
132+
133+
$body = [pscustomobject]@{'Results' = @($results) }
134+
135+
# Associate values to output bindings by calling 'Push-OutputBinding'.
136+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
137+
StatusCode = if ($HasErrors) { [HttpStatusCode]::InternalServerError } else { [HttpStatusCode]::OK }
138+
Body = $Body
139+
})
140+
}

0 commit comments

Comments
 (0)