AD DS

PowerShell: Rename an Active Directory User

This came up at work the other day. Another admin had attempted to rename an AD User account and it had only partially gotten renamed -- the SAM Account, Name and Display name were all correct, but the old user name was still showing up in a couple of places, including the login screen. The user was not happy, so I was asked to fix it, and provide a script that would handle it correctly. I poked around a bit and found the issue - even if you set all of the obvious properties correctly (and the other admin had missed UPN), it still won't show correctly on that logon screen -- you need to actually rename the AD object itself. So, after I fixed the problem user's account, I wrote up a script to solve the problem for the next time. I chose to use a CSV file as the input, but you could easily re-work this to work off either a CSV file or a set of command-line parameters. But honestly, I don't ever want to have to enter that many command-line parameters for a simple script. Especially if I have more than one to change.

 

The script uses Get-ADUser with the old name, then pipes it to Set-ADUser, and finally pipes it to Rename-ADObject to finish the process.  I even gave it basic help. :)

<#
.Synopsis
Renames the Active Directory users
.Description
Rename-myADUser reads a CSV file to identify an array of users. The users are then renamed to the new name in Active Directory.
.Example
Rename-myADUser
Renames the AD Accounts of the users in the default "ADUsers.csv" source file
.Example
Rename-myADUser -Path "C:\temp\ChangedUsers.txt"
Renames the AD accounts of the users listed in the file C:\temp\ChangedUsers.txt"
.Parameter Path
The path to the input CSV file of format:
OldSam,NewName,GivenName,Surname,DisplayName,SAMAccountName,UserPrincipalName,EmailAddress

The default value is ".\ADUsers.csv".  
.Inputs
[string]
.Notes
    Author: Charlie Russel
 Copyright: 2017 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 03/09/2017 (cpr)
   ModHist: 
          :
#>
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$False,Position=0)]
     [string]
     $Path = ".\ADUsers.csv" 
     )

$ADUsers = @()
If (Test-Path $Path ) {
   $ADUsers = Import-CSV $Path
} else { 
   Throw  "This script requires a CSV file with user names and properties."
}
$PDC = (Get-ADDomain).PDCEmulator
Write-Verbose "The PDC Emulator has been identified as $PDC"
Write-Verbose " "

ForEach ($User in $ADUsers ) {
   Write-Verbose "Modifying $user.OldSam to $user.NewName" 
   Sleep 3
   Get-ADUser -Identity $User.OldSam -Properties * | `
   Set-ADUser -Server $PDC `
              -DisplayName $user.DisplayName `
              -EmailAddress $User.EmailAddress `
              -SamAccountName $User.SamAccountName `
              -GivenName $User.GivenName `
              -Surname $User.Surname `
              -UserPrincipalName $user.UserPrincipalName `
              -PassThru | `
   Rename-ADObject -NewName $user.NewName -Server $PDC -PassThru
}

Building a Lab in Hyper-V with PowerShell, Part 4

Creating a new forest

In the previous sections of this series, I've covered how to build VMs using PowerShell, but labs aren't much good if they don't actually have any structure. So, let's create a new forest and domain to manage our labs. I'm going to assume for this post that you've gotten started already and created a new Windows Server 2012R2 or Windows Server 2016 virtual machine. For this, it can be a graphical install or a Server Core installation and either Server Standard or Datacenter. Since we're going to be using only PowerShell to create the forest, there's no need for a GUI.

The things we'll need to have identified before we start are:

  • Server IP address
  • Server name
  • DNS namespace for the root domain of the forest
  • Domain name for the root domain of the forest
  • DNS Server type (AD-integrated or standalone)

Set Server IP Address

We need set our server to a fixed IP address. While not absolutely required, I think it's a really bad idea to not do this. And, since our lab doesn't yet have DHCP in it, you need to anyway. (We'll add a DHCP server in the next installment. )

To configure the network adapter for a static IP address, I need to know either the interface alias (name) or the interface index. To get those, use Get-NetAdapter from a PowerShell window. (Note: if you're doing this on a new Windows Server Core installation, you can open a PowerShell window with Start PowerShell.exe at the command prompt. To start a PowerShell window automatically for this user, at logon, see my May post. )

Get-NetAdapter | Format-Table -AutoSize Name,Status,IFIndex,MacAddress

Name       Status ifIndex MacAddress
----       ------ ------- ----------
Ethernet 2 Up           3 00-15-5D-32-0A-02
Ethernet   Up           5 00-15-5D-32-CE-02

Which tells us that the DC has two network adapters, and the one that is on the Local-10 switch (from New-myVM.ps1) is at an ifIndex of 3, while the one on the "199 Network" switch has an ifIndex of 5. Now, we'll set the static IP addresses for these two adapters. First, the NIC on Local-10:

# Set IPv4
$NIC2 = Get-NetAdapter -ifIndex 3
$NIC2 | Set-NetIPInterface -DHCP Disabled
$NIC2 | New-NetIPAddress -AddressFamily  IPv4 `
                         -IPAddress      192.168.10.2 `
                         -PrefixLength   24 `
                         -Type Unicast `
                         -DefaultGateway 192.168.10.1
# Set IPv6
$NIC2 | New-NetIPAddress -AddressFamily  IPv6 `
                         -IPAddress      2001:db8:0:10::2 `
                         -PrefixLength   64 `
                         -Type Unicast `
                         -DefaultGateway 2001:db8:0:10::1

# Set DNS Server Addresses to self
Set-DnsClientServerAddress -InterfaceIndex  $NIC2.ifIndex `
                           -ServerAddresses 192.168.10.2,2001:db8:0:10::2

#Now, for the 199 Network, which I use for internal communications between lab hosts, I want to set a pure IPv4 address with no IPv6, so instead of setting an IPv6 address for the NIC, I'll disable it with Disable-NetAdapterBinding.

$NIC = Get-NetAdapter -ifIndex 5

# Disable IPv6
Disable-NetAdapterBinding -Name $NIC.Name -ComponentID ms_tcpip6

# Set IPv4 to 192.168.199.2
$NIC | Set-NetIPInterface -Dhcp Disabled
$NIC | New-NetIPAddress -AddressFamily IPv4 `
                        -IPAddress     192.168.199.2 `
                        -PrefixLength  24 `
                        -Type Unicast
# Set DNS to self
Set-DnsClientServerAddress -InterfaceIndex  $NIC.ifIndex `
                           -ServerAddresses 192.168.199.2

(Note: Set-NetAdapterBinding is not available on Windows 7/Server 2008 R2)

 

Set Server Name

Next, let's set the name of the server to match our naming conventions for this lab. We do this now, knowing it will force a reboot before we go any further.

Rename-Computer -NewName trey-dc-02 -Restart -Force

This will give the computer a new name and restart it.

 

Create Forest and Install AD-integrated DNS

Now that we have static IP addresses for our network adapters, and we've set the name of the server, we can go ahead and create our AD forest. First, we install Active Directory and update the PowerShell Help files with:

Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
Update-Help -SourcePath \\labhost\PSHelp

This installs the ActiveDirectory and ADDSDeployment modules that we'll need to create the forest. Now, we promote the server to be the first domain controller in the new forest. Before we do the actual install, we test to make sure we don't have any issues with Test-ADDSForestInstallation:

Test-ADDSForestInstallation `
       -DomainName 'TreyResearch.net' `
       -DomainNetBiosName 'TREYRESEARCH' `
       -DomainMode 6 `
       -ForestMode 6 `
       -NoDnsOnNetwork `
       -SafeModeAdministratorPassword (ConvertTo-SecureString `
                                                  -String 'P@ssw0rd' `
                                                  -AsPlainText `
                                                  -Force) `
       -NoRebootOnCompletion

Even though this is a brand new forest in an isolated lab setting, it's still a good practice to test before you actually deploy. And it doesn't cost all that much time or annoyance. I've included the SafeModeAdministratorPassword parameter to avoid the prompts for it. This is a lab, not real life. :) Also note that we're setting the forest and domain modes to Server2012R2. If you need earlier versions of domain controllers in your lab, you can set the mode accordingly.

The results of the test are as expected:

WARNING: Windows Server 2016 domain controllers have a default for the security setting named "Allow cryptography
algorithms compatible with Windows NT 4.0" that prevents weaker cryptography algorithms when establishing security
channel sessions.

For more information about this setting, see Knowledge Base article 942564
(http://go.microsoft.com/fwlink/?LinkId=104751).

WARNING: A delegation for this DNS server cannot be created because the authoritative parent zone cannot be found or it
 does not run Windows DNS server. If you are integrating with an existing DNS infrastructure, you should manually
create a delegation to this DNS server in the parent zone to ensure reliable name resolution from outside the domain
"TreyResearch.net". Otherwise, no action is required.


Message                          Context                                  RebootRequired  Status
-------                          -------                                  --------------  ------
Operation completed successfully Test.VerifyDcPromoCore.DCPromo.General.3          False Success

With that confirmation, we can go ahead and finish creating the forest and configuring DNS with the command:

Install-ADDSForest `
     -DomainName 'TreyResearch.net' `
     -DomainNetBiosName 'TREYRESEARCH' `
     -DomainMode 6 `
     -ForestMode 6 `
     -NoDnsOnNetwork `
     -SkipPreChecks `
     -SafeModeAdministratorPassword (ConvertTo-SecureString `
                                                  -String 'P@ssw0rd' `
                                                  -AsPlainText `
                                                  -Force) `
     -Force

You'll notice that the options here match our test pass, except I chose to bypass a second test. If you want to keep your SafeMode Administrator password private you can eliminate that parameter and you'll be prompted at the command line. When this finishes and the server has rebooted, you can log in with the TREYRESEARCH\Administrator account and the local Administrator password you had before you promoted the VM to be a domain controller.  This may or may not be the same as the SafeModeAdministratorPassword you set during the installation.

Configuring Windows Server 2016 core as a DHCP Server with PowerShell

As I mentioned last time, I'm setting up a new domain controller and DHCP server for my internal domain on Windows Server 2016 Core, and I'm exclusively using PowerShell to do it. For both the DHCP Server and AD DS roles, we need to configure a fixed IP address on the server, so let's do that first. From my Deploying and Managing Active Directory with Windows PowerShell book from Microsoft Press, here's my little very quick and dirty script to set a fixed IP address:

# Quick and dirty IP address setter

[CmdletBinding()]
Param ([Parameter(Mandatory=$True)][string]$IP4,
       [Parameter(Mandatory=$True)][string]$IP6 
      )
$Network = "192.168.10."
$Network6 = "2001:db8:0:10::"
$IPv4 = $Network + "$IP4"
$IPv6 = $Network6 + "$IP6"
$Gateway4 = $Network + "1"
$Gateway6 = $Network6 + "1"

Write-Verbose "$network,$network6,$IP4,$IP6,$IPv4,$IPv6,$gateway4, $gateway6"

$Nic = Get-NetAdapter -name Ethernet
$Nic | Set-NetIPInterface -DHCP Disabled
$Nic | New-NetIPAddress -AddressFamily IPv4 `
                        -IPAddress $IPv4 `
                        -PrefixLength 24 `
                        -type Unicast `
                        -DefaultGateway $Gateway4
Set-DnsClientServerAddress -InterfaceAlias $Nic.Name `
                           -ServerAddresses 192.168.10.2,2001:db8:0:10::2
$Nic |  New-NetIPAddress -AddressFamily IPv6 `
                         -IPAddress $IPv6 `
                         -PrefixLength 64 `
                         -type Unicast `
                          -DefaultGateway $Gateway6

ipconfig /all

I warned you it was a quick and dirty script. But let's quickly look at what it does. First, we get the network adapter into a variable, $Nic. Then we turn off DHCP with Set-NetIPInterface, and configure the IPv4 and IPv6 addresses with New-NetIPAddress. Finally, we use Set-DnsClientServerAddress to configure the DNS Servers for this server.

 

Next, let's join the server to the TreyResearch.net domain with another little script. OK, I admit, you could do this all as a simple one-liner, but I do it so often that I scripted it.

<#
.Synopsis
Joins a computer to the domain
.Description
Joins a new computer to the domain. If the computer hasn't been renamed yet, 
it renames it as well.
.Parameter NewName
The new name of the computer
.Parameter Domain
The domain to join the computer to. Default value is TreyResearch.net
.Example
Join-myDomain -NewName trey-wds-11
.Example
Join-myDomain dc-contoso-04 -Domain Contoso.com
.Notes
     Name: Join-myDomain
   Author: Charlie Russel
Copyright: 2017 by Charlie Russel
         : Permission to use is granted but attribution is appreciated
  ModHist:  9 Apr, 2014 -- Initial
         : 25 Feb, 2015 -- Updated to allow name already matches
         :
#>
[CmdletBinding()]
Param ( [Parameter(Mandatory=$true,Position=0)]
        [String]$NewName,
        [Parameter(Mandatory=$false,Position=1)]
        [String]$Domain = "TreyResearch.net"
       )

$myCred = Get-Credential -UserName "$Domain\Charlie" `
                         -Message "Enter the Domain password for Charlie."

if ($ENV:COMPUTERNAME -ne $NewName ) {
   Add-Computer -DomainName $Domain -Credential $myCred -NewName $NewName -restart
} else {
   Add-Computer -DomainName $Domain -Credential $myCred -Restart
}

After the server restarts, log in with your domain credentials, not as "Administrator".  The account you logon with should be at least Domain Admin or equivalent, since you're going to be adding DHCP to the server and promoting it to be a domain controller.

 

To add the necessary roles to the server, use:

Install-WindowsFeature -Name DHCP,AD-Domain-Services `
                       -IncludeAllSubFeature `
                       -IncludeManagementTools

Next, download updated Get-Help files with Update-Help. Once you've got those, go ahead and restart the server, and when it comes back up, we'll do the base configuration for DHCP to enable it in the domain, and create the necessary accounts. Creating scopes, etc., is the topic of another day. Probably as part of my Lab series.

 

First, enable the DHCP server in AD (this assumes the $NewName from earlier was 'trey-core-03'. )

Add-DhcpServerInDC -DnsName 'trey-core-03' -PassThru

And, finally, create the necessary local groups:

# Create local groups for DHCP
# The WinNT in the following IS CASE SENSITIVE
$connection = [ADSI]"WinNT://trey-core-03"
$lGroup = $connection.Create("Group","DHCP Administrators")
$lGroup.SetInfo()
$lGroup = $connection.Create("Group","DHCP Users")
$lGroup.SetInfo()

This uses ADSI to create a local group, since there's no good way built into base PowerShell to do it except through ADSI.

 

Finally, we'll use my Promote-myDC.ps1 script to promote the server to domain controller. Again, I could easily do this by hand, but I'm building and rebuilding labs often enough that I scripted it. I'm lazy! Do it once, use the PowerShell interactive command line. Do it twice? Write a script!

<#
.Synopsis
Tests a candidate domain controller, and then promotes it to DC.
.Description
Promote-myDC first tests if a domain controller can be successfully promoted,
and, if the user confirms that the test was successful, completes the
promotion and restarts the new domain controller.
.Example
Promote-myDC -Domain TreyResearch.net

Tests if the local server can be promoted to domain controller for the
domain TreyResearch.net. The user is prompted after the test completes
and must press the Y key to continue the promotion.
.Parameter Domain
The domain to which the server will be promoted to domain controller.
.Inputs
[string]
.Notes
    Author: Charlie Russel
 Copyright: 2017 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 05/14/2016 (cpr)
   ModHist: 02/14/2017 (cpr) Default the domain name for standard lab builds
          :
#>
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$False,Position=0)]
     [string]$Domain = 'TreyResearch.net'
     )

Write-Verbose "Testing if ADDSDeployment module is available"
If ( (Get-WindowsFeature -Name AD-Domain-Services).InstallState -ne "Installed" ) {
   Write-Verbose "Installing the ActiveDirectory Windows Feature, since you seem to have forgotten that."
   Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
   Write-Host ""
}

If ( (Get-WindowsFeature -Name AD-Domain-Services).InstallState -ne "Installed" ) {
   throw "Failed to install the ActiveDirectory Windows Feature."
}

Write-Verbose "Testing if server $env:computername can be promoted to DC in the $Domain domain"
Write-Host ""
Test-ADDSDomainControllerInstallation `
      -NoGlobalCatalog:$false `
      -CreateDnsDelegation:$false `
      -CriticalReplicationOnly:$false `
      -DatabasePath "C:\Windows\NTDS" `
      -DomainName $Domain `
      -LogPath "C:\Windows\NTDS" `
      -NoRebootOnCompletion:$false `
      -SiteName "Default-First-Site-Name" `
      -SysvolPath "C:\Windows\SYSVOL" `
      -InstallDns:$true `
      -Force
Write-Host ""
Write-Host ""
Write-Host ""

Write-Host -NoNewLine "If the above looks correct, press Y to continue...  "
$Key = [console]::ReadKey($true)
$sKey = $key.key

Write-Verbose "The $sKey key was pressed."
Write-Host ""
Write-Host ""
If ( $sKey -eq "Y" ) {
   Write-Host "The $sKey key was pressed, so proceeding with promotion of $env:computername to domain controller."
   Write-Host ""
   sleep 5
   Install-ADDSDomainController `
      -SkipPreChecks `
      -NoGlobalCatalog:$false `
      -CreateDnsDelegation:$false `
      -CriticalReplicationOnly:$false `
      -DatabasePath "C:\Windows\NTDS" `
      -DomainName $Domain `
      -InstallDns:$true `
      -LogPath "C:\Windows\NTDS" `
      -NoRebootOnCompletion:$false `
      -SiteName "Default-First-Site-Name" `
      -SysvolPath "C:\Windows\SYSVOL" `
      -Force:$true
} else {
   Write-Host "The $sKey key was pressed, exiting to allow you to fix the problem."
   Write-Host ""
   Write-Host ""
}

This uses a little trick I haven't talked about before -

$Key = [console]::ReadKey($true)
$sKey = $key.key

This reads in a single keystroke and gets the value of the key. Because of the way this works, "Y" and "y" are equivalent. Useful to give yourself a last chance out if something doesn't look right, though obviously you'll want to remove those bits if you're creating a script that needs to run without interactive input.

 

Importing users into Active Directory

When you need to create a single user in Active Directory Domain Services (AD DS), the tendency is to just "do it" in the GUI - either Active Directory Users and Computers (ADUC) or Active Directory Administrative Center (ADAC). But if you've got 25 users to add, or even 5 users to add, that's just painful. Plus, having this scripted ensures that each user is correctly entered following the same format.

For me, the easiest way to do this is by putting the users in a spreadsheet and then leveraging PowerShell's Import-CSV command. So, for the sake of argument, let's assume I have 8 new users to add to my TreyResearch.net domain. I open Excel, and create a spreadsheet with the following information for each user: Name, Given Name, Surname, Display Name, SamAccountName, and Description. The spreadsheet would look like this:blog_01

We'll save that as "TreyUsers.csv", and ignore any warnings about formatting, etc. This gives us a file with:

Name,GivenName,Surname,DisplayName,SAMAccountName,Description
David Guy,David,Guy,Dave R. Guy,Dave,Customer Appreciation Manager
Alfredo Fettucine,Alfredo,Fettuccine,Alfie NoNose,Alfie,Shop Foreman
Stanley Behr,Stanley,Behr,Stanley T. Behr, Stanley,Webmaster
Priscilla Catz,Priscilla,Catz,Dame Priscilla,Priscilla,Shop Steward
Harold Catz,Harold,Catz,Harold S. Catz,Harold,Engineering Manager
William Wallace,William,Wallace,Sir William Wallace,Wally,Marketing Manager
Trey Barksdale,Trey,Barksdale,Lord Barksalot,Trey,Sales Manager
Charlie Derby,Charles,Derby,Sparky,Sparky,Chief Security Officer

Now, we use PowerShell's Import-CSV to import this set of users into a variable $TreyUsers.

$TreyUsers = Import-CSV TreyUsers.csv

To create the users, we'll use a simple ForEach loop:

ForEach ($user in $TreyUsers ) {
   New-AdUser -DisplayName $User.DisplayName `
              -Description $user.Description `
              -GivenName $user.GivenName `
              -Name $User.Name `
              -SurName $User.SurName `
              -SAMAccountName $User.SAMAccountName `
              -Enabled $True `
              -ChangePasswordAtLogon $True `
              -PasswordNeverExpires $False `
              -UserPrincipalName $user.SAMAccountName `
              -AccountPassword (ConvertTo-SecureString `
                   -AsPlainText `
                   -Force `
                   -String 'P@ssw0rd!' ) 2>$NULL
}

(If you find that plain text password a bit problematic, use a Read-Line -AsSecureString early in the script to prompt you for an initial password. )

Finally, if you want to copy the security groups of an existing template user, stay tuned -- I'll cover that shortly

 

ETA: To correct ill-advised use of double-quotes in ConvertTo-SecureString.

Active Directory — Unlocking a User Account with PowerShell

As any SysAdmin knows, users periodically lock themselves out of their accounts, usually because they forgot a password or somehow mistyped it too many times. And after all, if it failed once, why not keep trying it? Unlocking that account is NOT something you do with Set-ADUser, unfortunately, because the PowerShell ActiveDirectory module has a special, single-purpose cmdlet - Unlock-ADAccount. Now, it doesn't take a whole script to run a single cmdlet, but sometimes there are good reasons to wrap a command in a more complicated script, and this is one I've had to. Primarily because I work on at least three different domains that are unrelated - my home domain, my "work" domain, and my testlab domain. With this script, I can also take advantage of an encrypted password for one of those domains, stored securely on my main desktop's hard disk. Plus, I've added in a bit of code to go grab the current holder of the FSMO PDCEmulator role, since I'd prefer to unlock the account directly against that PDC.

Also, to make life easier, and allow us to stick this in the middle of a pipeline when we need to, we'll add the ability to handle parameters from the pipeline. We do that in the Param section:

[CmdletBinding()]
Param(
     [Parameter(Mandatory=$True,ValueFromPipeline=$true,Position=0)]
     [alias("user","account")]
     [string[]]
     $Identity,
     [Parameter(Mandatory=$False,ValueFromPipeline=$True,Position=1)]
     [string]
     $Domain = "DOMAIN",
     [Parameter(Mandatory=$False,ValueFromPipeline=$True)]
     [PSCredential]
     $Credential = $NULL
     )

You'll notice here that we've allowed for each of our parameters to accept pipeline input. And they'll accept that "ByValue", meaning that the piped objects must have the same .NET type, or must be able to be converted to that type. So only an actual PSCredential object can be accepted for the -Credential parameter.

 

Next, we want to be able to handle credentials in any of several ways. So we use:

if ( $Credential ) {
   $myCred = $Credential
} else {
   # Test if there is a stored, encrypted, password we can use
   $pwPath = "$home\Documents\WindowsPowerShell\$domainPW.txt"
   if (Test-Path $pwPath ) {
      $mypw = Get-Content $pwPath | ConvertTo-SecureString
      $myCred = New-Object -TypeName System.Management.Automation.PSCredential `
                           -ArgumentList "$DOMAIN\Charlie",$mypw
   } else {
      # Prompt for a credential, since we don't seem to have one here. 
      $myCred = Get-Credential 
   }
}

Let's look at this for a moment. First, we know from the Param section that the default value of $Credential is $NULL, so our first test is whether that's been overridden at the command line with either a pipelined parameter, or a specifically entered credential object. If so, we're good, and we use that. But failing that, we'll check if there's one stored securely on disk. (More on storing credentials on disks in another post soon.) If we've stored one on disk that matches the domain we're going against, we'll use that. If not, and we still don't have a PSCredential object we can use, we'll simply prompt for one.

 

Next up, we want to run this against the domain controller that hosts the PDCEmulator role. Not a big deal in simplified domain environment, but really a good idea in a complicated, multi-site environment where site-to-site propagation is a bit slow.

$PDC = (Get-ADDomain -Identity $DOMAIN -Credential $myCred).PDCEmulator

We'll plug the value of $PDC into the -Server parameter of our AD commands.

Finally, the meat of the whole thing. I'm not assuming you're only unlocking a single account, so I've designed this to take a list of strings ([string[]]). (A list can, of course, be a list of length one.)

ForEach ($usr in $Identity) {
   Unlock-ADAccount -Identity $usr `
                    -Credential $myCred `
                    -Server $PDC `
                    -PassThru `
       | Get-ADUser -Properties LockedOut

That last line ensures that we report back in a way that shows the account no longer locked out. But for Get-ADUser to actually do anything, you need to include the -PassThru parameter in the Unlock-ADAccount command. Otherwise, nothing at all gets passed to the Get-ADUser command.

So, here's the whole thing:

<#
.Synopsis
Unlocks a  domain account
.Description
Unlock-myUser accepts an array of DOMAIN account names and unlocks the accounts 

This script accepts pipeline input for the credential. If no credential is supplied, 
it will attempt to use one you have stored on disk. Failing that, it will prompt 
you for credentials. 
.Example
Unlock-myUser -Identity Charlie
Unlocks the account of Charlie 
.Example
Unlock-myUser -Identity Charlie, Sharon
Unlocks the accounts of Charlie and Sharon 
.Example
Unlock-myUser -Identity Charlie -Domain TREYRESEARCH `
              -Credential (Get-Credential `
                               -username "TREYRESEARCH\Alfie"  `
                               -Message "Enter your Domain PW")

Unlocks the account of TreyResearch\Charlie, prompting Alfie for credentials. 
.Parameter Identity
The AD identity of the user or users whose acounts are to be reset.
.Parameter Domain
The Active Directory domain of the user or users whose accounts are to be reset
.Parameter Credential
The user credentials to run the script under. 
.Inputs
[string[]]
[string]
[PSCredential]
.Notes
    Author: Charlie Russel
 Copyright: 2016 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 07 Sept, 2016 (cpr)
   ModHist: 09 Sept, 2016 (cpr) - added PDC test and Domain parameter 
          :
#>
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$True,ValueFromPipeline=$true,Position=0)]
     [alias("user","account")]
     [string[]]
     $Identity,
     [Parameter(Mandatory=$False,ValueFromPipeline=$True,Position=1)]
     [string]
     $Domain = "DOMAIN",
     [Parameter(Mandatory=$False,ValueFromPipeline=$True)]
     [PSCredential]
     $Credential = $NULL
     )

if ( $Credential ) {
   $myCred = $Credential
} else {
   # Test if there is a stored, encrypted, password we can use
   $pwPath = "$home\Documents\WindowsPowerShell\$domainPW.txt"
   if (Test-Path $pwPath ) {
      $mypw = Get-Content $pwPath | ConvertTo-SecureString
      $myCred = New-Object -TypeName System.Management.Automation.PSCredential `
                           -ArgumentList "DOMAIN\Charlie",$mypw
   } else {
      # Prompt for a credential, since we don't seem to have one here. 
      $myCred = Get-Credential 
   }
}

# Find out which server holds the PDC role. 
# Useful in complicated, multi-site environments where 
# Domain changes might not propagate quickly. 

$PDC = (Get-ADDomain -Identity $DOMAIN -Credential $myCred).PDCEmulator

ForEach ($usr in $Identity) {
   Unlock-ADAccount -Identity $usr `
                    -Credential $myCred `
                    -Server $PDC `
                    -PassThru `
       | Get-ADUser -Properties LockedOut
}

As always, feel free to use this script or any portion of it that you find useful. However, if you do, I'd appreciate attribution and a pointer back to my blog.

Promoting a new domain controller

I’ve been working with Windows Server 2016 CTP5 recently, and because I installed it without the Desktop Experience (what we used to call a Server Core installation), I’m having to do everything in Windows PowerShell. No complaints, I enjoy it, but it does force me to think about things a bit sometimes.

One of the tasks I needed to do was promote a new server to be a secondary domain controller. The PowerShell command for this is: Install-ADDSDomainController. But before you start promoting a new DC, it’s a really good idea to test that the promotion will succeed by using the Test-ADDSDomainControllerInstallation cmdlet. So, I combined the two steps into a simple script that allows you to run the test, and if the output looks clean, finish the installation and initiate a reboot.

The script is smart enough to realize you haven’t installed the ActiveDirectory feature yet, and goes ahead and installs it for you.

<# 
.Synopsis 
Tests a candidate domain controller, and then promotes it to DC.

.Description 
Promote-myDC first tests if a domain controller can be successfully promoted, and, 
if the user confirms that the test was successful, completes the promotion and 
restarts the new domain controller. 

.Example 
Promote-myDC -Domain TreyResearch.net

Tests if the local server can be promoted to domain controller for the 
domain TreyResearch.net. The user is prompted after the test completes 
and must press the Y key to continue the promotion. 

.Parameter Domain 
The domain to which the server will be promoted to domain controller. 

.Inputs 
[string] 

.Notes 
    Author: Charlie Russel 
 Copyright: 2016 by Charlie Russel 
          : Permission to use is granted but attribution is appreciated 
   Initial: 05/14/2016 (cpr) 
   ModHist: 
          : 
#> 
[CmdletBinding()] 
Param( 
     [Parameter(Mandatory=$True,Position=0)] 
     [string] 
     $Domain 
     )

Write-Verbose "Testing if ADDSDeployment module is available" 
If ( ! (Get-Module ADDSDeployment )) { 
   Write-Verbose "Installing the ActiveDirectory Windows Feature, since you seem to have forgotten that." 
   Install-WindowsFeature -Name ActiveDirectory -IncludeManagementTools 
   Write-Host "" 
}

If ( ! (Get-Module ADDSDeployment )) { 
   throw "Failed to install the ActiveDirectory Windows Feature." 
}

Write-Verbose "Testing if server $env:computername can be promoted to DC in the $Domain domain" 
Write-Host "" 
Test-ADDSDomainControllerInstallation ` 
      -NoGlobalCatalog:$false ` 
      -CreateDnsDelegation:$false ` 
      -CriticalReplicationOnly:$false ` 
      -DatabasePath "C:\Windows\NTDS" ` 
      -DomainName $Domain ` 
      -LogPath "C:\Windows\NTDS" ` 
      -NoRebootOnCompletion:$false ` 
      -SiteName "Default-First-Site-Name" ` 
      -SysvolPath "C:\Windows\SYSVOL" ` 
      -InstallDns:$true ` 
      -Force 
Write-Host "" 
Write-Host "" 
Write-Host ""

Write-Host -NoNewLine "If the above looks correct, press Y to continue...  " 
$Key = [console]::ReadKey($true) 
$sKey = $key.key

Write-Verbose "The $sKey key was pressed." 
Write-Host "" 
Write-Host "" 
If ( $sKey -eq "Y" ) { 
   Write-Host "The $sKey key was pressed, so proceeding with promotion of $env:computername to domain controller." 
   Write-Host "" 
   sleep 5 
   Install-ADDSDomainController ` 
      -SkipPreChecks ` 
      -NoGlobalCatalog:$false ` 
      -CreateDnsDelegation:$false ` 
      -CriticalReplicationOnly:$false ` 
      -DatabasePath "C:\Windows\NTDS" ` 
      -DomainName $Domain ` 
      -InstallDns:$true ` 
      -LogPath "C:\Windows\NTDS" ` 
      -NoRebootOnCompletion:$false ` 
      -SiteName "Default-First-Site-Name" ` 
      -SysvolPath "C:\Windows\SYSVOL" ` 
      -Force:$true 
} else { 
   Write-Host "The $sKey key was pressed, exiting to allow you to fix the problem." 
   Write-Host "" 
   Write-Host "" 
}