Skip to content

Search-PSMDPropertyValue Recursion #216

Open

Description

@FriedrichWeinmann

The command should be improved to prevent recursion and not follow up syncroots incorrectly.

Examples:

Get-Item C:\

NTFS Security Module

$object is returned via PSRemoting from another machine, SmbShare property is the result of Get-SmbShare

$object.SmbShare.ShareNtfsData.NTFSFolderAccess | where-object {$_.IdentityReference -eq 'DOMAIN\username'}

Activity

  1. OpossumWithAKeyboard commented on Sep 7, 2025

    @OpossumWithAKeyboard

    Here's the full code that can recreate the recursion bug with the NTFSSecurity module. It (soft) requires a remote computer that's accessible via winrm and have the local and remote computer be on the same domain for the kerberos auth. These are soft requirements and could be commented out to run against the local machine, but it would help with populating the custom object with data from multiple servers.

    Here's the object structure via multiple get-member against the correct path that gets us to the the IdentityReference value. In the pasted script below, the full path is $select.ShareDetails.ShareNtfsData.NTFSFolderAccess.IdentityReference

    $select | gm
    TypeName: System.Management.Automation.PSCustomObject
    
    Name         MemberType   Definition
    #----         ----------   ----------
    ServerName   NoteProperty System.String ServerName=Server1.contoso.com
    ShareDetails NoteProperty Selected.System.Management.Automation.PSCustomObject ShareDetails=@{ShareFullPath=System.Object[]; ShareName=System.Object[];...
    
    -------------------
    $select.ShareDetails | gm
    TypeName: Selected.System.Management.Automation.PSCustomObject
    
    ShareAccessControlType NoteProperty Object[] ShareAccessControlType=System.Object[]
    ShareAccessRight       NoteProperty Object[] ShareAccessRight=System.Object[]
    ShareFullPath          NoteProperty Object[] ShareFullPath=System.Object[]
    ShareName              NoteProperty Object[] ShareName=System.Object[]
    ShareNtfsData          NoteProperty Object[] ShareNtfsData=System.Object[]
    SharePermission        NoteProperty Object[] SharePermission=System.Object[]
    
    -------------------
    $select.ShareDetails.ShareNtfsData | gm
    TypeName: System.Management.Automation.PSCustomObject
    
    NTFSFolderAccess NoteProperty AuthorizationRuleCollection NTFSFolderAccess=System.Security.AccessControl.AuthorizationRuleCollection
    NTFSFolderOwner  NoteProperty string NTFSFolderOwner=BUILTIN\Administrators
    NTFSFolderPath   NoteProperty System.String NTFSFolderPath=\\Server1.contoso.com\Office\
    
    -------------------
    $select.ShareDetails.ShareNtfsData.NTFSFolderAccess | gm
    TypeName: System.Security.AccessControl.FileSystemAccessRule
    
    AccessControlType Property   System.Security.AccessControl.AccessControlType AccessControlType {get;}
    FileSystemRights  Property   System.Security.AccessControl.FileSystemRights FileSystemRights {get;}
    IdentityReference Property   System.Security.Principal.IdentityReference IdentityReference {get;}
    InheritanceFlags  Property   System.Security.AccessControl.InheritanceFlags InheritanceFlags {get;}
    IsInherited       Property   bool IsInherited {get;}
    PropagationFlags  Property   System.Security.AccessControl.PropagationFlags PropagationFlags {get;}
    
    #This is a custom module, just gets all the windows servers in AD and tests the winrm to make sure they can be accessed via remote powershell.
    $OnlineServers = Get-OnlineServers -Windows -WinrmPort
    
    #throttle limit at 10 takes 33 seconds, while 8 takes 41
    Write-Verbose -Message 'Starting the invoke-command block to all servers'
    
    $InvokedMachines = $OnlineServers | ForEach-Object -ThrottleLimit 10 -Parallel { 
        [PSCustomObject]@{
            InvokedServer   = $_
            SmbShareDetails = Invoke-Command -ComputerName $_ -Authentication Kerberos -HideComputerName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -ScriptBlock {
                try {
                    $ServerSmbShareDetails = Get-SmbShare | Where-Object { ($_.ShareType -eq "FileSystemDirectory") -and ($_.Name -notmatch '\$$') } | ForEach-Object {
                        $CurrentShare = $_
                        Get-SmbShareAccess -Name $CurrentShare.Name | Select-Object Name, @{N = "FullSharePath"; E = { $CurrentShare.Path } }, AccountName, AccessControlType, AccessRight
                    } 
                } catch {
                    Write-Error $_.ErrorDetails
                }
                $ServerSmbShareDetails
            } | Select-Object * -ExcludeProperty RunspaceId
        }
    }
    
    Write-Verbose -Message 'Excluding any servers that do not have non-default SMB shares from the check.'
    
    $ServersWithShares = $InvokedMachines | Where-Object { $null -ne $_.SmbShareDetails } | Sort-Object InvokedServer
    
    $global:ComputerReporting = $ServersWithShares | ForEach-Object {
        $Item_ServerWithShares = $_
    
        Write-Verbose -Message "In $($Item_ServerWithShares.InvokedServer)"
        $ServerSmbShareArrayCheck = ($Item_ServerWithShares | ForEach-Object { $_.SmbShareDetails.Name | Select-Object -Unique })
    
        [PSCustomObject] @{
            ServerName   = $_.InvokedServer
            ShareDetails = [PSCustomObject]@{
                ShareFullPath          = $Item_ServerWithShares.SmbShareDetails.FullSharePath
                ShareName              = $Item_ServerWithShares.SmbShareDetails.Name
                SharePermission        = $Item_ServerWithShares.SmbShareDetails.AccountName
                ShareAccessControlType = $Item_ServerWithShares.SmbShareDetails.AccessControlType
                ShareAccessRight       = $Item_ServerWithShares.SmbShareDetails.AccessRight
                ShareNtfsData          =
                if ($ServerSmbShareArrayCheck -is [array]) {
                    foreach ($Item_NestedInfo1 in $ServerSmbShareArrayCheck) {
                        Write-Verbose -Message "Checking Nested ACLs for \\$($Item_ServerWithShares.InvokedServer)\$($Item_NestedInfo1)."
    
                        try {
                            $NestedNtfsFindings = Get-Acl -Path ("\\$($Item_ServerWithShares.InvokedServer)\$($Item_NestedInfo1)") -ErrorAction Stop
                            [PSCustomObject]@{
                                NTFSFolderPath   = (Convert-Path $NestedNtfsFindings.Path)
                                NTFSFolderOwner  = $NestedNtfsFindings.Owner
                                NTFSFolderAccess = $NestedNtfsFindings.Access
                            }
                        } catch {
                            "Access is denied, or the folder no longer exists."
                        }
                    }
                } elseif ($ServerSmbShareArrayCheck -isnot [array]) {
                    Write-Verbose -Message "Checking single level ACLs for \\$($Item_ServerWithShares.InvokedServer)\$($ServerSmbShareArrayCheck)."
    
                    try {
                        $SingleNtfsFindings = Get-Acl -Path ("\\$($Item_ServerWithShares.InvokedServer)\$($ServerSmbShareArrayCheck)") -ErrorAction Stop
                        [PSCustomObject]@{
                            NTFSFolderPath   = (Convert-Path $SingleNtfsFindings.Path)
                            NTFSFolderOwner  = $SingleNtfsFindings.Owner
                            NTFSFolderAccess = $SingleNtfsFindings.Access
                        }
                    } catch {
                        "Access is denied, or the folder no longer exists."
                    }
                }
            }
        } 
    }
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions