DSCResources/cVMNetworkAdapterVlan/cVMNetworkAdapterVlan.psm1

# Fallback message strings in en-US
DATA localizedData
{
    # same as culture = "en-US"
ConvertFrom-StringData @'
    HyperVModuleNotFound=Hyper-V PowerShell Module not found.
    VMNameAndManagementTogether=VMName cannot be provided when ManagementOS is set to True.
    MustProvideVMName=Must provide VMName parameter when ManagementOS is set to False.
    GetVMNetAdapter=Getting VM Network Adapter information.
    FoundVMNetAdapter=Found VM Network Adapter.
    NoVMNetAdapterFound=No VM Network Adapter found.
    VMNetAdapterDoesNotExist=VM Network Adapter does not exist.
    PerformVMVlanSet=Perfoming VM Network Adapter VLAN setting configuration.
    IgnoreVlan=Ignoring VLAN configuration when the opeartion mode chosen is Untagged.
    VlanIdRequiredInAccess=VlanId must be specified when chosen operation mode is Access.
    MustProvideNativeVlanId=NativeVlanId must be specified when chosen operation mode is Trunk.
    PrimaryVlanIdRequired=PrimaryVlanId is required when the chosen operation mode is Community or Isolated or Promiscuous.
    AccessVlanMustChange=VlanId in Access mode is different. It will be changed.
    AdaptersExistsWithVlan=VM Network adapter exists with required VLAN configuration.
    NativeVlanMustChange=NativeVlanId in Trunk mode is different and it wil be changed.
    AllowedVlanListMustChange=AllowedVlanIdList is different in trunk mode. It will be changed.
    PrimaryVlanMustChange=PrimaryVlanId is different and must be changed.
    SecondaryVlanMustChange=SecondaryVlanId is different and must be changed.
    SecondaryVlanListMustChange=SecondaryVlanIdList is different and must be changed.
    AdapterExistsWithDifferentVlanMode=VM Network adapter exists with different Vlan configuration. It will be fixed.
'@

}

if (Test-Path $PSScriptRoot\en-us)
{
    Import-LocalizedData LocalizedData -filename cVMNetworkAdapterVlan.psd1
}

Function Get-TargetResource {
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    Param (
        [Parameter(Mandatory)]
        [String] $Name,

        [Parameter(Mandatory)]
        [Bool] $ManagementOS,

        [Parameter()]
        [String] $VMName
    )

    if(!(Get-Module -ListAvailable -Name Hyper-V))
    {
        Throw $localizedData.HyperVModuleNotFound
    }

    if ($VMName -and $ManagementOS) {
        throw $localizedData.VMNameAndManagementTogether
    }

    if ((-not $ManagementOS) -and (-not $VMName)) {
        throw $localizedData.MustProvideVMName
    }

    $Arguments = @{
        Name = $Name
    }

    if ($VMName) {
        $Arguments.Add('VMName',$VMName)
    } elseif ($ManagementOS) {
        $Arguments.Add('ManagementOS', $true)
    }

    Write-Verbose $localizedData.GetVMNetAdapter
    $AdapterExists = Get-VMNetworkAdapter @Arguments -ErrorAction SilentlyContinue

    if ($AdapterExists) {
        Write-Verbose $localizedData.FoundVMNetAdapter
        $Configuration = $Arguments
        $Configuration.Remove('Name')
        $Configuration.Add('AdapterMode',$AdapterExists.VlanSetting.OperationMode)
        $Configuration.Add('VlanId',$AdapterExists.VlanSetting.AccessVlanId)
        $Configuration.Add('NativeVlanId',$AdapterExists.VlanSetting.NativeVlanId)
        $Configuration.Add('PrimaryVlanId',$AdapterExists.VlanSetting.PrimaryVlanId)
        $Configuration.Add('SecondaryVlanId',$AdapterExists.VlanSetting.SecondaryVlanId)
        $Configuration.Add('SecondaryVlanIdList',$AdapterExists.VlanSetting.SecondaryVlanIdListString)
        $Configuration.Add('AllowedVlanIdList',$AdapterExists.VlanSetting.AllowedVlanIdListString)
    }

    $Configuration
}

Function Set-TargetResource {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [String] $Name,

        [Parameter(Mandatory)]
        [Bool] $ManagementOS,

        [Parameter()]
        [String] $VMName,

        [Parameter()]
        [ValidateSet('Untagged','Access','Trunk','Communnity','Isolated','Promiscuous')]
        [String] $AdapterMode = 'Untagged',

        [Parameter()]
        [uint32] $VlanId,

        [Parameter()]
        [uint32] $NativeVlanId,

        [Parameter()]
        [String] $AllowedVlanIdList,
        
        [Parameter()]
        [uint32] $PrimaryVlanId,

        [Parameter()]
        [uint32] $SecondaryVlanId,

        [Parameter()]
        [String] $SecondaryVlanIdList
    )

    if(!(Get-Module -ListAvailable -Name Hyper-V))
    {
        Throw $localizedData.HyperVModuleNotFound
    }

    if ($VMName -and $ManagementOS) {
        throw $localizedData.VMNameAndManagementTogether
    }

    if ((-not $ManagementOS) -and (-not $VMName)) {
        throw $localizedData.MustProvideVMName
    }

    $Arguments = @{
        Name = $Name
    }

    if ($VMName) {
        $Arguments.Add('VMName',$VMName)
    } elseif ($ManagementOS) {
        $Arguments.Add('ManagementOS', $true)
    }

    Write-Verbose $localizedData.GetVMNetAdapter
    $AdapterExists = Get-VMNetworkAdapter @Arguments -ErrorAction SilentlyContinue

    if ($AdapterExists) {
        Write-Verbose $localizedData.FoundVMNetAdapter
        $SetArguments = $Arguments
        $SetArguments.Remove('Name')
        $SetArguments.Add('VMNetworkAdapterName',$Name)
        switch ($AdapterMode) {
            'Untagged' {
                $SetArguments.Add('Untagged',$true)
                break
            }

            'Access' {
                $SetArguments.Add('Access',$true)
                $SetArguments.Add('VlanId',$VlanId)
                break
            }

            'Trunk' {
                $SetArguments.Add('Trunk',$true)
                $SetArguments.Add('NativeVlanId',$NativeVlanId)
                if ($AllowedVlanIdList) {
                    $SetArguments.Add('AllowedVlanIdList',$AllowedVlanIdList)
                }
                break
            }

            'Community' {
                $SetArguments.Add('Community',$true)
                $SetArguments.Add('PrimaryVlanId',$PrimaryVlanId)
                if ($SecondaryVlanId) {
                    $SetArguments.Add('SecondaryVlanId',$SecondaryVlanId)
                }
                break
            }

            'Isolated' {
                $SetArguments.Add('Isolated',$true)
                $SetArguments.Add('PrimaryVlanId',$PrimaryVlanId)
                if ($SecondaryVlanId) {
                    $SetArguments.Add('SecondaryVlanId',$SecondaryVlanId)
                }
                break
            }

            'Promiscuous' {
                $SetArguments.Add('Promiscuous',$true)
                $SetArguments.Add('PrimaryVlanId', $PrimaryVlanId)
                if ($SecondaryVlanIdList) {
                    $SetArguments.Add('SecondaryVlanIdList', $SecondaryVlanIdList)
                }
                break
            }
        }
    }
    
    Write-Verbose $localizedData.PerformVMVlanSet
    Set-VMNetworkAdapterVlan @SetArguments -ErrorAction Stop
}

Function Test-TargetResource {
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    Param (
        [Parameter(Mandatory)]
        [String] $Name,

        [Parameter(Mandatory)]
        [Bool] $ManagementOS,

        [Parameter()]
        [String] $VMName,

        [Parameter()]
        [ValidateSet('Untagged','Access','Trunk','Communnity','Isolated','Promiscuous')]
        [String] $AdapterMode = 'Untagged',

        [Parameter()]
        [uint32] $VlanId,

        [Parameter()]
        [uint32] $NativeVlanId,

        [Parameter()]
        [String] $AllowedVlanIdList,
        
        [Parameter()]
        [uint32] $PrimaryVlanId,

        [Parameter()]
        [uint32] $SecondaryVlanId,

        [Parameter()]
        [String] $SecondaryVlanIdList
    )

    if(!(Get-Module -ListAvailable -Name Hyper-V))
    {
        Throw $localizedData.HyperVModuleNotFound
    }

    if ($VMName -and $ManagementOS) {
        throw $localizedData.VMNameAndManagementTogether
    }

    if ((-not $ManagementOS) -and (-not $VMName)) {
        throw $localizedData.MustProvideVMName
    }

    $Arguments = @{
        Name = $Name
    }

    if ($VMName) {
        $Arguments.Add('VMName',$VMName)
    } elseif ($ManagementOS) {
        $Arguments.Add('ManagementOS', $true)
    }

    switch ($AdapterMode) {
        'Untagged' {
            if ($VlanId -or $NativeVlanId -or $PrimaryVlanId -or $SecondaryVlanId -or $AllowedVlanIdList -or $SecondaryVlanIdList) {
                Write-Verbose $localizedData.IgnoreVlan
            }
            break
        }

        'Access' {
            if (-not $VlanId) {
                throw $localizedData.VlanIdRequiredInAccess
            }
            break
        }

        'Trunk' {
            if (-not $NativeVlanId) {
                throw $localizedData.MustProvideNativeVlanId
            }
            break
        }

        'Community' {
            if (-not $PrimaryVlanId) {
                throw $localizedData.PrimaryVlanIdRequired    
            }
            break
        }

        'Isolated' {
            if (-not $PrimaryVlanId) {
                throw $localizedData.PrimaryVlanIdRequired
            }
            break
        }

        'Promiscuous' {
            if (-not $PrimaryVlanId) {
                throw $localizedData.PrimaryVlanIdRequired
            }
            break
        }
    }
    
    Write-Verbose $localizedData.GetVMNetAdapter
    $AdapterExists = Get-VMNetworkAdapter @Arguments -ErrorAction SilentlyContinue
    
    if ($AdapterExists) {
        Write-Verbose $localizedData.FoundVMNetAdapter
        if ($AdapterExists.VlanSetting.OperationMode -eq $AdapterMode) {
            switch ($AdapterExists.VlanSetting.OperationMode) {
                'Access' {
                    if ($VlanId -ne $AdapterExists.VlanSetting.AccessVlanId) {
                        Write-Verbose $localizedData.AccessVlanMustChange
                        return $false
                    } else {
                        Write-Verbose $localizedData.AdaptersExistsWithVlan
                        return $true
                    }
                    break
                }

                'Trunk' {
                    if ($NativeVlanId -ne $AdapterExists.VlanSetting.NativeVlanId) {
                        Write-Verbose $localizedData.NativeVlanMustChange
                        return $false
                    } elseif ($AllowedVlanIdList -ne $AdapterMode.VlanSetting.AllowedVlanIdListString) {
                        Write-Verbose $localizedData.AllowedVlanListMustChange
                        return $false
                    } else {
                        Write-Verbose $localizedData.AdaptersExistsWithVlan
                        return $true
                    }
                    break
                }

                'Untagged' {
                    if ($AdapterMode -eq 'Untagged') {
                        Write-Verbose $localizedData.AdaptersExistsWithVlan
                        Write-Verbose $localizedData.IgnoreVlan
                        return $true
                    }
                    break
                }

                ('Community' -or 'isolated') {
                    if ($PrimaryVlanId -ne $AdapterExists.VlanSetting.PrimaryVlanId) {
                        Write-Verbose $localizedData.PrimaryVlanMustChange
                        return $false
                    } elseif ($SecondaryVlanId -ne $AdapterExists.VlanSetting.SecondaryVlanId) {
                        Write-Verbose $localizedData.SecondaryVlanMustChange
                        return $false
                    } else {
                        Write-Verbose $localizedData.AdaptersExistsWithVlan
                        return $true
                    }
                    break
                }

                'Promiscuous' {
                    if ($PrimaryVlanId -ne $AdapterExists.VlanSetting.PrimaryVlanId) {
                        Write-Verbose $localizedData.PrimaryVlanMustChange
                        return $false
                    } elseif ($SecondaryVlanIdList -ne $AdapterExists.VlanSetting.SecondaryVlanIdListString) {
                        Write-Verbose $localizedData.SecondaryVlanListMustChange
                        return $false
                    } else {
                        Write-Verbose $localizedData.AdaptersExistsWithVlan
                        return $true
                    }
                }
            }
        } else {
            Write-Verbose $localizedData.AdapterExistsWithDifferentVlanMode
            return $false
        }
    } else {
        throw $localizedData.VMNetAdapterDoesNotExist
    }
}