Retrieve all unique permissions of all folders in SharePoint libraries - powershell

I am trying to generate a report with all unique permissions in all folders of a SharePoint Online library (and not user permissions, the goal is to retrieve group permissions). I wrote a Powershell script using SharePoint PnP. Please see below.
$permissionCSV = "path-to-csv.csv"
$CAMLQuery = "<View Scope='RecursiveAll'><RowLimit>5000</RowLimit></View>"
$items = Get-PnPListItem -List "My-List" -Query $CAMLQuery
$ctx = $Get-PnPContext
foreach($item in $items) {
if(item.FileSystemObjectType -eq 'folder') {
Get-PnPProperty -ClientObject $item -Property HasUniqueRoleAssignments
if ($item.HasUniqueRoleAssignments) {
$ctx.load($item.RoleAssignments)
$ctx.load($item.Folder)
$ctx.ExecuteQuery()
foreach($RoleAssignments in $item.RoleAssignments) {
$ctx.Load($RoleAssignments.Member)
$ctx.Load($RoleAssignments.RoleDefinitionBindings)
$ctx.ExecuteQuery()
foreach($RoleDefinition in $RoleAssignments.RoleDefinitionBindings) {
$RoleDefinition |
Select #{expression={$item.Folder.Name};Label="Name"},
#{expression={$item.Folder.ServerRelativeUrl};Label="Path"},
#{expression={$RoleAssignments.Member.Title};Label="Role"},
#{expression={$_.Name};Label="Permission"}|
Export-CSV $permissionCSV -Append -Force -Encoding UTF8 -NoTypeInformation
}
}
}
}
}
This code works. However, because our library is very large (more than 5000 folders), such a script would take days to be executed. So I am wondering if I am taking a wrong approach. All the posts I read only mention this method, which requires network communication for each single folder...
Does anyone knows the best way to generate such report without taking days? Any idea is welcome, including using other programming languages, but I would like to avoid paid third-party services if possible.
Thank you for your help.

Related

Power Automate Owners not found?

Looking to get all owners for each flow in Power Automate. Randomly, a large portion of them are reporting no owners, but I can manually check that they do have owners. Help!
Remove-Variable * -ErrorAction SilentlyContinue
$Flows = Get-adminflow
Foreach ($flow in $flows)
{
$flow.DisplayName
Get-Adminflowownerrole -EnvironmentName $flow.EnvironmentName -flowname $flow.flowname
}
It can pull the names of all of them. They are all in different environments, so that isn't the root cause.

Exposing the Connection token from Connect-AzureAd

I am using the AzureAd Powershell module for user management. However it does not have all the functionality that I need, specifically, I can't assign Application Extension values to objects, (although I can create delete and remove application extensions themselves via [Get/New/Remove]-AzureADApplicationExtensionProperty).
I know from watching the API calls with Fiddler that the graph calls are using bearer tokens, and I've called the graph API directly from Postman manually so I know how to use the Bearer token if I could get it. How do I get it?
To get the token simply use:
$token = [Microsoft.Open.Azure.AD.CommonLibrary.AzureSession]::AccessTokens['AccessToken']
But how could one come to this conclusion?
First look for where the module is located:
(Get-Module AzureAd).Path
C:\Program Files\WindowsPowerShell\Modules\AzureAD\2.0.1.3\Microsoft.Open.AzureAD16.Graph.PowerShell.dll
Now lets just make 2 assumptions. First that the token is stored in a static member of a static class, and second that it might not be stored in that dll, but any of the DLLs in the folder.
$fileInfo = New-Object 'IO.FileInfo' (Get-Module AzureAd).Path
$moduleFolder = $fileInfo.Directory.FullName
$assemblies = [AppDomain]::CurrentDomain.GetAssemblies() | where { $_.Location -ne $null -and $_.Location.StartsWith($moduleFolder)}
$assemblies | select -expandproperty ExportedTypes | Where { $_.IsSealed -and $_.IsAbstract } | Select Name, FullName
That last line btw is because of the weird way static types are noted in IL.
Which outputs a very small list:
Name FullName
---- --------
RestSharpExtensionMethods Microsoft.Open.Azure.AD.CommonLibrary.RestSharpExtensionMethods
AzureSession Microsoft.Open.Azure.AD.CommonLibrary.AzureSession
DictionaryExtensions Microsoft.Open.Azure.AD.CommonLibrary.DictionaryExtensions
Logger Microsoft.Open.Azure.AD.CommonLibrary.Logger
ImageUtils Microsoft.Open.Azure.AD.CommonLibrary.Utilities.ImageUtils
SecureStringExtension Microsoft.Open.Azure.AD.CommonLibrary.Extensions.SecureStringExtension
AzureEnvironmentConstants Microsoft.Open.Azure.AD.CommonLibrary.AzureEnvironment+AzureEnvironmentConstants
TypeToOdataTypeMapping Microsoft.Open.AzureAD16.Client.TypeToOdataTypeMapping
JsonConvert Newtonsoft.Json.JsonConvert
Extensions Newtonsoft.Json.Linq.Extensions
Extensions Newtonsoft.Json.Schema.Extensions
TypeToOdataTypeMapping Microsoft.Open.MSGraphV10.Client.TypeToOdataTypeMapping
AdalError Microsoft.IdentityModel.Clients.ActiveDirectory.AdalError
AuthenticationContextIntegratedAuthExtensions Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions
AdalOption Microsoft.IdentityModel.Clients.ActiveDirectory.AdalOption
MiscExtensions RestSharp.Extensions.MiscExtensions
ReflectionExtensions RestSharp.Extensions.ReflectionExtensions
ResponseExtensions RestSharp.Extensions.ResponseExtensions
ResponseStatusExtensions RestSharp.Extensions.ResponseStatusExtensions
StringExtensions RestSharp.Extensions.StringExtensions
XmlExtensions RestSharp.Extensions.XmlExtensions
RestClientExtensions RestSharp.RestClientExtensions
SimpleJson RestSharp.SimpleJson
We could pipe through Out-Gridview if the list was longer, but my attention was immediatly drawn to AzureSession. After that a little PowerShell autocomplete, and I found my way to [Microsoft.Open.Azure.AD.CommonLibrary.AzureSession]::AccessTokens['AccessToken']

Handeling non-stopping errors for Get-ChildItem in PowerShell

I'm currently writing a script which should be used to get the average access/list time for a directory tree on a CIFS share. To do this I'm using the following code (as a snippet):
$time = Measure-Command {
try{
$subitems = Get-ChildItem $directory
}catch{
$msg = "Error accessing "+$dir+": "+$_.Exception.Message
}
}
That piece of code is working fine and does get me the information I want. But one issue I'm facing is that there are non stopping errors for Get-ChildItem which are not caught by the catch (as they are non stopping). To prevent this I could add -ErrorAction Stop to Get-ChildItem but if I do that I won't be able to get a listing for the directory that has even one item that throws an error.
Examples of this include missing permissions and paths exceeding 260 characters (for whatever reason that is still a thing). I really would like to get that information in some way to do some further handling/reporting on it. Would anyone know how to catch those/react to those?
My research so far always suggests to use -ErrorAcction Stop which would "discard" any information for $subitems that I could use.
So you want to catch the error and the script to continue,I have modified your code to redirect error output and then check previous command's exit status to check whether any error occured.
Is this what you are looking for?
$time = Measure-Command {
try{
$subitems = Get-ChildItem $directory 2> $outnull
if(-not $?){
#whatever action you want to perform
$msg = $msg + "Error accessing "+$dir+": "+$error[0].Exception.Message
}
}catch{
$msg = "Error accessing "+$dir+": "+$_.Exception.Message
}
}
I am concatenating $msg in the block with itself ,so that no msg will be lost by overwriting

Assigning rights to sharepoint folder with Powershell

I am new to powershell and a bit more experienced with Sharepoint.
I need to assign rights to a folder within a list to a specific user with Contribute.
Site is http://contoso.com/stores/Form1/001
List: Form1
Folder: 001
User: contoso\user001
I want the simplest code possible to assign a single user the "Contribute" right for the folder 001.
Thank you very much everyone!
When I google on the subject, every thread gets back to this site but I am unable to make this work. If someone could tweak it for me with my informations or make a new code for me it would be very appreciated. I will be glad to accept answer and boost your rep for it!
http://sharepoint2010tutor.blogspot.com.au/2011/08/grant-folder-permissionsharepoint-using.html
You need to pass in the user id of an existing SharePoint user when you call the GrantUserPermission function. Right now you're passing the permission level you want assign. That is already set in the role binding.
if($folder.Name.Equals("001"))
{
# don't pass the permission level here --> GrantUserPermission("Contribute")
# pass a valid SharePoint user.
GrantUserPermission("adventureworks\jdoe")
}
Currently this is what I was using:
Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue
Add-SPShellAdmin
cls
$site = new-object Microsoft.SharePoint.SPSite("http://contoso.com/stores")
$web = $site.OpenWeb()
function GrantUserpermission($userName)
{
[Microsoft.SharePoint.SPUserCollection]$spusers= [Microsoft.SharePoint.SPUserCollection]$web.SiteUsers
[Microsoft.SharePoint.SPUser]$spuser=$spusers[$userName]
$sproleass=new-object Microsoft.SharePoint.SPRoleAssignment([Microsoft.SharePoint.SPPrincipal]$spuser)
$folder.BreakRoleInheritance("true")
$sproleass.RoleDefinitionBindings.Add($web.RoleDefinitions["Contribute"])
$folder.RoleAssignments.Add($sproleass);
Write-Host "Permission provided for user ", $userName
}
$doclib=[Microsoft.SharePoint.SPDocumentLibrary]$web.Lists["Form1"]
$foldercoll=$doclib.Folders;
foreach($folder in $foldercoll)
{
Write-Host $folder.Name
if($folder.Name.Equals("001"))
{
GrantUserPermission("Contribute")
}
}
Write-Host "Completed...."
$web.Close()
$site.Dispose()

PowerShell Move User to different OU in different Domain with Credentials without Quest/ADAM/Cmdlets

I'm using
[System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($context, $idtype, $sam)
How do I move this use to a new OU?
I've tried:
...
$user_adspath = $user.Properties.adspath
$user_ou = [ADSI]"$user_adspath"
$user_ou.PSBase.MoveTo("LDAP://$target")
I recive a "General Access Denied" error. Due to the fact that I need rights. This works though.
...
$user.description += " MOVED"
$user.Enabled = $False
$user.Save()
Remember this is on a non-2008 server without Quest, ADAM, Cmdlets do not work. The only thing I have working is:
"Add-Type -AssemblyName System.DirectoryServices.AccountManagement"
I need something along the lines of this:
$user.MoveTo("LDAP://$target")
$user.Save()
You appear to be on the right track. Do you have write permissions in the target OU? It should be as simple as this:
[adsi]$OU="LDAP://OU=Disabled Accounts,OU=Employees,DC=mycompany,DC=local"
[adsi]$User="LDAP://CN=Art Deco,OU=Sales,OU=Employees,DC=mycompany,DC=local"
$user.psbase.Moveto($OU)
You don't need to load any assemblies or use anything else.
Figured it out:
[adsi]$dest = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://ou=somwhere,dc=company,dc=local","domain\username","password")
$user_move = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://cn=user,ou=somehow,dc=company,dc=local","domain\username","password")
$user_move.PSBase.MoveTo($dest)
Took awhile!