DSCResources/CTX_PVSCommon/CTX_PVSCommon.psm1

Import-LocalizedData -BindingVariable localized -FileName CTX_PVSCommon.Resources.psd1;

#region Private Functions

function StartWaitProcess {
    <#
    .SYNOPSIS
        Starts and waits for a process to exit.
    .NOTES
        This is an internal function and shouldn't be called from outside.
#>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([System.Int32])]
    param (
        # Path to process to start.
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [System.String] $FilePath,

        # Arguments (if any) to apply to the process.
        [Parameter()]
        [AllowNull()]
        [System.String[]] $ArgumentList,

        # Credential to start the process as.
        [Parameter()]
        [AllowNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.CredentialAttribute()]
        $Credential,

        # Working directory
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String] $WorkingDirectory = (Split-Path -Path $FilePath -Parent)
    )
    process {

        $startProcessParams = @{
            FilePath         = $FilePath;
            WorkingDirectory = $WorkingDirectory;
            NoNewWindow      = $true;
            PassThru         = $true;
        };
        $displayParams = '<None>';
        if ($ArgumentList) {
            $displayParams = [System.String]::Join(' ', $ArgumentList);
            $startProcessParams['ArgumentList'] = $ArgumentList;
        }
        Write-Verbose ($localized.StartingProcess -f $FilePath, $displayParams);
        if ($Credential) {
            Write-Verbose ($localized.StartingProcessAs -f $Credential.UserName);
            $startProcessParams['Credential'] = $Credential;
        }
        if ($PSCmdlet.ShouldProcess($FilePath, 'Start Process')) {
            $process = Start-Process @startProcessParams -ErrorAction Stop;
        }
        if ($PSCmdlet.ShouldProcess($FilePath, 'Wait Process')) {
            Write-Verbose ($localized.ProcessLaunched -f $process.Id);
            Write-Verbose ($localized.WaitingForProcessToExit -f $process.Id);
            $process.WaitForExit();
            $exitCode = [System.Convert]::ToInt32($process.ExitCode);
            Write-Verbose ($localized.ProcessExited -f $process.Id, $exitCode);
        }
        return $exitCode;

    } #end process
} #end function StartWaitProcess

function Add-PSSnapin {
    <#
    .SYNOPSIS
        Proxy function to load Citrix PowerShell snapins within a module
        at the global scope.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [System.String[]] $Name
    )
    process {

        foreach ($snapinName in $Name) {

            $modulePath = Join-Path -Path $PSScriptRoot -ChildPath "$snapinName.psm1";
            Import-Module -Name $modulePath -Global -Verbose:$false;

        } #end foreach snapin

    } #end process
} #end function Add-PSSnapin


function TestPVSInstalledRole {
    <#
    .SYNOPSIS
        Tests whether a Citrix PVS role is installed.
#>

    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param (
        ## Citrix PVS role to query.
        [Parameter(Mandatory)]
        [ValidateSet('Console', 'Server', 'TDS')]
        [System.String[]] $Role
    )
    process {
        $installedRoles = GetPVSInstalledRole -Role $Role;
        foreach ($r in $Role) {

            if ($installedRoles -notcontains $r) {
                return $false;
            }
        }

        return $true;

    } #end process
} #end function TestXDRole


function GetPVSInstalledRole {
    <#
    .SYNOPSIS
        Returns installed Citrix PVS installed products.
#>

    [CmdletBinding()]
    [OutputType([System.String[]])]
    param (
        ## Citrix PVS role to query.
        [Parameter(Mandatory)]
        [ValidateSet('Console', 'Server', 'TDS')]
        [System.String[]] $Role
    )
    process {
        $installedProducts = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' -ErrorAction SilentlyContinue |
            Where-Object { $_.DisplayName -like '*Citrix*' -and $_.DisplayName -notlike '*snap-in' } |
            Select-Object -ExpandProperty DisplayName;


        $installedRoles = @();
        foreach ($r in $Role) {
            switch ($r) {

                'Console' {
                    $filter = '*Provisioning Services Console x64,Citrix*Provisioning Console x64*';
                }
                'Server' {
                    $filter = '*Provisioning Services x64,*Citrix*Provisioning Server x64*';
                }
                'TDS' {
                    $filter = '*Provisioning Services Target Device x64,Citrix*Provisioning Target Device x64*';
                }
            }

            $role_locales = $filter.Split(",")

            foreach ($locale in $role_locales) {
                
                $result = $installedProducts -like $locale;
        
                if ([System.String]::IsNullOrEmpty($result)) {

                }
                elseif ($result) {
                    $installedRoles += $r;
                    Write-Verbose "Role found: $installedRoles"
                    break;
                }
                
            }
        }

        return $installedRoles;

    } #end process
} #end function GetXDInstalledProduct

function GetPVSUninstallString {
    <#
        .SYNOPSIS
            Returns Citrix PVS uninstall string.
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param (
        ## Citrix PVS role to query.
        [Parameter(Mandatory)]
        [ValidateSet('Console', 'Server', 'TDS')]
        [System.String] $Role
    )
    process {
    
        $installedProducts = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' -ErrorAction SilentlyContinue |
            Where-Object { $_.DisplayName -like '*Citrix*' -and $_.DisplayName -notlike '*snap-in' } |
            Select-Object DisplayName, UninstallString;
    
    
        $uninstallStrings = New-Object System.Collections.ArrayList;
 
    
        switch ($Role) {

            'Console' {
                $filter = '*Provisioning Services Console x64';
            }
            'Server' {
                $filter = '*Provisioning Services x64';
            }
            'TDS' {
                $filter = '*Provisioning Services Target Device x64';
            }
        }
    
        $role_locales = $filter.Split(",")

        foreach ($locale in $role_locales) {
                
            Write-Verbose "Locale: $locale"

            foreach ($installedProduct in $installedProducts)
            {
                if ($installedProduct.DisplayName -like $locale)
                {
                    Write-Verbose "$($installedProduct.DisplayName) found..."
                    $null = $uninstallStrings.Add($installedProduct.UninstallString)
                }

            }
        
            if ([System.String]::IsNullOrEmpty($uninstallStrings)) {
                Write-Host "ist null"
            }
                
        }
    
        return $uninstallStrings;
    
    } #end process
} #end function GetPVSUninstallString

function ResolvePVSSetupMedia {
    <#
    .SYNOPSIS
        Resolve the correct installation media source for the
        local architecture depending on the role.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param (
        ## Citrix PVS role to install/uninstall.
        [Parameter(Mandatory)]
        [ValidateSet('Console', 'Server', 'TDS')]
        [System.String] $Role,

        ## Citrix PVS installation media path.
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [System.String] $SourcePath
    )
    process {

        $architecture = 'x86';
        if ([System.Environment]::Is64BitOperatingSystem) {
            $architecture = 'x64';
        }

        if ($Role -contains 'Console') {
            $installMedia = 'Console\PVS_Console_x64.exe';
        }
        elseif ($Role -contains 'Server') {
            $installMedia = 'Server\PVS_Server_x64.exe';
        }
        else {
            $installMedia = 'Device\PVS_Device_x64.exe';
        }

        $installMediaPath = Get-ChildItem -Path $SourcePath -Filter $installMedia -File;

        if (-not $installMediaPath) {
            throw ($localized.NoValidSetupMediaError -f $installMedia, $SourcePath);
        }

        return $installMediaPath.FullName;

    } #end process
} #end function ResolveXDSetupMedia


function ResolvePVSSetupArguments {
    <#
    .SYNOPSIS
        Resolve the installation arguments for the associated PVS role.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param (
        ## Citrix PVS role to install/uninstall.
        [Parameter(Mandatory)]
        [ValidateSet('Console', 'Server', 'TDS')]
        [System.String] $Role,

        ## Citrix PVS installation media path.
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String] $LogPath = (Join-Path -Path $env:TMP -ChildPath '\Citrix\PVS Installer\PVS.log'),

        ## Uninstall Citrix PVS product.
        [Parameter()]
        [System.Management.Automation.SwitchParameter] $Uninstall
    )
    process {
        $parentLogFolder = Split-Path $LogPath -Parent
        if (!(Test-Path $LogPath))
        {
            [ref] $null = New-Item -ItemType Directory -Force -Path $parentLogFolder
        }

        $arguments = New-Object -TypeName System.Collections.ArrayList -ArgumentList @();
        $arguments.AddRange(@('/s', '/v"', '/qn', "/l `\`"$LogPath`\`"`""));
        #$arguments.AddRange(@('/s', '/v"', '/qn', "/l c:\windows\temp\citrix\pvs.log`""));

        ## Additional install parameters per role
        if ($Role -contains 'TDS') {
            # TBD - [ref] $null = $arguments.Add('/NOSQL');
        }

        Write-Verbose "arguments: $arguments"

        return [System.String]::Join(' ', $arguments.ToArray());

    } #end process
} #end function ResolveXDSetupArguments

function RemovePVSServerFromFarm {
    <#
        .SYNOPSIS
            Removes Citrix PVS server from farm.
    #>

    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param (
        ## Citrix PVS server to be removed.
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String] $ServerName
    )
    process {
        try
        {
            LoadPVSConsoleSnapin
            Stop-PvsStreamService -ServerName $ServerName
            Remove-PvsServer -ServerName $ServerName
        }
        catch
        {
            Write-Verbose "Removing PVS Server failed: $Error[0]"
        }

        return $true;
    
    } #end process
} #end function RemovePVSServerFromFarm

function LoadPVSConsoleSnapin {
    <#
        .SYNOPSIS
            Loads PVS Console Powershell Snapin.
    #>

    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param ()

    process {

        if (Get-Module Citrix.PVS.SnapIn) { return $true}

        try {
            $key = "HKLM:\SOFTWARE\Citrix\ProvisioningServices"
            $PVSConsoleTargetDir = Get-ItemPropertyValue -Path $key -Name ConsoleTargetDir -ErrorAction:SilentlyContinue
            $PVSConsoleSnapin = $PVSConsoleTargetDir + "Citrix.PVS.SnapIn.dll"
    
            if (Test-Path $PVSConsoleSnapin)
            {
                Write-Verbose "Loading module '$PVSConsoleSnapin'"
                Import-Module $PVSConsoleSnapin -Global -Verbose:$false
            }
            else
            {
                throw "PVS Console Snapin $PVSConsoleSnapin not found, make sure PVS Console is installed on the target server..."
            }
        }
        catch {
            throw "Error loading PVS Powershell module..."
        }

        return $true;
    
    } #end process
} #end function LoadPVSConsoleSnapin

#endregion Private Functions