- SolarWinds Server Performance and Configuration Bundle - Tue, Jun 18 2019
- SolarWinds Patch Manager: Updating Windows and third-party software - Tue, Apr 30 2019
- Monitor file changes in Windows with PowerShell and pswatch - Fri, Feb 1 2019
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.
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."
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.
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."
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.
What was your reasoning for using a workflow to ping the list of computers in $computers?
You could have more easily used the following instead of your ping workflow.
No need for a workflow to ping a list of computers here.
(I wish comments were editable on this site.)
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.
https://blogs.technet.microsoft.com/heyscriptingguy/2012/11/20/use-powershell-workflow-to-ping-computers-in-parallel/
This is my reasoning. I prefer a workflow for scalability.