I have to print the permissions of a user for a specific folder. Below, I have a working code. However it only scans if the user has been specifically given permissions. Now I also want to check, if a group of which the user is a member of, has permissions on the folder.
I thought about listing all groups in which my user is a MemberOf and then add them into a Generic List. Afterwards, I will execute the following code for each entry of this list.
$User = "testumgebung\cbruehwiler"
$Path = "T:\"
# Generic list object to store output in
$List = New-Object System.Collections.Generic.List[System.Object]
# Fields we want in list, an array of calculated properties.
$OutputFields = #(
#{name="Item" ; expression={$_.Path.split(':',3)[-1]}}
#{name="Rights" ; expression={$Right.FileSystemRights}}
#{name="AccessType" ; expression={$Right.AccessControlType}}
# #{name="User" ; expression={$User}}
)
# Store all objects in variable
$FileSystemObjects = Get-ChildItem $Path -Recurse | ForEach-Object {Get-Acl $_.FullName}
# Iterate through every object
foreach ($Item in $FileSystemObjects) {
# Iterate through every individual user right within each object
# Add it to our list if it matchers our $User
foreach ($Right in $Item.Access) {
if ($Right.IdentityReference -eq $User) {
$List.Add(($Item | Select-Object $OutputFields))
}
}
}
$List | Out-File C:\Users\cbruehwiler\Desktop\PermissionCheck.txt
My list prints the folder name, the different permissions and if it has access or not. I don't really want to change the structure too much.
I found a solution.
Import-Module ActiveDirectory
$User = "Testumgebung\cbruehwiler"
$UserOhneDomain = "cbruehwiler"
$Path = "T:\"
$List = New-Object System.Collections.Generic.List[System.Object]
$Groups = Get-ADPrincipalGroupMembership $UserOhneDomain
$GroupArrayList = New-Object System.Collections.ArrayList
foreach ($Group in $Groups)
{
$GroupArrayList.Add($Group.Name) | Out-Null
}
# Fields we want in list, an array of calculated properties.
$OutputFields = #(
#{name="Item" ; expression={$_.Path.split(':',3)[-1]}}
#{name="Rights" ; expression={$Right.FileSystemRights}}
#{name="AccessType" ; expression={$Right.AccessControlType}}
# #{name="User" ; expression={$User}}
)
$FileSystemObjects = Get-ChildItem $Path -Recurse | ForEach-Object {Get-Acl $_.FullName}
foreach ($Item in $FileSystemObjects) {
foreach ($Right in $Item.Access) {
if ($Right.IdentityReference -eq $User)
{
$List.Add(($Item | Select-Object $OutputFields))
}
}
}
foreach ($Item in $FileSystemObjects) {
foreach ($Right in $Item.Access) {
foreach ($GroupArrayItem in $GroupArrayList){
if ($Right.IdentityReference -eq ("TESTUMGEBUNG\" + $GroupArrayItem))
{
$List.Add(($Item | Select-Object $OutputFields))
}
}
}
}
$List | Out-File C:\Users\cbruehwiler\Desktop\PermissionCheck.txt
This Script checks all rights from a user on a file or share including all of the groups the user is a member of.
You just have to enter the user once with the "DOMAIN\username" variant and once with the "username" variant.
Hope this helps
Related
I have a script which adds users to Teams from a .csv, but there are likely to be additions/removals which will need to be accounted for in the first weeks after creation.
# Read team users from CSV file
$TeamUsers = Import-CSV "File_Path"
$i = 0
# Group the objects by their TeamDesc property, so we only query each group once
$TeamUsers | Group-Object TeamDesc | ForEach-Object {
# Get the GroupId of this Group
$groupId = (Get-Team -DisplayName $_.Name).GroupId
# If the Group couldn't be found, just skip below logic
if(-not $groupId) { return }
# If the Group could be found, create a hashtable for future splatting
$params = #{ GroupId = $groupId }
# now we can enumerate each object in each group of objects
foreach($user in $_.Group) {
try {
# create a hashtable for splatting progress
$progress = #{
PercentComplete = $i++ / $TeamUsers.Count * 100
Activity = 'Adding Users to MS Teams!!'
Status = 'Working on Team: "{0}" and User: "{1}"' -f $_.Name, $user.UserPrincipleName
}
Write-Progress #progress
# add this user with this role to the hashtable
$params['User'] = $user.UserPrincipleName
$params['Role'] = $user.Role
Add-TeamUser #params
}
catch {
('Error occurred for {0} - {1}' -f $user.TeamName, $user.UserPrincipleName),
$_.ToString() | Write-Warning
}
}
}
Currently, I can add users to the Team by adding their user information to the .csv but I want to be able to remove users if they are not found in the file. I found, in another answer, this selection:
$validUsers = Import-Csv 'C:\path\to\your.csv' | Select-Object -Expand dn
$invalidUsers = Get-ADGroupMember 'groupname' |
Where-Object { $validUsers -notcontains $_.distinguishedName }
Remove-ADGroupMember 'groupname' $invalidUsers -WhatIf
I am unsure of the best way to include this. Where in my current script can I incorporate checking for and removing users NOT found in a .csv from the populated Teams?
Can you please try the below logic:
[array]$teams = "T1","T2","T3"
$users = gc C:\temp\UserList.txt
foreach ($user in $users) {
$user = ($user -split "#")[0] # SamAccountName from UPN
foreach($t in $teams) {
$tMems = Get-AdGroupMember $t |select -exp SamAccountName
if ($tMems -match $user) {
RemoveAdGroupMember $t $user
}
}
}
I run an all users command for calendar delegation. I then report. The issue is how do I include someone that has no calendar delegation ? (Code Below)
In this line If ($null -ne $DelegateCal) I make sure someone has calendar delegation then build the object.
If I don't use += I am not sure how to build the object when I add a Else for the $null
<#All Google Calendar delegate report#>
# filename function
Import-Module C:\tasks\Modules\MRNAP\MRNAP.psm1
$AllGoogleUsers = gam print users fields suspended | ConvertFrom-Csv | Where-Object { $_.suspended -eq $False } | Select-Object -ExpandProperty PrimaryEmail
ForEach ($UserEmail in $AllGoogleUsers) {
$DelegateCal = gam calendar $UserEmail print acls | convertfrom-csv | Where-Object { $_.'scope.type' -eq 'user' -and $_.'Scope.value' -ne $UserEmail } -ErrorAction SilentlyContinue
If ($null -ne $DelegateCal) {
$CalendarDelegateList = foreach ($line in $DelegateCal) {
[PSCustomObject]#{
Owner = $line.calendarId
Type = 'Calendar'
Delegate = $line.'scope.value'
Role = $line.role
}
}
}
}
$CalendarDelegateList = $CalendarDelegateList | Sort-Object -Property Owner
$filename = MRNAP -ReportName WhoIsCalendarDelegated -Move
$CalendarDelegateLis | Export-Csv $filename -NoTypeInformation | Format-Table text-align=left -AutoSize
This is how I would do it with +=
$AllGoogleUsers = gam print users fields suspended | ConvertFrom-Csv | Where-Object { $_.suspended -eq $False } | Select-Object -ExpandProperty PrimaryEmail
ForEach ($UserEmail in $AllGoogleUsers) {
$DelegateCal = gam calendar $UserEmail print acls | convertfrom-csv | Where-Object { $_.'scope.type' -eq 'user' -and $_.'Scope.value' -ne $UserEmail } -ErrorAction SilentlyContinue
If ($null -ne $DelegateCal) {
foreach ($line in $DelegateCal) {
$CalendarDelegateList += [PSCustomObject]#{
Owner = $UserEmail
Type = 'Calendar'
Delegate = $line.'scope.value'
Role = $line.role
}
}
}
Else {
$CalendarDelegateList += [PSCustomObject]#{
Owner = $UserEmail
Type = 'Calendar'
Delegate = 'None'
Role = 'None'
}
}
}
It is always preferable to let PowerShell collect statement output in an array for you rather than building a list of outputs manually - both for concision and performance; see this answer for more information.
This even works with nested foreach loops, as in your case.
Applied to your scenario (abridged):
[array] $CalendarDelegateList =
foreach ($UserEmail in $AllGoogleUsers) {
$DelegateCal = gam calendar $UserEmail print acls | ConvertFrom-Csv | Where-Object { $_.'scope.type' -eq 'user' -and $_.'Scope.value' -ne $UserEmail } -ErrorAction SilentlyContinue
If ($null -ne $DelegateCal) {
foreach ($line in $DelegateCal) {
# Construct *and ouput* a [pscustomobject] instance.
[PSCustomObject]#{
Owner = $UserEmail
# ...
}
}
}
Else {
[PSCustomObject]#{
Owner = $UserEmail
# ...
}
}
}
All [pscustomobject] instances (implicitly) output from inside the foreach loop (whether directly or from the nested one) are automatically collected in variable $CalendarDelegateList.
Note:
With two or more output objects from the loop, the $CalendarDelegateList variable receives a regular PowerShell array (of type [object[]]).
The [array] type constraint (short for: [object[]]) additionally ensures that the result is an array even if the loop outputs only one object.
I was able to check through this code, but it is showing the permissions of all subfolders, I would like it to show only the current path, how to adjust this code in this way?
Import-Module ActiveDirectory
$User = "domain\user"
$UserOhneDomain = "user"
$Path = "folderpath"
$List = New-Object System.Collections.Generic.List[System.Object]
$Groups = Get-ADPrincipalGroupMembership $UserOhneDomain
$GroupArrayList = New-Object System.Collections.ArrayList
foreach ($Group in $Groups)
{
$GroupArrayList.Add($Group.Name) | Out-Null
}
# Fields we want in list, an array of calculated properties.
$OutputFields = #(
#{name="Item" ; expression={$_.Path.split(':',3)[-1]}}
#{name="Rights" ; expression={$Right.FileSystemRights}}
#{name="AccessType" ; expression={$Right.AccessControlType}}
# #{name="User" ; expression={$User}}
)
$FileSystemObjects = Get-ChildItem $Path -Recurse | ForEach-Object {Get-Acl $_.FullName}
foreach ($Item in $FileSystemObjects) {
foreach ($Right in $Item.Access) {
if ($Right.IdentityReference -eq $User)
{
$List.Add(($Item | Select-Object $OutputFields))
}
}
}
foreach ($Item in $FileSystemObjects) {
foreach ($Right in $Item.Access) {
foreach ($GroupArrayItem in $GroupArrayList){
if ($Right.IdentityReference -eq ("domain\" + $GroupArrayItem))
{
$List.Add(($Item | Select-Object $OutputFields))
}
}
}
}
$List | Out-File C:\Users\user\Desktop\PermissionCheck.txt
Try to remove the "-Recurse" parameter in the following line:
$FileSystemObjects = Get-ChildItem $Path -Recurse | ForEach-Object {Get-Acl $_.FullName}
I don't have a AD to check now, but i think that is the problem here.
I have some questions about GenericLists in PowerShell.
The script below prints all access rights on a file share for a specific user with his groups. Now I want to add a new row in my GenericList which shows from where (user/group) the right is inherited.
$User = "Testumgebung\cbruehwiler"
$UserOhneDomain = "cbruehwiler"
$Path = "T:\"
$List = New-Object System.Collections.Generic.List[System.Object]
$Groups = Get-ADPrincipalGroupMembership $UserOhneDomain
$GroupArrayList = New-Object System.Collections.ArrayList
foreach ($Group in $Groups) {
$GroupArrayList.Add($Group.Name) | Out-Null
}
# Fields we want in list, an array of calculated properties.
$OutputFields = #(
#{name="Item" ; expression={$_.Path.split(':',3)[-1]}}
#{name="Rights" ; expression={$Right.FileSystemRights}}
#{name="AccessType" ; expression={$Right.AccessControlType}}
#{name="From" ; expression={$User}}
)
$FileSystemObjects = Get-ChildItem $Path -Recurse | ForEach-Object {Get-Acl $_.FullName}
foreach ($Item in $FileSystemObjects) {
foreach ($Right in $Item.Access) {
if ($Right.IdentityReference -eq $User) {
$List.Add(($Item | Select-Object $OutputFields))
}
}
}
foreach ($Item in $FileSystemObjects) {
foreach ($Right in $Item.Access) {
foreach ($GroupArrayItem in $GroupArrayList){
if ($Right.IdentityReference -eq ("TESTUMGEBUNG\" + $GroupArrayItem)) {
$List.Add(($Item | Select-Object $OutputFields))
}
}
}
}
$List | Out-File C:\Users\cbruehwiler\Desktop\PermissionCheck.txt
The result looks like this:
Item Rights AccessType From
---- ------ ---------- ----
T:\TestFolder FullControl Allow Testumgebung\cbruehwiler
T:\TestFolder Read, Synchronize Allow Testumgebung\cbruehwiler
T:\TestFolder Write, ReadAndExecute, Synchronize Allow Testumgebung\cbruehwiler
The last row now prints only my user. However it should show the user or the group.
You could even merge the two loops into one, like this:
foreach ($Item in $FileSystemObjects) {
foreach ($Right in $Item.Access) {
foreach ($GroupArrayItem in $GroupArrayList) {
# not needed; just for convenience
[string]$id = $Right.IdentityReference
# test if the $Right.IdentityReference corresponds with the user name
if ($id -eq $User) {
$List.Add(($Item | Select-Object $OutputFields))
}
# test if the $Right.IdentityReference without the 'Domain\' part can be found in the list of groups
elseif (($id.Split("\", 2)[-1]) -in $GroupArrayList) {
# set the $User variable to the value of $Right.IdentityReference
$User = "Group: $id"
$List.Add(($Item | Select-Object $OutputFields))
}
}
}
}
I have this awesome script I use to generate a list of folders with their assigned security groups and each user in each group.
When I run it, I type .\getfolderacls.ps1 -verbose | export-csv c:\temp\filename.csv -notypeinformation.
That works perfectly, but I'd like to hardcode the | export-csv... part so that I can just run it without the arguments (or are they parameters?).
I tried simply appending | export-csv c:\temp\test.csv -notypeinformation to the bottom of the script, but that throws the error An empty pipe element is not allowed.
Script:
[CmdletBinding()]
Param (
[ValidateScript({Test-Path $_ -PathType Container})]
[Parameter(Mandatory=$false)]
[string]$Path
)
Write-Verbose "$(Get-Date): Script begins!"
Write-Verbose "Getting domain name..."
$Domain = (Get-ADDomain).NetBIOSName
Write-Verbose "Getting ACLs for folder $Path"
Write-Verbose "...and all sub-folders"
Write-Verbose "Gathering all folder names, this could take a long time on bigger folder trees..."
$Folders = Get-ChildItem -Path I:\foldername -Directory -Recurse -Depth 2
Write-Verbose "Gathering ACL's for $($Folders.Count) folders..."
ForEach ($Folder in $Folders)
{ Write-Verbose "Working on $($Folder.FullName)..."
$ACLs = Get-Acl $Folder.FullName | ForEach-Object { $_.Access | where{$_.IdentityReference -ne "BUILTIN\Administrators" -and $_.IdentityReference -ne "BUILTIN\Users" }}
ForEach ($ACL in $ACLs)
{ If ($ACL.IdentityReference -match "\\")
{ If ($ACL.IdentityReference.Value.Split("\")[0].ToUpper() -eq $Domain.ToUpper())
{ $Name = $ACL.IdentityReference.Value.Split("\")[1]
If ((Get-ADObject -Filter 'SamAccountName -eq $Name').ObjectClass -eq "group")
{ ForEach ($User in (Get-ADGroupMember $Name -Recursive | Select -ExpandProperty Name))
{ $Result = New-Object PSObject -Property #{
Path = $Folder.Fullname
Group = $Name
User = $User
FileSystemRights = $ACL.FileSystemRights
}
$Result | Select Path,Group,User,FileSystemRights
}
}
Else
{ $Result = New-Object PSObject -Property #{
Path = $Folder.Fullname
Group = ""
User = Get-ADUser $Name | Select -ExpandProperty Name
FileSystemRights = $ACL.FileSystemRights
}
$Result | Select Path,Group,User,FileSystemRights
}
}
Else
{ $Result = New-Object PSObject -Property #{
Path = $Folder.Fullname
Group = ""
User = $ACL.IdentityReference.Value
FileSystemRights = $ACL.FileSystemRights
}
$Result | Select Path,Group,User,FileSystemRights
}
}
}
}
Write-Verbose "$(Get-Date): Script completed!"
Your script's output is being produced inside a foreach loop - ForEach ($Folder in $Folders) ... (as opposed to via the ForEach-Object cmdlet, which, unfortunately, is also aliased to foreach).
In order to send a foreach loop's output to the pipeline, you can wrap it in a script block ({ ... }) and invoke it with the dot-sourcing operator (.).
Alternatively, use the call operator (&), in which case the loop runs in a child scope.
Here are simplified examples:
# FAILS, because you can't use a foreach *loop* directly in a pipeline.
PS> foreach ($i in 1..2) { "[$i]" } | Write-Output
# ...
An empty pipe element is not allowed.
# ...
# OK - wrap the loop in a script block and invoke it with .
PS> . { foreach ($i in 1..2) { "[$i]" } } | Write-Output
[1]
[2]
Note: I'm using Write-Output as an example of a cmdlet you can pipe to, solely for the purpose of this demonstration. What's required in your case is to wrap your foreach loop in . { ... } and to follow it with | Export-Csv ... instead of Write-Output.
Using . { ... } or & { ... } sends the output generated inside the loop to the pipeline as it is being produced, one by one, aka in streaming fashion - as (typically) happens with output produced by a cmdlet.
An alternative is to use $(...), the subexpression operator (or #(...), the array-subexpression operator, which works the same in this scenario), in which case the loop output is collected in memory as a whole, up front, before it is sent through the pipeline - this is typically faster, but requires more memory:
# OK - call via $(...), with output collected up front.
PS> $(foreach ($i in 1..2) { "[$i]" }) | Write-Output
[1]
[2]
To spell the . { ... } solution out in the context of your code - the added lines are marked with # !!! comments (also note the potential to improve your code based on Lee_Dailey's comment on the question):
[CmdletBinding()]
Param (
[ValidateScript({Test-Path $_ -PathType Container})]
[Parameter(Mandatory=$false)]
[string]$Path
)
Write-Verbose "$(Get-Date): Script begins!"
Write-Verbose "Getting domain name..."
$Domain = (Get-ADDomain).NetBIOSName
Write-Verbose "Getting ACLs for folder $Path"
Write-Verbose "...and all sub-folders"
Write-Verbose "Gathering all folder names, this could take a long time on bigger folder trees..."
$Folders = Get-ChildItem -Path I:\foldername -Directory -Recurse -Depth 2
Write-Verbose "Gathering ACL's for $($Folders.Count) folders..."
. { # !!!
ForEach ($Folder in $Folders)
{ Write-Verbose "Working on $($Folder.FullName)..."
$ACLs = Get-Acl $Folder.FullName | ForEach-Object { $_.Access | where{$_.IdentityReference -ne "BUILTIN\Administrators" -and $_.IdentityReference -ne "BUILTIN\Users" }}
ForEach ($ACL in $ACLs)
{ If ($ACL.IdentityReference -match "\\")
{ If ($ACL.IdentityReference.Value.Split("\")[0].ToUpper() -eq $Domain.ToUpper())
{ $Name = $ACL.IdentityReference.Value.Split("\")[1]
If ((Get-ADObject -Filter 'SamAccountName -eq $Name').ObjectClass -eq "group")
{ ForEach ($User in (Get-ADGroupMember $Name -Recursive | Select -ExpandProperty Name))
{ $Result = New-Object PSObject -Property #{
Path = $Folder.Fullname
Group = $Name
User = $User
FileSystemRights = $ACL.FileSystemRights
}
$Result | Select Path,Group,User,FileSystemRights
}
}
Else
{ $Result = New-Object PSObject -Property #{
Path = $Folder.Fullname
Group = ""
User = Get-ADUser $Name | Select -ExpandProperty Name
FileSystemRights = $ACL.FileSystemRights
}
$Result | Select Path,Group,User,FileSystemRights
}
}
Else
{ $Result = New-Object PSObject -Property #{
Path = $Folder.Fullname
Group = ""
User = $ACL.IdentityReference.Value
FileSystemRights = $ACL.FileSystemRights
}
$Result | Select Path,Group,User,FileSystemRights
}
}
}
}
} | Export-Csv c:\temp\test.csv -notypeinformation # !!!
Write-Verbose "$(Get-Date): Script completed!"