DSCResources/MSFT_NetworkTeam/MSFT_NetworkTeam.psm1

$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules'

# Import the Networking Common Modules
Import-Module -Name (Join-Path -Path $modulePath `
        -ChildPath (Join-Path -Path 'NetworkingDsc.Common' `
            -ChildPath 'NetworkingDsc.Common.psm1'))

# Import Localization Strings
$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_NetworkTeam'

<#
    .SYNOPSIS
    Returns the current state of a Network Team.
 
    .PARAMETER Name
    Specifies the name of the network team to create.
 
    .PARAMETER TeamMembers
    Specifies the network interfaces that should be a part of the network team.
    This is a comma-separated list.
#>

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter(Mandatory = $true)]
        [System.String[]]
        $TeamMembers
    )

    $configuration = @{
        Name        = $Name
        TeamMembers = $TeamMembers
        Ensure      = 'Absent'
    }

    Write-Verbose -Message ($script:localizedData.GetTeamInfo -f $Name)
    $networkTeam = Get-NetLBFOTeam -Name $Name -ErrorAction SilentlyContinue

    if ($networkTeam)
    {
        Write-Verbose -Message ($script:localizedData.FoundTeam -f $Name)
        $configuration.Add('LoadBalancingAlgorithm', $networkTeam.LoadBalancingAlgorithm)
        $configuration.Add('TeamingMode', $networkTeam.TeamingMode)
        $configuration.Ensure = 'Present'

        if ($null -eq (Compare-Object -ReferenceObject $TeamMembers -DifferenceObject $networkTeam.Members))
        {
            Write-Verbose -Message ($script:localizedData.TeamMembersMatch -f $Name)
        }
        else
        {
            $configuration.TeamMembers = $networkTeam.Members
            Write-Verbose -Message ($script:localizedData.TeamMembersNotMatch -f $Name)
        }
    }
    else
    {
        Write-Verbose -Message ($script:localizedData.TeamNotFound -f $Name)
    }

    return $configuration
}

<#
    .SYNOPSIS
    Adds, updates or removes a Network Team.
 
    .PARAMETER Name
    Specifies the name of the network team to create.
 
    .PARAMETER TeamMembers
    Specifies the network interfaces that should be a part of the network team.
    This is a comma-separated list.
 
    .PARAMETER TeamingMode
    Specifies the teaming mode configuration.
 
    .PARAMETER LoadBalancingAlgorithm
    Specifies the load balancing algorithm for the network team.
 
    .PARAMETER Ensure
    Specifies if the network team should be created or deleted.
#>

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter(Mandatory = $true)]
        [System.String[]]
        $TeamMembers,

        [Parameter()]
        [ValidateSet('SwitchIndependent', 'LACP', 'Static')]
        [System.String]
        $TeamingMode = 'SwitchIndependent',

        [Parameter()]
        [ValidateSet('Dynamic', 'HyperVPort', 'IPAddresses', 'MacAddresses', 'TransportPorts')]
        [System.String]
        $LoadBalancingAlgorithm = 'HyperVPort',

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present'
    )

    Write-Verbose -Message ($script:localizedData.GetTeamInfo -f $Name)

    $networkTeam = Get-NetLBFOTeam -Name $Name -ErrorAction SilentlyContinue

    if ($Ensure -eq 'Present')
    {
        if ($networkTeam)
        {
            Write-Verbose -Message ($script:localizedData.FoundTeam -f $Name)

            $setArguments = @{
                Name = $Name
            }

            if ($networkTeam.loadBalancingAlgorithm -ne $LoadBalancingAlgorithm)
            {
                Write-Verbose -Message ($script:localizedData.LoadBalancingAlgorithmDifferent -f $LoadBalancingAlgorithm)

                $setArguments.Add('LoadBalancingAlgorithm', $LoadBalancingAlgorithm)
                $isNetModifyRequired = $true
            }

            if ($networkTeam.TeamingMode -ne $TeamingMode)
            {
                Write-Verbose -Message ($script:localizedData.TeamingModeDifferent -f $TeamingMode)

                $setArguments.Add('TeamingMode', $TeamingMode)
                $isNetModifyRequired = $true
            }

            if ($isNetModifyRequired)
            {
                Write-Verbose -Message ($script:localizedData.ModifyTeam -f $Name)

                Set-NetLbfoTeam @setArguments -ErrorAction Stop -Confirm:$false
            }

            $netTeamMembers = Compare-Object `
                -ReferenceObject $TeamMembers `
                -DifferenceObject $networkTeam.Members

            if ($null -ne $netTeamMembers)
            {
                Write-Verbose -Message ($script:localizedData.MembersDifferent -f $Name)

                $membersToRemove = ($netTeamMembers | Where-Object -FilterScript {
                        $_.SideIndicator -eq '=>'
                    }).InputObject

                if ($membersToRemove)
                {
                    Write-Verbose -Message ($script:localizedData.RemovingMembers -f ($membersToRemove -join ','))

                    $null = Remove-NetLbfoTeamMember -Name $membersToRemove `
                        -Team $Name `
                        -ErrorAction Stop `
                        -Confirm:$false
                }

                $membersToAdd = ($netTeamMembers | Where-Object -FilterScript {
                        $_.SideIndicator -eq '<='
                    }).InputObject

                if ($membersToAdd)
                {
                    Write-Verbose -Message ($script:localizedData.AddingMembers -f ($membersToAdd -join ','))

                    $null = Add-NetLbfoTeamMember -Name $membersToAdd `
                        -Team $Name `
                        -ErrorAction Stop `
                        -Confirm:$false
                }
            }
        }
        else
        {
            Write-Verbose -Message ($script:localizedData.CreateTeam -f $Name)

            try
            {
                $null = New-NetLbfoTeam `
                    -Name $Name `
                    -TeamMembers $teamMembers `
                    -TeamingMode $TeamingMode `
                    -LoadBalancingAlgorithm $loadBalancingAlgorithm `
                    -ErrorAction Stop `
                    -Confirm:$false

                Write-Verbose -Message $script:localizedData.CreatedNetTeam
            }

            catch
            {
                New-InvalidOperationException `
                    -Message ($script:localizedData.failedToCreateTeam -f $_.Exception.Message)
            }
        }
    }
    else
    {
        Write-Verbose -Message ($script:localizedData.RemoveTeam -f $Name)

        $null = Remove-NetLbfoTeam -Name $name -ErrorAction Stop -Confirm:$false
    }
}

<#
    .SYNOPSIS
    Tests is a specified Network Team is in the correct state.
 
    .PARAMETER Name
    Specifies the name of the network team to create.
 
    .PARAMETER TeamMembers
    Specifies the network interfaces that should be a part of the network team.
    This is a comma-separated list.
 
    .PARAMETER TeamingMode
    Specifies the teaming mode configuration.
 
    .PARAMETER LoadBalancingAlgorithm
    Specifies the load balancing algorithm for the network team.
 
    .PARAMETER Ensure
    Specifies if the network team should be created or deleted.
#>

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [Parameter(Mandatory = $true)]
        [System.String[]]
        $TeamMembers,

        [Parameter()]
        [ValidateSet('SwitchIndependent', 'LACP', 'Static')]
        [System.String]
        $TeamingMode = 'SwitchIndependent',

        [Parameter()]
        [ValidateSet('Dynamic', 'HyperVPort', 'IPAddresses', 'MacAddresses', 'TransportPorts')]
        [System.String]
        $LoadBalancingAlgorithm = 'HyperVPort',

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present'
    )

    Write-Verbose -Message ($script:localizedData.GetTeamInfo -f $Name)

    $networkTeam = Get-NetLbfoTeam -Name $Name -ErrorAction SilentlyContinue

    if ($ensure -eq 'Present')
    {
        if ($networkTeam)
        {
            Write-Verbose -Message ($script:localizedData.FoundTeam -f $Name)

            if (
                ($networkTeam.LoadBalancingAlgorithm -eq $LoadBalancingAlgorithm) -and
                ($networkTeam.teamingMode -eq $TeamingMode) -and
                ($null -eq (Compare-Object -ReferenceObject $TeamMembers -DifferenceObject $networkTeam.Members))
            )
            {
                Write-Verbose -Message ($script:localizedData.TeamExistsNoAction -f $Name)

                return $true
            }
            else
            {
                Write-Verbose -Message ($script:localizedData.TeamExistsWithDifferentConfig -f $Name)

                return $false
            }
        }
        else
        {
            Write-Verbose -Message ($script:localizedData.TeamDoesNotExistShouldCreate -f $Name)

            return $false
        }
    }
    else
    {
        if ($networkTeam)
        {
            Write-Verbose -Message ($script:localizedData.TeamExistsShouldRemove -f $Name)

            return $false
        }
        else
        {
            Write-Verbose -Message ($script:localizedData.TeamDoesNotExistNoAction -f $Name)

            return $true
        }
    }
}