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
$NLM_ENUM_NETWORK_CONNECTED=1
$NLM_ENUM_NETWORK_DISCONNECTED=2
$NLM_ENUM_NETWORK_ALL=3

$Networks = $NetworkListManager.GetNetworks($NLM_ENUM_NETWORK_CONNECTED)

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

# 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
$NetCategories.Add(0x00,"NLM_NETWORK_CATEGORY_PUBLIC")
$NetCategories.Add(0x01,"NLM_NETWORK_CATEGORY_PRIVATE")
$NetCategories.Add(0x02,"NLM_NETWORK_CATEGORY_DOMAIN_AUTHENTICATED")
$NetCategories.Get_Item($Network.GetCategory())

# Domain type
$DomainTypes = New-Object -TypeName System.Collections.Hashtable
$DomainTypes.Add(0x00,"NLM_DOMAIN_TYPE_NON_DOMAIN_NETWORK")
$DomainTypes.Add(0x01,"NLM_DOMAIN_TYPE_DOMAIN_NETWORK")
$DomainTypes.Add(0x02,"NLM_DOMAIN_TYPE_DOMAIN_AUTHENTICATED")
$DomainTypes.Get_Item($Network.GetDomainType())

# NLM Connectivity
$NLMConnectivity = New-Object -TypeName System.Collections.Hashtable
$NLMConnectivity.Add(0x0000,"NLM_CONNECTIVITY_DISCONNECTED")
$NLMConnectivity.Add(0x0001,"NLM_CONNECTIVITY_IPV4_NOTRAFFIC")
$NLMConnectivity.Add(0x0002,"NLM_CONNECTIVITY_IPV6_NOTRAFFIC")
$NLMConnectivity.Add(0x0010,"NLM_CONNECTIVITY_IPV4_SUBNET")
$NLMConnectivity.Add(0x0020,"NLM_CONNECTIVITY_IPV4_LOCALNETWORK")
$NLMConnectivity.Add(0x0040,"NLM_CONNECTIVITY_IPV4_INTERNET")
$NLMConnectivity.Add(0x0100,"NLM_CONNECTIVITY_IPV6_SUBNET")
$NLMConnectivity.Add(0x0200,"NLM_CONNECTIVITY_IPV6_LOCALNETWORK")
$NLMConnectivity.Add(0x0400,"NLM_CONNECTIVITY_IPV6_INTERNET")

# 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){
$NLMConnectivity.Get_Item($KeyBand)
}
}

# 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 0x0000}{"NLM_CONNECTIVITY_DISCONNECTED"}
{$_ -band 0x0001}{"NLM_CONNECTIVITY_IPV4_NOTRAFFIC"}
{$_ -band 0x0002}{"NLM_CONNECTIVITY_IPV6_NOTRAFFIC"}
{$_ -band 0x0010}{"NLM_CONNECTIVITY_IPV4_SUBNET"}
{$_ -band 0x0020}{"NLM_CONNECTIVITY_IPV4_LOCALNETWORK"}
{$_ -band 0x0040}{"NLM_CONNECTIVITY_IPV4_INTERNET"}
{$_ -band 0x0100}{"NLM_CONNECTIVITY_IPV6_SUBNET"}
{$_ -band 0x0200}{"NLM_CONNECTIVITY_IPV6_LOCALNETWORK"}
{$_ -band 0x0400}{"NLM_CONNECTIVITY_IPV6_INTERNET"}
}

# Display all active connectivity types (method d)
Enum NLM_CONNECTIVITY {
NLM_CONNECTIVITY_DISCONNECTED = 0x0000
NLM_CONNECTIVITY_IPV4_NOTRAFFIC = 0x0001
NLM_CONNECTIVITY_IPV6_NOTRAFFIC = 0x0002
NLM_CONNECTIVITY_IPV4_SUBNET = 0x0010
NLM_CONNECTIVITY_IPV4_LOCALNETWORK = 0x0020
NLM_CONNECTIVITY_IPV4_INTERNET = 0x0040
NLM_CONNECTIVITY_IPV6_SUBNET = 0x0100
NLM_CONNECTIVITY_IPV6_LOCALNETWORK = 0x0200
NLM_CONNECTIVITY_IPV6_INTERNET = 0x0400
}
[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 , , , , , , , , , | Leave a 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
Enabled:
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
Hive: HKEY_LOCAL_MACHINE
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

Targeting…:
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
    $Matches[1]
}

$Loop = $true

while($Loop){
    [int]$PercentComplete = Get-BDEPercent
    if($PercentComplete -ne 100){
        Write-Progress -Activity "Bitlocker Drive Encryption Status" -Status "Encrypting" -PercentComplete $PercentComplete
        Start-Sleep -Seconds 5
    }else{
        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:
storage-replica-driver-layering

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 , , , , , , , , | Leave a 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:

Enable-PSRemoting

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 0.0.0.0 like this:

TCP 0.0.0.0:5985 0.0.0.0:0 LISTENING 4

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

netsh http show iplisten

and you’ll only see 127.0.0.1 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 127.0.0.1 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 127.0.0.1.

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

Windows Server 2016

Lots of good stuff. This video about Storage Replica from the guy who heads the Storage team up, Ned Pyle, is definitely worth watching. Informative and funny.

There’s also a free eBook: Introducing Windows Server 2016

Posted in Free training, Storage | Tagged , , , , , , | Leave a comment

Create a PFX file from separate private and public key certificate files

Whilst installing ADFS I needed to import my wildcard certificate. This proved fiddly as Microsoft need it to be in pfx format, and I only had separate private .key and public .crt files.

PFX is a Microsoft certificate format that combines the public and private keys into one file, but is different from the combined format used by (e.g.) OpenSSL. I found that I had to go through several steps to achieve this.

Your CRT public key file should start with the following (open it in notepad to check):

-----BEGIN CERTIFICATE-----

Convert the public key into a PVK file

If you open your public key file (e.g. in notepad) and it says

-----BEGIN RSA PRIVATE KEY-----

then you’ll need to convert it into a PVK file. a PVK file is a binary file, so it’ll look have lots of non-alphanumeric characters in it. To do the conversion, I used a utility called pvk written by Dr Stephen N Henson (thank you!). The command line for pvk.exe is:

pvk.exe -in rcmtech_private.key -out rcmtech_private.pvk -topvk

You’ll be prompted for a password, and you do need to specify one or the next step in the process will fail.

Download the Windows SDK

Now you need a utility called pvk2pfx. Annoyingly this only comes as part of the Windows SDK, so you need to download that first – you just need to tick the Windows Software Development Kit option when selecting the features to install. Once you’ve pulled it down (it is several GB), you’ll find pvk2pfx in the following folder (you are using a 64-bit OS, right?):

C:\Program Files (x86)\Windows Kits\10\bin\x64\pvk2pfx.exe

Create the PFX file

The command line for pvk2pfx.exe is:

pvk2pfx.exe /pvk rcmtech_private.pvk /spc rcmtech_public.crt /pfx rcmtech.pfx

You’ll get a pop up asking for your pvk file password, which you specified when creating it using pvk.exe. (If you didn’t specify a password, and just hit enter on this popup you’ll just get an error: ERROR: Password incorrect. (Error Code = 0x80070056).)

You should now find yourself with a PFX file, which you can use to import into Windows (e.g. ADFS config wizard).

Posted in Security, Windows | Tagged , , , , , , , , , , , , , | 2 Comments

Send email via Yahoo with PowerShell

Quick reference for sending email via a Yahoo! mail account and their SMTP servers from within a PowerShell script.

You’ll need the Yahoo SMTP server details, which require you to use a specific port (587 – I couldn’t get 465 to work) and an encrypted connection.

I also set up a Yahoo “app password” for PowerShell, which means I don’t have to use my regular password in plain text in a script, which is a good thing. To do this click the settings cog from within Yahoo Mail, go to Account info, then when that opens go to Account Security. From there you’ll find Manage app passwords and can add a new one called (e.g.) PowerShell. Copy and paste the password generated into the PowerShell script. Note that the generated password is shown in four blocks of four letters with spaces in between but when you paste it it’ll come out with no spaces – and this is correct – you do not need the spaces displayed on the Yahoo web page.

Here’s the sample script:

$Username = "something@yahoo.co.uk"
$Password = "abcdefghijklmnop"

$SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $Username, $SecurePassword

$RcptTo = "somebody@mailserver.com"
$Subject = "Yahoo Test"
$Body = "This is a test message"
Send-MailMessage -From $Username -To $RcptTo -Subject $Subject -Body $Body -SmtpServer smtp.mail.yahoo.com -Port 587 -UseSsl -Credential $Credentials

And here’s the same thing as a function:

function Send-YahooMail ($Username, $Password, $RcptTo, $Subject, $Body){
    $SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force
    $Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $Username, $SecurePassword
    Send-MailMessage -From $Username -To $RcptTo -Subject $Subject -Body $Body -SmtpServer smtp.mail.yahoo.com -Port 587 -UseSsl -Credential $Credentials
}

If you don’t want any kind of password in plain text in a script then you need to also look at this post of mine too.

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