DSCResources/MSFT_xIPAddress/MSFT_xIPAddress.psm1

<#######################################################################################
 # MSDSCPack_IPAddress : DSC Resource that will set/test/get the current IP
 # Address, by accepting values among those given in MSDSCPack_IPAddress.schema.mof
 #######################################################################################>


######################################################################################
# The Get-TargetResource cmdlet.
# This function will get the present list of IP Address DSC Resource schema variables on the system
######################################################################################
function Get-TargetResource
{
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$IPAddress,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$InterfaceAlias,

        [uInt32]$SubnetMask = 16,

        [ValidateNotNullOrEmpty()]
        [String]$DefaultGateway,

        [ValidateSet("IPv4", "IPv6")]
        [String]$AddressFamily = "IPv4"
    )

    $returnValue = @{
        IPAddress = [System.String]::Join(", ",(Get-NetIPAddress -InterfaceAlias $InterfaceAlias `
            -AddressFamily $AddressFamily).IPAddress)
        SubnetMask = $SubnetMask
        DefaultGateway = $DefaultGateway
        AddressFamily = $AddressFamily
        InterfaceAlias=$InterfaceAlias
    }

    $returnValue
}

######################################################################################
# The Set-TargetResource cmdlet.
# This function will set a new IP Address in the current node
######################################################################################
function Set-TargetResource
{
    param
    (
        #IP Address that has to be set
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$IPAddress,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$InterfaceAlias,

        [uInt32]$SubnetMask,

        [ValidateNotNullOrEmpty()]
        [String]$DefaultGateway,

        [ValidateSet("IPv4", "IPv6")]
        [String]$AddressFamily = "IPv4"
    )

    ValidateProperties @PSBoundParameters -Apply
}

######################################################################################
# The Test-TargetResource cmdlet.
# This will test if the given IP Address is among the current node's IP Address collection
######################################################################################
function Test-TargetResource
{
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$IPAddress,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$InterfaceAlias,

        [uInt32]$SubnetMask,

        [ValidateNotNullOrEmpty()]
        [String]$DefaultGateway,

        [ValidateSet("IPv4", "IPv6")]
        [String]$AddressFamily = "IPv4"
    )

    ValidateProperties @PSBoundParameters
}

#######################################################################################
# Helper function that validates the IP Address properties. If the switch parameter
# "Apply" is set, then it will set the properties after a test
#######################################################################################
function ValidateProperties
{
    param
    (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$IPAddress,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$InterfaceAlias,

        [ValidateNotNullOrEmpty()]
        [String]$DefaultGateway,

        [uInt32]$SubnetMask = 16,

        [ValidateSet("IPv4", "IPv6")]
        [String]$AddressFamily = "IPv4",

        [Switch]$NoReset,

        [Switch]$Apply
    )

    if(-not ([System.Net.Ipaddress]::TryParse($IPAddress, [ref]0)))
    {
        throw ( @(
            "IP Address *$IPAddress* is not in the correct format. Please correct the IPAddress "
            "parameter in the configuration and try again."
            ) -join ""
        )
    }

    try
    {
        # Flag to signal whether settings are correct
        $requiresChanges = $false

        Write-Verbose -Message "Checking the IPAddress ..."
        # Get the current IP Address based on the parameters given.
        $currentIP = Get-NetIPAddress -InterfaceAlias $InterfaceAlias -AddressFamily `
            $AddressFamily -ErrorAction Stop

        # Test if the IP Address passed is present
        if(-not $currentIP.IPAddress.Contains($IPAddress))
        {
            Write-Verbose -Message ( @(
                "IPAddress does NOT match desired state. Expected $IPAddress, actual "
                "$($currentIP.IPAddress)."
                ) -join ""
            )
            $requiresChanges = $true
        }
        else
        {
            Write-Verbose -Message "IPAddress is correct."

            # Filter the IP addresses for the IP address to check
            $filterIP = $currentIP | where { $_.IPAddress -eq $IPAddress }

            # Only test the Subnet Mask if the IP address is present
            if(-not $filterIP.PrefixLength.Equals([byte]$SubnetMask))
            {
                Write-Verbose -Message ( @(
                    "Subnet mask does NOT match desired state. Expected $SubnetMask, actual "
                    "$($filterIP.PrefixLength)."
                    ) -join ""
                )
                $requiresChanges = $true
            }
            else
            {
                Write-Verbose -Message "Subnet mask is correct."
            }
        }

        # Use $AddressFamily to select the IPv4 or IPv6 destination prefix
        $DestinationPrefix = "0.0.0.0/0"
        if($AddressFamily -eq "IPv6")
        {
            $DestinationPrefix = "::/0"
        }

        # Get all the default routes
        $defaultRoutes = Get-NetRoute -InterfaceAlias $InterfaceAlias -AddressFamily `
            $AddressFamily -ErrorAction Stop | `
            where { $_.DestinationPrefix -eq $DestinationPrefix }

        # Test if the Default Gateway passed is equal to the current default gateway
        if($DefaultGateway)
        {
            if(-not ([System.Net.Ipaddress]::TryParse($DefaultGateway, [ref]0)))
            {
                throw ( @(
                    "Default Gateway *$DefaultGateway* is NOT in the correct format. Please "
                    "correct the DefaultGateway parameter in the configuration and try again."
                    ) -join ""
                )
            }

            # Filter for the specified $DefaultGateway
            $filterGateway = $defaultRoutes | where { $_.NextHop -eq $DefaultGateway }

            if(-not $filterGateway)
            {
                Write-Verbose -Message ( @(
                    "Default gateway does NOT match desired state. Expected $DefaultGateway, "
                    "actual $($defaultRoutes.NextHop)."
                    ) -join ""
                )
                $requiresChanges = $true
            }
            else
            {
                Write-Verbose -Message "Default gateway is correct."
            }
        }

        # Test if DHCP is already disabled
        if(-not (Get-NetIPInterface -InterfaceAlias $InterfaceAlias -AddressFamily `
            $AddressFamily).Dhcp.ToString().Equals('Disabled'))
        {
            Write-Verbose -Message "DHCP is NOT disabled."
            $requiresChanges = $true
        }
        else
        {
            Write-Verbose -Message "DHCP is already disabled."
        }

        if($requiresChanges)
        {
            # Apply is true in the case of set - target resource - in which case, it will apply the
            # required IP configuration
            if($Apply)
            {
                Write-Verbose -Message ( @(
                    "At least one setting differs from the passed parameters. Applying "
                    "configuration..."
                    ) -join ""
                )

                # Build parameter hash table
                $Parameters = @{
                    IPAddress = $IPAddress
                    PrefixLength = $SubnetMask
                    InterfaceAlias = $InterfaceAlias
                }
                if($DefaultGateway)
                {
                    $Parameters['DefaultGateway'] = $DefaultGateway
                }

                if(-not $NoReset)
                {
                    # Remove any default routes on the specified interface -- it is important to do
                    # this *before* removing the IP address, particularly in the case where the IP
                    # address was auto-configured by DHCP
                    if($defaultRoutes)
                    {
                        $defaultRoutes | Remove-NetRoute -confirm:$false -ErrorAction Stop
                    }

                    # Remove any IP addresses on the specified interface
                    if($currentIP)
                    {
                        $currentIP | Remove-NetIPAddress -confirm:$false -ErrorAction Stop
                    }
                }
                else
                {
                    # If the desired IP or default gateway is present, remove it even if NoReset
                    # was requested; this is required if one of the settings is correct but others
                    # are not -- otherwise, New-NetIPAddress will throw an error.
                    if($filterGateway)
                    {
                        $filterGateway | Remove-NetRoute -Confirm:$false
                    }

                    if($filterIP)
                    {
                        $filterIP | Remove-NetIPAddress -confirm:$false -ErrorAction Stop
                    }
                }

                # Apply the specified IP configuration
                $null = New-NetIPAddress @Parameters -ErrorAction Stop

                # Make the connection profile private
                Get-NetConnectionProfile -InterfaceAlias $InterfaceAlias | `
                    Set-NetConnectionProfile -NetworkCategory Private -ErrorAction SilentlyContinue
                Write-Verbose -Message "IP Interface was set to the desired state."
            }
            else
            {
                return $false
            }
        }
        else
        {
            Write-Verbose -Message "IP interface is in the desired state."
            return $true
        }
    }
    catch
    {
        Write-Error -Message ( @(
            "Can not set or find valid IPAddress using InterfaceAlias $InterfaceAlias and "
            "AddressFamily $AddressFamily"
            ) -join ""
        )
        throw $_.Exception
    }
}

# FUNCTIONS TO BE EXPORTED
Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource