1

I am trying to get the profile folders of the user User1 but don't want User10 folders from a directory.

Here is what I have tried:

Get-ChildItem -Path "$Profilesroot" -Recurse -Filter "*User1*"                     

and the out put shows User1, User1.v1, User10, User10.v1 folders

Same out put for the for the following:

Get-ChildItem "$profilesroot" -Recurse | Select-String -Pattern "User1"
Get-ChildItem "$profilesroot" -Recurse | Where {$_.Name -match 'User1'}
 Get-ChildItem "$profilesroot" -Recurse | Where {$_.Name -like '*User1*'}

If I try the following, I am getting the required output but I feel there is a better option:

 Get-ChildItem $profiles -Recurse | Where {$_.Name -like 'User1'}
 Get-ChildItem $profiles -Recurse | Where {$_.Name -like 'User1.*'}

Apologies if the ask was not clear.

cognic
  • 25
  • 4

2 Answers2

1

To get user's profile path, you should rely on operating system, but not on path concatenation.

$path = @(Get-WmiObject -Class 'Win32_UserProfile' |
    Where-Object { $_.Special -eq $false } |
    Where-Object { -not [string]::IsNullOrWhiteSpace($_.LocalPath) } |
    Where-Object { $_.LocalPath.EndsWith("\User1") } |
    Select-Object -ExpandProperty 'LocalPath' )[0]

More accurate way, because

  • sometimes profile path not matches username
  • sometimes profile path matches other user's name
  • sometimes profile path is not in profileroot
  • sometimes user have more than one profile and some of this profiles are broken and not used by user.

 $path = @(Get-WmiObject -Class 'Win32_UserProfile' |
    Where-Object { $_.Special -eq $false } |
    Where-Object { -not [string]::IsNullOrWhiteSpace($_.LocalPath) } |
    Where-Object { $_.SID -eq ([System.Security.Principal.NTAccount]('User1')).Translate([System.Security.Principal.SecurityIdentifier]).ToString() } |
    Select-Object -ExpandProperty 'LocalPath' )[0]

Even more accurate way using SID matching and WMI filtering

$userSid = ([System.Security.Principal.NTAccount]('User1')).Translate([System.Security.Principal.SecurityIdentifier]).ToString()
$path = @(Get-WmiObject -Class 'Win32_UserProfile' -Filter "(Special = FALSE) AND (LocalPath LIKE '%\\%') AND (SID = '$($userSid)')" -Property @('LocalPath'))[0].LocalPath
filimonic
  • 3,988
  • 2
  • 19
  • 26
  • 1
    PowerShell 7 is based on PowerShell Core which does not support WIM. Use CIM functions. https://github.com/PowerShell/PowerShell/issues/13292 – lit May 14 '21 at 14:16
  • Thank you for a detailed answer. Sorry, I should've mentioned bit more about profiles. These are not local but the ones in AD but for some reason where we store the profiles, there are .v2, .v4, .v6 for the same user. – cognic May 14 '21 at 14:41
  • @cognic then you should check it using pairs `[SID|PATH]` because if user was renamed, it will "loose" it's profile. And you can find two profiles like "JSmith.v2" and "JBlack.v2" belonging to same user ( see (Get-Acl $path).Owner ) – filimonic May 14 '21 at 15:33
  • @lit Powershell 7 is not *yet* default powershell in Windows world, so nothing bad using `WMI` – filimonic May 14 '21 at 15:35
  • @filimonic, the CIM cmdlets (e.g., `Get-CimInstance`) superseded the WMI cmdlets (e.g., `Get-WmiObject`) in Windows PowerShell _v3_ (released in September 2012). For more information, see [this answer](https://stackoverflow.com/a/54508009/45375). – mklement0 May 14 '21 at 20:22
1

If you use Get-ChildItem's -Include parameter instead of -Filter, you can pass multiple PowerShell wildcard expressions.

Note:

  • Use of -Include is generally slower than use of -Filter, because the latter filters at the source, whereas -Include collects all items and then filters. Conversely, the -Include uses PowerShell's wildcard expressions, and not the more limited ones supported by -Filter, which are additionally saddled with legacy quirks.

  • However, a single Get-ChildItem call still outperforms a Get-ChildItem -Recurse ... | Where-Object { ... } pipeline.

    • That is - generally speaking - use something like
      Get-ChildItem -Recurse ... | Where-Object { $_.Name -match '^User1\b' }, as suggested by Mathias R. Jessen, only if you truly need the enhanced matching flexibility that -match, the regular expression-based string-matching operator, provides.

If you're really looking for matching directories at all levels of the $ProfilesRoot subtree (-Recurse), use the following:

Get-ChildItem $ProfilesRoot -Recurse -Directory -Include User1, User1.*

If, by contrast, you're just looking for matching directories located directly in $ProfilesRoot:

Get-ChildItem $ProfilesRoot\* -Directory -Include User1, User1.*

Note the addition of \* to the (positionally implied) -Path argument, because in the absence of -Recurse -Include is unexpectedly only applied to the input path itself, not its children - see this answer for a detailed discussion of this counterintuitive behavior.

mklement0
  • 382,024
  • 64
  • 607
  • 775