DSCResources/xHyper-V/DSCResources/MSFT_xVHD/MSFT_xVHD.psm1

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

        [parameter(Mandatory)]
        [String]$Path,

        # Virtual disk format - Vhd or Vhdx
        [ValidateSet("Vhd","Vhdx")]
        [String]$Generation = "Vhd"
    )
    
    # 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"
    }
   
    # Construct the full path for the vhdFile
    $vhdName = GetNameWithExtension -Name $Name -Generation $Generation
    $vhdFilePath = Join-Path -Path $Path -ChildPath $vhdName
    Write-Debug -Message "Vhd full path is $vhdFilePath"

    $vhd = Get-VHD -Path $vhdFilePath -ErrorAction SilentlyContinue
    @{
        Name             = $Name
        Path             = $Path
        ParentPath       = $vhd.ParentPath
        Generation       = $vhd.VhdFormat
        Ensure           = if($vhd){"Present"}else{"Absent"}
        ID               = $vhd.DiskIdentifier
        Type             = $vhd.VhdType
        FileSizeBytes    = $vhd.FileSize
        MaximumSizeBytes = $vhd.Size
        IsAttached       = $vhd.Attached
    }
}

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        # Name of the VHD File
        [parameter(Mandatory)]
        [String]$Name,

        # Folder where the VHD will be created
        [parameter(Mandatory)]
        [String]$Path,

        # Parent VHD file path, for differencing disk
        [String]$ParentPath,

        # Size of Vhd to be created
        [Uint64]$MaximumSizeBytes,

        # Virtual disk format - Vhd or Vhdx
        [ValidateSet("Vhd","Vhdx")]
        [String]$Generation = "Vhd",

        # Should the VHD be created or deleted
        [ValidateSet("Present","Absent")]
        [String]$Ensure = "Present"
    )
    
    # 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"
    }

    # Construct the full path for the vhdFile
    $vhdName = GetNameWithExtension -Name $Name -Generation $Generation
    $vhdFilePath = Join-Path -Path $Path -ChildPath $vhdName
    Write-Debug -Message "Vhd full path is $vhdFilePath"

    Write-Verbose -Message "Checking if $vhdFilePath is $Ensure ..."

    # If vhd should be absent, delete it
    if($Ensure -eq "Absent")
    {
        if (Test-Path $vhdFilePath)
        {
            Write-Verbose -Message "$vhdFilePath is not $Ensure"
            Remove-Item -Path $vhdFilePath -Force -ErrorAction Stop             
        }
        Write-Verbose -Message "$vhdFilePath is $Ensure"
    }  

    else
    {
        # Check if the Vhd is present
        try
        {
                $vhd = Get-VHD -Path $vhdFilePath -ErrorAction Stop

                # If this is a differencing disk, check the parent path
                if($ParentPath)
                {
                    Write-Verbose -Message "Checking if $vhdFilePath parent path is $ParentPath ..."
        
                    # If the parent path is not set correct, fix it
                    if($vhd.ParentPath -ne $ParentPath)
                    {
                        Write-Verbose -Message "$vhdFilePath parent path is not $ParentPath."
                        Set-VHD -Path $vhdFilePath -ParentPath $ParentPath
                        Write-Verbose -Message "$vhdFilePath parent path is now $ParentPath."
                    }
                    else
                    {
                        Write-Verbose -Message "$vhdFilePath is $Ensure and parent path is set to $ParentPath."                
                    }
                }

                # This is a fixed disk, check the size
                else
                {
                    Write-Verbose -Message "Checking if $vhdFilePath size is $MaximumSizeBytes ..."

                    # If the size is not correct, fix it
                    if($vhd.Size -ne $MaximumSizeBytes)
                    {
                        Write-Verbose -Message "$vhdFilePath size is not $MaximumSizeBytes."
                        Resize-VHD -Path $vhdFilePath -SizeBytes $MaximumSizeBytes
                        Write-Verbose -Message "$vhdFilePath size is now $MaximumSizeBytes."
                    }
                    else
                    {
                        Write-Verbose -Message "$vhdFilePath is $Ensure and size is $MaximumSizeBytes."                
                    }
                }
        }    

    # Vhd file is not present
    catch [System.Management.Automation.ActionPreferenceStopException]
    {
      
            Write-Verbose -Message "$vhdFilePath is not $Ensure"
            if($ParentPath)
            {
                $null = New-VHD -Path $vhdFilePath -ParentPath $ParentPath
            }
            else
            {
                $null = New-VHD -Path $vhdFilePath -SizeBytes $MaximumSizeBytes
            }
            Write-Verbose -Message "$vhdFilePath is now $Ensure"
    }

 }
    
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        # Name of the VHD File
        [parameter(Mandatory)]
        [String]$Name,

        # Folder where the VHD will be created
        [parameter(Mandatory)]
        [String]$Path,

        # Parent VHD file path, for differencing disk
        [String]$ParentPath,

        # Size of Vhd to be created
        [Uint64]$MaximumSizeBytes,

        # Virtual disk format - Vhd or Vhdx
        [ValidateSet("Vhd","Vhdx")]
        [String]$Generation = "Vhd",

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

    #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"
    }

    if(! ($ParentPath -or $MaximumSizeBytes))
    {
       Throw "Either specify ParentPath or MaximumSizeBytes property." 
    }
            
    if($ParentPath)
    {
        # Ensure only one value is specified - differencing disk or new disk
        if($MaximumSizeBytes)
        {
            Throw "Cannot specify both ParentPath and MaximumSizeBytes. Specify only one and try again."
        }
        
        if(! (Test-Path -Path $ParentPath))
        {
            Throw "$ParentPath does not exists"
        }
            
        # Check if the generation matches parenting disk
        if($Generation -and ($ParentPath.Split('.')[-1] -ne $Generation))
        {
            Throw "Generation $geneartion should match ParentPath extension $($ParentPath.Split('.')[-1])"
        }
    }
    if(!(Test-Path -Path $Path))
    {
        Throw "$Path does not exists"
    }

    # Construct the full path for the vhdFile
    $vhdName = GetNameWithExtension -Name $Name -Generation $Generation
    $vhdFilePath = Join-Path -Path $Path -ChildPath $vhdName
    Write-Debug -Message "Vhd full path is $vhdFilePath"

    # Add the logic here and at the end return either $true or $false.
    $result = Test-VHD -Path $vhdFilePath -ErrorAction SilentlyContinue
    Write-Verbose -Message "Vhd $vhdFilePath is present:$result and Ensure is $Ensure"
    return ($result -and ($Ensure -eq "Present"))
}

# Appends the generation to the name provided if it is not part of the name already.
function GetNameWithExtension
{
    param(
    # Name of the VHD File
        [parameter(Mandatory)]
        [String]$Name,
        [parameter(Mandatory)]
        [String]$Generation ='Vhd'
      )

     # If the name ends with vhd or vhdx don't append the generation to the vhdname.
    if ($Name -like '*.vhd' -or $Name -like '*.vhdx')
    {
        $extension = $Name.Split('.')[-1]
        if ($Generation -ne $extension)
        {
            throw "the extension $extension on the name does match the generation $Generation"
        }
        else
        {                
            Write-Debug -Message "Vhd full name is $vhdName"
            $vhdName = $Name
        }
    }
    else
    {
        # Append generation to the name
        $vhdName = "$Name.$Generation"
        Write-Debug -Message "Vhd full name is $vhdName"
    }

    return $vhdName
}

Export-ModuleMember -Function *-TargetResource