PSMPSession.psm1

# .ExternalHelp PSMPSession-help.xml
function New-PSMPSession {
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        # Vault Logon Username
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $VaultUser,

        # Target Account Username
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $TargetAccount,

        # Domain of the Target Account
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $TargetDomain,

        # Target to connect to
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $TargetAddress,

        # PSMP to connect through
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias('PSMPAddress')]
        [string]
        $TargetMachine,

        # Target ssh port
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [ValidateRange(1, 65535)]
        [int]
        $TargetPort,

        # Tunnel target port
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [ValidateRange(1, 65535)]
        [int]
        $TunnelPort,

        # Arguments passed to ssh before the connection string
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [string[]]
        $SSHArgument,

        # Command to run on the target
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $Command,

        # Additional Delimiter, default "%"
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [string]
        $AdditionalDelimiter,

        # Optional Delimiter, default "#"
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [string]
        $TargetAddressPortDelimiter
    )

    begin {

        if ($PSBoundParameters.ContainsKey('AdditionalDelimiter')) {
            $Delimiter = $AdditionalDelimiter
        } else { $Delimiter = '%' }
        if ($PSBoundParameters.ContainsKey('TargetAddressPortDelimiter')) {
            $OptionalDelimiter = $TargetAddressPortDelimiter
        } else { $OptionalDelimiter = '#' }

    }

    process {

        if (($TargetAccount -like '*@*') -and (-not $TargetDomain)) {
            throw 'TargetDomain must be provided when TargetAccount is in UserPrincipalName format.'
        }

        if ($TunnelPort -and (-not $TargetPort)) {
            throw 'TargetPort must be provided when TunnelPort is specified.'
        }

        if ($TargetDomain) {
            #Target UPN
            #Domain Account Object
            $Account = "$TargetAccount$OptionalDelimiter$TargetDomain"
        } else {
            $Account = $TargetAccount
        }

        $Address = $TargetAddress
        if ($TargetPort) {
            $Address = "$Address$OptionalDelimiter$TargetPort"
        }
        if ($TunnelPort) {
            $Address = "$Address$OptionalDelimiter$TunnelPort"
        }

        if (($VaultUser -like '*@*') -or ($TargetAccount -like '*@*')) {
            #Vault UPN: Local Account Object
            #Vault UPN: Domain Account Object
            #Vault User: Target UPN
            #Vault UPN: Target UPN
            $ConnectionString = "$VaultUser$Delimiter$Account$Delimiter$Address@$TargetMachine"
        } Else {
            #Local Account Object
            #Domain Account Object
            $ConnectionString = "$VaultUser@$Account@$Address@$TargetMachine"
        }

        Write-Debug $ConnectionString

        $SSHArgs = @($SSHArgument | Where-Object { $_ }) + $ConnectionString
        if ($Command) {
            $SSHArgs += $Command
        }

        if ($PSCmdlet.ShouldProcess($ConnectionString, 'Connect SSH')) {

            #Invoke SSH client connection with PSMP formatted connection string
            ssh @SSHArgs

        }

    }

}

# .ExternalHelp PSMPSession-help.xml
function New-SIASession {
    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'ZSP')]
    param (
        # Identity Username
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $User,

        # Tenant Subdomain
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $Subdomain,

        # Target to connect to
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $TargetAddress,

        # Target Account Username
        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'Vaulted',
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $TargetAccount,

        # Domain of the Target Account
        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'Vaulted',
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $TargetDomain,

        # Target ssh port
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [ValidateRange(1, 65535)]
        [int]
        $TargetPort,

        # Connector pool network name
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $NetworkName,

        # SIA SSH gateway
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $Gateway,

        # Arguments passed to ssh before the connection string
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [string[]]
        $SSHArgument,

        # Command to run on the target
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $Command
    )

    process {

        $ConnectionString = "$User#$Subdomain"

        if ($PSCmdlet.ParameterSetName -eq 'Vaulted') {
            if ($TargetDomain) {
                #Vaulted: Domain Account
                $ConnectionString = "$ConnectionString@$TargetAccount#$TargetDomain"
            } else {
                #Vaulted: Local Account
                $ConnectionString = "$ConnectionString@$TargetAccount"
            }
        }

        $ConnectionString = "$ConnectionString@$TargetAddress"
        if ($TargetPort) {
            $ConnectionString = "$ConnectionString`:$TargetPort"
        }
        if ($NetworkName) {
            $ConnectionString = "$ConnectionString#$NetworkName"
        }

        if ($Gateway) {
            $SSHGateway = $Gateway
        } else { $SSHGateway = "$Subdomain.ssh.cyberark.cloud" }

        $ConnectionString = "$ConnectionString@$SSHGateway"

        Write-Debug $ConnectionString

        $SSHArgs = @($SSHArgument | Where-Object { $_ }) + $ConnectionString
        if ($Command) {
            $SSHArgs += $Command
        }

        if ($PSCmdlet.ShouldProcess($ConnectionString, 'Connect SSH')) {

            #Invoke SSH client connection with SIA formatted connection string
            ssh @SSHArgs

        }

    }

}

# .ExternalHelp PSMPSession-help.xml
function Save-SIASSHKey {
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([System.IO.FileInfo])]
    param (
        # Identity Username
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $User,

        # Tenant Subdomain
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $Subdomain,

        # Key file path
        [Parameter(
            Mandatory = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $Path,

        # Key format
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [ValidateSet('OpenSSH', 'PPK')]
        [string]
        $Format = 'OpenSSH',

        # SIA SSH gateway
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $Gateway,

        # Arguments passed to sftp before the connection string
        [Parameter(
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $false
        )]
        [string[]]
        $SFTPArgument
    )

    process {

        if ($Format -eq 'PPK') {
            $KeyType = 'key_ppk'
        } else { $KeyType = 'key' }

        if ($Gateway) {
            $SSHGateway = $Gateway
        } else { $SSHGateway = "$Subdomain.ssh.cyberark.cloud" }

        $ConnectionString = "$User#$Subdomain@$KeyType@$SSHGateway`:/key"

        $Destination = $PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
        if (Test-Path -Path $Destination -PathType Container) {
            $Destination = Join-Path -Path $Destination -ChildPath 'key'
        }

        Write-Debug "$ConnectionString $Destination"

        $SFTPArgs = @($SFTPArgument | Where-Object { $_ }) + $ConnectionString + $Destination

        if ($PSCmdlet.ShouldProcess($Destination, "Save SIA SSH key from $SSHGateway")) {

            #Invoke SFTP client with SIA formatted key retrieval string
            sftp @SFTPArgs

            if (-not (Test-Path -Path $Destination -PathType Leaf)) {
                throw "SSH key was not saved to $Destination"
            }

            #Restrict key file access to the current user
            if (($PSVersionTable.PSEdition -eq 'Desktop') -or $IsWindows) {
                $Acl = New-Object -TypeName System.Security.AccessControl.FileSecurity
                $Acl.SetAccessRuleProtection($true, $false)
                $Rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList @(
                    [System.Security.Principal.WindowsIdentity]::GetCurrent().User,
                    'FullControl',
                    'Allow'
                )
                $Acl.AddAccessRule($Rule)
                Set-Acl -Path $Destination -AclObject $Acl
            } else {
                chmod 600 $Destination
            }

            Get-Item -Path $Destination

        }

    }

}