DSCResources/MSFT_xVMHyperV/MSFT_xVMHyperV.psm1

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

        [parameter(Mandatory = $true)]
        [System.String]
        $VhdPath
    )

    # Check if Hyper-V module is present for Hyper-V cmdlets
    if(!(Get-Module -ListAvailable -Name Hyper-V))
    {
        Throw "Please ensure that Hyper-V role is installed with its PowerShell module"
    }

    $vmobj = Get-VM -Name $Name -ErrorAction SilentlyContinue
    
    # Check if 1 or 0 VM with name = $name exist
    if($vmobj.count -gt 1)
    {
       Throw "More than one VM with the name $Name exist." 
    }
    
    
    $vmSecureBootState = $false;
    if ($vmobj.Generation -eq 2) {
       # Retrieve secure boot status (can only be enabled on Generation 2 VMs) and convert to a boolean.
       $vmSecureBootState = ($vmobj | Get-VMFirmware).SecureBoot -eq 'On'
    } 
    
    @{
        Name             = $Name
        VhdPath          = $vmObj.HardDrives[0].Path
        SwitchName       = $vmObj.NetworkAdapters.SwitchName
        State            = $vmobj.State
        Path             = $vmobj.Path
        Generation       = $vmobj.Generation
        SecureBoot       = $vmSecureBootState
        StartupMemory    = $vmobj.MemoryStartup
        MinimumMemory    = $vmobj.MemoryMinimum
        MaximumMemory    = $vmobj.MemoryMaximum
        MACAddress       = $vmObj.NetWorkAdapters.MacAddress
        ProcessorCount   = $vmobj.ProcessorCount
        Ensure           = if($vmobj){"Present"}else{"Absent"}
        ID               = $vmobj.Id
        Status           = $vmobj.Status
        CPUUsage         = $vmobj.CPUUsage
        MemoryAssigned   = $vmobj.MemoryAssigned
        Uptime           = $vmobj.Uptime
        CreationTime     = $vmobj.CreationTime
        HasDynamicMemory = $vmobj.DynamicMemoryEnabled
        NetworkAdapters  = $vmobj.NetworkAdapters.IPAddresses
        EnableGuestService = ($vmobj | Get-VMIntegrationService -Name 'Guest Service Interface').Enabled
    }
}

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        # Name of the VM
        [parameter(Mandatory)]
        [String]$Name,
        
        # VHD associated with the VM
        [parameter(Mandatory)]
        [String]$VhdPath,
        
        # Virtual switch associated with the VM
        [String[]]$SwitchName,

        # State of the VM
        [AllowNull()]
        [ValidateSet("Running","Paused","Off")]
        [String]$State,

        # Folder where the VM data will be stored
        [String]$Path,

        # Virtual machine generation
        [ValidateRange(1,2)]
        [UInt32]$Generation = 1,

        # Startup RAM for the VM
        [ValidateRange(32MB,17342MB)]
        [UInt64]$StartupMemory,

        # Minimum RAM for the VM. This enables dynamic memory
        [ValidateRange(32MB,17342MB)]
        [UInt64]$MinimumMemory,

        # Maximum RAM for the VM. This enables dynamic memory
        [ValidateRange(32MB,1048576MB)]
        [UInt64]$MaximumMemory,

        # MAC address of the VM
        [String[]]$MACAddress,

        # Processor count for the VM
        [UInt32]$ProcessorCount,

        # Waits for VM to get valid IP address
        [Boolean]$WaitForIP,

        # If specified, shutdowns and restarts the VM as needed for property changes
        [Boolean]$RestartIfNeeded,

        # Should the VM be created or deleted
        [ValidateSet("Present","Absent")]
        [String]$Ensure = "Present",
        [System.String]
        $Notes,

        # Enable secure boot for Generation 2 VMs
        [Boolean]$SecureBoot = $true,

        # Enable Guest Services
        [Boolean]$EnableGuestService = $false
    )

    # Check if Hyper-V module is present for Hyper-V cmdlets
    if(!(Get-Module -ListAvailable -Name Hyper-V))
    {
        Throw "Please ensure that Hyper-V role is installed with its PowerShell module"
    }

    Write-Verbose -Message "Checking if VM $Name exists ..."
    $vmObj = Get-VM -Name $Name -ErrorAction SilentlyContinue

    # VM already exists
    if($vmObj)
    {
        Write-Verbose -Message "VM $Name exists"

        # If VM shouldn't be there, stop it and remove it
        if($Ensure -eq "Absent")
        {
            Write-Verbose -Message "VM $Name should be $Ensure"
            Get-VM $Name | Stop-VM -Force -Passthru -WarningAction SilentlyContinue | Remove-VM -Force
            Write-Verbose -Message "VM $Name is $Ensure"
        }

        # If VM is present, check its state, startup memory, minimum memory, maximum memory,processor countand mac address
        # One cannot set the VM's vhdpath, path, generation and switchName after creation
        else
        {
            # If state has been specified and the VM is not in right state, set it to right state
            if($State -and ($vmObj.State -ne $State))
            {
                Write-Verbose -Message "VM $Name is not $State. Expected $State, actual $($vmObj.State)"
                Set-VMState -Name $Name -State $State -WaitForIP $WaitForIP
                Write-Verbose -Message "VM $Name is now $State"
            }

            $changeProperty = @{}
            # If the VM does not have the right startup memory
            if($StartupMemory -and ($vmObj.MemoryStartup -ne $StartupMemory))
            {
                Write-Verbose -Message "VM $Name does not have correct startup memory. Expected $StartupMemory, actual $($vmObj.MemoryStartup)"
                $changeProperty["MemoryStartup"]=$StartupMemory
            }
            elseif($MinimumMemory -and ($vmObj.MemoryStartup -lt $MinimumMemory))
            {
                Write-Verbose -Message "VM $Name has a startup memory $($vmObj.MemoryStartup) lesser than minimum memory $MinimumMemory. Setting startup memory to be equal to $MinimumMemory" 
                $changeProperty["MemoryStartup"]=$MinimumMemory
            }
            elseif($MaximumMemory -and ($vmObj.MemoryStartup -gt $MaximumMemory))
            {
                Write-Verbose -Message "VM $Name has a startup memory $($vmObj.MemoryStartup) greater than maximum memory $MaximumMemory. Setting startup memory to be equal to $MaximumMemory"
                $changeProperty["MemoryStartup"]=$MaximumMemory
            }
            
            # If the VM does not have the right minimum or maximum memory, stop the VM, set the right memory, start the VM
            if($MinimumMemory -or $MaximumMemory)
            {
                $changeProperty["DynamicMemory"]=$true

                if($MinimumMemory -and ($vmObj.Memoryminimum -ne $MinimumMemory))
                {
                    Write-Verbose -Message "VM $Name does not have correct minimum memory. Expected $MinimumMemory, actual $($vmObj.MemoryMinimum)"
                    $changeProperty["MemoryMinimum"]=$MinimumMemory
                }
                if($MaximumMemory -and ($vmObj.Memorymaximum -ne $MaximumMemory))
                {
                    Write-Verbose -Message "VM $Name does not have correct maximum memory. Expected $MaximumMemory, actual $($vmObj.MemoryMaximum)"
                    $changeProperty["MemoryMaximum"]=$MaximumMemory
                }
            }

            # If the VM does not have the right processor count, stop the VM, set the right memory, start the VM
            if($ProcessorCount -and ($vmObj.ProcessorCount -ne $ProcessorCount))
            {
                Write-Verbose -Message "VM $Name does not have correct processor count. Expected $ProcessorCount, actual $($vmObj.ProcessorCount)"
                $changeProperty["ProcessorCount"]=$ProcessorCount
            }

            # Stop the VM, set the right properties, start the VM only if there are properties to change
            if ($changeProperty.Count -gt 0) {
                Change-VMProperty -Name $Name -VMCommand "Set-VM" -ChangeProperty $changeProperty -WaitForIP $WaitForIP -RestartIfNeeded $RestartIfNeeded
                Write-Verbose -Message "VM $Name updated"
            }

            ## Set VM network switches. This can be done while the VM is running.
            for ($i = 0; $i -lt $SwitchName.Count; $i++)
            {
                $switch = $SwitchName[$i]
                $nic = $vmObj.NetworkAdapters[$i]
                if ($nic) {
                    ## We cannot change the MAC address whilst the VM is running..
                    if ($nic.SwitchName -ne $switch) {
                        Write-Verbose -Message "VM $Name NIC $i is not connected to the correct switch. Expected $switch, actual $($nic.SwitchName)"
                        $nic | Connect-VMNetworkAdapter -SwitchName $switch
                        Write-Verbose -Message "VM $Name NIC $i connected to the $switch switch."
                    }
                }
                else {
                    Write-Verbose -Message "VM $Name NIC $i is not present. Expected $switch, actual <missing>"
                    if ($MACAddress -and (-not [System.String]::IsNullOrEmpty($MACAddress[$i])))
                    {
                        Add-VMNetworkAdapter -VMName $Name -SwitchName $switch -StaticMacAddress $MACAddress[$i]
                        Write-Verbose -Message "VM $Name NIC $i added to the correct switch $switch with MAC address $($MACAddress[$i])."
                    }
                    else
                    {
                        Add-VMNetworkAdapter -VMName $Name -SwitchName $switch
                        Write-Verbose -Message "VM $Name NIC $i added to the correct switch $switch."
                    }
                    ## Refresh the NICs after we've added one
                    $vmObj = Get-VM -Name $Name -ErrorAction SilentlyContinue
                }
            }
            
            # If the VM does not have the right MACAddress, stop the VM, set the right MACAddress, start the VM
            for ($i = 0; $i -lt $MACAddress.Count; $i++)
            { 
                $address = $MACAddress[$i]
                $nic = $vmObj.NetworkAdapters[$i]
                if ($nic.MacAddress -ne $address)
                {
                    Write-Verbose -Message "VM $Name NIC $i does not have correct MACAddress. Expected $address, actual $($nic.MacAddress)"
                    Change-VMMACAddress -Name $Name -NICIndex $i -MACAddress $address -WaitForIP $WaitForIP -RestartIfNeeded $RestartIfNeeded
                }
            }

            if ($Generation -eq 2)
            {
                ## Retrive the current secure boot state
                $vmSecureBoot = Test-VMSecureBoot -Name $Name
                if ($SecureBoot -ne $vmSecureBoot)
                {
                    Write-Verbose -Message "VM $Name secure boot is incorrect. Expected $SecureBoot, actual $vmSecureBoot"
                    ## Cannot change the secure boot state whilst the VM is powered on.
                    Change-VMSecureBoot -Name $Name -SecureBoot $SecureBoot -RestartIfNeeded $RestartIfNeeded
                    Write-Verbose -Message "VM $Name secure boot is now correct."
                }
            }

            if($Notes -ne $null)
            {
                # If the VM notes do not match the desire notes, update them. This can be done while the VM is running.
                if($vmObj.Notes -ne $Notes)
                {
                    Set-Vm -Name $Name -Notes $Notes
                }
            }

            #If the VM doesn't have Guest Service Interface correctly configured, update it.
            $GuestServiceStatus = $vmObj | Get-VMIntegrationService -Name 'Guest Service Interface'
            if ($GuestServiceStatus.Enabled -eq $false -and $EnableGuestService) {
                Write-Verbose -Message "VM $Name has Guest Service Interface disabled and should be enabled"
                $vmObj | Enable-VMIntegrationService -Name 'Guest Service Interface'
                Write-Verbose -Message "VM $Name Guest Service Interface configured correctly"
            }
            elseif ($GuestServiceStatus.Enabled -and -not $EnableGuestService)
            {
                Write-Verbose -Message "VM $Name has Guest Service Interface enabled and should be disabled"
                $vmObj | Disable-VMIntegrationService -Name 'Guest Service Interface'
                Write-Verbose -Message "VM $Name Guest Service Interface configured correctly"
            }
        }
    }

    # VM is not present, create one
    else
    {
        Write-Verbose -Message "VM $Name does not exists"
        if($Ensure -eq "Present")
        {
            Write-Verbose -Message "Creating VM $Name ..."
            
            $parameters = @{}
            $parameters["Name"] = $Name
            $parameters["VHDPath"] = $VhdPath
            $parameters["Generation"] = $Generation

            # Optional parameters
            if($SwitchName)
            {
                $parameters["SwitchName"]=$SwitchName[0]
            }
            if($Path){$parameters["Path"]=$Path}
            $defaultStartupMemory = 512MB
            if($StartupMemory){$parameters["MemoryStartupBytes"]=$StartupMemory}
            elseif($MinimumMemory -and $defaultStartupMemory -lt $MinimumMemory){$parameters["MemoryStartupBytes"]=$MinimumMemory}
            elseif($MaximumMemory -and $defaultStartupMemory -gt $MaximumMemory){$parameters["MemoryStartupBytes"]=$MaximumMemory}
            $null = New-VM @parameters

            $parameters = @{}
            $parameters["Name"] = $Name
            if($MinimumMemory -or $MaximumMemory)
            {
                $parameters["DynamicMemory"]=$true
                if($MinimumMemory){$parameters["MemoryMinimumBytes"]=$MinimumMemory}
                if($MaximumMemory){$parameters["MemoryMaximumBytes"]=$MaximumMemory}
            }

            if($Notes)
            {
                $parameters["Notes"] = $Notes
            }

            if($ProcessorCount)
            {
                $parameters["ProcessorCount"]=$ProcessorCount
            }

            $null = Set-VM @parameters

            ## There's always a NIC added with New-VM
            if($MACAddress)
            {
                Set-VMNetworkAdapter -VMName $Name -StaticMacAddress $MACAddress[0]
            }

            ## Add additional NICs
            for ($i = 1; $i -lt $SwitchName.Count; $i++)
            {
                $addVMNetworkAdapterParams = @{
                    VMName = $Name;
                    SwitchName = $SwitchName[$i];
                }
                if ($MACAddress -and (-not [System.String]::IsNullOrEmpty($MACAddress[$i])))
                {
                    $addVMNetworkAdapterParams['StaticMacAddress'] = $MACAddress[$i];
                }
                Write-Verbose -Message "Adding additional NIC to '$($SwitchName[$i])' switch"
                Add-VMNetworkAdapter @addVMNetworkAdapterParams
            }

            if ($Generation -eq 2) {
                # Secure boot is only applicable to Generation 2 VMs and it defaults to on.
                # Therefore, we only need to explicitly set it to off if specified.
                if ($SecureBoot -eq $false)
                {
                    Set-VMFirmware -VMName $Name -EnableSecureBoot Off
                }
            }

            if ($EnableGuestService)
            {
                Enable-VMIntegrationService -VMName $Name -Name 'Guest Service Interface'
            }
            
            Write-Verbose -Message "VM $Name created"

            if ($State)
            {
                Set-VMState -Name $Name -State $State -WaitForIP $WaitForIP
                Write-Verbose -Message "VM $Name is $State"
            }
            
        }
    }
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        # Name of the VM
        [parameter(Mandatory)]
        [String]$Name,
        
        # VHD associated with the VM
        [parameter(Mandatory)]
        [String]$VhdPath,
        
        # Virtual switch associated with the VM
        [String[]]$SwitchName,

        # State of the VM
        [AllowNull()]
        [ValidateSet("Running","Paused","Off")]
        [String]$State,

        # Folder where the VM data will be stored
        [String]$Path,

        # Virtual machine generation
        [ValidateRange(1,2)]
        [UInt32]$Generation = 1,

        # Startup RAM for the VM
        [ValidateRange(32MB,17342MB)]
        [UInt64]$StartupMemory,

        # Minimum RAM for the VM. This enables dynamic memory
        [ValidateRange(32MB,17342MB)]
        [UInt64]$MinimumMemory,

        # Maximum RAM for the VM. This enables dynamic memory
        [ValidateRange(32MB,1048576MB)]
        [UInt64]$MaximumMemory,

        # MAC address of the VM
        [String[]]$MACAddress,

        # Processor count for the VM
        [UInt32]$ProcessorCount,

        # Waits for VM to get valid IP address
        [Boolean]$WaitForIP,

        # If specified, shutdowns and restarts the VM as needed for property changes
        [Boolean]$RestartIfNeeded,

        # Should the VM be created or deleted
        [ValidateSet("Present","Absent")]
        [String]$Ensure = "Present",
        [System.String]
        $Notes,

        # Enable secure boot for Generation 2 VMs
        [Boolean]$SecureBoot = $true,

        [Boolean]$EnableGuestService = $false
    )

    #region input validation
    
    # Check if Hyper-V module is present for Hyper-V cmdlets
    if(!(Get-Module -ListAvailable -Name Hyper-V))
    {
        Throw "Please ensure that Hyper-V role is installed with its PowerShell module"
    }

    # Check if 1 or 0 VM with name = $name exist
    if((Get-VM -Name $Name -ErrorAction SilentlyContinue).count -gt 1)
    {
       Throw "More than one VM with the name $Name exist." 
    }
    
    # Check if $VhdPath exist
    if(!(Test-Path $VhdPath))
    {
        #Throw "$VhdPath does not exists"
    }

    # Check if Minimum memory is less than StartUpmemory
    if($StartupMemory -and $MinimumMemory -and  ($MinimumMemory -gt $StartupMemory))
    {
        Throw "MinimumMemory($MinimumMemory) should not be greater than StartupMemory($StartupMemory)"
    }
    
    # Check if Minimum memory is greater than Maximummemory
    if($MaximumMemory -and $MinimumMemory -and ($MinimumMemory -gt $MaximumMemory))
    {
        Throw "MinimumMemory($MinimumMemory) should not be greater than MaximumMemory($MaximumMemory)"
    }
    
    # Check if Startup memory is greater than Maximummemory
    if($MaximumMemory -and $StartupMemory -and ($StartupMemory -gt $MaximumMemory))
    {
        Throw "StartupMemory($StartupMemory) should not be greater than MaximumMemory($MaximumMemory)"
    }        

    <# VM Generation has no direct relation to the virtual hard disk format and cannot be changed
        after the virtual machine has been created. Generation 2 VMs do not support .VHD files. #>

    if(($Generation -eq 2) -and ($VhdPath.Split('.')[-1] -eq 'vhd'))
    {
        Throw "Generation 2 virtual machines do not support the .VHD virtual disk extension."
    }


    # Check if $Path exist
    if($Path -and !(Test-Path -Path $Path))
    {
        Throw "$Path does not exists"
    }

    #endregion

    $result = $false

    try
    {
        $vmObj = Get-VM -Name $Name -ErrorAction Stop
        if($Ensure -eq "Present")
        {
            if($vmObj.HardDrives.Path -notcontains $VhdPath){return $false}
            for ($i = 0; $i -lt $SwitchName.Count; $i++)
            {
                if ($vmObj.NetworkAdapters[$i].SwitchName -ne $SwitchName[$i])
                {
                    Write-Verbose -Message "Network Adapter '$i' is not connected to the correct switch. Expected '$($SwitchName[$i])', actual '$($vmObj.NetworkAdapters[$i].SwitchName)'"
                    return $false
                }
            }
            if($state -and ($vmObj.State -ne $State)){return $false}
            if($StartupMemory -and ($vmObj.MemoryStartup -ne $StartupMemory)){return $false}
            for ($i = 0; $i -lt $MACAddress.Count; $i++)
            { 
                if ($vmObj.NetworkAdapters[$i].MACAddress -ne $MACAddress[$i])
                {
                    Write-Verbose -Message "Network Adapter '$i' MAC address is incorrect. Expected '$($MACAddress[$i])', actual '$($vmObj.NetworkAdapters[$i].MACAddress)'"
                    return $false
                }
            }
            if($Generation -ne $vmObj.Generation){return $false}
            if($ProcessorCount -and ($vmObj.ProcessorCount -ne $ProcessorCount)){return $false}
            if($MaximumMemory -and ($vmObj.MemoryMaximum -ne $MaximumMemory)){return $false}
            if($MinimumMemory -and ($vmObj.MemoryMinimum -ne $MinimumMemory)){return $false}

            if($vmObj.Generation -eq 2) {
                if ($SecureBoot -ne (Test-VMSecureBoot -Name $Name)){return $false}
            }
            if (($vmObj | Get-VMIntegrationService -Name 'Guest Service Interface').Enabled -ne $EnableGuestService) {return $false}
            return $true
        }
        else
        {
            return $false
        }
    }
    catch [System.Management.Automation.ActionPreferenceStopException]
    {
        ($Ensure -eq 'Absent')
    }
}

#region Helper function

function Set-VMState
{
    param
    (
        [Parameter(Mandatory)]
        [String]$Name,

        [Parameter(Mandatory)]
        [ValidateSet("Running","Paused","Off")]
        [String]$State,

        [Boolean]$WaitForIP
    )

    switch ($State)
    {
        'Running' {
            $oldState = (Get-VM -Name $Name).State
            # If VM is in paused state, use resume-vm to make it running
            if($oldState -eq "Paused"){Resume-VM -Name $Name}
            # If VM is Off, use start-vm to make it running
            elseif ($oldState -eq "Off"){Start-VM -Name $Name}
            
            if($WaitForIP) { Get-VMIPAddress -Name $Name -Verbose }
        }
        'Paused' {if($oldState -ne 'Off'){Suspend-VM -Name $Name}}
        'Off' {Stop-VM -Name $Name -Force -WarningAction SilentlyContinue}
    }
}

function Change-VMMACAddress
{
    param
    (
        [Parameter(Mandatory)]
        [String]$Name,

        [Parameter(Mandatory)]
        [String]$MACAddress,

        [Parameter(Mandatory)]
        [Int]$NICIndex,

        [Boolean]$WaitForIP,

        [Boolean]$RestartIfNeeded
    )
    $vmObj = Get-VM -Name $Name
    $originalState = $vmObj.state
    if($originalState -ne "Off" -and $RestartIfNeeded)
    { 
        Set-VMState -Name $Name -State Off
        $vmObj.NetworkAdapters[$NICIndex] | Set-VMNetworkAdapter -StaticMacAddress $MACAddress

        # Can not move a off VM to paused, but only to running state
        if($originalState -eq "Running")
        {
            Set-VMState -Name $Name -State Running -WaitForIP $WaitForIP
        }

        Write-Verbose -Message "VM $Name NIC $NICIndex now has $MACAddress MACAddress."

        # Cannot make a paused VM to go back to Paused state after turning Off
        if($originalState -eq "Paused")
        {
            Write-Warning -Message "VM $Name state will be OFF and not Paused"
        }
    }
    elseif($originalState -eq "Off")
    {
        $vmObj.NetworkAdapters[$NICIndex] | Set-VMNetworkAdapter -StaticMacAddress $MACAddress
        Write-Verbose -Message "VM $Name NIC $NICIndex now has $MACAddress MACAddress."
    }
    else
    {
        Write-Error -Message "Can not change properties for VM $Name in $($vmObj.State) state unless RestartIfNeeded is set to true"
    }
}

function Change-VMProperty
{
    param
    (
        [Parameter(Mandatory)]
        [String]$Name,

        [Parameter(Mandatory)]
        [String]$VMCommand,

        [Parameter(Mandatory)]
        [Hashtable]$ChangeProperty,

        [Boolean]$WaitForIP,

        [Boolean]$RestartIfNeeded
    )

    $vmObj = Get-VM -Name $Name
    $originalState = $vmObj.state
    if($originalState -ne "Off" -and $RestartIfNeeded)
    { 
        Set-VMState -Name $Name -State Off
        &$VMCommand -Name $Name @ChangeProperty

        # Can not move a off VM to paused, but only to running state
        if($originalState -eq "Running")
        {
            Set-VMState -Name $Name -State Running -WaitForIP $WaitForIP
        }

        Write-Verbose -Message "VM $Name now has correct properties."

        # Cannot make a paused VM to go back to Paused state after turning Off
        if($originalState -eq "Paused")
        {
            Write-Warning -Message "VM $Name state will be OFF and not Paused"
        }
    }
    elseif($originalState -eq "Off")
    {
        &$VMCommand -Name $Name @ChangeProperty 
        Write-Verbose -Message "VM $Name now has correct properties."
    }
    else
    {
        Write-Error -Message "Can not change properties for VM $Name in $($vmObj.State) state unless RestartIfNeeded is set to true"
    }
}

# The 'Change-VMProperty' method cannot be used as it's hard-coded to use the -Name
# parameter and unfortunately, the Set-VMFirmware cmdlet uses the -VMName parameter instead!
function Change-VMSecureBoot
{
    param
    (
        [Parameter(Mandatory)]
        [String]$Name,

        [Boolean]$SecureBoot,

        [Boolean]$RestartIfNeeded
    )

    $vmObj = Get-VM -Name $Name
    $originalState = $vmObj.state
    if($originalState -ne "Off" -and $RestartIfNeeded)
    { 
        Set-VMState -Name $Name -State Off
        if ($SecureBoot)
        {
            Set-VMFirmware -VMName $Name -EnableSecureBoot On
        }
        else {
            Set-VMFirmware -VMName $Name -EnableSecureBoot Off
        }

        # Can not move a off VM to paused, but only to running state
        if($originalState -eq "Running")
        {
            Set-VMState -Name $Name -State Running -WaitForIP $true
        }

        Write-Verbose -Message "VM $Name now has correct properties."

        # Cannot make a paused VM to go back to Paused state after turning Off
        if($originalState -eq "Paused")
        {
            Write-Warning -Message "VM $Name state will be OFF and not Paused"
        }
    }
    elseif($originalState -eq "Off")
    {
        if ($SecureBoot)
        {
            Set-VMFirmware -VMName $Name -EnableSecureBoot On
        }
        else {
            Set-VMFirmware -VMName $Name -EnableSecureBoot Off
        }
    }
    else
    {
        Write-Error -Message "Can not change properties for VM $Name in $($vmObj.State) state unless RestartIfNeeded is set to true"
    }
}

function Test-VMSecureBoot
{
    param (
        [Parameter(Mandatory)]
        [string]$Name
    )
    $vm = Get-VM -Name $Name;
    return (Get-VMFirmware -VM $vm).SecureBoot -eq 'On';
}

function Get-VMIPAddress
{
    param
    (
        [Parameter(Mandatory)]
        [string]$Name
    )

    while((Get-VMNetworkAdapter -VMName $Name).ipaddresses.count -lt 2)
    {
        Write-Verbose -Message "Waiting for IP Address for VM $Name ..." -Verbose
        Start-Sleep -Seconds 3;
    }
}

#endregion

Export-ModuleMember -Function *-TargetResource