1

I need to filter out only the AD groups from the Net User command in powershell. All the AD groups begin with an * so i would like to filter out the string by displaying everything that's preceeded by an *

I get an error since '*' is a special character and cannot be used. How do i get powershell to ignore it as a special character?

I cannot use any other commands to get AD groups, so Get-AD is not an option. i only have Net user to work with.

My base script,

Net User USER /domain | Select-String '*'

I cannot use any other script than Net user to accomplish this task, even though Get-AD would be simpler, i do not have the option.

2
  • 3
    You should be able to use ADSI, but you just have to escape the token since Select-String's -Pattern parameter works off RegEx. Either use \*, or specify -SimpleMatch. Commented Jan 25, 2023 at 22:04
  • why not adsi ? this has been available in dotnet since long time Commented Jan 25, 2023 at 22:06

3 Answers 3

1

Santiago's helpful answer shows a more robust, OO solution that is much more in the spirit of PowerShell.


To answer your question as asked:

Select-String by default interprets its (positionally implied) -Pattern argument as a regex (regular expression), where * is a metacharacter.

While \-escaping regex metacharacters is possible (and is necessary in the event that you need more sophisticated matching that requires a regex), the direct solution is to add the
-SimpleMatch switch, which causes the -Pattern argument to be interpreted as a literal (verbatim) string:

net user $someuser /domain | Select-String * -SimpleMatch

Also note that what Select-String outputs by default aren't just the matching input lines as-is, but Microsoft.PowerShell.Commands.MatchInfo objects that provide metadata for each match, with the matching line text stored in the .Line property.

While that distinction doesn't matter much for displaying results, it may for programmatic processing, so if you only want to output the text of the matching lines, add -Raw in PowerShell (Core) 7+, or pipe to | ForEach-Object Line in Windows PowerShell.


The above will show those net user output lines that contain a literal *, and therefore all group memberships, which is good enough for the human observer.

You indeed need regex matching and operations if you want to extract the group names individually, for later programmatic processing:

# Use an ordered hashtable to collect the group names in,
# with keys 'Local' and 'Global', targeting the *current* user in this example.
$groupMemberships = [ordered] @{}
(((net user $env:USERNAME) -join "`n") -split "`n`n")[-1] -split '\n' -match ' \*' |
  ForEach-Object { 
    $tokens = $_ -split ' \*'
    if ($tokens[0] -notmatch '^ ') {
      $key = if ($groupMemberships.Count -eq 0) { 'Local' } else { 'Global' }
    }
    $groupMemberships[$key] += @($tokens[1..($tokens.Count-1)].Trim())
}

$groupMemberships # display result.

Sample output:

Name                           Value
----                           -----
Local                          { Administrators }
Global                         { Department1, Region1 }

That is $groupMemberships.Local $groupMemberships.Global then contains the name(s) of the local / global (AD) groups the user is a member of, respectively, as an array.

Note:

  • The solution above is complex, because it tries to be as robust as possible.

  • Notably, it is possible - albeit not likely in practice - that output lines that are unrelated to group names contain * as well, notably the Comment and User's comment fields.

  • Therefore, only the last paragraph of net user's output is considered, which is known to contain the group names - note that matching lines by field-name parts such as Local and Global is explicitly avoided, as the field names are localized based on your system's display language.

  • The last paragraph is known to list the local group memberships first, followed by the global (AD) ones. Each line in the last paragraph can contain multiple (*-prefixed) group names and there can be overflow lines for additional groups that don't fit on the first line for the given scope. Such overflow flow lines can be detected by starting with whitespace.

Sign up to request clarification or add additional context in comments.

Comments

1

Instead of trying to parse the output from net user USER /domain I would use what's already available in . You can get the current logged on user's Active Directory Group Membership using adsi and adsisearcher.

Here are 2 different ways of accomplishing it.

  1. By querying the user's memberof attribute:
$searcher = [adsisearcher]::new(
    [adsi] "LDAP://$env:USERDNSDOMAIN",
    [string] "(cn=$env:USERNAME)",
    [string[]] ("memberOf", "cn")
)

$searcher.FindOne().Properties['memberof'] | ForEach-Object {
    $searcher.Filter = "(distinguishedName=$_)"
    $searcher.FindOne().Properties['cn'][0]
}
  1. By querying all groups having the user as a member:
$searcher = [adsisearcher]::new(
    [adsi] "LDAP://$env:USERDNSDOMAIN",
    [string] "(cn=$env:USERNAME)",
    [string[]] ("distinguishedName", "cn")
)

$userDn = $searcher.FindOne().Properties['distinguishedName'][0]
$searcher.Filter = "(&(objectCategory=group)(member=$userDn))"
$searcher.FindAll() | ForEach-Object {
    $_.Properties['cn'][0]
}

Comments

-1

You can use a backslash to escape regex special characters and use ^ to specify start of string:

> @("a", "*b", "c*", "*d", "e**") | Select-String -Pattern '^\*'
*b
*d

So, to display the groups you could use, for example:

Net User USER /domain | % { $_ -split "\s+" -match "^\*" }

As per the comment, if the group names may contain spaces then obviously splitting on space characters would be inappropiate.

An alternative:

Net User USER /domain | % { $_ -split '^[^*]+\*?' -match '.+' }

Or, if we only want to look at the lines beginning "Local Group Memberships" or "Global Group Memberships" we could use, for example:

Net User USER /domain | 
? { $_ -match '^(?:Local|Global) Group Memberships +\*(.+)' } | % { $matches[1] }

8 Comments

Splitting by whitespace isn't appropriate, given that group names can - and frequently do - contain spaces (as an aside: -split ' foo bar ' is the simpler way to split by whitespace). It may also be worth removing the * from the group names, and distinguishing local from global (AD) groups.
Your solutions are still broken: multiple groups on a single line are reported as a single result, and the overflow lines that can occur are completely ignored in the last solution. The middle solution retains * chars. before the names. The last solution not only doesn't indicate which groups are local vs. global, due to ignoring overflow lines it wouldn't even work if the results were limited to Global.
@mklement0 And you suggested I was being combative? Your criticisms are pretty feeble. You say I retain the * yet your own solution retained the whole line! There was no requirement to indicate which groups are local or global and there was obviously no intention to do so. Your own solution is overwrought and more brittle than the above.
Factually describing in which way a solution is broken isn't combative - it's at the very least helpful to future readers - assuming the diagnosis is correct, but determining that would require engaging with the specific points made; the OP specifically asked about AD (aka global) groups. The simple, for-the-human-observer -SimpleString solution preserves that information, as does my "overwrought" solution - which isn't brittle (if you have specific evidence, please share it, and I'd be happy to update my solution). Obviously, reaching a shared understanding is always best.
@mklement0 More combative cant. Most obviously, your solution fails if a * appears in a line which does not contain a group name. The OP wants to 'display everything that's preceeded by an *' which my solution does. Local domain groups are AD groups as far as I know, and you yourself are displaying them. Unfortunately, I am not familiar enough with the output of Net User to be clear what you mean by overflow lines in this context. If you have a specific example then I will address it.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.