DSCResources/xHyper-V/DSCResources/MSFT_xVMHardDiskDrive/MSFT_xVMHardDiskDrive.psm1

#region localizedData
if (Test-Path "${PSScriptRoot}\${PSUICulture}")
{
    Import-LocalizedData `
        -BindingVariable LocalizedData `
        -Filename MSFT_xVMHardDiskDrive.strings.psd1 `
        -BaseDirectory "${PSScriptRoot}\${PSUICulture}"
}
else
{
    # fallback to en-US
    Import-LocalizedData `
        -BindingVariable LocalizedData `
        -Filename MSFT_xVMHardDiskDrive.strings.psd1 `
        -BaseDirectory "${PSScriptRoot}\en-US"
}
#endregion

<#
    .SYNOPSIS
    Returns the current status of the VM hard disk drive.
    .PARAMETER VMName
    Specifies the name of the virtual machine whose hard disk drive status is to be fetched.
    .PARAMETER Path
    Specifies the full path of the VHD file linked to the hard disk drive.
#>

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

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

    $hardDiskDrive = Get-VMHardDiskDrive -VMName $VMName -ErrorAction Stop |
                        Where-Object { $_.Path -eq $Path } 
    Write-Verbose "the result of the get is: $hardDiskDrive"

    if ($null -eq $hardDiskDrive)
    {
        $ensure = 'Absent'
    }
    else
    {
        $ensure = 'Present'
    }

    $returnValue = @{
        VMName = $VMName
        Path = $hardDiskDrive.Path
        ControllerType = $hardDiskDrive.ControllerType
        ControllerNumber = $hardDiskDrive.ControllerNumber
        ControllerLocation = $hardDiskDrive.ControllerLocation
        Ensure = $ensure
    }

    return $returnValue
}

<#
    .SYNOPSIS
    Tests the state of a VM hard disk drive.
    .PARAMETER VMName
    Specifies the name of the virtual machine whose hard disk drive is to be tested.
    .PARAMETER Path
    Specifies the full path of the VHD file to be tested.
    .PARAMETER ControllerType
    Specifies the type of controller to which the the hard disk drive is to be set (IDE/SCSI).
    Default to SCSI.
    .PARAMETER ControllerNumber
    Specifies the number of the controller to which the hard disk drive is to be set.
    If not specified, this parameter assumes the value of the first available controller at the
    location specified in the ControllerLocation parameter.
    .PARAMETER ControllerLocation
    Specifies the number of the location on the controller at which the hard disk drive is to be
    set. If not specified, the first available location in the controller specified with the
    ControllerNumber parameter is used.
    .PARAMETER Ensure
    Specifies if the hard disk drive should exist or not. Defaults to Present.
#>

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

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

        [Parameter()]
        [ValidateSet("IDE","SCSI")]
        [System.String]
        $ControllerType = "SCSI",

        [Parameter()]
        [ValidateSet(0,1,2,3)]
        [System.UInt32]
        $ControllerNumber,

        [Parameter()]
        [ValidateRange(0,63)]
        [System.UInt32]
        $ControllerLocation,

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

    $resource = Get-TargetResource -VMName $VMName -Path $Path

    # Throw exception when the ControllerNumber or the ControllerLocation are out of bounds for IDE
    if ($ControllerType -eq 'IDE' -and ($ControllerNumber -gt 1 -or $ControllerLocation -gt 1))
    {
       throw ($localizedData.NumberOrLocationOutOfBounds -f $ControllerNumber, $ControllerLocation) 
    }

    $result = $true
    foreach ($key in $resource.Keys)
    {
        Write-Verbose ($localizedData.ComparingDesiredActual -f $key,
                                                                $PSBoundParameters[$key],
                                                                $resource[$key])
        $result = $result -and ($PSBoundParameters[$key] -eq $resource[$key])
    }

    return $result    
}

<#
    .SYNOPSIS
    Tests the state of a VM hard disk drive.
    .PARAMETER VMName
    Specifies the name of the virtual machine whose hard disk drive is to be tested.
    .PARAMETER Path
    Specifies the full path of the VHD file to be tested.
    .PARAMETER ControllerType
    Specifies the type of controller to which the the hard disk drive is to be set (IDE/SCSI).
    Default to SCSI.
    .PARAMETER ControllerNumber
    Specifies the number of the controller to which the hard disk drive is to be set.
    If not specified, this parameter assumes the value of the first available controller at the
    location specified in the ControllerLocation parameter.
    .PARAMETER ControllerLocation
    Specifies the number of the location on the controller at which the hard disk drive is to be
    set. If not specified, the first available location in the controller specified with the
    ControllerNumber parameter is used.
    .PARAMETER Ensure
    Specifies if the hard disk drive should exist or not. Defaults to Present.
#>

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

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

        [Parameter()]
        [ValidateSet("IDE","SCSI")]
        [System.String]
        $ControllerType = "SCSI",

        [Parameter()]
        [ValidateSet(0,1,2,3)]
        [System.UInt32]
        $ControllerNumber,

        [Parameter()]
        [ValidateRange(0,63)]
        [System.UInt32]
        $ControllerLocation,

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

    $hardDiskDrive = Get-VMHardDiskDrive -VMName $VMName | Where-Object { $_.Path -eq $Path } 

    if ($Ensure -eq "Present") 
    {
        $null = $PSBoundParameters.Remove('Ensure')

        Write-Verbose ($localizedData.CheckingIfTheDiskIsAlreadyAttachedToTheVM)
        if ($hardDiskDrive) 
        {
            Write-Verbose ($localizedData.FoundDiskButWithWrongSettings)
            $null = $PSBoundParameters.Remove('VMName')
            $null = $PSBoundParameters.Remove('Path')
            # As the operation is a move, we must use ToController... instead of Controller...
            if ($PSBoundParameters.ContainsKey('ControllerType')) 
            {
                $null = $PSBoundParameters.remove('ControllerType')
                $null = $PSBoundParameters.Add('ToControllerType', $ControllerType)
            }
            if ($PSBoundParameters.ContainsKey('ControllerNumber')) 
            {
                $null = $PSBoundParameters.Remove('ControllerNumber')
                $null = $PSBoundParameters.Add('ToControllerNumber', $ControllerNumber)
            }
            if ($PSBoundParameters.ContainsKey('ControllerLocation')) 
            {
                $null = $PSBoundParameters.Remove('ControllerLocation')
                $null = $PSBoundParameters.Add('ToControllerLocation', $ControllerLocation)
            }
            $null = $hardDiskDrive | Set-VMHardDiskDrive @PSBoundParameters
            return
        }
        
        Write-Verbose ($localizedData.CheckingIfThereIsAnotherDiskOnThisLocation)
        $splatGetHardDiskDrive = @{
            VMName = $VMName 
            ControllerType = $ControllerType 
            ControllerNumber = $ControllerNumber 
            ControllerLocation = $ControllerLocation
        }
        $hardDiskDrive = Get-VMHardDiskDrive @splatGetHardDiskDrive
        if ($PSBoundParameters.ContainsKey('ControllerType') -and
            $PSBoundParameters.ContainsKey('ControllerNumber') -and
            $PSBoundParameters.ContainsKey('ControllerLocation') -and
            $null -ne $hardDiskDrive)
        {
            Write-Warning ($localizedData.ThereIsAnotherDiskOnThisLocation -f $hardDiskDrive.Path)
            $null = $hardDiskDrive | Set-VMHardDiskDrive @PSBoundParameters -Path $Path 
            return
        }

        Write-Verbose ($localizedData.AddingTheDiskToTheFreeLocation)
        $null = Add-VMHardDiskDrive @PSBoundParameters 
    } 
    else # We must ensure that the disk is absent
    {
        if ($hardDiskDrive) 
        {
            Write-Verbose ($localizedData.RemovingVHDFromVM -f $Path)
            $null = $hardDiskDrive | Remove-VMHardDiskDrive 
        }
        else 
        {
            Write-Warning $localizedData.CouldNotFindDiskToRemove
        }
    }
}

Export-ModuleMember -Function *-TargetResource