PowerShell Set-ADAccountPassword -WhatIf bug

Today (2017 March) I unfortunately learned of the -WhatIf bug for the PowerShell Active Directory cmdlet Set-ADAccountPassword.

About -WhatIf

“-WhatIf” is a common PowerShell System State parameter that is supposed to “Explain what will happen if the command is executed, without actually executing the command.(Boolean ) -whatif:$false or -whatif:$true.”

I use it all of the time while writing and developing PowerShell scripts to see what would happen if I actually executed the command. It’s a quick and easy way to ensure that your scope and syntax are correct.

Here’s a good -WhatIf example. My objective is to simply stop the process for Notepad++. Being a lazy admin, I couldn’t be bothered to write out the full process name so I used wildcards (e.g. note). Thankfully, I used -WhatIf first, which showed me that my scope was not narrow enough; for Stop-Process would have also killed OneNote. Whew — good catch -WhatIf!.

A -WhatIf Example

1
2
3
4
# Stop only the NotePad++ process
Get-Process *note* | Stop-Process -WhatIf
What if: Performing the operation "Stop-Process" on target "notepad++ (9276)".
What if: Performing the operation "Stop-Process" on target "ONENOTEM (460)".

Set-ADAccountPassword ignores -WhatIf

Unfortunately, the Set-ADAccountPassword cmdlet ignores the -WhatIf parameter and will execute your changes anyway. While I thought I was just testing my scope and syntax, Set-ADAccountPassword -WhatIf decided to go ahead and execute the password changes.

I knew something was wrong because What if: Performing the operation “Set-ADAccountPassword” on target was not being outputted to my screen. To confirm, I used the Get-ADUser cmdlet with a PasswordLastSet filter. Yep, it looks like a bunch of accounts just had their password changed.

Query which AD accounts changed their password today

1
2
3
4
5
6
7
8
9
10
11
12
# Retrieve accounts that had their passwords changed today
$Days = (Get-Date).AddDays(-1)
Get-ADUser -Filter {PasswordLastSet -ge $Days} -Properties PasswordLastSet | Sort PasswordLastSet -Descending | Format-Table PasswordLastSet, GivenName
 
PasswordLastSet     GivenName
---------------     ---------
03/04/2017 10:36:54 William
03/04/2017 10:36:54 Brian
03/04/2017 10:36:54 Steven
03/04/2017 10:36:54 Edward
03/04/2017 10:36:54 Timothy
etc.

At this point I was repeatedly typing the expletive parameter -WTF! Thankfully, my scope was correct and I had targeted the correct users for the password change. But had I made an error and targeted more users than desired, I would have had a problem.

Microsoft says the bug is fixed

In late 2015, Microsoft’s Ned Pyle wrote “This is fixed in Windows Server 2016 and the accompanying RSAT.”

That’s great, but I was still using Windows 7 and PowerShell Version 5 when I was affected by this bug.

Windows 7 PowerShell Version 5

1
2
3
4
5
$PSVersionTable.PSVersion
 
Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      14409  1005

I have also run Update-Help, yet neither Get-Help Set-ADAccountPassword -Online nor Get-Help Set-ADAccountPassword -Full make mention of the bug and still indicate that -WhatIf is a working parameter.

Here are the Syntax and Notes from Get-Help Set-ADAccountPassword -Full:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NAME
Set-ADAccountPassword
 
SYNOPSIS
Modifies the password of an Active Directory account.
 
PARAMETERS
-WhatIf [<SwitchParameter>]
Describes what would happen if you executed the command without actually executing the command.
 
Required?                    false
Position?                    named
Default value                
Accept pipeline input?       false
Accept wildcard characters?  false
 
NOTES
This cmdlet does not work with an Active Directory Snapshot.
This cmdlet does not work with a read-only domain controller. This cmdlet does not work when connected to Global Catalog port.

It would be ideal if the bug was fixed for all platforms that run PowerShell 5. Until then, at least update the content retrieved by Update-Help to remove -WhatIf as a valid parameter and make a mention of the bug in the Notes section.

British Airways says my randomly-generated password is invalid

I attempted to create an account on britishairways.com today, but repeatedly failed, receiving the message “Error: The password you have supplied is invalid. Passwords need to be at least 6 characters in length and use a mix of letters (English A-Z) and numbers.”

british-airways-password-invalid

I tried the following passed.pw randomly generated passwords:

  • awseecfejunknyZVWCNQPAVZUTE57264
  • nxmsgnkgqSYHXDPRJRRGGKKTHCE52872
  • edmzuszfecUVWSFHVCBHVBAV84822792
  • vdntgfssatdHMRPZNMWVPTVCBZA32444
  • sgsyejwdapybfjxfewmyMFXBUQCMR968

All of these values exceed the requirements stated in British Airways’ error message, yet all of my passwords were rejected.

I then noticed an information icon (blue circle, white “i”) providing more information about what types of passwords they expect me to enter. This content is from their Password Help page, which said:

Password Help

Passwords must be at least six characters long and include a mix of letters (English A-Z) and numbers. You can also include the following special characters: ! ” # $ ‘ ( ) * + , – . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~.

Make your password stronger:

  • Use a longer password. The longer it is, the harder it is for someone to guess.
  • Include a mixture of capital letters and special characters.
  • Create your own acronym or abbreviation like 1!vCht for ‘I love chocolate’, or use phonetic replacements such as ‘Luv2L#f’ for ‘Love to Laugh’, to help you remember.

Good tips. I’d like to do all of this please (except for the last tip about leet-speak).

According to this content, I should also use special characters and longer passwords. So here’s what I tried next. All of which were also rejected:

  • kyqegnftgxrzhwbpnxKNNGJDKCWNSEXPR2543593.)&)-),:
  • ddhmxaxubmatnkfjtQUFRSSDJJUYNENVFS4585865)-:!?)&
  • suhptvykwqdkeuHYMXATJSKFUNSXQGY853862.))?:.!:/?)
  • tbfusgdaawhfebrsgPKYBQJAQKFDZW76767822243(!/&,./
  • kqydjsghhnmunRPMUCURVCJEKFKNMEZA7942(;@(/&:/&./-

All of these passwords meet British Airways’ password requirements, yet all of them get rejected and prevent me from creating an account. What level of additional length or complexity do you require British Airways? How do I create a password that you will accept?

Use PowerShell to log logon and logoff activity on domain computers

There are many ways to log user activity on a domain. One of the ways that I prefer is to write user logon and logoff activity to plain text files on a network share. I used to do this via a .bat file, but recently rewrote the process using PowerShell.

Create a Shared Folder for your Scripts

Your first step is to create a shared folder on a server that is accessible by all users on a domain. All users/computers will require read/execute permission.

  • e.g. \SERVER\Shared\Scripts\logon.ps1
  • e.g. \SERVER\Shared\Scripts\logoff.ps1

Create Shared Folders for your Log Files

Also create the following shared folders that will hold your log files. All users will require write/modify permission (remove read/delete/etc). Your Help Desk and Domain Administrators should also have Read permission. We will write the exact same information to three folders, but use different folders and filenames to make this data easy to find.

  • e.g. \SERVER\Shared\ActivityLogs\LogonLogoff\Computer\
  • e.g. \SERVER\Shared\ActivityLogs\LogonLogoff\Date\
  • e.g. \SERVER\Shared\ActivityLogs\LogonLogoff\User\

Create two PowerShell Scripts

This is my PowerShell Logon script (logon.ps1).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Record Logon information 
# Script written by Jason Pearce 2016 Dec (jasonpearce.com)
# Purpose is to record Logon information (Username, Computername, and Date) to some log files
 
# Clean and sortable date and time
$DateYearMonthDay = Get-Date -Format "yyyy-MM-dd"
$TimeHourMinuteSecond = Get-Date -Format "HH:mm:ss"
 
# Target Folders
$TargetFolderComputer = "\\SERVER\Shared\ActivityLogs\LogonLogoff\Computer\"
$TargetFolderDate = "\\SERVER\Shared\ActivityLogs\LogonLogoff\Date\"
$TargetFolderUser = "\\SERVER\Shared\ActivityLogs\LogonLogoff\User\"
 
# Target Files
$TargetFileComputer = $TargetFolderComputer + $env:computername + ".txt"
$TargetFileDate = $TargetFolderDate + $DateYearMonthDay + ".txt" 
$TargetFileUser = $TargetFolderUser + $env:username + ".txt"
 
# Create a new object array containing Date, Time, Computer, User, Action (logon or logoff)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "Date" -Value $DateYearMonthDay
$obj | Add-Member -MemberType NoteProperty -Name "Time" -Value $TimeHourMinuteSecond
$obj | Add-Member -MemberType NoteProperty -Name "Computer" -Value $env:computername
$obj | Add-Member -MemberType NoteProperty -Name "User" -Value $env:username
$obj | Add-Member -MemberType NoteProperty -Name "Action" -Value "Logon"
 
# Write information to a few log files
$obj | Export-CSV -Force -NoTypeInformation -Append $TargetFileComputer
$obj | Export-CSV -Force -NoTypeInformation -Append $TargetFileDate
$obj | Export-CSV -Force -NoTypeInformation -Append $TargetFileUser

This is my PowerShell Logoff script (logoff.ps1).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Record Logoff information 
# Script written by Jason Pearce 2016 Dec (jasonpearce.com)
# Purpose is to record Logon information (Username, Computername, and Date) to some log files
 
# Clean and sortable date and time
$DateYearMonthDay = Get-Date -Format "yyyy-MM-dd"
$TimeHourMinuteSecond = Get-Date -Format "HH:mm:ss"
 
# Target Folders
$TargetFolderComputer = "\\SERVER\Shared\ActivityLogs\LogonLogoff\Computer\"
$TargetFolderDate = "\\SERVER\Shared\ActivityLogs\LogonLogoff\Date\"
$TargetFolderUser = "\\SERVER\Shared\ActivityLogs\LogonLogoff\User\"
 
# Target Files
$TargetFileComputer = $TargetFolderComputer + $env:computername + ".txt"
$TargetFileDate = $TargetFolderDate + $DateYearMonthDay + ".txt" 
$TargetFileUser = $TargetFolderUser + $env:username + ".txt"
 
# Create a new object array containing Date, Time, Computer, User, Action (logon or logoff)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "Date" -Value $DateYearMonthDay
$obj | Add-Member -MemberType NoteProperty -Name "Time" -Value $TimeHourMinuteSecond
$obj | Add-Member -MemberType NoteProperty -Name "Computer" -Value $env:computername
$obj | Add-Member -MemberType NoteProperty -Name "User" -Value $env:username
$obj | Add-Member -MemberType NoteProperty -Name "Action" -Value "Logoff"
 
# Write information to a few log files
$obj | Export-CSV -Force -NoTypeInformation -Append $TargetFileComputer
$obj | Export-CSV -Force -NoTypeInformation -Append $TargetFileDate
$obj | Export-CSV -Force -NoTypeInformation -Append $TargetFileUser

Create a Group Policy that runs these scripts

On a domain controller, create and link a new Group Policy to the users you wish to target. Consider adding User Group Policy loopback processing mode, depending on how your OUs are organized and what you target.

  • Create a new Group Policy named “Log Logon and Logoff via PowerShell”
  • Go to “User Configuration > Policies > Windows Settings > Scripts”
  • Edit Logon > PowerShell Scripts > Add > \SERVER\Shared\Scripts\logon.ps1
  • Edit Logoff > PowerShell Scripts > Add > \SERVER\Shared\Scripts\logoff.ps1

Results

After your Group Policy has populated and some users have either logged on or logged off of machines, you’ll find some new files in your three log folders:

  • e.g. \SERVER\Shared\ActivityLogs\LogonLogoff\Computer\COMPUTER1.txt
  • e.g. \SERVER\Shared\ActivityLogs\LogonLogoff\Date\2017-12-26.txt
  • e.g. \SERVER\Shared\ActivityLogs\LogonLogoff\User\USER1.txt

All three files will contain data that looks like this:

1
2
3
"Date","Time","Computer","User","Action"
"2016-12-25","16:12:17","COMPUTER-052","username","Logoff"
"2016-12-26","08:02:22","COMPUTER-014","username","Logon"

I like using three files because it is easier to find user activity by computer, date, or user; depending on your needs. After time — once a month — I zip and archive all of the data to start over with new log files.