With the recent release of PowerShell 5.1—part of Windows Management Framework (WMF) 5.1—Microsoft introduced new cmdlets for working with local user and group accounts: Get-LocalUser, New-LocalUser, Remove-LocalUser, New-LocalGroup, Add-LocalGroupMember, and Get-LocalAdministrators. In this article, I will explore how to use these cmdlets by showing a few simple examples as well as how to perform some advanced tasks.
Avatar

Prior to this release, having to perform tasks with local users and groups from the Windows command line could be cumbersome. It was necessary to revert to commands such as net user, VB scripting, or using the Active Directory Service Interfaces (ADSI) WinNT provider such as Sitaram showed here on 4sysops.

In my PowerShell console, I can view these commands by typing Get-Command -Module Microsoft.PowerShell.LocalAccounts.

Output of Get Command Module

Output of Get Command Module

Working with local users

To illustrate using these cmdlets to work with local users, I will do the following:

  • Show the current local users on my computer
  • Create a new local user named "Dan"
  • Change the password for the "Dan" account
  • Remove the account from my system

First, I run Get-LocalUser, which shows the current users on my machine: the Administrator, the DefaultAccount, and the Guest. By default, the output shows the user name, the description, and whether the account is enabled or not.

PS C:\> Get-LocalUser

Name           Enabled Description
----           ------- -----------
Administrator  True    Built-in account for administering the computer/domain
DefaultAccount False   A user account managed by the system.
Guest          False   Built-in account for guest access to the computer/domain

I can see all the properties of local users, such as Description, PasswordExpires, and LastLogon, by using Get-LocalUser -Name 'Administrator' | Select-Object *. One scenario where this output would be helpful is if you wanted to see the last time a password was set, which is located in the PasswordLastSet property.

PS C:\> Get-LocalUser -Name 'Administrator' | Select-Object *

AccountExpires         :
Description            : Built-in account for administering the computer/domain
Enabled                : True
FullName               :
PasswordChangeableDate : 4/3/2017 3:16:36 PM
PasswordExpires        :
UserMayChangePassword  : True
PasswordRequired       : True
PasswordLastSet        : 4/3/2017 3:16:36 PM
LastLogon              : 4/3/2017 4:30:52 PM
Name                   : Administrator
SID                    : S-1-5-21-2911828324-1476334892-104699680-500
PrincipalSource        : Local
ObjectClass            : User

Next, I will create a local account with the username "Dan" and set an initial password using the $Password variable I set with Read-Host. I include the FullName and Description parameters. Note the account is automatically enabled upon creation, similar to what would occur if you created the account in the GUI.

PS C:\> $Password = Read-Host -AsSecureString
*********
PS C:\> New-LocalUser -FullName 'Dan Franciscus' -Name 'Dan' -Password $Password ‑Description 'Demo account'

Name Enabled Description
---- ------- -----------
Dan  True    Demo account

Changing the password for my local account is just a matter of running Set-LocalUser and using the -Password parameter.

PS C:\> $Password = Read-Host -AsSecureString
********
PS C:\> Set-LocalUser -Name 'Dan' -Password $Password -Verbose
VERBOSE: Performing the operation "Modify local user" on target "Dan".

To remove the "Dan" account, I run the Remove-LocalUser command while specifying the username of the account in the –Name parameter.

PS C:\> Remove-LocalUser -Name 'Dan' -Verbose
VERBOSE: Performing the operation "Remove local user" on target "Dan".

Working with local groups

Working with local groups using this module is just as simple as working with local users. With these cmdlets you can create, change, list, rename, and remove local groups. In addition, you can add and remove group members.

In this next example I will show you how to:

  • Create a new local group named "Testgroup"
  • Add members to "Testgroup"
  • Remove group members from "Testgroup"
  • Rename "Testgroup" to "MyGroup"

I create a local group named "Testgroup" by using New-LocalGroup.

PS C:\> New-LocalGroup -Name 'Testgroup' -Description 'Testing group'

Name      Description
----      -----------
Testgroup Testing group

Then I add a few members to this group. I will add the local user "Dan" as well as the local groups "Users" and "Remote Desktop Users."

PS C:\> Add-LocalGroupMember -Group 'Testgroup' -Member ('Users','Dan','Remote Desktop Users')  -Verbose
VERBOSE: Performing the operation "Add member BUILTIN\Users" on target "Testgroup".
VERBOSE: Performing the operation "Add member WIN10TEST\Dan" on target "Testgroup".
VERBOSE: Performing the operation "Add member BUILTIN\Remote Desktop Users" on target "Testgroup".

With the Get-LocalGroupMember command, I can see that these users are now members of "Testgroup."

Output of Get LocalGroupMember

Output of Get LocalGroupMember

At this point I have decided I want to remove the user "Dan" from "Testgroup" while keeping the other groups as members. Using Remove-LocalGroupMember and using "Dan" as the value for -Member allows me to do this.

Output shows the Dan user is removed

Output shows the "Dan" user is removed

To rename "Testgroup" to "MyGroup," I use Rename-LocalGroup specifying the -Name and ‑NewName parameters. I can see the name of the group has changed and members are still the same by using Get-LocalGroupMember.

PS C:\> Rename-LocalGroup -Name 'Testgroup' -NewName 'MyGroup'
PS C:\> Get-LocalGroupMember -Name 'MyGroup'

ObjectClass Name                         PrincipalSource
----------- ----                         ---------------
Group       BUILTIN\Remote Desktop Users Local
Group       BUILTIN\Users                Local

Find domain users that have local administrator rights

A best practice for Windows security is ensuring end users do not have local administrator access on their workstations. With a PowerShell function that uses the Get-LocalGroupMember cmdlet, I can quickly find any domain user that is part of the local administrator group for multiple computers.  With the -FilterGroups parameter, I can also filter out any domain users/groups that should have access, such as "Domain Admins."

To validate whether computers in the -ComputerName parameter are online, I included a workflow named Test-Ping. Keep in mind that the clients used in this function require having WMF 5.1 installed.

#requires -version 5.1
function Get-LocalAdministrators {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string[]]$ComputerName,
        [Parameter(Mandatory=$true)]
        [string[]]$FilterGroups,
        [Parameter(Mandatory=$true)]
        [string[]]$DomainName,
        [Parameter(Mandatory=$true)]
        [pscredential]$Credential
    )

    begin {
        # Use Test-Ping workflow to test connectivity to computers in $ComputerName parameter
        workflow Test-Ping {
        param(
            [string[]]$Computers
        )
            foreach -parallel ($Computer in $Computers) {
                if (Test-Connection -Count 1 $Computer -Quiet -ErrorAction SilentlyContinue) {
                    $Computer
                }
            }
        }
        $ComputerName = Test-Ping -Computers $ComputerName
    }

    process {
        # Finds domain users who are in the local administrators group while filtering other domain users/groups such as 'Domain Admins'
        Invoke-Command -ComputerName $ComputerName -ScriptBlock {
            Get-LocalGroupMember -Group 'Administrators' | Where-Object {
                $Using:FilterGroups -notcontains $_.Name -and $_.Name -like "$Using:DomainName*"}
        } -Credential $Credential -ArgumentList $FilterGroups,$DomainName | Select-Object PSComputerName,Name
    }
}

In this example, I have an Active Directory domain named NTDOMAIN. I run the Get-LocalAdministrators function against all computers contained in the "Windows 10" organizational unit (OU) by using Get-ADComputer and specifying the OU in the -SearchBase parameter. The output of Get-ADComputer is an array of computers, which I then pass to the ‑ComputerName parameter. I use the -FilterGroups parameter to filter out "Domain Admins" and "HelpDeskAdmins" since these groups are supposed to have local administrator access.

Subscribe to 4sysops newsletter!

C:\> Get-LocalAdministrators -ComputerName (Get-ADComputer -SearchBase 'OU=Windows 10,DC=NTDOMAIN,DC=COM' -Filter * | select name -ExpandProperty name) -FilterGroups
'NTDOMAIN\Domain Admins','NTDOMAIN\HelpDeskAdmins' -DomainName 'NTDOMAIN' -Credential (Get-Credential) -Verbose

cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential

PSComputerName Name
-------------- ----
WIN10          NTDOMAIN\DansAccount

The output of Get-LocalAdministrators shows that it found my "NTDOMAIN\DansAccount" domain account in the local administrator group on the computer "WIN10."

avataravataravatar
5 Comments
  1. Avatar
    Michael 7 years ago

    Very nice!

    Just FYI, at least on larger customers, and at least on hardened servers, it is common to remove “Domain Admins” from the local Administrators group.

  2. Avatar
    Matthew Swint (Rank 1) 7 years ago

    What was your reasoning for using a workflow to ping the list of computers in $computers?

  3. Avatar
    Matthew Swint (Rank 1) 7 years ago

    You could have more easily used the following instead of your ping workflow.

    $comps = @()
    foreach ($comp in $computername) {
          if (Test-Connection -ComputerName $comp -Count 1 -Quiet -ErrorAction SilentlyContinue) {
               $comps += $comp
          }
    }

    No need for a workflow to ping a list of computers here.

    (I wish comments were editable on this site.)

  4. Avatar
    Matthew Swint (Rank 1) 7 years ago

    In fact, I don’t think you need that BEGIN {} block at all. Then again, this just shows that there’s more than one way to skin a cat. Take a look at my revision of your code below.

    #requires -version 5.1
    
    function Get-LocalAdministrators {
       [CmdletBinding()]
       param(
         [Parameter(Mandatory=$true)]
           [string[]]$ComputerName,
         [Parameter(Mandatory=$true)]
           [string[]]$FilterGroups,
         [Parameter(Mandatory=$true)]
           [string[]]$DomainName,
         [Parameter(Mandatory=$true)]
           [pscredential]$Credential
       )
      BEGIN {}
      PROCESS {
        foreach ($comp in $computername) {
          if (Test-Connection -Comp $comp -Count 1 -Quiet -EA SilentlyContinue{
             Invoke-Command -Comp $comp -ScriptBlock {
                Get-LocalGroupMember -Group 'Administrators' |
                Where {
                   $Using:FilterGroups -notcontains $_.Name -and $_.Name -like                "$Using:DomainName*"
                }
             } -Cred $Credential -Arg $FilterGroups,$DomainName | Select PSComputerName,Name
          }
        }
      }
      END {}
    }

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account