public/PSRemotely.ps1

function PSRemotely
{
<#
    .SYNOPSIS
    Provides a Keyword to wrap around the existing Ops validation tests.
 
    .PARAMETER Body
    Scriptblock enclosing the Node block to target the remote ops validation at.
 
    .PARAMETER ConfigurationData
    Provide DSC style ConfigurationData for environment details to PSRemotely.
     
    .PARAMETER Path
    The ConfigurationData can be supplied via a .psd1 or .json file.
    PSRemotely will be able to work with all these configuration data sources, until it follows the DSC syntax for it.
     
    .PARAMETER ArgumentList
    Key-Value pairs corresponding to variable-value which are passed to and made available on all the remote nodes.
    This might be useful if you need a variable across on all nodes while executing ops validation tests.
 
    .PARAMETER CredentialHash
    Specify a hash with node name as key and credential object as value. PSRemotely will use this to open a PSSession
    to the nodes.
     
    $CredHashTable = @{
        'Compute-11'= $(Import-CliXML -Path .\Compute_Cred.xml);
        'Storage-12'= $(Get-Credential)
    }
 
    .NOTES
    Read the documentation hosted on GitHub for the project for using the DSL.
     
    .LINK
    Node
    Invoke-PSRemotely
 
#>

    [OutputType([String[]])]
    [CmdletBinding(DefaultParameterSetName='ConfigurationData')]
    param 
    (       
        [Parameter(Mandatory = $true, Position = 0)]
        [ScriptBlock] $body,

        [Parameter(Position = 1,
                    ParameterSetName='ConfigurationData')]
        [hashtable] $configurationData,
        
        [Parameter(Position=1, ParameterSetName='ConfigDataFromFile')]
        [ValidateScript({ '.json','.psd1'  -contains $([System.IO.Path]::GetExtension($_))})] 
        [ValidateScript({Test-Path -Path $_})]
        [String]$Path,

        # Key-Value pairs corresponding to variable-value which are passed to the PSRemotely node.
        [Parameter(Mandatory = $false, Position = 2)]
        [Hashtable]$argumentList,

        # Credentials in the node name - PSCredential Object (key-Value) pair.
        [Parameter(Mandatory = $false, Position = 3)]
        [hashtable]$credentialHash
    )
    BEGIN {
        TRY {
            # Add CredentialHash & ArgumentList in Script scope
            if ($credentialHash){
                Write-VerboseLog -Message 'Setting CredentialHash passed in Script scope'
                Set-Variable -Name CredentialHash  -Scope  Script
            }

            if ($argumentList){
                Write-VerboseLog -Message 'Setting ArgumentList passed in Script scope'
                Set-Variable -Name ArgumentList  -Scope  Script -Value $ArgumentList
            }
            else {
                Write-VerboseLog -Message 'Creating an empty Argumnelist variable in Script scope'
                New-Variable -Name ArgumentList -Scope Script -Value @{} -Force -ErrorAction SilentlyContinue
            }
            
            Switch -Exact ($PSCmdlet.ParameterSetName) {
                'ConfigurationData' {
                    Write-VerboseLog -Message 'ParameterSet - ConfigurationData'
                    break
                }
                'ConfigDataFromFile' {
                    Write-VerboseLog -Message 'ParameterSet - ConfigDataFromFile'
                    $ConfigurationData = LoadConfigDataFromFile -Path $Path
                    break
                }
            }
            
            #region create the PSSessions & bootstrap nodes
            if ($ConfigurationData) {
                Write-VerboseLog -Message 'Configuration data supplied, processing now.'
                # validate the config data
                Write-VerboseLog -Message 'Testing the configuration data supplied.'
                Test-ConfigData -ConfigurationData $configurationData

                Write-VerboseLog -Message 'Updating the configuration data supplied'
                $configurationData = Update-ConfigData -ConfigurationData $configurationData

                # Define the AllNodes variable in current scope
                Write-VerboseLog -Message 'Creating the AllNodes global scope variable'
                New-Variable -Name AllNodes -Value $configurationData.AllNodes -Scope Script  -Force
                
                if ($AllNodes.NodeName) {
                    Write-VerboseLog -Message 'Creating sessions'
                    CreateSessions -ConfigData $configurationData -CredentialHash $CredentialHash  -ArgumentList $ArgumentList

                    if( $PSRemotely.sessionHashTable.Values.count -le 0) {
                        Write-VerboseLog -Message 'Error - No PSSessions opened'
                        throw 'No sessions created'
                    }
                    else {
                        Write-VerboseLog -Message 'PSSessions found open'
                        foreach($sessionInfo in $PSRemotely.sessionHashTable.Values.GetEnumerator()) {
                            Write-VerboseLog -Message "Checking and Reconnecting if needed for $($sessionInfo.Session.ComputerName)"
                            CheckAndReConnect -sessionInfo $sessionInfo
                            if(TestRemotelyNodeBootStrapped -SessionInfo $sessionInfo) {
                                # In memory Node map, has the node marked as bootstrapped
                                Write-VerboseLog -Message "$($sessionInfo.Session.Computername) is bootstrapped."
                                # archive the existing tests files on the PSRemotely node
                                Write-VerboseLog -Message "Cleaning up $($PSRemotely.PSRemotelyNodePath) on Node -> $($sessionInfo.Session.ComputerName)"
                                CleanupPSRemotelyNodePath -Session $sessionInfo.session -PSRemotelyNodePath $PSRemotely.PSRemotelyNodePath
                            }
                            else {
                                # run the bootstrap function
                                Write-VerboseLog -Message "$($sessioninfo.Session.ComputerName) is NOT bootstrapped. Trying now."
                                BootstrapRemotelyNode -Session $sessionInfo.Session -FullyQualifiedName $PSRemotely.modulesRequired -PSRemotelyNodePath $PSRemotely.PSRemotelyNodePath
                            }
                        }
                    }
                }
            }
        } #end Try
        CATCH {
            Write-VerboseLog -ErrorInfo $PSitem
            $PSCmdlet.ThrowTerminatingError($PSitem)
        }
    } #end Begin
    PROCESS {
        
    }
    END {
        Write-VerboseLog -Message 'Invoking the body of PSRemotely'
        
        & $Body # invoke the body
        Write-VerboseLog -Message 'Clearing the AllNodes global variable'
        Remove-Variable -Name AllNodes -Scope Script -Force -ErrorAction SilentlyContinue
    
    }

}