Skip to content

Commit 256af2b

Browse files
authored
Merge pull request #333 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents bf2e598 + 5a87efc commit 256af2b

File tree

6 files changed

+386
-24
lines changed

6 files changed

+386
-24
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using namespace System.Net
2+
3+
Function Invoke-AddRoomList {
4+
<#
5+
.FUNCTIONALITY
6+
Entrypoint
7+
.ROLE
8+
Exchange.Room.ReadWrite
9+
#>
10+
[CmdletBinding()]
11+
param($Request, $TriggerMetadata)
12+
13+
$APIName = $Request.Params.CIPPEndpoint
14+
$Headers = $Request.Headers
15+
Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug'
16+
17+
$Tenant = $Request.Body.tenantFilter ?? $Request.Body.tenantid
18+
19+
$Results = [System.Collections.Generic.List[Object]]::new()
20+
$RoomListObject = $Request.Body
21+
22+
# Construct email address from username and domain
23+
$EmailAddress = if ($RoomListObject.primDomain.value) {
24+
"$($RoomListObject.username)@$($RoomListObject.primDomain.value)"
25+
} else {
26+
"$($RoomListObject.username)@$($Tenant)"
27+
}
28+
29+
# Parameters for New-DistributionGroup with RoomList
30+
$AddRoomListParams = @{
31+
Name = $RoomListObject.username
32+
DisplayName = $RoomListObject.displayName
33+
RoomList = $true
34+
PrimarySMTPAddress = $EmailAddress
35+
}
36+
37+
try {
38+
$AddRoomListRequest = New-ExoRequest -tenantid $Tenant -cmdlet 'New-DistributionGroup' -cmdParams $AddRoomListParams
39+
$Results.Add("Successfully created room list: $($RoomListObject.displayName).")
40+
Write-LogMessage -Headers $Headers -API $APINAME -tenant $Tenant -message "Created room list $($RoomListObject.displayName) with id $($AddRoomListRequest.identity)" -Sev 'Info'
41+
42+
$StatusCode = [HttpStatusCode]::OK
43+
} catch {
44+
$ErrorMessage = Get-CippException -Exception $_
45+
$Message = "Failed to create room list: $($RoomListObject.displayName). Error: $($ErrorMessage.NormalizedError)"
46+
Write-LogMessage -Headers $Headers -API $APIName -tenant $Tenant -message $Message -Sev 'Error' -LogData $ErrorMessage
47+
$Results.Add($Message)
48+
$StatusCode = [HttpStatusCode]::InternalServerError
49+
}
50+
51+
$Body = [pscustomobject] @{ 'Results' = @($Results) }
52+
# Associate values to output bindings by calling 'Push-OutputBinding'.
53+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
54+
StatusCode = $StatusCode
55+
Body = $Body
56+
})
57+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
using namespace System.Net
2+
3+
Function Invoke-EditRoomList {
4+
<#
5+
.FUNCTIONALITY
6+
Entrypoint
7+
.ROLE
8+
Exchange.Room.ReadWrite
9+
#>
10+
[CmdletBinding()]
11+
param($Request, $TriggerMetadata)
12+
13+
$APIName = $Request.Params.CIPPEndpoint
14+
$Headers = $Request.Headers
15+
Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug'
16+
17+
$Results = [System.Collections.Generic.List[string]]::new()
18+
$RoomListObj = $Request.Body
19+
$GroupId = $RoomListObj.groupId
20+
$TenantId = $RoomListObj.tenantFilter
21+
22+
try {
23+
# Edit basic room list properties
24+
if ($RoomListObj.displayName -or $RoomListObj.description -or $RoomListObj.mailNickname) {
25+
$SetRoomListParams = @{
26+
Identity = $GroupId
27+
}
28+
29+
if ($RoomListObj.displayName) {
30+
$SetRoomListParams.DisplayName = $RoomListObj.displayName
31+
}
32+
33+
if ($RoomListObj.description) {
34+
$SetRoomListParams.Description = $RoomListObj.description
35+
}
36+
37+
if ($RoomListObj.mailNickname) {
38+
$SetRoomListParams.Name = $RoomListObj.mailNickname
39+
}
40+
41+
try {
42+
$null = New-ExoRequest -tenantid $TenantId -cmdlet 'Set-DistributionGroup' -cmdParams $SetRoomListParams -useSystemMailbox $true
43+
$Results.Add("Successfully updated room list properties for $($RoomListObj.displayName)")
44+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Updated room list properties for $($RoomListObj.displayName)" -Sev 'Info'
45+
} catch {
46+
$Results.Add("Failed to update room list properties: $($_.Exception.Message)")
47+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Failed to update room list properties: $($_.Exception.Message)" -Sev 'Error'
48+
}
49+
}
50+
51+
# Add room members
52+
if ($RoomListObj.AddMember) {
53+
foreach ($Member in $RoomListObj.AddMember) {
54+
try {
55+
$MemberEmail = if ($Member.value) { $Member.value } else { $Member }
56+
$AddMemberParams = @{
57+
Identity = $GroupId
58+
Member = $MemberEmail
59+
BypassSecurityGroupManagerCheck = $true
60+
}
61+
62+
$null = New-ExoRequest -tenantid $TenantId -cmdlet 'Add-DistributionGroupMember' -cmdParams $AddMemberParams -useSystemMailbox $true
63+
$Results.Add("Successfully added room $MemberEmail to room list")
64+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Added room $MemberEmail to room list $GroupId" -Sev 'Info'
65+
} catch {
66+
$Results.Add("Failed to add room $MemberEmail : $($_.Exception.Message)")
67+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Failed to add room $MemberEmail : $($_.Exception.Message)" -Sev 'Error'
68+
}
69+
}
70+
}
71+
72+
# Remove room members
73+
if ($RoomListObj.RemoveMember) {
74+
foreach ($Member in $RoomListObj.RemoveMember) {
75+
try {
76+
$MemberEmail = if ($Member.value) { $Member.value } else { $Member }
77+
$RemoveMemberParams = @{
78+
Identity = $GroupId
79+
Member = $MemberEmail
80+
BypassSecurityGroupManagerCheck = $true
81+
}
82+
83+
$null = New-ExoRequest -tenantid $TenantId -cmdlet 'Remove-DistributionGroupMember' -cmdParams $RemoveMemberParams -useSystemMailbox $true
84+
$Results.Add("Successfully removed room $MemberEmail from room list")
85+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Removed room $MemberEmail from room list $GroupId" -Sev 'Info'
86+
} catch {
87+
$Results.Add("Failed to remove room $MemberEmail from room list: $($_.Exception.Message)")
88+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Failed to remove room $MemberEmail from room list: $($_.Exception.Message)" -Sev 'Error'
89+
}
90+
}
91+
}
92+
93+
# Handle owners (ManagedBy property)
94+
if ($RoomListObj.AddOwner -or $RoomListObj.RemoveOwner) {
95+
try {
96+
# Get current owners
97+
$CurrentGroup = New-ExoRequest -tenantid $TenantId -cmdlet 'Get-DistributionGroup' -cmdParams @{ Identity = $GroupId } -useSystemMailbox $true
98+
$CurrentOwners = [System.Collections.Generic.List[string]]::new()
99+
100+
if ($CurrentGroup.ManagedBy) {
101+
# Convert ManagedBy objects to strings explicitly
102+
foreach ($ManagedByItem in $CurrentGroup.ManagedBy) {
103+
$StringValue = [string]$ManagedByItem
104+
$CurrentOwners.Add($StringValue)
105+
}
106+
}
107+
108+
# Remove owners
109+
if ($RoomListObj.RemoveOwner) {
110+
foreach ($Owner in $RoomListObj.RemoveOwner) {
111+
$OwnerToRemove = if ($Owner.addedFields.id) { $Owner.addedFields.id } else { $Owner.value }
112+
if ($CurrentOwners -contains $OwnerToRemove) {
113+
$CurrentOwners.Remove($OwnerToRemove)
114+
$Results.Add("Removed owner $(if ($Owner.label) { $Owner.label } else { $OwnerToRemove }) from room list")
115+
}
116+
}
117+
}
118+
119+
# Add owners
120+
if ($RoomListObj.AddOwner) {
121+
foreach ($Owner in $RoomListObj.AddOwner) {
122+
$OwnerToAdd = if ($Owner.addedFields.id) { $Owner.addedFields.id } else { $Owner.value }
123+
if ($CurrentOwners -notcontains $OwnerToAdd) {
124+
$CurrentOwners.Add($OwnerToAdd)
125+
$Results.Add("Added owner $(if ($Owner.label) { $Owner.label } else { $OwnerToAdd }) to room list")
126+
}
127+
}
128+
}
129+
130+
# Update ManagedBy with new owners list
131+
$SetOwnersParams = @{
132+
Identity = $GroupId
133+
ManagedBy = $CurrentOwners.ToArray()
134+
}
135+
136+
$null = New-ExoRequest -tenantid $TenantId -cmdlet 'Set-DistributionGroup' -cmdParams $SetOwnersParams -useSystemMailbox $true
137+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Updated owners for room list $GroupId" -Sev 'Info'
138+
} catch {
139+
$Results.Add("Failed to update room list owners: $($_.Exception.Message)")
140+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Failed to update room list owners: $($_.Exception.Message)" -Sev 'Error'
141+
}
142+
}
143+
144+
# Handle external email settings
145+
if ($null -ne $RoomListObj.allowExternal) {
146+
try {
147+
$SetExternalParams = @{
148+
Identity = $GroupId
149+
RequireSenderAuthenticationEnabled = !$RoomListObj.allowExternal
150+
}
151+
152+
$null = New-ExoRequest -tenantid $TenantId -cmdlet 'Set-DistributionGroup' -cmdParams $SetExternalParams -useSystemMailbox $true
153+
154+
if ($RoomListObj.allowExternal) {
155+
$Results.Add('Enabled external email access for room list')
156+
} else {
157+
$Results.Add('Disabled external email access for room list')
158+
}
159+
160+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Updated external email settings for room list $GroupId" -Sev 'Info'
161+
} catch {
162+
$Results.Add("Failed to update external email settings: $($_.Exception.Message)")
163+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Failed to update external email settings: $($_.Exception.Message)" -Sev 'Error'
164+
}
165+
}
166+
167+
} catch {
168+
$Results.Add("An error occurred while editing the room list: $($_.Exception.Message)")
169+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Failed to edit room list: $($_.Exception.Message)" -Sev 'Error'
170+
}
171+
172+
# Associate values to output bindings by calling 'Push-OutputBinding'.
173+
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
174+
StatusCode = [HttpStatusCode]::OK
175+
Body = @{'Results' = @($Results) }
176+
})
177+
}

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Resources/Invoke-ListRoomLists.ps1

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,102 @@ Function Invoke-ListRoomLists {
1616

1717
# Interact with query parameters or the body of the request.
1818
$TenantFilter = $Request.Query.tenantFilter
19+
$GroupID = $Request.Query.groupID
20+
$Members = $Request.Query.members
21+
$Owners = $Request.Query.owners
1922

2023
try {
21-
$params = @{
22-
uri = 'https://graph.microsoft.com/beta/places/microsoft.graph.roomlist'
23-
tenantid = $TenantFilter
24-
AsApp = $true
25-
}
26-
$GraphRequest = New-GraphGetRequest @params
24+
if ($GroupID) {
25+
# Get specific room list with detailed information
26+
$GroupInfo = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-DistributionGroup' -cmdParams @{Identity = $GroupID } -useSystemMailbox $true |
27+
Select-Object -ExcludeProperty *data.type*
28+
29+
$Result = [PSCustomObject]@{
30+
groupInfo = $GroupInfo | Select-Object *, @{ Name = 'primDomain'; Expression = { $_.PrimarySmtpAddress -split '@' | Select-Object -Last 1 } }
31+
members = @{}
32+
owners = @{}
33+
allowExternal = (!$GroupInfo.RequireSenderAuthenticationEnabled)
34+
}
35+
36+
# Get members if requested
37+
if ($Members -eq 'true') {
38+
$RoomListMembers = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-DistributionGroupMember' -cmdParams @{Identity = $GroupID } | Select-Object -ExcludeProperty *data.type* -Property @{Name = 'id'; Expression = { $_.ExternalDirectoryObjectId } },
39+
@{Name = 'displayName'; Expression = { $_.DisplayName } },
40+
@{Name = 'mail'; Expression = { $_.PrimarySmtpAddress } },
41+
@{Name = 'mailNickname'; Expression = { $_.Alias } },
42+
@{Name = 'userPrincipalName'; Expression = { $_.PrimarySmtpAddress } }
43+
$Result.members = @($RoomListMembers)
44+
}
45+
46+
# Get owners if requested
47+
if ($Owners -eq 'true' -and $GroupInfo.ManagedBy) {
48+
try {
49+
# Separate valid and invalid GUIDs
50+
$ValidOwnerIds = [System.Collections.Generic.List[string]]::new()
51+
$InvalidOwnerIds = [System.Collections.Generic.List[string]]::new()
52+
53+
foreach ($OwnerId in $GroupInfo.ManagedBy) {
54+
$OwnerIdString = [string]$OwnerId
55+
# Check if it's a valid GUID format
56+
if ($OwnerIdString -match '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$') {
57+
$ValidOwnerIds.Add($OwnerIdString)
58+
} else {
59+
$InvalidOwnerIds.Add($OwnerIdString)
60+
Write-Warning "Found invalid GUID for owner: $OwnerIdString"
61+
}
62+
}
63+
64+
$AllOwners = [System.Collections.Generic.List[PSCustomObject]]::new()
65+
66+
# Get valid owners from Graph API
67+
if ($ValidOwnerIds.Count -gt 0) {
68+
$body = ConvertTo-Json -InputObject @{ids = @($ValidOwnerIds) } -Compress
69+
$OwnersData = New-GraphPOSTRequest -tenantid $TenantFilter -uri 'https://graph.microsoft.com/beta/directoryObjects/getByIds' -body $body
70+
foreach ($Owner in $OwnersData.value) {
71+
$AllOwners.Add($Owner)
72+
}
73+
}
2774

28-
$StatusCode = [HttpStatusCode]::OK
75+
# Add invalid GUIDs as placeholder objects so they can be removed
76+
foreach ($InvalidId in $InvalidOwnerIds) {
77+
$PlaceholderOwner = [PSCustomObject]@{
78+
id = $InvalidId
79+
displayName = "Invalid Owner ID: $InvalidId"
80+
userPrincipalName = "invalid-$InvalidId"
81+
'@odata.type' = '#microsoft.graph.user'
82+
}
83+
$AllOwners.Add($PlaceholderOwner)
84+
}
85+
86+
$Result.owners = @($AllOwners)
87+
88+
} catch {
89+
Write-Warning "Failed to get owners: $($_.Exception.Message)"
90+
$Result.owners = @()
91+
}
92+
}
93+
94+
95+
96+
$StatusCode = [HttpStatusCode]::OK
97+
$ResponseBody = $Result
98+
} else {
99+
# Get all room lists (original functionality)
100+
$RoomLists = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-DistributionGroup' -cmdParams @{RecipientTypeDetails = 'RoomList'; ResultSize = 'Unlimited' } |
101+
Select-Object Guid, DisplayName, PrimarySmtpAddress, Alias, Phone, Identity, Notes, Description, Id -ExcludeProperty *data.type*
102+
$StatusCode = [HttpStatusCode]::OK
103+
$ResponseBody = @{ Results = @($RoomLists | Sort-Object DisplayName) }
104+
}
29105
} catch {
30106
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
31-
$StatusCode = [HttpStatusCode]::Forbidden
32-
$GraphRequest = $ErrorMessage
107+
$StatusCode = [HttpStatusCode]::InternalServerError
108+
$ResponseBody = $ErrorMessage
33109
}
110+
34111
# Associate values to output bindings by calling 'Push-OutputBinding'.
35112
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
36113
StatusCode = $StatusCode
37-
Body = @($GraphRequest | Sort-Object displayName)
114+
Body = $ResponseBody
38115
})
39116

40117
}

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ function Invoke-EditGroup {
1515
$Results = [System.Collections.Generic.List[string]]@()
1616
$UserObj = $Request.Body
1717
$GroupType = $UserObj.groupId.addedFields.groupType ? $UserObj.groupId.addedFields.groupType : $UserObj.groupType
18-
$GroupName = $UserObj.groupName ? $UserObj.groupName : $UserObj.groupId.addedFields.groupName
18+
# groupName is used in the Add to Group user action, displayName is used in the Edit Group page
19+
$GroupName = $UserObj.groupName ?? $UserObj.displayName ?? $UserObj.groupId.addedFields.groupName
1920
$GroupId = $UserObj.groupId.value ?? $UserObj.groupId
2021
$OrgGroup = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/$($GroupId)" -tenantid $UserObj.tenantFilter
2122

@@ -406,11 +407,33 @@ function Invoke-EditGroup {
406407
}
407408
}
408409

410+
# Only process hideFromOutlookClients if it was explicitly sent and is a Microsoft 365 group
411+
if ($null -ne $UserObj.hideFromOutlookClients -and $GroupType -eq 'Microsoft 365') {
412+
try {
413+
$Params = @{ Identity = $GroupId; HiddenFromExchangeClientsEnabled = $UserObj.hideFromOutlookClients }
414+
$null = New-ExoRequest -tenantid $TenantId -cmdlet 'Set-UnifiedGroup' -cmdParams $Params -useSystemMailbox $true
415+
416+
if ($UserObj.hideFromOutlookClients -eq $true) {
417+
$Results.Add("Successfully hidden group mailbox from Outlook for $($GroupName).")
418+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Successfully hidden group mailbox from Outlook for $($GroupName)." -Sev 'Info'
419+
} else {
420+
$Results.Add("Successfully made group mailbox visible in Outlook for $($GroupName).")
421+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Successfully made group mailbox visible in Outlook for $($GroupName)." -Sev 'Info'
422+
}
423+
} catch {
424+
$ErrorMessage = Get-CippException -Exception $_
425+
Write-Warning "Error in hideFromOutlookClients: $($ErrorMessage.NormalizedError) - $($_.InvocationInfo.ScriptLineNumber)"
426+
$action = if ($UserObj.hideFromOutlookClients -eq $true) { 'hide' } else { 'show' }
427+
$Results.Add("Failed to $action group mailbox in Outlook for $($GroupName).")
428+
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantId -message "Failed to $action group mailbox in Outlook for $($GroupName). Error:$($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage
429+
}
430+
}
431+
409432
$body = @{'Results' = @($Results) }
410433
# Associate values to output bindings by calling 'Push-OutputBinding'.
411434
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
412435
StatusCode = [HttpStatusCode]::OK
413436
Body = $Body
414437
})
415438

416-
}
439+
}

0 commit comments

Comments
 (0)