cMoveAzureTempDrive.psm1

[DscResource()]
class cMoveAzureTempDrive
{
    <#
        This property is the name of the system - its not used for anything
        other than they key.
    #>

    [DscProperty(Key)]
    [string]$Name

    [DscProperty(Mandatory)]
    [string]$TempDriveLetter

    <#
        This method is equivalent of the Get-TargetResource script function.
        The implementation should use the keys to find appropriate resources.
        This method returns an instance of this class with the updated key
         properties.
    #>

    [cMoveAzureTempDrive] Get()
    {
        $this.Name = $Env:COMPUTERNAME
        #This is the default PartitionType
        $this.TempDriveLetter = $this.GetTempDriveLetter()
        return $this
    }

    <#
        This method is equivalent of the Test-TargetResource script function.
        It should return True or False, showing whether the resource
        is in a desired state.
    #>

    [bool] Test()
    {
        try {
            Write-Verbose "cMoveAzureTempDrive:Test - Start"
            $TestResult = $true

            $this.TempDriveLetter = $this.ValidateDriveLetter($this.TempDriveLetter)
            If ($this.TempDriveLetter -eq 'D:' ) {
                Throw "New Temp Drive cannot be D:"
            }
                        
            #See if the Azure Temp drive is on the TempDriveLetter
            $CurTempDrive = $this.GetTempDriveLetter()
            Write-Verbose "cMoveAzureTempDrive:Test - Azure temp drive is on '$CurTempDrive'"
            If ($this.TempDriveLetter -ne $CurTempDrive ) {
                Write-Verbose ("cMoveAzureTempDrive:Test - Temp drive is on '$CurTempDrive' not on " + $this.TempDriveLetter)
                $TestResult = $false
            } else {
                #and the pagefile is on that drive
                if ( -not  ( $this.DoesPageFileExistOnDrive($this.TempDriveLetter) )) {
                    Write-Verbose ("cMoveAzureTempDrive:Test - Pagefile not on " + $this.TempDriveLetter)          
                    $TestResult = $false
                } else {
                    Write-Verbose ("cMoveAzureTempDrive:Test - Found pagefile on temp drive: " + $this.TempDriveLetter )
                }
            }

            return $TestResult
        }
        finally {
            Write-Verbose "cMoveAzureTempDrive:Test - End"
        }
    }

    <#
        This method is equivalent of the Set-TargetResource script function.
        It sets the resource to the desired state.
    #>

    [void] Set()
    {
        try {
            Write-Verbose "cMoveAzureTempDrive:Set - Start"

            $this.TempDriveLetter = $this.ValidateDriveLetter($this.TempDriveLetter)

            #Our inital state should be TempDrive - D:, Page: D:

            #First, see if AzureTempDrive is on D:
            $curTempDrive = $this.GetTempDriveLetter() 
            Write-Verbose ("cMoveAzureTempDrive:Set - Current Temp Drive Letter: " + $curTempDrive)

            If ($curTempDrive -ne $this.TempDriveLetter) {
                Write-Verbose ("cMoveAzureTempDrive:Set - Current Temp Drive Letter: " + $curTempDrive + " not on " + $this.TempDriveLetter)
                #See if PageFile is still on D
                if ($this.DoesPageFileExistOnDrive( $curTempDrive )) {
                    Write-Verbose ("cMoveAzureTempDrive:Set - Pagefile exists on Current Temp Drive Letter: " + $curTempDrive )
                    #Remove page file
                    $filter = ("name like '" + $curTempDrive  + "\\%'")
                    $pf = Get-WmiObject -Class win32_pagefilesetting -Filter $filter 
                    if ($pf -ne $null) {
                        Write-Verbose ("cMoveAzureTempDrive:Set - Deleting pagefile from " + $curTempDrive )
                        $pf.Delete()
                        Write-Verbose "cMoveAzureTempDrive:Set - Rebooting"
                        #Restart here - Our state will be TempDrive - D:, Page: Null
                        Restart-Computer -Force 
                        return 
                    }                    
                } else {
                    #Move this drive to the new temp drive letter
                    try {
                        Write-Verbose ("cMoveAzureTempDrive:Set - Moving azure temp drive from '$curTempDrive' to '" + $this.TempDriveLetter + "'")
                        $filter = 'DriveLetter = "' + $curTempDrive + '"'
                        $drv = Get-WmiObject win32_volume -filter $filter
                        $drv.DriveLetter = $this.TempDriveLetter
                        $drv.Put() | out-null
                        #Our state should now be TempDrive - T:, Page: Null:
                        #Re-read our current Temp Drive Letter
                        $curTempDrive = $this.GetTempDriveLetter() 
                    } catch {
                        Write-Verbose ("cMoveAzureTempDrive:Set - Error Moving azure temp drive: '" + $this.TempDriveLetter + "'")
                    }
                }
            }
            
            If ($curTempDrive -eq $this.TempDriveLetter) {
                #Our state should now be TempDrive - T:, Page: Null:
                $filter = "name like '" + $this.TempDriveLetter + "\\%'"
                $pf = Get-WmiObject -Class win32_pagefilesetting -Filter $filter 
                if ($pf -eq $null) {
                    #re-enable page file on new Drive
                    $PageFile = $this.TempDriveLetter + '\pagefile.sys'
                    Set-WMIInstance -Class Win32_PageFileSetting -Arguments @{ Name = $PageFile ; InitialSize=0; MaximumSize = 0; }
                    Restart-Computer -Force    
                } 
            }
        } finally {
            Write-Verbose "cMoveAzureTempDrive:Set - End"
        }
    }

    <#
        Helper method to get the Temp Drive letter
    #>

    [string] GetTempDriveLetter()
    {
        #Change temp drive and Page file Location
        $filter = 'Label = "Temporary Storage"'
        $drive = Get-WmiObject -Class win32_volume -Filter $filter
        if ($drive -eq $null) {
            throw "Could not find 'Temporary Storage' drive"    
        } 
        #This returns C: not just the letter
        return $drive.DriveLetter
    }

    <#
        Tests to see if any pagefiles exist
    #>

    [bool] DoesPageFileExist() 
    {
        $pf = Get-WmiObject -Class win32_pagefilesetting
        if ($pf -eq $null) {
            return $false
        } else {
            return $true
        }
    }

    <#
        Tests to see if a pagefile exists on a given drive letter
    #>

    [bool] DoesPageFileExistOnDrive([string] $DriveLetter) 
    {
        Write-Debug ("DoesPageFileExistOnDrive - Start - '$DriveLetter'")
        try {
            $DriveLetter = $this.ValidateDriveLetter($DriveLetter)

            $TestResult = $false
            $pf = Get-WmiObject -Class win32_pagefilesetting
            if ($pf -ne $null) {
                #$pf can be an array so we need to iterate
                foreach ($item in $pf) {
                    if ($pf.Name.ToUpper().StartsWith($DriveLetter.ToUpper() ) ) {
                        #Found it
                        $TestResult = $true
                    }
                }
            }
            return $TestResult 
        }
        finally {
            Write-Debug ("DoesPageFileExistOnDrive - End")
        }

    }

    <#
        Validates drive letter
    #>

    [string] ValidateDriveLetter([string] $DriveLetter) 
    {
        Write-Debug ("ValidateDriveLetter - Start - '$DriveLetter'")
        try {
            #WMI always requires format X:
            If ($DriveLetter -match "^[d-z]$" ) {
                $DriveLetter = $DriveLetter.ToUpper() + ":"
            } elseif ($DriveLetter -match "^[d-z]:$" ) {
                $DriveLetter = $DriveLetter.ToUpper()
            } else {
                throw "DriveLetter must in the form [D-Z] or [D-Z]: i.e. 'T' or 'T:' "
            }
            return $DriveLetter  
        } finally {
            Write-Debug ("ValidateDriveLetter - End")
        }        
    }
}