PowerShell Exchange mailbox move progress monitor

This is a very basic script that’ll give you a progress bar to monitor a mailbox move process. You’ll need to run it from an Exchange Management Shell PowerShell prompt.

param([string]$Identity = "")
$Stats = Get-MoveRequestStatistics -Identity $Identity
    $Stats = Get-MoveRequestStatistics -Identity $Identity
    Write-Progress -Activity "Moving $Identity" -Status ([string]$Stats.BytesTransferred+" transferred of "+[string]$Stats.TotalMailboxSize) -PercentComplete $Stats.PercentComplete
    Start-Sleep -Milliseconds 500
while (($Stats.PercentComplete -le 99) -and ($Stats.Status -eq "InProgress"))
    $Stats = Get-MoveRequestStatistics -Identity $Identity
    Write-Progress -Activity "Moving $Identity" -Status $Stats.Status -PercentComplete 100
    Start-Sleep -Milliseconds 500
while ($Stats.StatusDetail -ne "Completed")
Posted in Exchange, PowerShell | Tagged , , , , , , , | Leave a comment

Change BitLocker Recovery Password with PowerShell

When BitLocker detects certain changes to the computer it’ll trigger Recovery Mode, and prompt for the Recovery Password. Likewise, you also need the recovery password if you need to access the encrypted disk from another machine or via Windows Recovery Environment (Windows RE).

If you need to provide your users with their BitLocker recovery password, you might want to change it afterwards. It allows them to get into the disk via alternative methods and thus bypass NTFS security. This is a bad thing.

If you have BitLocker set up right, it’ll write any new recovery passwords that it generates to Active Directory. Therefore the script (which you might want to run as a scheduled task, even if only on demand) does not display the new recovery password to screen. You can easily modify it to show the password by removing “-WarningAction SilentlyContinue”.

$MountPoint = "C:"
# Register the event log source
$LogSource = "RCMTech"
New-EventLog -LogName Application -Source $LogSource -ErrorAction SilentlyContinue
# Get the key protectors
$KeyProtectors = (Get-BitLockerVolume -MountPoint $MountPoint).KeyProtector
foreach($KeyProtector in $KeyProtectors){
    if($KeyProtector.KeyProtectorType -eq "RecoveryPassword"){
            # Remove then re-add the RecoveryPassword protector
            Remove-BitLockerKeyProtector -MountPoint $MountPoint -KeyProtectorId $KeyProtector.KeyProtectorId | Out-Null
            # Assuming BitLocker is configured properly, the recovery password will be stored in Active Directory, don't display it on screen
            Add-BitLockerKeyProtector -MountPoint $MountPoint -RecoveryPasswordProtector -WarningAction SilentlyContinue | Out-Null
            # If we get this far, eveything has worked, write a success to the event log
            Write-EventLog -LogName Application -Source $LogSource -EntryType Information -EventId 1000 -Message "BitLocker Recovery Password for $MountPoint has been changed"
            Write-Host "Successfully changed BitLocker Recover Password" -ForegroundColor Green
            # Something went wrong, display the error details and write an error to the event log
            Write-EventLog -LogName Application -Source $LogSource -EntryType Warning -EventId 1001 -Message "Failed to change Bitlocker Recovery Password for $MountPoint"
Posted in PowerShell, Security, Windows | Tagged , , , , , , , , , | 1 Comment

Get or update SysInternals tools with PowerShell

This is version 2 – the previous version relied on mapping a new PSDrive directly to \\live.sysinternals.com\tools which made things easy, but that no longer seems to work (or at least not for me, might be my web filtering blocking it).

So as an alternative, here is one that pulls the files directly via HTTPS by parsing the listing from http://live.sysinternals.com/tools

$SysIntFolder = "C:\sysint"
$Page = (Invoke-WebRequest -Uri "https://live.sysinternals.com/tools").Content
$MatchedItems = ([regex]"<A HREF.*?<\/A>").Matches($Page)
$ItemCount = $MatchedItems.Count
$Copied = 0
foreach($Match in $MatchedItems.Value){
    if($Match -match ">(.*?\..*?)<"){
        Write-Progress -Activity "Update SysInt" -Status $Matches[1] -PercentComplete ($Copied / $ItemCount * 100)
        Invoke-WebRequest -Uri ("https://live.sysinternals.com/tools/" + $Matches[1]) -OutFile (Join-Path -Path $SysIntFolder -ChildPath $Matches[1])

Write-Progress -Activity "Update SysInt" -Completed
Posted in PowerShell, Windows | Tagged , , , , , , | Leave a comment

Network List Manager in PowerShell

I’m writing a network diagnostic script, and wanted to use the network status and connectivity information that’s already been captured by the Network List Manager (NLM).

The code for pulling various bits of info is below. NLM_Connectivity was a good opportunity to learn how to work with enumeration of flags, so I’ve included several examples of working with these, to both list all the active connectivity types, and also ways to check for a specific connectivity type.

$NetworkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID(‘DCB00C01-570F-4A9B-8D69-199FDBA5723B’))

# Set enums for GetNetworks

$Networks = $NetworkListManager.GetNetworks($NLM_ENUM_NETWORK_CONNECTED)

foreach($Network in $Networks){
# Network name

# Values from INetworkListManager interface https://msdn.microsoft.com/en-us/library/windows/desktop/aa370769(v=vs.85).aspx

# Network category
$NetCategories = New-Object -TypeName System.Collections.Hashtable

# Domain type
$DomainTypes = New-Object -TypeName System.Collections.Hashtable

# NLM Connectivity
$NLMConnectivity = New-Object -TypeName System.Collections.Hashtable

# Several methods for working with the connectivity flags

# Display all active connectivity types (method a)
foreach($Key in $NLMConnectivity.Keys){
$KeyBand = $Key -band $net.GetConnectivity()
if($KeyBand -gt 0){

# Display all active connectivity types (method b)
$NLMConnectivity.Keys | Where-Object {$_ -band $Network.GetConnectivity()} | ForEach-Object {$NLMConnectivity.Get_Item($_)}

# Display all active connectivity types (method c)
switch ($Network.GetConnectivity()){
{$_ -band 0x0010}{"NLM_CONNECTIVITY_IPV4_SUBNET"}
{$_ -band 0x0100}{"NLM_CONNECTIVITY_IPV6_SUBNET"}

# Display all active connectivity types (method d)
[enum]::GetValues([NLM_CONNECTIVITY]) | Where-Object {$_.value__ -band $Network.GetConnectivity()}

# Check for a particular type of connectivity (method a)
if(0x0040 -band $Network.GetConnectivity()){Write-Host "NLM_CONNECTIVITY_IPV4_INTERNET is active"}

# Check for a particular type of connectivity (method b)
$NLMConnectivityActiveFlags = $NLMConnectivity.Keys | Where-Object {$_ -band $Network.GetConnectivity()} | ForEach-Object {$NLMConnectivity.Get_Item($_)}
if($NLMConnectivityActiveFlags.Contains("NLM_CONNECTIVITY_IPV4_SUBNET")){Write-Host "NLM_CONNECTIVITY_IPV4_SUBNET is active"}

Posted in PowerShell | Tagged , , , , , , , , , | 1 Comment

Group Policy Preference Drive Maps closing

I’ve been dealing with an issue where users that leave Windows File Explorer windows open for extended periods find that they close now and then.

This seems to be a common problem with later versions of Windows (8.1, 10) and is caused by the Replace setting being used on the Group Policy Preference Drive Map.

The File Explorer windows close because Replace causes the drive map to be removed and re-added. You want to use Replace because you also want to use the option to Remove this item when it is no longer applied. And also because Update will not change a drive mapping that has been done manually by a user.

Part of the fix is to set the following group policy:

Computer Configuration/Administrative Templates/System/Group Policy/Configure Drive Maps preference extension policy processing
Allow processing across a slow network connection: Enabled
Process even if the Group Policy objects have not changed: Disabled
Background priority: Idle

The key setting being Process even if the Group Policy objects have not changed, set to Disabled.

This will stop File Explorer closing on every group policy refresh.

This is only part of the solution though. You might think that this would cause the drive maps preference to only be processed if a drive map preference within the GPO has changed. That would seem sensible, right? Well, no. If you have other stuff in the GPO, and any of that changes, the drive maps will still be processed (i.e. removed and re-added) even though they’ve not changed.

You can see this by looking in Event Viewer, in the Microsoft-Windows-GroupPolicy/Operational log, and information event 4016. The text will say:

Starting Group Policy Drive Maps Extension Processing. 

List of applicable Group Policy objects: (Changes were detected.)

Your GPO Name

So what I’ve now done is to move my GP Pref drive mappings into a dedicated GPO, which I’ll hardly ever have to change. Thus the group policy engine will hardly ever detect any changes, and thus users will hardly ever see their File Explorer windows closing.

Posted in Windows | Tagged , , , , , , , , , , , , , , , , , , | Leave a comment

Don’t display last username if C drive encrypted with BitLocker

I’m moving from encrypting laptops with a third party disk encryption product to BitLocker. The third party product uses a pre-boot username & password prompt, and only boots into Windows if the credentials are correct. Users then have to log on to Windows using the Active Directory credentials. The last username is remembered so they just type in their AD password and the desktop loads.

With BitLocker I’m using the Trusted Platform Module (TPM) in the laptops to tie the disk to the laptop, rather than giving users and extra set of credentials to remember. When they turn on, BitLocker checks the TPM and the laptop hardware for changes, and if all is normal Windows will boot and present the user with the logon screen. However, currently this means that a lost or stolen laptop will boot and display the name of the last user, which I don’t want. If the username isn’t displayed, there are now two pieces of information required to get past the Windows Logon screen.

I’ve achieved this using a Group Policy Preference, targeted with a WMI query. The GPPref sets the dontdisplaylastusername policy registry value if the C: drive is encrypted, or removes the value if the drive is not encrypted – i.e. during the transition phase, users with the old disk encryption won’t be forced to type in their AD username at the Windows logon screen, they’ll still be remembered from their previous logon.

The configuration is as follows:
Computer Configuration – Preferences – Windows Settings – Registry
General tab:

Action: Replace
Key Path: SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system
Value: dontdisplaylastusername
Value type: REG_DWORD
Value Data: 00000001

Common tab:

Remove this item when it is no longer applied: ticked
Item-level targeting: ticked

WMI query:

Query: select IsVolumeInitializedForProtection from Win32_EncryptableVolume where DriveLetter = 'C:' and IsVolumeInitializedForProtection = True
Namespace: root\CIMv2\Security\MicrosoftVolumeEncryption
Property: IsVolumeInitializedForProtection
Posted in Security, Storage, Windows | Tagged , , , , | Leave a comment

PowerShell: BitLocker Encryption Progress Bar

When enabling Bitlocker I want to know how far through the process of encrypting the drive it has got.

The script gets the encryption status from the manage-bde.exe command, parses it using a regular expression to get the percent complete, and goes into a loop updating a progress bar until the progress gets to 100%. Then the script quits.

Here’s the script:

function Get-BDEPercent{
    $BDEStatus = & manage-bde.exe -status c:
    $BDEStatus = $BDEStatus -join " "
    $Matches = $null
    $BDEStatus -match '\:\s([\d]{2,})\.\d\%' | Out-Null

$Loop = $true

    [int]$PercentComplete = Get-BDEPercent
    if($PercentComplete -ne 100){
        Write-Progress -Activity "Bitlocker Drive Encryption Status" -Status "Encrypting" -PercentComplete $PercentComplete
        Start-Sleep -Seconds 5
        Write-Progress -Activity "Bitlocker Drive Encryption Status" -Completed
        $Loop = $false
Posted in PowerShell, Storage, Windows | Tagged , , , , , , , , | Leave a comment

Storage Replica in Windows Server 2016

What is Storage Replica?

Block level synchronous or asynchronous, volume based data replication.

What does it do?

Replicates storage data from a disk attached to one instance of Windows Server to another disk attached to a different instance of Windows Server. Because it works at the block level, it doesn’t care about open files. It operates way down the driver stack:

It allows you to create stretch clusters (clusters that don’t have shared storage), or you can just use it to replicate data from one place to another.

How do you get it?

Storage Replica is new in Windows Server 2016. You need the Datacenter edition, but your Hyper-V/vSphere hosts are probably licensed for that anyway, right? It’a available in the “full” editions (i.e. with desktop or core) and also nano. It’s just a Windows Feature, so enable it with Server Manager or PowerShell:

Install-WindowsFeature -Name Storage-Replica

Synchronous vs Asynchronous

It’ll do both, you just need to pick the appropriate one. Firstly, is your network latency between the two servers greater than 5ms?  In which case you shouldn’t use Synchronous replication, the performance impact will be too great. If it’s 5ms or less, you can choose either, based on your needs.

Synchronous replicationstorage-replica-synchronous-replication

This works pretty much the same as oldskool SAN replication, kit like the EMC Clariion’s MirrorView feature does exactly the same thing (but for somewhat more than the cost of a Windows Server licence and some cheap server hardware – which is entertaining as certain models of Clariion actually ran Windows Server internally!). The key point with Synchronous replication is that the write is not confirmed on the source storage system until it has been written to the log disk on both the source and destination storage systems. This gives you peace of mind that your data is safe, at (usually) the expense of performance.

Asynchronous replication

See diagram for Synchronous replication, but confirm the write after step 2 – in other words, once the data has been written to the source log disk. The data is written to the destination log disk separately.

How Storage Replica changes your disk IO

On a volume that is being replicated via Storage Replica, all write activity happens to the Log disk. This data is then “destaged” to the Data disk, which thus only ever handles Because the Log disk is a log, the writes to this are sequential. Despite this, it’s recommended that you make the log disk an SSD – writes tend to slower than reads, and Windows (and applications, e.g. SQL Server, Exchange) can cache data for read operations in RAM. Write operations have to be securely written to disk, and so if the write speed of the log disk isn’t fast enough it’ll become the point of contention.

Is it safe to use now?

Probably – Microsoft has had customers running this technology on production systems since 2014. I plan on using it right now.

Want to know more?

Watch Ned Pyle’s excellent, informative, hilarious Ignite session video (where I stole the diagrams above from!). Hipsters beware…!

Posted in Storage, Uncategorized, Windows | Tagged , , , , , , , , | 1 Comment

Fix PowerShell WinRM remote connection errors

I’ve had two annoying PowerShell errors today, both for the same server:

Enter-PSSession : Connecting to remote server server-a.rcmtech.co.uk failed with the
following error message : The client cannot connect to the destination specified in
the request. Verify that the service on the destination is running and is accepting
requests. Consult the logs and documentation for the WS-Management service running
on the destination, most commonly IIS or WinRM. If the destination is the WinRM
service, run the following command on the destination to analyze and configure the
WinRM service: "winrm quickconfig". For more information, see the
about_Remote_Troubleshooting Help topic.

The standard “internet” response to this is to open an Administrator command prompt and run

winrm qc

(or winrm quickconfig). Or open an Administrator PowerShell prompt and run:


But these are quite annoying solutions if you know that it should be working, and indeed is working on all your other servers, because you’ve configured it via Group Policy! There’s no harm in running the commands anyway, but most likely they’ll just come back and say “already configured” or words to that effect.

So the fix (for me) for the above error was to check what IP addresses the listener was listening on using:

netstat -aon | find "5985"

where 5985 is the default port used by WinRM. You should see the system process (ID 4) listening on like this:


However on my problem server it was listening on This can be confirmed using:

netsh http show iplisten

and you’ll only see in the list.
On the problem server itself you’ll find that using:

Enter-PSSession -Computername localhost

works, whereas:

Enter-PSSession -Computername server-a.rcmtech.co.uk

does not.
localhost is the name for the loopback address whereas the fully qualified server name will give you the IP address of the server as seen from your network.
The fix for this is to delete the loopback address from the http listener, which then makes it listen on all valid addresses:

netsh http delete iplisten

Check what addresses it is now listening on (plus the port) by using:

winrm e winrm/config/listener

Problem solved. Or not, I then got this error:

Enter-PSSession : Connecting to remote server server-a.rcmtech.co.uk failed with
the following error message : The WinRM client sent a request to an HTTP server and 
got a response saying the requested HTTP URL was not available. This is usually
returned by a HTTP server that does not support the WS-Management protocol. For
more information, see the about_Remote_Troubleshooting Help topic.

Which it turns out can be caused by having IPv6 enabled on the server.
In the netstat output (see above) I also had some IPv6 addresses showing as listening on port 5985.
I don’t use IPv6 but it’s on by default in Windows and will auto-assign itself a link local address (starts with fe80:). So I disabled IPv6 and the error went away. There’s also a workaround using the hosts file if you don’t want to disable IPv6.

Posted in PowerShell, Windows | Tagged , , , , , , , , , , , | Leave a comment

Extract private key from Microsoft CA-issued certificate

I wanted to use my internal Active Directory Certificate Services server to create a certificate for a Synology NAS. The Synology needs the private key and the certificate to be in separate files.

I created the certificate by using the CA web interface https://my-ad-cs/certsrv and then choosing the following options:

  • Request a certificate
  • advanced certificate request
  • Create and submit a request to this CA
  • Template: Web Server (5 years) – note that this is a custom certificate template that I created that allows the private key to be exported. This enables the Mark keys as exportable option, which needs to be ticked.
  • Fill in the identifying information, and leave all other options as default

Once the certificate was created, I installed it. I then opened certmgr.msc, found the certificate (under Personal – Certificates).

Next, I right-clicked the certificate, and chose All tasks – Export. I chose to export the private key, and under the .pfx option ticked Include all certificates in the certification path if possible. I set a password and saved the file.

Then I downloaded the latest version of OpenSSL and extracted the zip file.

I copied the pfx file into the folder where I’d extracted OpenSSL to, and opened a command prompt in that folder. I used the following two command lines to extract the private key and certificate from the pfx file.

openssl pkcs12 -in extracted.pfx -nocerts -out privatekey.pem -nodes

openssl pkcs12 -in extracted.pfx -nokeys -out cert.pem

These two files were then uploaded to the Synology from Control Panel, Security, Certificate.

Once the certificate was installed, I selected it and clicked Edit, then ticked Set as default certificate. Finally, I clicked Configure and changed each of the services to use the new certificate. Upon clicking OK, the web services restarted and are now using my CA certificate.


Posted in Security, Storage | Tagged , , , , , , , , , , , , | Leave a comment