PowerShell DSC: IPv6 cIPv6NicBinding

  |   Source

This post covers the details of my PowerShell Desired State Configuration cIPv6NicBinding Resource, version 1. My next post will follow up with version 2 of this Resource, which was written using the Windows Management Framework 5.0 Class based functionality for Desired State Configuration Resources.


I know it’s still a somewhat common practice for internal servers to have IPv6 unbound from their network adapters. A Desired State Configuration Resource would be the simplest way of handling this when building new servers.

To build the Resource I figured that identifying bound IP addresses using WMI was going to be a nice backward-compatible process, and using Microsoft’s nvspbind.exe was also a nice backward-compatible way to manage the IPv6 binding. I came across nvspbind.exe when attempting to script the unbinding of IPv6 on my first Hyper-V 2012 (Server Core on Windows Server 2012) farm and it was so simple to use I thought why not re-use it here. It probably isn’t ideal to have a file pre-requisite for the Resource but it works (note my updated Class based Resource removes this pre-requisite).


Before getting into the cIPv6NicBinding resource, here’s the File resource I use to deploy nvspbind.exe from an internal file share (note I manually downloaded the executable from Microsoft as my servers do not have internet access). Nvspbind.exe is an executable written by Keith Mange of Microsoft’s Hyper-V team back in January 2010 to overcome protocol binding issues with Server Core installations of Windows Server 2008 and Windows Server 2008 R2. More information on nvspbind.exe can be found on .

File nvspbind
{
    Ensure            = 'Present'
    SourcePath        = "$sourcepath\nvspbind.exe"
    DestinationPath   = "$env:windir\System32\"
    Type              = 'File'
}

As you can see, this is nothing out of the ordinary, just a standard File resource.


Now back to my cIPv6Nicbinding Desired State Configuration Resource. I’ll start with the single helper function I’m using, Test-IPv6Enabled. This function checks whether the specified Network Adapter has a valid IPv6 binding and returns ‘Yes’ or ‘No’.

Function Test-IPv6Enabled
{
    $NA = Get-WmiObject -Class Win32_NetworkAdapter
    $NeNACtworkAdapterConfiguration = Get-WmiObject -Class Win32_NetworkAdapterConfiguration
    $MACAddress = ($NA | Where-Object {$_.NetConnectionID -eq $InterfaceAlias}).MACAddress
    $IpAddresses = ($NAC | Where-Object {$_.MACAddress -eq $MACAddress}).IPAddress

    $retVal = 'No'
    ForEach ($IP in $IpAddresses)
    {
        If (([System.Net.IPAddress]$IP).AddressFamily -eq 'InterNetworkV6')
        {
            $retVal = 'Yes'
        }
    }
    return $retVal
}

This function uses the WMI class Win32_NetworkAdapter to retrieve the MAC Address of the Network Adapter with the specified InterfaceAlias. With the MAC Address in hand we can use the WMI class Win32_NetworkAdapterConfiguration to retrieve the Network Adapters active IP Addresses. This process is required as the Win32_NetworkAdapterConfiguration class doesn’t include the InterfaceAlias in any of the returned properties.

Once the IP Addresses are retrieved, we’ll pass them through the .NET System.Net.IPAddres class to return the AddressFamily property. ‘InterNetwork’ equals an IPv4 address while ‘InterNetworkV6’ equals an IPv6 address.

This function will be used in the logic of our Resource to test whether there is a valid IPv6 binding or not.


The three parameters used in this Desired State Configuration Resource can be found in the in the cIPv6NicBinding.schema.mof file.

InterfaceAlias is the name of the network adapter (this is simply the network adapters name as seen in the Windows GUI), IPv6Enabled is a ‘Yes’/’No’ value specifying whether you want IPv6 to be bound to the specified network adapter or not, nvspbind is the path to the nvspbind executable as pushed out with the File Resource above (the full path, including ‘nvspbind.exe’).

[ClassVersion("1.0.0"), FriendlyName("cIPv6NicBinding")]
class cIPv6NicBinding : OMI_BaseResource
{
    [Key, Description("Network Adapter Name")] string InterfaceAlias;
    [Write, Description("Enable or disable IPv6"),ValueMap{"Yes", "No"},Values{"Yes", "No"}] string IPv6Enabled;
    [Write, Description("Path to nvspbind executable")] string nvspbind;
};

The Get-TargetResource function is nothing special. The primary check here uses the Test-IPv6Enabled helper function to return a ‘Yes’/’No’ value of whether the specified Network Adapter has a valid IPv6 IP address.

We then create a hashtable with the same parameters as the Resource and return it.

Function Get-TargetResource
{
    [OutputType([Hashtable])]
    param
    (
        # Network Adapter Name
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]
        $InterfaceAlias,

        # Enable or disable the supplied configuration
        [Parameter(Mandatory)]
        [ValidateSet('Yes', 'No')]
        [String]
        $IPv6Enabled,

        # Path to nvspbind executable
        [Parameter(Mandatory)]
        [String]
        $nvspbind
    )

    Write-Verbose "GET: Get IP Addresses for the Network Interface [$InterfaceAlias]"
    $ActiveIPv6Status = Test-IPv6Enabled

    $returnValue = @{
        NicName     = $InterfaceAlias
        IPv6Enabled = $ActiveIPv6Status
        nvspbind    = $nvspbind
    }

    return $returnValue
}

The Test-TargetResource function simply returns a $true/$false based on whether the called $IPv6Enabled parameter equals the result of the helper function Test-IPv6Enabled.

Function Test-TargetResource
{
    [OutputType([Boolean])]
    param
    (
        # Firewall Group Name
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $InterfaceAlias,

        # Enable or disable the supplied configuration
        [ValidateSet('Yes', 'No')]
        [String]
        $IPv6Enabled,

        # Path to nvspbind executable
        [String]
        $nvspbind
    )

    If ($IPv6Enabled -eq (Test-IPv6Enabled)) {$returnValue = $true }
    Else                                     {$returnValue = $false}

    Write-Verbose "TEST: Returning $returnValue"
    return $returnValue
}

The Set-TargetResource function needs to retrieve the Network Adapters GUID as that’s required when calling nvspbind.exe. Next up, nvspbind.exe is launched to enable or disable the IPv6 binding.

A number of lines in this function are to provide -WhatIf support, which should be included in all Set-TargetResource functions.

Function Set-TargetResource
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    param
    (
        # Firewall Group Name
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $InterfaceAlias,

        # Enable or disable the supplied configuration
        [ValidateSet('Yes', 'No')]
        [String]
        $IPv6Enabled,

        # Path to nvspbind executable
        [String]
        $nvspbind
    )

    $NA = Get-WmiObject -Class Win32_NetworkAdapter
    $GUID = ($NA | Where-Object {$_.NetConnectionID -eq $InterfaceAlias}).GUID

    # Text output if '-WhatIf' is called
    If     ($IPv6Enabled -eq 'Yes') {$WhatIf = $InterfaceAlias}
    ElseIf ($IPv6Enabled -eq 'No')  {$WhatIf = $InterfaceAlias}

    If ($PSCmdlet.ShouldProcess($WhatIf))
    {
        If ($IPv6Enabled -eq 'Yes')
        {
            Write-Verbose "Set: Enabling IPv6 on the [$InterfaceAlias] Network Adapter"
            Start-Process -FilePath $nvspbind -ArgumentList "-e $GUID ms_tcpip6" -NoNewWindow -Wait
        }
        ElseIf ($IPv6Enabled -eq 'No')
        {
            Write-Verbose "Set: Disabling IPv6 on the [$InterfaceAlias] Network Adapter"
            Start-Process -FilePath $nvspbind -ArgumentList "-d $GUID ms_tcpip6" -NoNewWindow -Wait
        }
    }
}

I won’t provide details of the Module Manifest file as it’s a standard affair, created with the New-ModuleManifest cmdlet. The Module Manifest is included in the 7zip file linked at the bottom of this post.

Here’s an example Desired State Configuration for this Resource:

Configuration IPv6NicBindingTest
{
    Param
    (
        [Parameter(Mandatory)]
        [string]
        $ComputerName
    )

    Import-DscResource -Name cIPv6NicBinding

    node ($ComputerName) {
        File nvspbind {
            Ensure            = 'Present'
            SourcePath        = '\\fileserver\share\nvspbind\nvspbind.exe'
            DestinationPath   = "$env:windir\System32\"
            Type              = 'File'
        }

        cIPv6NicBinding ipv6binding {
            InterfaceAlias = 'Local Area Connection'
            IPv6Enabled    = 'No'
            nvspbind       = "$env:windir\System32\nvspbind.exe"
            DependsOn      = '[File]nvspbind'
        }
    }
}

IPv6NicBindingTest -ComputerName localhost -OutputPath C:\Scripts
Start-DscConfiguration -ComputerName localhost -Path C:\Scripts -Verbose -Wait

When running the above Desired State Configuration this was the consoles output.

VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName'
= root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer SVR2012R2 with user sid S-1-5-21-4259290700-248647520-2809069105-1001.
VERBOSE: [SVR2012R2]: LCM:  [ Start  Set      ]
VERBOSE: [SVR2012R2]: LCM:  [ Start  Resource ]  [[File]nvspbind]
VERBOSE: [SVR2012R2]: LCM:  [ Start  Test     ]  [[File]nvspbind]
VERBOSE: [SVR2012R2]:                            [[File]nvspbind] The related file/directory is: C:\Windows\System32\.
VERBOSE: [SVR2012R2]:                            [[File]nvspbind] Building file list from cache.
VERBOSE: [SVR2012R2]: LCM:  [ End    Test     ]  [[File]nvspbind]  in 0.0150 seconds.
VERBOSE: [SVR2012R2]: LCM:  [ Start  Set      ]  [[File]nvspbind]
VERBOSE: [SVR2012R2]:                            [[File]nvspbind] The related file/directory is: C:\Windows\System32\.
VERBOSE: [SVR2012R2]:                            [[File]nvspbind] Building file list from cache.
VERBOSE: [SVR2012R2]:                            [[File]nvspbind] Copying file \\fileserver\share\nvspbind\nvspbind.exe to C:\Windows\System32\nvspbind.exe.
VERBOSE: [SVR2012R2]: LCM:  [ End    Set      ]  [[File]nvspbind]  in 0.0000 seconds.
VERBOSE: [SVR2012R2]: LCM:  [ End    Resource ]  [[File]nvspbind]
VERBOSE: [SVR2012R2]: LCM:  [ Start  Resource ]  [[cIPv6NicBinding]ipv6binding]
VERBOSE: [SVR2012R2]: LCM:  [ Start  Test     ]  [[cIPv6NicBinding]ipv6binding]
VERBOSE: [SVR2012R2]:                            [[cIPv6NicBinding]ipv6binding] TEST: Returning False
VERBOSE: [SVR2012R2]: LCM:  [ End    Test     ]  [[cIPv6NicBinding]ipv6binding]  in 0.1410 seconds.
VERBOSE: [SVR2012R2]: LCM:  [ Start  Set      ]  [[cIPv6NicBinding]ipv6binding]
VERBOSE: [SVR2012R2]:                            [[cIPv6NicBinding]ipv6binding] Performing the operation "Set-TargetResource" on target "Local Area Connection".
VERBOSE: [SVR2012R2]:                            [[cIPv6NicBinding]ipv6binding] Set: Disabling IPv6 on the [Local Area Connection] Network Adapter
VERBOSE: [SVR2012R2]: LCM:  [ End    Set      ]  [[cIPv6NicBinding]ipv6binding]  in 1.1110 seconds.
VERBOSE: [SVR2012R2]: LCM:  [ End    Resource ]  [[cIPv6NicBinding]ipv6binding]
VERBOSE: [SVR2012R2]: LCM:  [ End    Set      ]    in  1.3460 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 1.373 seconds

So there you have it – my Desired State Configuration Resource: cIPv6NicBinding. The full Resource (including the Manifest) can be found here. I hope you find this Resource helpful!

If anyone in the community has any feedback or advice I’d be more than happy for some peer review. And yes, one day I’ll get round to setting up a github repository to store my Resources on. One day...