DSCResources/VE_SFCommon/VE_SFCommon.psm1

Import-LocalizedData -BindingVariable localizedData -FileName Resources.psd1;

#region Private Functions

function ImportSFModule {
<#
    .SYNOPSIS
        Imports a Storefront Powershell module/snapin and throws an error if it's not available.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()]
        [System.String[]] $Name,
        
        [Parameter()]
        [System.Management.Automation.SwitchParameter] $IsSnapin
    )
    process {
        foreach ($moduleName in $Name) {
            if (-not (TestSFModule -Name $moduleName -IsSnapin:$IsSnapin)) {
                ThrowInvalidProgramException -ErrorId $Name -ErrorMessage $localizedData.StorefrontSDKNotFoundError;
            }
            elseif ($IsSnapin) {
                Add-PSSnapin -Name $moduleName -Verbose:$false;
            }
            else {
                Import-Module -Name $moduleName -Force -Global -Verbose:$false;
            }
        } #end foreach module
    } #end process
} #end function FindModule

function TestSFModule {
<#
    .SYNOPSIS
        Tests whether a Storefront Powershell module/snapin is available/registered.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()]
        [System.String[]] $Name,
        
        [Parameter()]
        [System.Management.Automation.SwitchParameter] $IsSnapin
    )
    process {
        $isCompliant = $true;
        foreach ($moduleName in $Name) {
            if ($IsSnapin) {
                if (-not (Get-PSSnapin -Name $moduleName -Registered)) {
                    $isCompliant = $false;
                }
            }
            else {
                if (-not (Get-Module -Name $moduleName -ListAvailable)) {
                    $isCompliant = $false;
                }
            }
        } #end foreach module
        return $isCompliant;
    } #end process
} #end TestModule

function RemoveDuplicateArrayMembers {
    [CmdletBinding()]
    [OutputType([System.String[]])]
    param (
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()]
        [System.String[]] $Members
    )
    process {
        $destIndex = 0;
        for([int] $sourceIndex = 0 ; $sourceIndex -lt $Members.Count; $sourceIndex++) {
            $matchFound = $false;
            for([int] $matchIndex = 0; $matchIndex -lt $destIndex; $matchIndex++) {
                if($Members[$sourceIndex] -eq $Members[$matchIndex]) {
                    # A duplicate is found. Discard the duplicate.
                    Write-Verbose -Message ($localizedData.RemovingDuplicateEntry -f $Members[$sourceIndex]);
                    $matchFound = $true;
                    continue;
                }
            }
    
            if(-not $matchFound) {
                $Members[$destIndex++] = $Members[$sourceIndex].ToLowerInvariant();
            }
        }
    
        # Create the output array.
        $destination = New-Object -TypeName System.String[] -ArgumentList $destIndex;
    
        # Copy only distinct elements from the original array to the destination array.
        [System.Array]::Copy($Members, $destination, $destIndex);
    
        return $destination;
    } #end process
} #end function RemoveDuplicateMembers

function TestStringArrayEqual {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()]
        [System.String[]] $Expected,
        
        [Parameter(Mandatory)] [AllowNull()]
        [System.String[]] $Actual
    )
    process {
        $inDesiredState = $true;
        ## Got to compare the arrays contents
        foreach ($value in $Expected) {
            if ($value -notin $Actual) {
                $inDesiredState = $false;
                break;
            }
        }
        if ($null -eq $Actual) {
            ## $Expected cannot be $null so it $Actual is $null..
            $inDesiredState = $false;
        }
        else {
            foreach ($value in $Actual) {
                if ($Expected -notcontains $value) {
                    $inDesiredState = $false;
                    break;
                }
            }
        }
        return $inDesiredState;
    } #end process
} #end function TestArray

function GetStoreFarm {
<#
    .SYNOPSIS
        Returns Storefront farm by virtual path and farm name
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.String] $StoreVirtualPath,

        [Parameter(Mandatory)]
        [System.String] $FarmName,

        [Parameter(ValueFromRemainingArguments)]
        $RemainingArguments
    )
    process {
        $store = GetStoreService -VirtualPath $StoreVirtualPath -ThrowIfNull;
        return (Get-STFStoreFarm -StoreService $store -FarmName $FarmName -ErrorAction SilentlyContinue);
    } #end process
} #end function GetStoreFarm

function GetWebReceiverService {
<#
    .SYNOPSIS
        Returns Storefront WebReceiver store by virtual path and site Id (if supplied)
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.String] $VirtualPath,

        [Parameter()]
        [System.UInt64] $SiteId = 1,

        [Parameter(ValueFromRemainingArguments)]
        $RemainingArguments
    )
    process {
        $getSTFWebReceiverServiceParams = @{
            VirtualPath = $VirtualPath;
        }
        if ($PSBoundParameters.ContainsKey('SiteId')) {
            $getSTFWebReceiverServiceParams['SiteId'] = $SiteId;
        }
        return (Get-STFWebReceiverService @getSTFWebReceiverServiceParams);
    } #end process
} #end function GetWebReceiverService

function GetStoreService {
<#
    .SYNOPSIS
        Returns Storefront store by virtual path and site Id (if supplied)
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.String] $VirtualPath,

        ## Store virtual path, e.g. /Citrix/Store
        [Parameter()]
        [System.String] $StoreVirtualPath,

        [Parameter()]
        [System.UInt64] $SiteId = 1,

        [Parameter()]
        [System.Management.Automation.SwitchParameter] $ThrowIfNull,

        [Parameter(ValueFromRemainingArguments = $true)]
        $RemainingArguments

    )
    process {
        $getSTFStoreServiceParams = @{
            VirtualPath = $VirtualPath;
        }
        if ($PSBoundParameters.ContainsKey('StoreVirtualPath')) {
            ## Override the VirtualPath with the more explicit StoreVirtualPath
            $getSTFStoreServiceParams['VirtualPath'] = $StoreVirtualPath;
        }
        if ($PSBoundParameters.ContainsKey('SiteId')) {
            $getSTFStoreServiceParams['SiteId'] = $SiteId;
        }
        $store = Get-STFStoreService @getSTFStoreServiceParams;
        if ($ThrowIfNull -and ($null -eq $store)) {
            $errorMessage = $localizedData.InvalidStorefrontStoreError -f $getSTFStoreServiceParams.VirtualPath;
            ThrowOperationCanceledException -ErrorId InvalidStore -ErrorMessage $errorMessage;
        } 
        return $store;
    } #end process
} #end function GetStoreService

function GetAuthenticationService {
<#
    .SYNOPSIS
    Returns Storefront authentication service by virtual path and site Id (if supplied)
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.String] $VirtualPath,

        [Parameter()]
        [System.UInt64] $SiteId = 1,

        [Parameter()]
        [System.Management.Automation.SwitchParameter] $ThrowIfNull,

        [Parameter(ValueFromRemainingArguments)]
        $RemainingArguments
    )
    process {
        $getSTFAuthenticationServiceParams = @{
            VirtualPath = $VirtualPath;
        }
        if ($PSBoundParameters.ContainsKey('SiteId')) {
            $getSTFAuthenticationServiceParams['SiteId'] = $SiteId;
        }
        $authenticationService =Get-STFAuthenticationService @getSTFAuthenticationServiceParams;
        if ($ThrowIfNull -and ($null -eq $authenticationService)) {
            $errorMessage = $localizedData.InvalidStorefrontAuthenticationServiceError -f $VirtualPath;
            ThrowOperationCanceledException -ErrorId InvalidAuthenticationService -ErrorMessage $errorMessage;
        } 
        return $authenticationService;
    } #end process
} #end function GetAuthenticationService

function GetAuthenticationServiceMethods {
<#
    .SYNOPSIS
    Returns Storefront authentication service methods by virtual path
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.String] $VirtualPath,
        
        [Parameter(ValueFromRemainingArguments)]
        $RemainingArguments
    )
    process {
        $authenticationService = GetAuthenticationService -VirtualPath $VirtualPath -ThrowIfNull;
        $authenticationMethods = Get-STFAuthenticationServiceProtocol -AuthenticationService $authenticationService |
            Where-Object { $_.Enabled -eq $true } |
                Select-Object -ExpandProperty Name;
        return $authenticationMethods;
    } #end process
} #end function GetAuthenticationServiceMethods

function AddInvokeScriptBlockCredentials {
    <#
    .SYNOPSIS
        Adds the required Invoke-Command parameters for loopback processing with CredSSP.
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param (
        [Parameter(Mandatory)]
        [System.Collections.Hashtable] $Hashtable,
        
        [Parameter(Mandatory)]
        [System.Management.Automation.PSCredential] $Credential
    )
    process {
        $Hashtable['ComputerName'] = $env:COMPUTERNAME;
        $Hashtable['Credential'] = $Credential;
        $Hashtable['Authentication'] = 'Credssp';
    }
} #end function AddInvokeScriptBlockCredentials

function GetHostname {
    [CmdletBinding()]
    [OutputType([System.String])]
    param ( )
    process {
        $globalIpProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties();
        if ($globalIpProperties.DomainName) {
            return '{0}.{1}' -f $globalIpProperties.HostName, $globalIpProperties.DomainName;
        }
        else {
            return $globalIpProperties.HostName;
        }
    } #end process
} #end function GetHostname

function GetRegistryValue {
    <#
    .SYNOPSIS
        Returns a registry string value.
    .NOTES
        This is an internal function and shouldn't be called from outside.
        This function enables registry calls to be unit tested.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        # Registry key name/path to query.
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [Alias('Path')] [System.String] $Key,
        # Registry value to return.
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [System.String] $Name
    )
    process {
        $itemProperty = Get-ItemProperty -Path $Key -Name $Name -ErrorAction SilentlyContinue;
        if ($itemProperty.$Name) {
            return $itemProperty.$Name;
        }
        return '';
    }
} #end function GetRegistryValue

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] $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 ($localizedData.StartingProcess -f $FilePath, $displayParams);
        if ($Credential) {
            Write-Verbose ($localizedData.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 ($localizedData.ProcessLaunched -f $process.Id);
            Write-Verbose ($localizedData.WaitingForProcessToExit -f $process.Id);
            $process.WaitForExit();
            $exitCode = [System.Convert]::ToInt32($process.ExitCode);
            Write-Verbose ($localizedData.ProcessExited -f $process.Id, $exitCode);
        }
        return $exitCode;
    } #end process
} #end function StartWaitProcess



function ThrowInvalidArgumentError {
    <#
    .SYNOPSIS
        Throws terminating error of category InvalidArgument with specified errorId and errorMessage.
    #>

    param(
        [Parameter(Mandatory)]
        [System.String] $ErrorId,
        
        [Parameter(Mandatory)]
        [System.String] $ErrorMessage
    )
    $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument;
    $exception = New-Object -TypeName 'System.ArgumentException' -ArgumentList $ErrorMessage;
    $errorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $exception, $ErrorId, $errorCategory, $null;
    throw $errorRecord;
} #end function ThrowInvalidArgumentError

function ThrowInvalidOperationException {
    <#
    .SYNOPSIS
        Throws terminating error of category InvalidOperation with specified errorId and errorMessage.
    #>

    param(
        [Parameter(Mandatory)]
        [System.String] $ErrorId,
        
        [Parameter(Mandatory)]
        [System.String] $ErrorMessage
    )
    $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidOperation;
    $exception = New-Object -TypeName 'System.InvalidOperationException' -ArgumentList $ErrorMessage;
    $errorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $exception, $ErrorId, $errorCategory, $null;
    throw $errorRecord;
} #end function ThrowInvalidOperationException

function ThrowInvalidProgramException {
    <#
    .SYNOPSIS
        Throws terminating error of category NotInstalled with specified errorId and errorMessage.
    #>

    param(
        [Parameter(Mandatory)]
        [System.String] $ErrorId,
        
        [Parameter(Mandatory)]
        [System.String] $ErrorMessage
    )
    $errorCategory = [System.Management.Automation.ErrorCategory]::NotInstalled;
    $exception = New-Object -TypeName 'System.InvalidProgramException' -ArgumentList $ErrorMessage;
    $errorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $exception, $ErrorId, $errorCategory, $null;
    throw $errorRecord;
} #end function ThrowInvalidProgramException

function ThrowOperationCanceledException {
    <#
    .SYNOPSIS
        Throws terminating error of category InvalidOperation with specified errorId and errorMessage.
    #>

    param(
        [Parameter(Mandatory)]
        [System.String] $ErrorId,
        
        [Parameter(Mandatory)]
        [System.String] $ErrorMessage
    )
    $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidOperation;
    $exception = New-Object -TypeName 'System.OperationCanceledException' -ArgumentList $ErrorMessage;
    $errorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $exception, $ErrorId, $errorCategory, $null;
    throw $errorRecord;
} #end function ThrowOperationCanceledException

#endregion Private Functions