#region Private Helpers <# .SYNOPSIS Tests if a PnP Group exists. .DESCRIPTION This function tests if a PnP Group exists in the current SharePoint site. .PARAMETER Identity The identity of the group to test. .EXAMPLE Test-PnPGroup -Identity "MyGroup" #> function Test-PnPGroup { param( [string]$Identity ) try { Get-PnPGroup -Identity $Identity -ErrorAction Stop | Out-Null return $true } catch { return $false } } <# .SYNOPSIS Tests if an Entra ID Group exists and PnP can resolve it. .DESCRIPTION This function tests if a Entra ID Group exists and can be resolved by PnP. .PARAMETER Identity The identity of the group to test. .EXAMPLE Test-EntraIdGroup -Identity "MyGroup" #> function Test-EntraIdGroup { param( [string]$Identity ) try { Get-PnPEntraIdGroup -Identity $Identity -ErrorAction Stop | Out-Null return $true } catch { return $false } } #endregion #region Public Functions <# .SYNOPSIS Sets permissions on a folder in a SharePoint document library. .DESCRIPTION This script breaks permission inheritance on a specified folder in a SharePoint document library and assigns permissions to a specified owner group and additional groups defined in the ACL parameter. .PARAMETER Name The name of the folder to set permissions on. .PARAMETER List The name of the document library containing the folder. Default is 'Shared Documents'. .PARAMETER Owner The name of the SharePoint group to assign as the owner of the folder with 'Full Control' permissions. .PARAMETER Acl An array of objects defining additional groups and their permissions to assign to the folder. Each object should have a 'DisplayName' property for the group name and a 'Role' property for the permission level (e.g., 'Read', 'Edit'). .EXAMPLE $Acl = @( @{ DisplayName = "SG-ADMIN-AdvocateFloats-Dynamic"; Role = "Edit" }, @{ DisplayName = "SG-ADMIN-AdvocateManagers-Dynamic"; Role = "Edit" } ) .\Set-PnPFolderAcl.ps1 -Name "ProjectX" -List "Shared Documents" -Owner "Project Owners" -Acl $Acl #> function Set-PnPFolderAcl { [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'High' )] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] [string]$FolderName, [Parameter()] [string]$List = 'Shared Documents', [Parameter(Mandatory)] [string]$Owner, [Parameter(Mandatory)] [pscustomobject[]]$Acl ) begin { if (-not (Get-PnPContext)) { Throw "Not connected to a SharePoint site. Run Connect-PnPOnline first." } if (-not (Get-PnPList -Identity $List -ErrorAction SilentlyContinue)) { Throw "The specified list '$List' does not exist." } if (-not (Test-PnPGroup -Identity $Owner)) { Throw "The specified owner group '$Owner' does not exist." } $ValidRoles = Get-PnPRoleDefinition | Select-Object -ExpandProperty Name if (-not $ValidRoles) { throw "Unable to retrieve SharePoint role definitions." } foreach ($entry in $Acl) { if (-not ($entry.PSObject.Properties.Name -contains 'Group' -and $entry.PSObject.Properties.Name -contains 'Role')) { Throw "Each ACL entry must contain 'Group' and 'Role' properties." } if (-not (Test-PnPGroup -Identity $entry.Group) -and -not (Test-EntraIdGroup -Identity $entry.Group)) { Throw "The specified group '$($entry.Group)' does not exist as a PnP Group or Entra ID Group." } if ($entry.Role -notin $ValidRoles) { Throw "Invalid role '$($entry.Role)' specified for group '$($entry.Group)'. Valid roles are: $($ValidRoles -join ', ')." } } } process { $FolderPath = "$List/$FolderName" # Retrieve folder and current permissions $FolderItem = Get-PnPFolder -Url $FolderPath -ErrorAction Stop $ListItem = Get-PnPListItem -List $List -Id $FolderItem.ListItemAllFields.Id # Break inheritance ONLY IF the folder doesn't already have unique permissions if (-not $ListItem.HasUniqueRoleAssignments) { if ($PSCmdlet.ShouldProcess( $FolderPath, "Break inheritance and grant Full Control to '$Owner' on '$FolderPath'." )) { Write-Verbose "Breaking inheritance for '$FolderPath'." Write-Verbose "Granting 'Full Control' to '$Owner' on '$FolderPath'." Set-PnPFolderPermission ` -List $List ` -Identity $FolderPath ` -Group $Owner ` -AddRole 'Full Control' ` -ClearExisting } } # Get existing role assignments $RoleAssignments = Get-PnPProperty -ClientObject $ListItem -Property RoleAssignments $CurrentAcl = foreach ($ra in $RoleAssignments) { $principal = Get-PnPProperty -ClientObject $ra -Property Member $bindings = Get-PnPProperty -ClientObject $ra -Property RoleDefinitionBindings foreach ($binding in $bindings) { [pscustomobject]@{ Principal = $principal.Title Role = $binding.Name } } } # foreach ($entry in $CurrentAcl) { # if ($entry.Principal -eq $Owner) { # continue # } # if # } foreach ($entry in $Acl) { $GroupName = $entry.Group $Role = $entry.Role $AlreadyAssigned = $ResolvedRoles | Where-Object { $_.Principal -eq $GroupName -and $_.Role -eq $Role } if ($AlreadyAssigned) { Write-Verbose "Permission '$Role' already assigned to '$GroupName' on '$FolderPath'. Skipping." continue } if (Test-PnPGroup $GroupName) { if ($PSCmdlet.ShouldProcess( $FolderPath, "Grant '$Role' to SharePoint group '$GroupName' on '$FolderPath'." )) { Write-Verbose "Granting '$Role' to SharePoint group '$GroupName' on '$FolderPath'." Set-PnPFolderPermission ` -List $List ` -Identity $FolderPath ` -Group $GroupName ` -AddRole $Role } } elseif (Test-EntraIdGroup $GroupName) { if ($PSCmdlet.ShouldProcess( $FolderPath, "Grant '$Role' to Entra ID group '$GroupName' on '$FolderPath'." )) { Write-Verbose "Granting '$Role' to Entra ID group '$GroupName' on '$FolderPath'." Set-PnPFolderPermission ` -List $List ` -Identity $FolderPath ` -Group $GroupName ` -AddRole $Role } } } } } #endregion Export-ModuleMember -Function Set-PnPFolderAcl