From d4b5126b3be334b9b7ca479f38091dc13543a5b2 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Wed, 31 Jul 2024 15:49:48 -0500 Subject: [PATCH 01/10] Initial commit of current script --- .../Update-PublicFolderPermissions.ps1 | 280 ++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 PublicFolders/Update-PublicFolderPermissions.ps1 diff --git a/PublicFolders/Update-PublicFolderPermissions.ps1 b/PublicFolders/Update-PublicFolderPermissions.ps1 new file mode 100644 index 0000000000..85497689e3 --- /dev/null +++ b/PublicFolders/Update-PublicFolderPermissions.ps1 @@ -0,0 +1,280 @@ +############################################################################################################# +#.SYNOPSIS +# Updates client permissions of several users to a public folder +# +#.DESCRIPTION +# Updates the client permissions of a public folder (and its children if -recurse +# is provided) clearing the permissions a set of users have on the folder and setting +# the provided access rights +# +# Copyright (c) 2014 Microsoft Corporation. All rights reserved. +# +# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK +# OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. +# +#.PARAMETER IncludeFolders +# Identities of the Public Folders that will be updated +# +#.PARAMETER Users +# List of users whose current access rights to the folder will be overriten +# +#.PARAMETER AccessRights +# List of permissions to assign to the users +# +#.PARAMETER Recurse +# If provided the permission changes will also be applied to the children of the folders. +# +#.PARAMETER ExcludeFolderEntryIds +# List of EntryIds of the folders that should be ignored from the update. Notice however +# that if you use the Recurse option the children of these folders won't be ignored unless +# their EntryIds are also provided in this list. +# +#.PARAMETER SkipCurrentAccessCheck +# If provided the right access updates will be performed in the folder regardless of whether +# the current folder has the same permissions already applied. +# +#.PARAMETER Confirm +# If this switch parameter is set to $false all operations on the public folder will be +# performed without requesting confirmation from the user. +# +#.PARAMETER WhatIf +# If this switch parameter is present the operations on the public folder will not be +# performed but information on what task would be performed are printed to the console. +# +#.PARAMETER ProgressLogFile +# File to log EntryIds of folders that were successfully updated. The content of this file may +# become handy to save time if the previous execution of the script was aborted and you want to restart +# from the point the script stopped. To do this simply get the contents of the file (get-content) and +# provide the data to the ExcludeFolderEntryIds parameter. +# +# The default path is UpdatePublicFolderPermission.[yyyyMMdd_HHmm].log where the portion in square brackets +# gets replaced with the current date and time at the moment of execution of the script. +# +#.EXAMPLE +# .\Update-PublicFolderPermissions.ps1 -IncludeFolders "\MyFolder" -AccessRights "Owner" -Users "John", "Administrator" -Recurse -Confirm:$false +# +# This command replaces the current client permissions for users "John" and "Administrator" on the "\MyFolder" +# Public Folder and all its children. The users will be granted "Owner" access rights. These actions will be +# performed without requesting confirmation to the user. +# +#.EXAMPLE +# $foldersProcessed = get-content .\UpdatePublicFolderPermission.20141031_1820.log +# .\Update-PublicFolderPermissions.ps1 -IncludeFolders "\MyFolder" -AccessRights "Owner" -Users "John", "Administrator" -Recurse -ExcludeFolderEntryIds $foldersProcessed -Confirm:$false +# +# These commands replace the current client permissions for users "John" and "Administrator" on the "\MyFolder" +# Public Folder and all its children but skips those folders that were completd in the execution of Oct 30th 2014 at 6:20 pm. +# The users will be granted "Owner" access rights. These actions will be performed without requesting confirmation to the user. +############################################################################################################# + +param ( + [Parameter(Mandatory=$True)] + [string[]]$IncludeFolders, + [Parameter(Mandatory=$True)] + [string[]]$Users, + [Parameter(Mandatory=$True)] + [string[]]$AccessRights, + [switch]$Recurse, + [string[]]$ExcludeFolderEntryIds = @(), + [switch]$SkipCurrentAccessCheck, + [string]$ProgressLogFile = ".\UpdatePublicFolderPermission.$((Get-Date).ToString('yyyyMMdd_HHmm')).log", + [switch]$confirm, + [switch]$whatIf +) + +############################################################################################################# +# Returns the list of public folders to process ignoring duplicates and folders in the exclude list +############################################################################################################# +function FindFoldersToUpdate([string[]]$includeFolders, [bool]$recurseOnFolders, [string[]]$excludeFolderEntryIds) +{ + $folderToSkip = new-object 'System.Collections.Generic.HashSet[string]' -ArgumentList @(,$excludeFolderEntryIds) + $currentIncludeFolder=0; + foreach($includeFolder in $includeFolders) + { + $progress = 100 * $currentIncludeFolder / $includeFolders.Count; + Write-Progress -Activity "Retrieving folders to update" -Status $includeFolder -PercentComplete $progress + + $foldersFound = Get-PublicFolder -Recurse:$recurseOnFolders $includeFolder -ResultSize Unlimited + + if ($foldersFound -eq $null) + { + continue; + } + + foreach($foundFolder in $foldersFound) + { + if ($foundFolder -eq $null) + { + continue; + } + + if ($folderToSkip -notContains $foundFolder.EntryId) + { + #Return found folder + $foundFolder; + } + + $folderToSkip.Add($foundFolder.EntryId) > $null; + } + + $currentIncludeFolder++; + } +} + +############################################################################################################# +# Returns the Identity of the users that need processing. +############################################################################################################# +function GetUserIdentities([string[]]$Users) +{ + $userIdentities = new-object 'System.Collections.Generic.HashSet[object]' + $currentUserNumber=0; + foreach($user in $Users) + { + $progress = 100 * $currentUserNumber / $Users.Count; + Write-Progress -Activity "Retrieving users" -Status $user -PercentComplete $progress + $id = (Get-Recipient $user).Identity + + if ($id -ne $null) + { + $userIdentities.Add($id) > $null + } + + $currentUserNumber++; + } + + $userIdentities +} + +############################################################################################################# +# Returns whether all the elements of a collection are present in a reference collection. +############################################################################################################# +function CollectionContains($referenceCollection, $otherCollection) +{ + foreach($item in $otherCollection) + { + if ($referenceCollection -notcontains $item) + { + return $false + } + } + + return $true +} + +############################################################################################################# +# Verifies whether there is a mismatch between the desired and found permissions. +############################################################################################################# +function IsUpdateRequired ($currentAccessRights, $desiredAccessRights) +{ + $allDesiredPermissionsWhereFound = CollectionContains $currentAccessRights $desiredAccessRights + $allFoundPermissionsAreDesired = CollectionContains $desiredAccessRights $currentAccessRights + + return -not ($allDesiredPermissionsWhereFound -and $allFoundPermissionsAreDesired) +} + +############################################################################################################# +# Gets the list of users whose access right to a folder don't match the desired ones. +############################################################################################################# +function GetUsersToUpdate($currentFolder, [Array]$usersToUpdate, [string[]]$accessRights) +{ + Write-Progress -Id 1 -Activity "Querying current permissions" -Status "Processing"; + + $existingPermissions = [Array](Get-PublicFolderClientPermission $currentFolder.Identity); + $existingPermissionsPerUser = @{} + + $permissionCount = 0; + foreach($permission in $existingPermissions) + { + $progress = 100 * $permissionCount / $existingPermissions.Count; + Write-Progress -Id 1 -Activity "Processing current permissions" -PercentComplete $progress -Status "Processing"; + + $adIdentity = $permission.User.ADRecipient.Identity; + + if ($adIdentity -ne $null) + { + $existingPermissionsPerUser[$adIdentity] = $permission; + } + } + + $permissionCount = 0; + foreach($user in $usersToUpdate) + { + $progress = 100 * $permissionCount / $usersToUpdate.Count; + Write-Progress -Id 1 -Activity "Comparing permissions" -PercentComplete $progress -Status "Processing"; + + if (-not $existingPermissionsPerUser.ContainsKey($user)) + { + $user; + } + else + { + if (IsUpdateRequired $existingPermissionsPerUser[$user].AccessRights $AccessRights) + { + $user; + } + } + + $permissionCount++; + } +} + +############################################################################################################# +# Script logic. +############################################################################################################# + +$foldersToUpdate=[Array](FindFoldersToUpdate $IncludeFolders $Recurse $ExcludeFolderEntryIds); +$usersToUpdate=[Array](GetUserIdentities $Users) + +$foldersProcessed=0; +foreach($currentFolder in $foldersToUpdate) +{ + $percentFoldersProcessed = 100 * $foldersProcessed/($foldersToUpdate.Count); + Write-Progress -Activity "Processing folders" -Status $currentFolder.Identity -PercentComplete $percentFoldersProcessed + + $usersToUpdateForFolder = @() + if (-not $SkipCurrentAccessCheck) + { + $usersToUpdateForFolder = [Array](GetUsersToUpdate $currentFolder $usersToUpdate $AccessRights) + } + else + { + $usersToUpdateForFolder = $usersToUpdate; + } + + $folderOperationFailed=$false; + $usersProcessed=0; + + if (($usersToUpdateForFolder -eq $null) -or ($usersToUpdateForFolder.Count -eq 0)) + { + Write-Warning "Couldn't find any changes to perform for folder $($currentFolder.Identity)" + continue; + } + + foreach($user in $usersToUpdateForFolder) + { + $percentUsersProcessed = 100 * $usersProcessed/($usersToUpdateForFolder.Count) + + Write-Progress -Id 1 -Activity "Processing User" -Status $user -CurrentOperation "Removing exisitng permission" -PercentComplete $percentUsersProcessed + Remove-PublicFolderClientPermission -User $user $currentFolder.Identity -ErrorAction SilentlyContinue -Confirm:$confirm -WhatIf:$whatIf + + Write-Progress -Id 1 -Activity "Processing User" -Status $user -CurrentOperation "Adding permission" -PercentComplete $percentUsersProcessed + + try + { + Add-PublicFolderClientPermission -AccessRights $accessRights -User $user $currentFolder.Identity -ErrorAction Stop -Confirm:$confirm -WhatIf:$whatIf + } + catch + { + Write-Error $_ + $folderOperationFailed=$true; + } + + $usersProcessed++; + } + + if (-not $folderOperationFailed) + { + Add-Content $ProgressLogFile "$($currentFolder.EntryId)`n" -Confirm:$confirm -WhatIf:$whatIf + } + + $foldersProcessed++; +} From 6ab3f476a5904df4f8bc7bc4cce8ea5527a374fb Mon Sep 17 00:00:00 2001 From: Bill Long Date: Wed, 31 Jul 2024 15:50:42 -0500 Subject: [PATCH 02/10] Refactor to support propagating all existing rights --- .../Update-PublicFolderPermissions.ps1 | 164 ++++++++++++++---- 1 file changed, 133 insertions(+), 31 deletions(-) diff --git a/PublicFolders/Update-PublicFolderPermissions.ps1 b/PublicFolders/Update-PublicFolderPermissions.ps1 index 85497689e3..bd8d25a9d7 100644 --- a/PublicFolders/Update-PublicFolderPermissions.ps1 +++ b/PublicFolders/Update-PublicFolderPermissions.ps1 @@ -66,19 +66,29 @@ # The users will be granted "Owner" access rights. These actions will be performed without requesting confirmation to the user. ############################################################################################################# +[CmdletBinding(SupportsShouldProcess)] param ( - [Parameter(Mandatory=$True)] + [Parameter(Mandatory=$True, ParameterSetName='Default')] + [Parameter(Mandatory=$True, ParameterSetName='PropagateAll')] [string[]]$IncludeFolders, - [Parameter(Mandatory=$True)] + [Parameter(Mandatory=$True, ParameterSetName='Default')] [string[]]$Users, - [Parameter(Mandatory=$True)] + [Parameter(Mandatory=$True, ParameterSetName='Default')] [string[]]$AccessRights, + [Parameter(Mandatory=$True, ParameterSetName='PropagateAll')] + [switch]$PropagateAll, + [Parameter(Mandatory=$False, ParameterSetName='Default')] + [Parameter(Mandatory=$False, ParameterSetName='PropagateAll')] [switch]$Recurse, + [Parameter(Mandatory=$False, ParameterSetName='Default')] + [Parameter(Mandatory=$False, ParameterSetName='PropagateAll')] [string[]]$ExcludeFolderEntryIds = @(), + [Parameter(Mandatory=$False, ParameterSetName='Default')] + [Parameter(Mandatory=$False, ParameterSetName='PropagateAll')] [switch]$SkipCurrentAccessCheck, - [string]$ProgressLogFile = ".\UpdatePublicFolderPermission.$((Get-Date).ToString('yyyyMMdd_HHmm')).log", - [switch]$confirm, - [switch]$whatIf + [Parameter(Mandatory=$False, ParameterSetName='Default')] + [Parameter(Mandatory=$False, ParameterSetName='PropagateAll')] + [string]$ProgressLogFile = ".\UpdatePublicFolderPermission.$((Get-Date).ToString('yyyyMMdd_HHmm')).log" ) ############################################################################################################# @@ -86,6 +96,12 @@ param ( ############################################################################################################# function FindFoldersToUpdate([string[]]$includeFolders, [bool]$recurseOnFolders, [string[]]$excludeFolderEntryIds) { + Write-Verbose "$($MyInvocation.MyCommand): excludeFolderEntryIds.Count $($excludeFolderEntryIds.Count)" + if ($excludeFolderEntryIds.Count -gt 0) + { + $excludeFolderEntryIds | ForEach-Object { Write-Verbose "$($MyInvocation.MyCommand): excluded EntryID $_" } + } + $folderToSkip = new-object 'System.Collections.Generic.HashSet[string]' -ArgumentList @(,$excludeFolderEntryIds) $currentIncludeFolder=0; foreach($includeFolder in $includeFolders) @@ -93,7 +109,7 @@ function FindFoldersToUpdate([string[]]$includeFolders, [bool]$recurseOnFolders, $progress = 100 * $currentIncludeFolder / $includeFolders.Count; Write-Progress -Activity "Retrieving folders to update" -Status $includeFolder -PercentComplete $progress - $foldersFound = Get-PublicFolder -Recurse:$recurseOnFolders $includeFolder -ResultSize Unlimited + $foldersFound = @(Get-PublicFolder -Recurse:$recurseOnFolders $includeFolder -ResultSize Unlimited @script:CommonParams | Sort-Object Identity) if ($foldersFound -eq $null) { @@ -109,9 +125,14 @@ function FindFoldersToUpdate([string[]]$includeFolders, [bool]$recurseOnFolders, if ($folderToSkip -notContains $foundFolder.EntryId) { + Write-Verbose "$($MyInvocation.MyCommand): Returning found folder $($foundFolder.Identity) with EntryId $($foundFolder.EntryID)" #Return found folder $foundFolder; } + else + { + Write-Verbose "$($MyInvocation.MyCommand): Skipping excluded folder $($foundFolder.Identity) with EntryId $($foundFolder.EntryID)" + } $folderToSkip.Add($foundFolder.EntryId) > $null; } @@ -131,7 +152,7 @@ function GetUserIdentities([string[]]$Users) { $progress = 100 * $currentUserNumber / $Users.Count; Write-Progress -Activity "Retrieving users" -Status $user -PercentComplete $progress - $id = (Get-Recipient $user).Identity + $id = (Get-Recipient $user).PrimarySmtpAddress if ($id -ne $null) { @@ -171,14 +192,29 @@ function IsUpdateRequired ($currentAccessRights, $desiredAccessRights) return -not ($allDesiredPermissionsWhereFound -and $allFoundPermissionsAreDesired) } +############################################################################################################# +# Gets the value we should use as the user's identity, which may be Default or Anonymous +############################################################################################################# +function GetPermissionUserIdentity($permissionUser) +{ + if ($permissionUser.UserType.ToString() -eq 'Default' -or $permissionUser.UserType.ToString() -eq 'Anonymous') + { + $permissionUser.UserType.ToString(); + } + else + { + $permissionUser.RecipientPrincipal.PrimarySmtpAddress; + } +} + ############################################################################################################# # Gets the list of users whose access right to a folder don't match the desired ones. ############################################################################################################# -function GetUsersToUpdate($currentFolder, [Array]$usersToUpdate, [string[]]$accessRights) +function GetUsersToUpdate($currentFolder, [Array]$permissionsToPropagate) { Write-Progress -Id 1 -Activity "Querying current permissions" -Status "Processing"; - $existingPermissions = [Array](Get-PublicFolderClientPermission $currentFolder.Identity); + $existingPermissions = [Array](Get-PublicFolderClientPermission $currentFolder.Identity @script:CommonParams); $existingPermissionsPerUser = @{} $permissionCount = 0; @@ -187,29 +223,29 @@ function GetUsersToUpdate($currentFolder, [Array]$usersToUpdate, [string[]]$acce $progress = 100 * $permissionCount / $existingPermissions.Count; Write-Progress -Id 1 -Activity "Processing current permissions" -PercentComplete $progress -Status "Processing"; - $adIdentity = $permission.User.ADRecipient.Identity; + $principalIdentity = GetPermissionUserIdentity $permission.User; - if ($adIdentity -ne $null) + if ($null -ne $principalIdentity) { - $existingPermissionsPerUser[$adIdentity] = $permission; + $existingPermissionsPerUser[$principalIdentity] = $permission; } } $permissionCount = 0; - foreach($user in $usersToUpdate) + foreach($permission in $permissionsToPropagate) { - $progress = 100 * $permissionCount / $usersToUpdate.Count; + $progress = 100 * $permissionCount / $permissionsToPropagate.Count; Write-Progress -Id 1 -Activity "Comparing permissions" -PercentComplete $progress -Status "Processing"; - if (-not $existingPermissionsPerUser.ContainsKey($user)) + if (-not $existingPermissionsPerUser.ContainsKey($permission.User)) { - $user; + $permission; } else { - if (IsUpdateRequired $existingPermissionsPerUser[$user].AccessRights $AccessRights) + if (IsUpdateRequired $existingPermissionsPerUser[$permission.User].AccessRights $permission.AccessRights) { - $user; + $permission; } } @@ -221,8 +257,73 @@ function GetUsersToUpdate($currentFolder, [Array]$usersToUpdate, [string[]]$acce # Script logic. ############################################################################################################# +if ($PropagateAll -and $IncludeFolders.Count -gt 1) +{ + Write-Host "When -PropagateAll is used, -IncludeFolders is limited to one folder."; + return; +} + +$permissionsToPropagate = @(); +if ($PropagateAll) +{ + $topLevelPermissions = Get-PublicFolderClientPermission $IncludeFolders[0] @script:CommonParams; + if ($null -eq $topLevelPermissions) + { + Write-Host "Unable to retrieve permissions from folder $($IncludeFolders[0])"; + return; + } + + foreach ($permission in $topLevelPermissions) + { + $principal = GetPermissionUserIdentity $permission.User; + if ($null -eq $principal) + { + Write-Warning "Permission exists for $($permission.User), but this user appears to be invalid. Permissions cannot be propagated."; + exit; + } + + $permissionsToPropagate += [PSCustomObject]@{ + User = $principal; + AccessRights = $permission.AccessRights; + } + } +} +else +{ + $usersToUpdate=[Array](GetUserIdentities $Users) + foreach ($principal in $usersToUpdate) + { + $permissionsToPropagate += [PSCustomObject]@{ + User = $principal; + AccessRights = $AccessRights; + } + } +} + +$script:CommonParams = @{} +foreach ($p in "Confirm", "WhatIf", "Verbose") +{ + if ($null -ne $PSBoundParameters[$p]) + { + $script:CommonParams[$p] = $PSBoundParameters[$p].IsPresent + } +} + +Write-Host "The following permissions will be set:" +$permissionsToPropagate | Out-Host + +Write-Host "The following folders will be included. Recurse: $Recurse" +$IncludeFolders | Out-Host +Write-Host + $foldersToUpdate=[Array](FindFoldersToUpdate $IncludeFolders $Recurse $ExcludeFolderEntryIds); -$usersToUpdate=[Array](GetUserIdentities $Users) + +if ($PropagateAll) +{ + $foldersToUpdate = @($foldersToUpdate | Select-Object -Skip 1) +} + +Write-Host "Found $($foldersToUpdate.Count) folders to update." $foldersProcessed=0; foreach($currentFolder in $foldersToUpdate) @@ -230,37 +331,38 @@ foreach($currentFolder in $foldersToUpdate) $percentFoldersProcessed = 100 * $foldersProcessed/($foldersToUpdate.Count); Write-Progress -Activity "Processing folders" -Status $currentFolder.Identity -PercentComplete $percentFoldersProcessed - $usersToUpdateForFolder = @() + $permissionsToUpdateForFolder = @() if (-not $SkipCurrentAccessCheck) { - $usersToUpdateForFolder = [Array](GetUsersToUpdate $currentFolder $usersToUpdate $AccessRights) + $permissionsToUpdateForFolder = [Array](GetUsersToUpdate $currentFolder $permissionsToPropagate) } else { - $usersToUpdateForFolder = $usersToUpdate; + $permissionsToUpdateForFolder = $permissionsToPropagate; } $folderOperationFailed=$false; $usersProcessed=0; - if (($usersToUpdateForFolder -eq $null) -or ($usersToUpdateForFolder.Count -eq 0)) + if (($null -eq $permissionsToUpdateForFolder) -or ($permissionsToUpdateForFolder.Count -eq 0)) { Write-Warning "Couldn't find any changes to perform for folder $($currentFolder.Identity)" + $foldersProcessed++; continue; } - foreach($user in $usersToUpdateForFolder) + foreach($permission in $permissionsToUpdateForFolder) { - $percentUsersProcessed = 100 * $usersProcessed/($usersToUpdateForFolder.Count) + $percentUsersProcessed = 100 * $usersProcessed/($permissionsToUpdateForFolder.Count) - Write-Progress -Id 1 -Activity "Processing User" -Status $user -CurrentOperation "Removing exisitng permission" -PercentComplete $percentUsersProcessed - Remove-PublicFolderClientPermission -User $user $currentFolder.Identity -ErrorAction SilentlyContinue -Confirm:$confirm -WhatIf:$whatIf + Write-Progress -Id 1 -Activity "Processing User" -Status $permission.User -CurrentOperation "Removing exisitng permission" -PercentComplete $percentUsersProcessed + Remove-PublicFolderClientPermission -User $permission.User $currentFolder.Identity -ErrorAction SilentlyContinue @script:CommonParams - Write-Progress -Id 1 -Activity "Processing User" -Status $user -CurrentOperation "Adding permission" -PercentComplete $percentUsersProcessed + Write-Progress -Id 1 -Activity "Processing User" -Status $permission.User -CurrentOperation "Adding permission" -PercentComplete $percentUsersProcessed try { - Add-PublicFolderClientPermission -AccessRights $accessRights -User $user $currentFolder.Identity -ErrorAction Stop -Confirm:$confirm -WhatIf:$whatIf + Add-PublicFolderClientPermission -AccessRights $permission.AccessRights -User $permission.User $currentFolder.Identity -ErrorAction Stop @script:CommonParams } catch { @@ -273,7 +375,7 @@ foreach($currentFolder in $foldersToUpdate) if (-not $folderOperationFailed) { - Add-Content $ProgressLogFile "$($currentFolder.EntryId)`n" -Confirm:$confirm -WhatIf:$whatIf + Add-Content $ProgressLogFile "$($currentFolder.EntryId)`n" } $foldersProcessed++; From db09ae3f3ddef4d0dd6d68997835937a5b4a1fc7 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Wed, 31 Jul 2024 15:53:56 -0500 Subject: [PATCH 03/10] Apply CodeFormatter fixes --- .../Update-PublicFolderPermissions.ps1 | 258 +++++++----------- .../Update-PublicFolderPermissions.md | 4 + 2 files changed, 108 insertions(+), 154 deletions(-) create mode 100644 docs/PublicFolders/Update-PublicFolderPermissions.md diff --git a/PublicFolders/Update-PublicFolderPermissions.ps1 b/PublicFolders/Update-PublicFolderPermissions.ps1 index bd8d25a9d7..39fbd9879f 100644 --- a/PublicFolders/Update-PublicFolderPermissions.ps1 +++ b/PublicFolders/Update-PublicFolderPermissions.ps1 @@ -1,4 +1,7 @@ -############################################################################################################# +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +############################################################################################################# #.SYNOPSIS # Updates client permissions of several users to a public folder # @@ -7,11 +10,6 @@ # is provided) clearing the permissions a set of users have on the folder and setting # the provided access rights # -# Copyright (c) 2014 Microsoft Corporation. All rights reserved. -# -# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK -# OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. -# #.PARAMETER IncludeFolders # Identities of the Public Folders that will be updated # @@ -94,72 +92,60 @@ param ( ############################################################################################################# # Returns the list of public folders to process ignoring duplicates and folders in the exclude list ############################################################################################################# -function FindFoldersToUpdate([string[]]$includeFolders, [bool]$recurseOnFolders, [string[]]$excludeFolderEntryIds) -{ +function FindFoldersToUpdate([string[]]$includeFolders, [bool]$recurseOnFolders, [string[]]$excludeFolderEntryIds) { Write-Verbose "$($MyInvocation.MyCommand): excludeFolderEntryIds.Count $($excludeFolderEntryIds.Count)" - if ($excludeFolderEntryIds.Count -gt 0) - { + if ($excludeFolderEntryIds.Count -gt 0) { $excludeFolderEntryIds | ForEach-Object { Write-Verbose "$($MyInvocation.MyCommand): excluded EntryID $_" } } - $folderToSkip = new-object 'System.Collections.Generic.HashSet[string]' -ArgumentList @(,$excludeFolderEntryIds) - $currentIncludeFolder=0; - foreach($includeFolder in $includeFolders) - { - $progress = 100 * $currentIncludeFolder / $includeFolders.Count; + $folderToSkip = New-Object 'System.Collections.Generic.HashSet[string]' -ArgumentList @(, $excludeFolderEntryIds) + $currentIncludeFolder=0 + foreach ($includeFolder in $includeFolders) { + $progress = 100 * $currentIncludeFolder / $includeFolders.Count Write-Progress -Activity "Retrieving folders to update" -Status $includeFolder -PercentComplete $progress $foldersFound = @(Get-PublicFolder -Recurse:$recurseOnFolders $includeFolder -ResultSize Unlimited @script:CommonParams | Sort-Object Identity) - if ($foldersFound -eq $null) - { - continue; + if ($null -eq $foldersFound) { + continue } - foreach($foundFolder in $foldersFound) - { - if ($foundFolder -eq $null) - { - continue; + foreach ($foundFolder in $foldersFound) { + if ($null -eq $foundFolder) { + continue } - if ($folderToSkip -notContains $foundFolder.EntryId) - { + if ($folderToSkip -notContains $foundFolder.EntryId) { Write-Verbose "$($MyInvocation.MyCommand): Returning found folder $($foundFolder.Identity) with EntryId $($foundFolder.EntryID)" #Return found folder - $foundFolder; - } - else - { + $foundFolder + } else { Write-Verbose "$($MyInvocation.MyCommand): Skipping excluded folder $($foundFolder.Identity) with EntryId $($foundFolder.EntryID)" } - $folderToSkip.Add($foundFolder.EntryId) > $null; + $folderToSkip.Add($foundFolder.EntryId) > $null } - $currentIncludeFolder++; + $currentIncludeFolder++ } } ############################################################################################################# # Returns the Identity of the users that need processing. ############################################################################################################# -function GetUserIdentities([string[]]$Users) -{ - $userIdentities = new-object 'System.Collections.Generic.HashSet[object]' - $currentUserNumber=0; - foreach($user in $Users) - { - $progress = 100 * $currentUserNumber / $Users.Count; +function GetUserIdentities([string[]]$Users) { + $userIdentities = New-Object 'System.Collections.Generic.HashSet[object]' + $currentUserNumber=0 + foreach ($user in $Users) { + $progress = 100 * $currentUserNumber / $Users.Count Write-Progress -Activity "Retrieving users" -Status $user -PercentComplete $progress $id = (Get-Recipient $user).PrimarySmtpAddress - if ($id -ne $null) - { + if ($null -ne $id) { $userIdentities.Add($id) > $null } - $currentUserNumber++; + $currentUserNumber++ } $userIdentities @@ -168,12 +154,9 @@ function GetUserIdentities([string[]]$Users) ############################################################################################################# # Returns whether all the elements of a collection are present in a reference collection. ############################################################################################################# -function CollectionContains($referenceCollection, $otherCollection) -{ - foreach($item in $otherCollection) - { - if ($referenceCollection -notcontains $item) - { +function CollectionContains($referenceCollection, $otherCollection) { + foreach ($item in $otherCollection) { + if ($referenceCollection -notcontains $item) { return $false } } @@ -184,8 +167,7 @@ function CollectionContains($referenceCollection, $otherCollection) ############################################################################################################# # Verifies whether there is a mismatch between the desired and found permissions. ############################################################################################################# -function IsUpdateRequired ($currentAccessRights, $desiredAccessRights) -{ +function IsUpdateRequired ($currentAccessRights, $desiredAccessRights) { $allDesiredPermissionsWhereFound = CollectionContains $currentAccessRights $desiredAccessRights $allFoundPermissionsAreDesired = CollectionContains $desiredAccessRights $currentAccessRights @@ -195,61 +177,49 @@ function IsUpdateRequired ($currentAccessRights, $desiredAccessRights) ############################################################################################################# # Gets the value we should use as the user's identity, which may be Default or Anonymous ############################################################################################################# -function GetPermissionUserIdentity($permissionUser) -{ - if ($permissionUser.UserType.ToString() -eq 'Default' -or $permissionUser.UserType.ToString() -eq 'Anonymous') - { - $permissionUser.UserType.ToString(); - } - else - { - $permissionUser.RecipientPrincipal.PrimarySmtpAddress; +function GetPermissionUserIdentity($permissionUser) { + if ($permissionUser.UserType.ToString() -eq 'Default' -or $permissionUser.UserType.ToString() -eq 'Anonymous') { + $permissionUser.UserType.ToString() + } else { + $permissionUser.RecipientPrincipal.PrimarySmtpAddress } } ############################################################################################################# # Gets the list of users whose access right to a folder don't match the desired ones. ############################################################################################################# -function GetUsersToUpdate($currentFolder, [Array]$permissionsToPropagate) -{ - Write-Progress -Id 1 -Activity "Querying current permissions" -Status "Processing"; +function GetUsersToUpdate($currentFolder, [Array]$permissionsToPropagate) { + Write-Progress -Id 1 -Activity "Querying current permissions" -Status "Processing" - $existingPermissions = [Array](Get-PublicFolderClientPermission $currentFolder.Identity @script:CommonParams); + $existingPermissions = [Array](Get-PublicFolderClientPermission $currentFolder.Identity @script:CommonParams) $existingPermissionsPerUser = @{} - $permissionCount = 0; - foreach($permission in $existingPermissions) - { - $progress = 100 * $permissionCount / $existingPermissions.Count; - Write-Progress -Id 1 -Activity "Processing current permissions" -PercentComplete $progress -Status "Processing"; + $permissionCount = 0 + foreach ($permission in $existingPermissions) { + $progress = 100 * $permissionCount / $existingPermissions.Count + Write-Progress -Id 1 -Activity "Processing current permissions" -PercentComplete $progress -Status "Processing" - $principalIdentity = GetPermissionUserIdentity $permission.User; + $principalIdentity = GetPermissionUserIdentity $permission.User - if ($null -ne $principalIdentity) - { - $existingPermissionsPerUser[$principalIdentity] = $permission; + if ($null -ne $principalIdentity) { + $existingPermissionsPerUser[$principalIdentity] = $permission } } - $permissionCount = 0; - foreach($permission in $permissionsToPropagate) - { - $progress = 100 * $permissionCount / $permissionsToPropagate.Count; - Write-Progress -Id 1 -Activity "Comparing permissions" -PercentComplete $progress -Status "Processing"; + $permissionCount = 0 + foreach ($permission in $permissionsToPropagate) { + $progress = 100 * $permissionCount / $permissionsToPropagate.Count + Write-Progress -Id 1 -Activity "Comparing permissions" -PercentComplete $progress -Status "Processing" - if (-not $existingPermissionsPerUser.ContainsKey($permission.User)) - { - $permission; - } - else - { - if (IsUpdateRequired $existingPermissionsPerUser[$permission.User].AccessRights $permission.AccessRights) - { - $permission; + if (-not $existingPermissionsPerUser.ContainsKey($permission.User)) { + $permission + } else { + if (IsUpdateRequired $existingPermissionsPerUser[$permission.User].AccessRights $permission.AccessRights) { + $permission } } - $permissionCount++; + $permissionCount++ } } @@ -257,58 +227,49 @@ function GetUsersToUpdate($currentFolder, [Array]$permissionsToPropagate) # Script logic. ############################################################################################################# -if ($PropagateAll -and $IncludeFolders.Count -gt 1) -{ - Write-Host "When -PropagateAll is used, -IncludeFolders is limited to one folder."; - return; +if ($PropagateAll -and $IncludeFolders.Count -gt 1) { + Write-Host "When -PropagateAll is used, -IncludeFolders is limited to one folder." + return +} + +# We want to pass these to the cmdlets that we call +$script:CommonParams = @{} +foreach ($p in "Confirm", "WhatIf", "Verbose") { + if ($null -ne $PSBoundParameters[$p]) { + $script:CommonParams[$p] = $PSBoundParameters[$p].IsPresent + } } -$permissionsToPropagate = @(); -if ($PropagateAll) -{ - $topLevelPermissions = Get-PublicFolderClientPermission $IncludeFolders[0] @script:CommonParams; - if ($null -eq $topLevelPermissions) - { - Write-Host "Unable to retrieve permissions from folder $($IncludeFolders[0])"; - return; +$permissionsToPropagate = @() +if ($PropagateAll) { + $topLevelPermissions = Get-PublicFolderClientPermission $IncludeFolders[0] @script:CommonParams + if ($null -eq $topLevelPermissions) { + Write-Host "Unable to retrieve permissions from folder $($IncludeFolders[0])" + return } - foreach ($permission in $topLevelPermissions) - { - $principal = GetPermissionUserIdentity $permission.User; - if ($null -eq $principal) - { - Write-Warning "Permission exists for $($permission.User), but this user appears to be invalid. Permissions cannot be propagated."; - exit; + foreach ($permission in $topLevelPermissions) { + $principal = GetPermissionUserIdentity $permission.User + if ($null -eq $principal) { + Write-Warning "Permission exists for $($permission.User), but this user appears to be invalid. Permissions cannot be propagated." + exit } $permissionsToPropagate += [PSCustomObject]@{ - User = $principal; - AccessRights = $permission.AccessRights; + User = $principal + AccessRights = $permission.AccessRights } } -} -else -{ +} else { $usersToUpdate=[Array](GetUserIdentities $Users) - foreach ($principal in $usersToUpdate) - { + foreach ($principal in $usersToUpdate) { $permissionsToPropagate += [PSCustomObject]@{ - User = $principal; - AccessRights = $AccessRights; + User = $principal + AccessRights = $AccessRights } } } -$script:CommonParams = @{} -foreach ($p in "Confirm", "WhatIf", "Verbose") -{ - if ($null -ne $PSBoundParameters[$p]) - { - $script:CommonParams[$p] = $PSBoundParameters[$p].IsPresent - } -} - Write-Host "The following permissions will be set:" $permissionsToPropagate | Out-Host @@ -316,43 +277,36 @@ Write-Host "The following folders will be included. Recurse: $Recurse" $IncludeFolders | Out-Host Write-Host -$foldersToUpdate=[Array](FindFoldersToUpdate $IncludeFolders $Recurse $ExcludeFolderEntryIds); +$foldersToUpdate=[Array](FindFoldersToUpdate -includeFolders $IncludeFolders -recurseOnFolders $Recurse -excludeFolderEntryIds $ExcludeFolderEntryIds) -if ($PropagateAll) -{ +if ($PropagateAll) { $foldersToUpdate = @($foldersToUpdate | Select-Object -Skip 1) } Write-Host "Found $($foldersToUpdate.Count) folders to update." -$foldersProcessed=0; -foreach($currentFolder in $foldersToUpdate) -{ - $percentFoldersProcessed = 100 * $foldersProcessed/($foldersToUpdate.Count); +$foldersProcessed=0 +foreach ($currentFolder in $foldersToUpdate) { + $percentFoldersProcessed = 100 * $foldersProcessed/($foldersToUpdate.Count) Write-Progress -Activity "Processing folders" -Status $currentFolder.Identity -PercentComplete $percentFoldersProcessed $permissionsToUpdateForFolder = @() - if (-not $SkipCurrentAccessCheck) - { + if (-not $SkipCurrentAccessCheck) { $permissionsToUpdateForFolder = [Array](GetUsersToUpdate $currentFolder $permissionsToPropagate) - } - else - { - $permissionsToUpdateForFolder = $permissionsToPropagate; + } else { + $permissionsToUpdateForFolder = $permissionsToPropagate } - $folderOperationFailed=$false; - $usersProcessed=0; + $folderOperationFailed=$false + $usersProcessed=0 - if (($null -eq $permissionsToUpdateForFolder) -or ($permissionsToUpdateForFolder.Count -eq 0)) - { + if (($null -eq $permissionsToUpdateForFolder) -or ($permissionsToUpdateForFolder.Count -eq 0)) { Write-Warning "Couldn't find any changes to perform for folder $($currentFolder.Identity)" - $foldersProcessed++; - continue; + $foldersProcessed++ + continue } - foreach($permission in $permissionsToUpdateForFolder) - { + foreach ($permission in $permissionsToUpdateForFolder) { $percentUsersProcessed = 100 * $usersProcessed/($permissionsToUpdateForFolder.Count) Write-Progress -Id 1 -Activity "Processing User" -Status $permission.User -CurrentOperation "Removing exisitng permission" -PercentComplete $percentUsersProcessed @@ -360,23 +314,19 @@ foreach($currentFolder in $foldersToUpdate) Write-Progress -Id 1 -Activity "Processing User" -Status $permission.User -CurrentOperation "Adding permission" -PercentComplete $percentUsersProcessed - try - { + try { Add-PublicFolderClientPermission -AccessRights $permission.AccessRights -User $permission.User $currentFolder.Identity -ErrorAction Stop @script:CommonParams - } - catch - { + } catch { Write-Error $_ - $folderOperationFailed=$true; + $folderOperationFailed=$true } - $usersProcessed++; + $usersProcessed++ } - if (-not $folderOperationFailed) - { + if (-not $folderOperationFailed) { Add-Content $ProgressLogFile "$($currentFolder.EntryId)`n" } - $foldersProcessed++; + $foldersProcessed++ } diff --git a/docs/PublicFolders/Update-PublicFolderPermissions.md b/docs/PublicFolders/Update-PublicFolderPermissions.md new file mode 100644 index 0000000000..11c077127a --- /dev/null +++ b/docs/PublicFolders/Update-PublicFolderPermissions.md @@ -0,0 +1,4 @@ +# ValidateExoPfDumpster + +Download the latest release: [Update-PublicFolderPermissions.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Update-PublicFolderPermissions.ps1) + From e4f8f6033ba7bf304cb32cc23fc81c279271ab77 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Tue, 20 Aug 2024 02:11:10 -0500 Subject: [PATCH 04/10] Add more verbose output and remove CommonParams from things that don't support it --- PublicFolders/Update-PublicFolderPermissions.ps1 | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/PublicFolders/Update-PublicFolderPermissions.ps1 b/PublicFolders/Update-PublicFolderPermissions.ps1 index 39fbd9879f..3bb603fbfc 100644 --- a/PublicFolders/Update-PublicFolderPermissions.ps1 +++ b/PublicFolders/Update-PublicFolderPermissions.ps1 @@ -104,7 +104,7 @@ function FindFoldersToUpdate([string[]]$includeFolders, [bool]$recurseOnFolders, $progress = 100 * $currentIncludeFolder / $includeFolders.Count Write-Progress -Activity "Retrieving folders to update" -Status $includeFolder -PercentComplete $progress - $foldersFound = @(Get-PublicFolder -Recurse:$recurseOnFolders $includeFolder -ResultSize Unlimited @script:CommonParams | Sort-Object Identity) + $foldersFound = @(Get-PublicFolder -Recurse:$recurseOnFolders $includeFolder -ResultSize Unlimited | Sort-Object Identity) if ($null -eq $foldersFound) { continue @@ -191,7 +191,9 @@ function GetPermissionUserIdentity($permissionUser) { function GetUsersToUpdate($currentFolder, [Array]$permissionsToPropagate) { Write-Progress -Id 1 -Activity "Querying current permissions" -Status "Processing" - $existingPermissions = [Array](Get-PublicFolderClientPermission $currentFolder.Identity @script:CommonParams) + $existingPermissions = [Array](Get-PublicFolderClientPermission $currentFolder.Identity) + Write-Verbose "$($MyInvocation.MyCommand): Found $($existingPermissions.Count) existing permissions" + Write-Verbose ($existingPermissions | Format-Table Identity, User, AccessRights | Out-String) $existingPermissionsPerUser = @{} $permissionCount = 0 @@ -212,10 +214,14 @@ function GetUsersToUpdate($currentFolder, [Array]$permissionsToPropagate) { Write-Progress -Id 1 -Activity "Comparing permissions" -PercentComplete $progress -Status "Processing" if (-not $existingPermissionsPerUser.ContainsKey($permission.User)) { + Write-Verbose "$($MyInvocation.MyCommand): No existing permission for $($permission.User)" $permission } else { if (IsUpdateRequired $existingPermissionsPerUser[$permission.User].AccessRights $permission.AccessRights) { + Write-Verbose "$($MyInvocation.MyCommand): Existing permission for $($permission.User) doesn't match desired permissions" $permission + } else { + Write-Verbose "$($MyInvocation.MyCommand): Existing permission for $($permission.User) matches desired permissions" } } @@ -242,7 +248,7 @@ foreach ($p in "Confirm", "WhatIf", "Verbose") { $permissionsToPropagate = @() if ($PropagateAll) { - $topLevelPermissions = Get-PublicFolderClientPermission $IncludeFolders[0] @script:CommonParams + $topLevelPermissions = Get-PublicFolderClientPermission $IncludeFolders[0] if ($null -eq $topLevelPermissions) { Write-Host "Unable to retrieve permissions from folder $($IncludeFolders[0])" return @@ -297,6 +303,8 @@ foreach ($currentFolder in $foldersToUpdate) { $permissionsToUpdateForFolder = $permissionsToPropagate } + Write-Verbose "$($MyInvocation.MyCommand): $($permissionsToUpdateForFolder.Count) permissions to apply for folder $($currentFolder.Identity)" + $folderOperationFailed=$false $usersProcessed=0 From 4fbcafce6d1ba880833846bac02bb1ed45c1a8e4 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Tue, 20 Aug 2024 02:11:17 -0500 Subject: [PATCH 05/10] Add doc --- .../Update-PublicFolderPermissions.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/docs/PublicFolders/Update-PublicFolderPermissions.md b/docs/PublicFolders/Update-PublicFolderPermissions.md index 11c077127a..af7805e0a9 100644 --- a/docs/PublicFolders/Update-PublicFolderPermissions.md +++ b/docs/PublicFolders/Update-PublicFolderPermissions.md @@ -2,3 +2,64 @@ Download the latest release: [Update-PublicFolderPermissions.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Update-PublicFolderPermissions.ps1) +This script can be used to set specific permissions on public folders in bulk or to propagate the full set of permissions from a parent folder to its entire subtree. + +## Syntax + +```powershell +Update-PublicFolderPermissions.ps1 + -IncludeFolders + -Users + -AccessRights + [-Recurse] + [-ExcludeFolderEntryIds ] + [-SkipCurrentAccessCheck] + [-ProgressLogFile ] + [-WhatIf] + [-Confirm] + [] + +Update-PublicFolderPermissions.ps1 + -IncludeFolders + -PropagateAll + [-Recurse] + [-ExcludeFolderEntryIds ] + [-SkipCurrentAccessCheck] + [-ProgressLogFile ] + [-WhatIf] + [-Confirm] + [] +``` + +## Usage + +```powershell +❯ .\Update-PublicFolderPermissions.ps1 -Users userone -AccessRights Owner -IncludeFolders "\FolderA" -Recurse -Confirm:$false +``` + +This syntax grants "userone" the Owner role on \FolderA and its entire subtree. + +```powershell +❯ .\Update-PublicFolderPermissions.ps1 -Users userone, usertwo -AccessRights Owner -IncludeFolders "\FolderA" -Recurse -Confirm:$false +``` + +This syntax grants both "userone" and "usertwo" the Owner role on \FolderA and its entire subtree. + +```powershell +❯ .\Update-PublicFolderPermissions.ps1 -PropagateAll -IncludeFolders "\FolderA" -Recurse -Confirm:$false +``` + +This syntax propagates all permissions from \FolderA to its entire subtree, including Default and Anonymous permissions. +Note that this option simply ensures that all the permission entries that exist on \FolderA also exist on all folders +underneath it. It does not remove permissions from subfolders when those permissions do not exist on \FolderA. + +## Notes about rights and roles + +Historically, the FolderContact right and the FolderVisible right could be toggled on and off without affecting +the role. This behavior can still be seen in classic Outlook. If a user is given the Owner role, FolderContact can be +toggled on or off. Either way, the user still has the Owner role. Similarly, in classic Outlook, a user can be given +the None role with or without FolderVisible. + +By contrast, the current EXO cmdlets assume that Owner always includes FolderContact, and None never includes +FolderVisible. Therefore, when propagating permissions with this script, None always means None _without_ FolderVisible, +and Owner always means Owner _with_ FolderContact. From 91938aeb31a7e1ccc556082e8de527570f3ac808 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Tue, 20 Aug 2024 02:25:35 -0500 Subject: [PATCH 06/10] SpellCheck fixes --- PublicFolders/Update-PublicFolderPermissions.ps1 | 6 +++--- docs/PublicFolders/Update-PublicFolderPermissions.md | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/PublicFolders/Update-PublicFolderPermissions.ps1 b/PublicFolders/Update-PublicFolderPermissions.ps1 index 3bb603fbfc..dc232ce068 100644 --- a/PublicFolders/Update-PublicFolderPermissions.ps1 +++ b/PublicFolders/Update-PublicFolderPermissions.ps1 @@ -14,7 +14,7 @@ # Identities of the Public Folders that will be updated # #.PARAMETER Users -# List of users whose current access rights to the folder will be overriten +# List of users whose current access rights to the folder will be overwritten # #.PARAMETER AccessRights # List of permissions to assign to the users @@ -60,7 +60,7 @@ # .\Update-PublicFolderPermissions.ps1 -IncludeFolders "\MyFolder" -AccessRights "Owner" -Users "John", "Administrator" -Recurse -ExcludeFolderEntryIds $foldersProcessed -Confirm:$false # # These commands replace the current client permissions for users "John" and "Administrator" on the "\MyFolder" -# Public Folder and all its children but skips those folders that were completd in the execution of Oct 30th 2014 at 6:20 pm. +# Public Folder and all its children but skips those folders that were completed in the execution of Oct 30th 2014 at 6:20 pm. # The users will be granted "Owner" access rights. These actions will be performed without requesting confirmation to the user. ############################################################################################################# @@ -317,7 +317,7 @@ foreach ($currentFolder in $foldersToUpdate) { foreach ($permission in $permissionsToUpdateForFolder) { $percentUsersProcessed = 100 * $usersProcessed/($permissionsToUpdateForFolder.Count) - Write-Progress -Id 1 -Activity "Processing User" -Status $permission.User -CurrentOperation "Removing exisitng permission" -PercentComplete $percentUsersProcessed + Write-Progress -Id 1 -Activity "Processing User" -Status $permission.User -CurrentOperation "Removing existing permission" -PercentComplete $percentUsersProcessed Remove-PublicFolderClientPermission -User $permission.User $currentFolder.Identity -ErrorAction SilentlyContinue @script:CommonParams Write-Progress -Id 1 -Activity "Processing User" -Status $permission.User -CurrentOperation "Adding permission" -PercentComplete $percentUsersProcessed diff --git a/docs/PublicFolders/Update-PublicFolderPermissions.md b/docs/PublicFolders/Update-PublicFolderPermissions.md index af7805e0a9..d4dc763527 100644 --- a/docs/PublicFolders/Update-PublicFolderPermissions.md +++ b/docs/PublicFolders/Update-PublicFolderPermissions.md @@ -34,16 +34,16 @@ Update-PublicFolderPermissions.ps1 ## Usage ```powershell -❯ .\Update-PublicFolderPermissions.ps1 -Users userone -AccessRights Owner -IncludeFolders "\FolderA" -Recurse -Confirm:$false +❯ .\Update-PublicFolderPermissions.ps1 -Users UserOne -AccessRights Owner -IncludeFolders "\FolderA" -Recurse -Confirm:$false ``` -This syntax grants "userone" the Owner role on \FolderA and its entire subtree. +This syntax grants "UserOne" the Owner role on \FolderA and its entire subtree. ```powershell -❯ .\Update-PublicFolderPermissions.ps1 -Users userone, usertwo -AccessRights Owner -IncludeFolders "\FolderA" -Recurse -Confirm:$false +❯ .\Update-PublicFolderPermissions.ps1 -Users UserOne, UserTwo -AccessRights Owner -IncludeFolders "\FolderA" -Recurse -Confirm:$false ``` -This syntax grants both "userone" and "usertwo" the Owner role on \FolderA and its entire subtree. +This syntax grants both "UserOne" and "UserTwo" the Owner role on \FolderA and its entire subtree. ```powershell ❯ .\Update-PublicFolderPermissions.ps1 -PropagateAll -IncludeFolders "\FolderA" -Recurse -Confirm:$false @@ -51,7 +51,7 @@ This syntax grants both "userone" and "usertwo" the Owner role on \FolderA and i This syntax propagates all permissions from \FolderA to its entire subtree, including Default and Anonymous permissions. Note that this option simply ensures that all the permission entries that exist on \FolderA also exist on all folders -underneath it. It does not remove permissions from subfolders when those permissions do not exist on \FolderA. +underneath it. It does not remove permissions from child folders when those permissions do not exist on \FolderA. ## Notes about rights and roles From 405955d87bc4443e1089e7f96d41c24b59b3e0c1 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Tue, 20 Aug 2024 02:26:47 -0500 Subject: [PATCH 07/10] Add doc to mkdocs.yml --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 955fc0e0e0..a3da5d3b12 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,6 +85,7 @@ nav: - SimplePerf: Performance/SimplePerf.md - PublicFolders: - SourceSideValidations: PublicFolders/SourceSideValidations.md + - Update-PublicFolderPermissions: PublicFolders/Update-PublicFolderPermissions.md - ValidateMailEnabledPublicFolders: PublicFolders/ValidateMailEnabledPublicFolders.md - ValidateEXOPFDumpster: PublicFolders/ValidateEXOPFDumpster.md - Retention: From e62bee9f41832e02685078f6fd590d06aad0e006 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Tue, 20 Aug 2024 10:00:40 -0500 Subject: [PATCH 08/10] Fix title and add supportability matrix --- docs/PublicFolders/Update-PublicFolderPermissions.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/PublicFolders/Update-PublicFolderPermissions.md b/docs/PublicFolders/Update-PublicFolderPermissions.md index d4dc763527..47f861fca3 100644 --- a/docs/PublicFolders/Update-PublicFolderPermissions.md +++ b/docs/PublicFolders/Update-PublicFolderPermissions.md @@ -1,9 +1,14 @@ -# ValidateExoPfDumpster +# Update-PublicFolderPermissions Download the latest release: [Update-PublicFolderPermissions.ps1](https://github.com/microsoft/CSS-Exchange/releases/latest/download/Update-PublicFolderPermissions.ps1) This script can be used to set specific permissions on public folders in bulk or to propagate the full set of permissions from a parent folder to its entire subtree. +Environment|Support +-|- +Exchange Online|Supported +Exchange 2019|Not Supported + ## Syntax ```powershell From 6b9b5e3f1f3911f4f2cfd6a2185466cc7f34ac8a Mon Sep 17 00:00:00 2001 From: Bill Long Date: Tue, 20 Aug 2024 10:12:17 -0500 Subject: [PATCH 09/10] Fix title and add supportability information --- PublicFolders/Update-PublicFolderPermissions.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PublicFolders/Update-PublicFolderPermissions.ps1 b/PublicFolders/Update-PublicFolderPermissions.ps1 index dc232ce068..ae4e5e57d3 100644 --- a/PublicFolders/Update-PublicFolderPermissions.ps1 +++ b/PublicFolders/Update-PublicFolderPermissions.ps1 @@ -1,6 +1,9 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +#Requires -Version 5.1 +#Requires -Modules ExchangeOnlineManagement + ############################################################################################################# #.SYNOPSIS # Updates client permissions of several users to a public folder From 393d56e2d51ba6f8e47c89c822edf172a2ff04a7 Mon Sep 17 00:00:00 2001 From: Bill Long Date: Fri, 23 Aug 2024 08:48:03 -0500 Subject: [PATCH 10/10] Update CODEOWNERS --- .github/CODEOWNERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e168f082dd..6c7770beae 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,3 +12,7 @@ # MDO script owners /M365/MDO/ @iserrano76 @rosspa05 @microsoft/css-exchange-admins /docs/M365/MDO/ @iserrano76 @rosspa05 @microsoft/css-exchange-admins + +# EXO PF Team +/PublicFolders/Update-PublicFolderPermissions.ps1 @vishmittal @microsoft/css-exchange-admins +/docs/PublicFolders/Update-PublicFolderPermissions.md @vishmittal @microsoft/css-exchange-admins