ModuleRoot.psm1

# srcFile: /drone/src/src/Store/Get-CredentialStore.ps1
function Get-CredentialStore {
    <#
    .SYNOPSIS
        Reads the complete content of the credential store and returns it as a new object.
 
    .DESCRIPTION
        The content is in a raw format. It means there is no transformation to the different credential types.
        You can not use the object properties to connect with remote host. Therefore please use
        Get-CredentialStoreItem.
 
    .PARAMETER Path
        Define a custom path to a shared CredentialStore.
 
    .PARAMETER Shared
        Switch to shared mode with this param. This enforces the command to work with a shared CredentialStore which
        can be decrypted across systems.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [PSObject] Returns the credential store content as PSObject.
 
    .EXAMPLE
        $CSContent = Get-CredentialStore -Path "C:\TMP\mystore.json"
    #>


    [CmdletBinding(DefaultParameterSetName = 'Private')]
    [OutputType('PSCredentialStore.Store')]
    param (
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]

        [string]$Path,

        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [switch]$Shared
    )

    begin {}

    process {
        # Set the CredentialStore for private, shared or custom mode.
        Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
        if ($PSCmdlet.ParameterSetName -eq 'Private') {
            $Path = Get-DefaultCredentialStorePath
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
            if (!($PSBoundParameters.ContainsKey('Path'))) {
                $Path = Get-DefaultCredentialStorePath -Shared
            }
        }

        if (Test-CredentialStore -Path $Path -Shared) {
            try {
                $FileContent = Get-Content -Path $Path -Raw
                $CS = ConvertFrom-Json $FileContent
                $CS.PSObject.TypeNames.Insert(0, 'PSCredentialStore.Store')
                Write-Output $CS
            }
            catch [System.Exception] {
                $MessageParams = @{
                    Message     = 'Unknown CredentialStore format. Invalid JSON file.'
                    ErrorAction = 'Stop'
                }
                Write-Error @MessageParams
            }
        }
        else {
            $MessageParams = @{
                Message     = 'Could not find the CredentialStore.'
                ErrorAction = 'Stop'
            }
            Write-Error @MessageParams
        }
    }

    end {}

}


# srcFile: /drone/src/src/Store/New-CredentialStore.ps1
function New-CredentialStore {
    <#
    .SYNOPSIS
        Creates a new credential store File
 
    .DESCRIPTION
        You need to run this script first to create a new credential store before you try to
        save new credentials with New-CredentialStoreItem.
 
    .PARAMETER Path
        Define a location for the new shared CredentialStore. The default store will be created in
        $Env:ProgramData\PSCredentialStore dir.
 
    .PARAMETER Shared
        Creates a CredentialStore in the Shared mode. This enables you to read the CredentialStore Items on
        different systems or profiles. In addition you can optionally provide a custom path wit the -Path parameter.
 
    .PARAMETER Force
        Use this switch to reset an existing store. The complete content will be wiped.
 
    .PARAMETER SkipPFXCertCreation
        You can skip the pfx certificate creation process. This makes sense if you have a previously created cert
        or want to import a cert in cross-platform environments.
 
    .Parameter UseCertStore
        Instead of using a plain pfx file beside your CredentialStore file you can import it into the user or
        machine certificate store. In this case the system itself secures the cert and you don't hat to set custom
        NTFS permissions so secure your shared certificate.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        ['PSCredentialStore.Store'] Returns the recently created CredentialStore object if the -PassThru parameter
        was given.
 
    .EXAMPLE
        New-CredentialStore
        # Creates a new private CredentialStore
 
    .EXAMPLE
        New-CredentialStore -Force
        # Resets an existing private CredentialStore
 
    .EXAMPLE
        New-CredentialStore -Shared
        # Creates a new shared CredentialStore
 
    .EXAMPLE
        New-CredentialStore -Shared -Path "C:\TMP\CredentialStore.json"
        # Creates a new shared CredentialStore in the given location.
    #>


    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Private')]
    [OutputType('PSCredentialStore.Store')]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [switch]$Shared,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [ValidateNotNullOrEmpty()]
        [System.IO.FileInfo]$Path,

        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [switch]$Force,

        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [switch]$PassThru,

        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [switch]$SkipPFXCertCreation,

        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [switch]$UseCertStore
    )

    begin {
        # Lets get the current Date in a human readable format.
        $CurrentDate = Get-Date -Format 'u'

        # test if the path input is a valid file path
        if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Path')) {
            if ($Path.Attributes -contains 'Directory') {
                $ErrorParams = @{
                    ErrorAction = 'Stop'
                    Exception   = [System.IO.InvalidDataException]::new(
                        'Please provide a full path containing the ' +
                        'credential store file name with the .json extension!'
                    )
                }
                Write-Error @ErrorParams
            }
            elseif ( ($null -eq $Path.Extension) -or ($Path.Extension -ne '.json')) {
                $ErrorParams = @{
                    ErrorAction = 'Stop'
                    Exception   = [System.IO.InvalidDataException]::new(
                        'Your provided path does not contain the required file extension .json!'
                    )
                }
                Write-Error @ErrorParams
            }
        }
    }

    process {
        # Set the CredentialStore for private, shared or custom mode.
        Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
        if ($PSCmdlet.ParameterSetName -eq 'Private') {
            $Path = Get-DefaultCredentialStorePath
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
            if (!($PSBoundParameters.ContainsKey('Path'))) {
                $Path = Get-DefaultCredentialStorePath -Shared
            }
        }

        # Test if in the CredentialStore already exists.
        Write-Verbose 'Test if there is already a credential store.'
        if ((Test-Path -Path $Path) -and ($Force -ne $true)) {
            $ErrorParams = @{
                ErrorAction = 'Stop'
                Exception   = [System.InvalidOperationException]::new(
                    'The given file already exists. Use the -Force switch to override the existing store.'
                )
            }
            Write-Error @ErrorParams
        }

        if (! $SkipPFXCertCreation.IsPresent) {
            $CRTParams = @{
                Country                = 'DE'
                State                  = 'PSCredentialStore'
                City                   = 'PSCredentialStore'
                Organization           = 'PSCredentialStore'
                OrganizationalUnitName = $PSCmdlet.ParameterSetName
                CommonName             = 'PSCredentialStore'
            }
            $CRTAttribute = New-CSCertAttribute @CRTParams

            # If we are working with a ne shared store we have to create the location first.
            # Otherwise openssl fails with unknown path

            $StoreHome = Split-Path -Path $Path -Parent
            if (! (Test-Path -Path $StoreHome)) {
                New-Item -ItemType Directory -Path $StoreHome -ErrorAction Stop
            }

            $PfxParams = @{
                CRTAttribute = $CRTAttribute
                KeyName      = Join-Path -Path $StoreHome -ChildPath 'private.key'
                CertName     = Join-Path -Path $StoreHome -ChildPath 'PSCredentialStore.pfx'
                ErrorAction  = 'Stop'
                Confirm      = $false
            }

            # test if there is already a cert
            if ((Test-Path $PfxParams.CertName) -and (! $Force.IsPresent)) {
                $ErrorParams = @{
                    Exception   = [System.IO.InvalidDataException]::new(
                        'There is already a PfxCertificate for a private CredentialStore!'
                    )
                    ErrorAction = 'Stop'
                }
                Write-Error @ErrorParams
            }

            try {
                New-CSCertificate @PfxParams
            }
            catch {
                $_.Exception.Message | Write-Error
                $ErrorParams = @{
                    ErrorAction = 'Stop'
                    Exception   = [System.Exception]::new(
                        'Could not create the private PfXCertificate!'
                    )
                }
                Write-Error @ErrorParams
            }

            try {
                $FreshCert = Get-PfxCertificate -FilePath $PfxParams.CertName -ErrorAction Stop
            }
            catch [System.Management.Automation.ItemNotFoundException] {
                $_.Exception.Message | Write-Error
                Write-Error -Message 'Could not read the new PfxCertificate.' -ErrorAction Stop
            }
        }

        # We need to use the IDictionary to keep the property sorting in the object.
        $ObjProperties = [ordered]@{
            PSTypeName     = 'PSCredentialStore.Store'
            Version        = $CSVersion
            Created        = $CurrentDate
            PfxCertificate = $null
            Thumbprint     = $null
            Type           = $null
        }

        if ($PSCmdlet.ParameterSetName -eq 'Shared') {
            $ObjProperties.Type = 'Shared'
        }
        else {
            $ObjProperties.Type = 'Private'
        }

        if (! $SkipPFXCertCreation.IsPresent) {
            $ObjProperties.Thumbprint = $FreshCert.Thumbprint

            if ($UseCertStore.IsPresent) {
                Write-Verbose 'Importing new PFX certificate file...'
                Import-CSCertificate -Type $ObjProperties.Type -Path $PfxParams.CertName
            }
            else {
                $ObjProperties.PfxCertificate = $PfxParams.CertName

            }
        }


        $CredentialStoreObj = [PSCustomObject]$ObjProperties
        try {
            $JSON = ConvertTo-Json -InputObject $CredentialStoreObj -ErrorAction Stop
            $JSON | Out-File -FilePath $Path -ErrorAction Stop -Force
        }
        catch {
            $_.Exception.Message | Write-Error
            $ErrorParams = @{
                ErrorAction = 'Stop'
                Exception   = [System.IO.IOException]::new(
                    'Unable to convert or write the CredentialStore'
                )
            }
            Write-Error @ErrorParams
        }

        if ($PassThru.IsPresent) {
            return $CredentialStoreObj
        }
    }

    end {}
}


# srcFile: /drone/src/src/Store/Test-CredentialStore.ps1
function Test-CredentialStore {
    <#
    .SYNOPSIS
        Returns the credential store state.
 
    .DESCRIPTION
        Use this script to test your credential store. For now it only checks if
        the file exists.
 
    .PARAMETER Path
        Define a custom path to a shared CredentialStore.
 
    .PARAMETER Shared
        Switch to shared mode with this param. This enforces the command to work with a shared CredentialStore which
        can be decrypted across systems.
 
    .EXAMPLE
        Test-CredentialStore -eq $true
    #>


    [CmdletBinding(DefaultParameterSetName = 'Private')]
    [OutputType([bool])]
    param (
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [string]$Path,

        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [switch]$Shared
    )

    begin {}

    process {
        # Set the CredentialStore for private, shared or custom mode.
        Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
        if ($PSCmdlet.ParameterSetName -eq 'Private') {
            $Path = Get-DefaultCredentialStorePath
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
            if (!($PSBoundParameters.ContainsKey('Path'))) {
                $Path = Get-DefaultCredentialStorePath -Shared
            }
        }
        Write-Verbose -Message ("Path is: {0}" -f $Path)

        if (Test-Path $Path) {
            Write-Verbose 'CredentialStore in given path found.'
            Write-Output $true
        }
        else {
            Write-Verbose 'The given CredentialStore does not exist!'
            Write-Output $false
        }
    }

    end {}

}


# srcFile: /drone/src/src/Certificate/Get-CSCertificate.ps1
function Get-CSCertificate {
    <#
    .SYNOPSIS
        Returns the current used valid PfX certificate.
 
    .DESCRIPTION
        Use this function to get the available pfx certificate respecting the config hierarchy.
 
    .PARAMETER Type
        Select the current credential store type.
 
    .PARAMETER Thumbprint
        Provide the credentials thumbprint for the search.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [System.Security.Cryptography.X509Certificates.X509Certificate2]
 
    .EXAMPLE
        Get-CSCertificate -Type 'Shared' -Thumbprint '12334456'
    #>


    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.X509Certificates.X509Certificate2])]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Private', 'Shared')]
        [string]$Type,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Thumbprint
    )

    begin {}

    process {
        if ($Type -eq 'Private') {
            Get-CSPfXCertificate -Thumbprint $Thumbprint -StoreName 'My' -StoreLocation 'CurrentUser'
        }
        elseif ($Type -eq 'Shared') {
            if ( $isLinux) {
                $cert = Get-CSPfxCertificate -Thumbprint $Thumbprint -StoreName 'My' -StoreLocation 'CurrentUser'
                if ($null -eq $cert) {
                    Get-CSPfxCertificate -Thumbprint $Thumbprint -StoreName 'Root' -StoreLocation 'LocalMachine'
                }
                else {
                    Write-Output $cert
                }
            }
            elseif ( (! $isLinux) -or ($isWindows) ) {
                $cert = Get-CSPfxCertificate -Thumbprint $Thumbprint -StoreName 'My' -StoreLocation 'LocalMachine'
                if ($null -eq $cert) {
                    Get-CSPfxCertificate -Thumbprint $Thumbprint -StoreName 'Root' -StoreLocation 'LocalMachine'
                }
                else {
                    Write-Output $cert
                }
            }
        }
    }
    end {}
}


# srcFile: /drone/src/src/Certificate/Import-CSCertificate.ps1
function Import-CSCertificate {
    <#
    .SYNOPSIS
        Imports a linked certificate to the valid store location.
 
    .DESCRIPTION
        Import-CSCertificate takes a pfx certificate file and imports it to the supposed certificate store for
        private and shared credential stores.
 
    .PARAMETER Type
        Select between the a private and shared credential store.
 
    .PARAMETER Path
        Provide a valid path to pfx certificate file.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        Import-CSCertificate -Type 'Private' -Path (Join-Path -Path $Env:APPDATA -ChildItem 'PfxCertificate.pfx')
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Private', 'Shared')]
        [string]$Type,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.IO.FileInfo]$Path

    )
    begin {
        if (! (Test-Path -Path $Path)) {
            $ErrorParams = @{
                ErrorAction = 'Stop'
                Exception   = [System.Exception]::new(
                    ('File {0} not found!') -f $Path
                )
            }
            Write-Error @ErrorParams
        }
    }

    process {
        # Import to CurrentUser\My store for windows and linux
        if ($Type -eq 'Private') {
            Import-CSPfxCertificate -Path $Path -StoreName 'My' -StoreLocation 'CurrentUser' -OpenFlags 'ReadWrite'
        }
        elseif ( (! $isLinux ) -and ($Type -eq 'Shared') ) {
            Import-CSPfxCertificate -Path $Path -StoreName 'My' -StoreLocation 'LocalMachine' -OpenFlags 'ReadWrite'
        }
        elseif ( ($isLinux) -and ($Type -eq 'Shared') ) {
            Import-CSPfxCertificate -Path $Path -StoreName 'My' -StoreLocation 'CurrentUser' -OpenFlags 'ReadWrite'
        }
    }
    end {
    }
}


# srcFile: /drone/src/src/Certificate/New-CSCertAttribute.ps1
function New-CSCertAttribute {
    <#
    .SYNOPSIS
        Creates required data for a certificate signing request.
 
    .DESCRIPTION
        Defines the certificate related properties for an upcoming New-PfxCertificate execution.
 
    .PARAMETER Country
        County code like EN, DE, IT, FR...
 
    .PARAMETER State
        Certificate state value.
 
    .PARAMETER City
        Certificate city value.
 
    .PARAMETER Organization
        Certificate organization value.
 
    .PARAMETER OrganizationalUnitName
        Certificate OrganizationalUnitName value.
 
    .PARAMETER CommonName
        The certificate common name.
 
    .PARAMETER Days
        The validation time itself.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [PSCredentialStore.Certificate.CSRDetails]
 
    .EXAMPLE
        $AttribParams = @{
            Country = 'DE'
            State = 'BW'
            City = 'Karlsruhe'
            Organization ='AwesomeIT'
            OrganizationalUnitName ='PSCredentialStore'
            CommonName ='MyPrivateCert'
        }
        New-CSCertAttribute @AttribParams
    #>


    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions',
        '',
        Justification = 'Returns a new object and does not change data'
    )]
    [OutputType('PSCredentialStore.Certificate.Attribute')]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateLength(2, 2)]
        [ValidateNotNull()]
        [string]$Country,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [string]$State,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [string]$City,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [string]$Organization,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [string]$OrganizationalUnitName,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [string]$CommonName,

        [Parameter(Mandatory = $false)]
        [ValidateNotNull()]
        [int]$Days = 365
    )
    begin {}

    process {
        return [PSCustomObject]@{
            PSTypeName = 'PSCredentialStore.Certificate.Attribute'
            Subject    = [PSCustomObject]@{
                PSTypeName             = 'PSCredentialStore.Certificate.Attribute.Subject'
                Country                = $Country
                State                  = $State
                City                   = $City
                Organization           = $Organization
                OrganizationalUnitName = $OrganizationalUnitName
                CommonName             = $CommonName
            }
            Days       = $Days
        }
    }
    end {}
}


# srcFile: /drone/src/src/Certificate/New-CSCertificate.ps1
function New-CSCertificate {
    <#
    .SYNOPSIS
        Creates a new PFX certificate for the CredentialStore encryption.
 
    .DESCRIPTION
        Use this function to create a custom self signed certificate used by the PSCredentialStore module.
 
    .PARAMETER CRTAttribute
        Provide certificate related attributes provided by function New-CRTAttribute.
 
    .PARAMETER KeyName
        Provide a custom full path and name for the private key. The file extension has to be `*.key`.
 
    .PARAMETER CertName
        Provide a custom full path and name for the PFX certificate file. The file extension has to be `*.pfx`
 
    .INPUTS
        [PSCredentialStore.Certificate.Attribute]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        New-CSCertificate -CRTAttribute $CRTAttribute -KeyName './myprivate.key' -CertName './mycert.pfx'
    #>


    [CmdletBinding(SupportsShouldProcess = $true)]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSAvoidUsingInvokeExpression',
        '',
        Justification = 'needed for openssl wrapping'
    )]
    [OutputType()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [PSTypeName('PSCredentialStore.Certificate.Attribute')]$CRTAttribute,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$KeyName = './private.key',

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$CertName = './certificate.pfx'
    )

    begin {
        $ModuleBase = Get-ModuleBase
        if ($isLinux -or $isMacOS) {
            try {
                $openssl = Get-Command -Name 'openssl' -ErrorAction Stop
            }
            catch {
                $_.Exception.Message | Write-Error
                $ErrorParams = @{
                    Message     = 'Can not find the openssl binary!'
                    ErrorAction = 'Stop'
                    Exception   = [System.IO.FileNotFoundException]::new()
                }
                Write-Error @ErrorParams
            }
        }
        elseif (
            ($PSVersionTable.PSEdition -eq 'Desktop' -and $PSVersionTable.PSVersion.Major -lt 6) -or
            ($IsWindows -eq $true)
        ) {
            $openssl = Join-Path -Path $ModuleBase -ChildPath '/Vendor/libressl/openssl.exe'
        }

        $Env:OPENSSL_CONF = Join-Path $ModuleBase -ChildPath '/openssl.conf'
    }

    process {
        $SubjPattern = "/C={0}/ST={1}/L={2}/O={3}/OU={4}/CN={5}"
        $SubjValues = @(
            $CRTAttribute.Subject.Country,
            $CRTAttribute.Subject.State,
            $CRTAttribute.Subject.City,
            $CRTAttribute.Subject.Organization,
            $CRTAttribute.Subject.OrganizationalUnitName,
            $CRTAttribute.Subject.CommonName
        )
        $Subj = $SubjPattern -f $SubjValues

        $PEMCertName = $CertName -replace '.pfx', '.crt'
        $ExpPattern = (
            '& ''{0}'' req -x509 -sha256 -nodes -days {1} -newkey rsa:2048 -keyout {2} -out {3} -subj "{4}" *>$null'
        )
        $ExpValues = @(
            $openssl,
            $CRTAttribute.Days
            $KeyName,
            $PEMCertName,
            $Subj
        )
        $PEMExp = $ExpPattern -f $ExpValues

        Write-Verbose -Message ( 'Expr string is: {0}' -f $PEMExp)

        # Edit the Error action for the openSLL command to make the redirect *>$null work.
        # There is always a stderr and stdout stream!
        $EAP = $ErrorActionPreference
        $ErrorActionPreference = 'Continue'
        Invoke-Expression -Command $PEMExp
        $ErrorActionPreference = $EAP

        # manually testing the openssl command results

        if (! (Test-Path -Path $KeyName)) {
            $ErrorParams = @{
                Message     = 'Could not create the private key ${0}' -f $KeyName
                ErrorAction = 'Stop'
                Exception   = [System.UnauthorizedAccessException]::new()
            }
            Write-Error @ErrorParams
        }
        if (! (Test-Path -Path $PEMCertName)) {
            $ErrorParams = @{
                Message     = 'Could not create the PEM certificate ${0}' -f $PEMCertName
                ErrorAction = 'Stop'
                Exception   = [System.Exception]::new()
            }
            Write-Error @ErrorParams
        }

        $PfxPattern = '& ''{0}'' pkcs12 -export -out {1} -inkey {2} -in {3} -passout pass:'
        $PfxValues = @(
            $openssl,
            $CertName,
            $KeyName,
            ($CertName -replace '.pfx', '.crt')
        )
        $PfxExp = $PfxPattern -f $PfxValues
        Write-Verbose -Message ( 'PfxExp string is: {0}' -f $PfxExp)
        Invoke-Expression -Command $PfxExp

        # Remove private key and crt file. Always ask user
        Remove-Item -Path $KeyName
        Remove-Item -Path ($CertName -replace '.pfx', '.crt')
    }
    end {
        Remove-Item Env:\OPENSSL_CONF -Confirm:$False -Force -ErrorAction SilentlyContinue
    }
}


# srcFile: /drone/src/src/Certificate/Test-CSCertificate.ps1
function Test-CSCertificate {
    <#
    .SYNOPSIS
        Tests if the linked certificate is store ein the specified cert stores.
 
    .DESCRIPTION
        Test-CSCertificate should be an easy high level test for the linked certificate.
 
    .PARAMETER Type
        Select between 'Private' or 'Shared'.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [bool]
 
    .EXAMPLE
        Test-CSCertificate -Type 'Shared'
    #>


    [CmdletBinding()]
    [OutputType([bool])]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Private', 'Shared')]
        [string]$Type
    )
    begin {
        if ($Type -eq 'Private') {
            $CS = Get-CredentialStore
        }
        elseif ($Type -eq 'Shared') {
            $CS = Get-CredentialStore -Shared
        }
        if ($null -ne $CS.PfxCertificate) {
            Write-Warning -Message (
                'There is a Pfx certificate file linked in the store. ' +
                'Certificates saved in the Cert store will be ignored!'
            )
        }

    }
    process {
        if ($Type -eq 'Private') {
            $cert = Get-CSPfXCertificate -Thumbprint $CS.Thumbprint -StoreName 'My' -StoreLocation 'CurrentUser'
        }
        elseif ($Type -eq 'Shared') {
            if ( $isLinux) {
                $cert = Get-CSPfxCertificate -Thumbprint $CS.Thumbprint -StoreName 'My' -StoreLocation 'CurrentUser'
                if ($null -eq $cert) {
                    $PFXParams = @{
                        Thumbprint    = $CS.Thumbprint
                        StoreName     = 'Root'
                        StoreLocation = 'LocalMachine'
                    }
                    $cert = Get-CSPfxCertificate @PFXParams
                }
            }
            elseif ( (! $isLinux) -or ($isWindows) ) {
                $PFXParams = @{
                    Thumbprint    = $CS.Thumbprint
                    StoreName     = 'My'
                    StoreLocation = 'LocalMachine'
                }
                $cert = Get-CSPfxCertificate @PFXParams
                if ($null -eq $cert) {
                    $PFXParams = @{
                        Thumbprint    = $CS.Thumbprint
                        StoreName     = 'Root'
                        StoreLocation = 'LocalMachine'
                    }
                    $cert = Get-CSPfxCertificate @PFXParams
                }
            }
        }
        if ($null -eq $cert) {
            return $false
        }
        else {
            return $true
        }
    }
    end {
    }
}


# srcFile: /drone/src/src/Certificate/Use-CSCertificate.ps1
function Use-CSCertificate {
    <#
    .SYNOPSIS
        Links an existing PFX Certificate to a CredentialStore.
 
    .DESCRIPTION
        Linking a certificate is needed if you plan to use the same CredentialStore in cross platform scenarios.
 
    .PARAMETER Path
        Specify the path to the PFX Certificate you want to link for usage.
 
    .PARAMETER CredentialStore
        Specify a custom path for a shared credential store.
 
    .PARAMETER Shared
        Use the credential store in shared mode.
 
    .PARAMETER UseCertStore
        Use the given certificate and import it into the corresponding certificate store.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        Use-CSCertificate -Path 'C:\cert.pfx'
    #>


    [CmdletBinding(DefaultParameterSetName = 'Private')]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [ValidateNotNullOrEmpty()]
        [string]$CredentialStore,

        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [switch]$Shared,

        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [switch]$UseCertStore
    )
    begin {}

    process {
        try {
            # We need to resolve the path to make sure it has the correct platform specific syntax.
            # And it should also exist.
            $validPath = Resolve-Path -Path $Path -ErrorAction Stop
            $PfxCertificate = Get-PfxCertificate -FilePath $validPath -ErrorAction Stop
        }
        catch {
            $_.Exception.Error | Write-Error
            $ErrorParams = @{
                Message     = 'The given PFX certificate does not exist!'
                ErrorAction = 'Stop'
            }
            Write-Error @ErrorParams
        }

        try {
            if ($PSCmdlet.ParameterSetName -eq 'Private') {
                $StorePath = Get-DefaultCredentialStorePath
                $CS = Get-CredentialStore
            }
            elseif ($PSCmdlet.ParameterSetName -eq 'Shared' ) {
                if (!($PSBoundParameters.ContainsKey('CredentialStore'))) {
                    $StorePath = Get-DefaultCredentialStorePath -Shared
                    $CS = Get-CredentialStore -Shared
                }
                else {
                    $StorePath = $CredentialStore
                    $CS = Get-CredentialStore -Shared -Path $CredentialStore
                }
            }
        }
        catch {
            $_.Exception.Error | Write-Error
            $ErrorParams = @{
                Message     = 'The given CredentialStore does not exist!'
                ErrorAction = 'Stop'
            }
            Write-Error @ErrorParams
        }

        # Lets first check if the thumbprint matches
        if (($CS.Thumbprint -notmatch $PfxCertificate.Thumbprint) -and ($CS.Thumbprint.Length -ne 0)) {
            Write-Warning @"
You are trying to map an unknown certificate.
Make sure you used the same AES keys for encrypting!
"@

        }

        if ($UseCertStore) {
            Import-CSCertificate -Type $PSCmdlet.ParameterSetName -Path $Path
            $CS.Thumbprint = $PfxCertificate.Thumbprint
            $CS.PfxCertificate = $null
        }
        else {
            $CS.PfxCertificate = $validPath.Path
        }
        $CS | ConvertTo-Json -Depth 5 | Out-File -FilePath $StorePath -Force -Encoding utf8
    }

    end {}
}


# srcFile: /drone/src/src/Item/Get-CredentialStoreItem.ps1
function Get-CredentialStoreItem {
    <#
    .SYNOPSIS
        Returns the Credential from a given remote host item.
 
    .DESCRIPTION
        Return the credential as PSCredential object.
 
 
    .PARAMETER RemoteHost
        Specify the host, for which you would like to change the credentials.
 
    .PARAMETER Identifier
            Provide a custom identifier to the given remote host key. This enables you to store multiple credentials
            for a single remote host entry. For example ad/sys1, ftp/sys1, mssql/sys1
 
    .PARAMETER Path
        Define a custom path to a shared CredentialStore.
 
    .PARAMETER Shared
        Switch to shared mode with this param. This enforces the command to work with a shared CredentialStore which
        can be decrypted across systems.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [System.Management.Automation.PSCredential]
 
    .EXAMPLE
        $myCreds = Get-CredentialStoreItem -Path "C:\TMP\mystore.json" -RemoteHost "esx01.myside.local"
    #>


    [CmdletBinding(DefaultParameterSetName = 'Private')]
    [OutputType([PSCredential])]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Private')]
        [ValidateNotNullOrEmpty()]
        [string]$RemoteHost,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [ValidateNotNullOrEmpty()]
        [string]$Identifier,

        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [switch]$Shared,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [ValidateNotNullOrEmpty()]
        [string]$Path
    )

    begin {
        # Set the CredentialStore for private, shared or custom mode.
        Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
        if ($PSCmdlet.ParameterSetName -eq 'Private') {
            $Path = Get-DefaultCredentialStorePath
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
            if (!($PSBoundParameters.ContainsKey('Path'))) {
                $Path = Get-DefaultCredentialStorePath -Shared
            }
        }
    }

    process {
        if ($Identifier -ne "") {
            $CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
        }
        else {
            $CredentialName = $RemoteHost
        }

        if (Test-CredentialStore -Shared -Path $Path) {
            $CS = Get-CredentialStore -Shared -Path $Path
            $CSMembers = Get-Member -InputObject $CS
            # Let's first check if the given remote host exists as object property
            if (($CSMembers.MemberType -eq 'NoteProperty') -and ($CSMembers.Name -contains $CredentialName)) {
                if ($null -eq $CS.PfxCertificate) {
                    $Cert = Get-CSCertificate -Type $CS.Type -Thumbprint $CS.Thumbprint
                }
                else {
                    $Cert = Get-PfxCertificate -FilePath $CS.PfxCertificate -ErrorAction Stop
                }
                $DecryptedKey = $Cert.PrivateKey.Decrypt(
                    [Convert]::FromBase64String($CS.$CredentialName.EncryptedKey),
                    [System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1
                )

                if (! $ExpandOutput.isPresent) {
                    [PSCredential]::new(
                        $CS.$CredentialName.User,
                        ($CS.$CredentialName.Password | ConvertTo-SecureString -Key $DecryptedKey)
                    )
                }
            }
            else {
                $MsgParams = @{
                    ErrorAction = 'Stop'
                    Message     = 'Could not find credentials for the given remote host: {0}' -f $RemoteHost
                }
                Write-Error @MsgParams
            }
        }
        else {
            $MsgParams = @{
                ErrorAction = 'Stop'
                Message     = 'The given credential store ({0}) does not exist!' -f $Path
            }
            Write-Error @MsgParams
        }
    }

    end {

    }

}


# srcFile: /drone/src/src/Item/New-CredentialStoreItem.ps1
function New-CredentialStoreItem {
    <#
    .SYNOPSIS
        Adds a credential store item containing host, user and password to the given store.
 
    .DESCRIPTION
        The credentials are stored without any relations to it's further use. If you need to change an existing
        item please use Set-CredentialStoreItem. You need to decide afterwards, whether to use the credential for
        a VIConnection, NetApp FAS or UCS Fabric Interconnect.
 
    .PARAMETER Path
        Define the store in which you would like to add a new item.
 
    .PARAMETER RemoteHost
        The identifier or rather name for the given credentials.
 
    .PARAMETER Identifier
        Provide a custom identifier to the given remote host key. This enables you to store multiple credentials
        for a single remote host entry. For example ad/sys1, ftp/sys1, mssql/sys1
 
    .PARAMETER Credential
        You can provide credentials optionally as pre existing pscredential object.
 
    .PARAMETER Shared
        Define the CredentialStore where you want to add the new item. Default is always personal but can be
        changed to shared, or even shared with custom path.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        New-CredentialStoreItem -Path 'C:\TMP\mystore.json' -RemoteHost esx01.myside.local'
    #>


    [CmdletBinding(DefaultParameterSetName = 'Private')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions',
        '',
        Justification = 'Adds data into an existing object/file'
    )]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Private')]
        [ValidateNotNullOrEmpty()]
        [string]$RemoteHost,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [ValidateNotNullOrEmpty()]
        [string]$Identifier,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [PSCredential]$Credential,

        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [switch]$Shared,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [ValidateNotNullOrEmpty()]
        [string]$Path


    )

    begin {
        # Set the CredentialStore for private, shared or custom mode.
        Write-Debug ('ParameterSetName: {0}' -f $PSCmdlet.ParameterSetName)
        if ($PSCmdlet.ParameterSetName -eq 'Private') {
            $Path = Get-DefaultCredentialStorePath
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
            if (!($PSBoundParameters.ContainsKey('Path'))) {
                $Path = Get-DefaultCredentialStorePath -Shared
            }
        }
    }

    process {
        # Lets do a quick test on the given CredentialStore.
        if (-not(Test-CredentialStore -Shared -Path $Path)) {
            $MessageParams = @{
                Exception   = [System.IO.FileNotFoundException]::new(
                    'The given credential store ({0}) does not exist!' -f $Path
                )
                ErrorAction = 'Stop'
            }
            Write-Error @MessageParams
        }

        $CSContent = Get-CredentialStore -Shared -Path $Path

        $CurrentDate = Get-Date -Format 'u'

        if ($Identifier -ne '') {
            $CredentialName = $RemoteHost = '{0}/{1}' -f $Identifier, $RemoteHost
        }
        else {
            $CredentialName = $RemoteHost
        }

        if (-not($Credential)) {
            $Credential = Get-Credential -Message $CredentialName
        }

        if ($Credential.UserName) {
            if ($null -eq $CSContent.PfxCertificate) {
                $Cert = Get-CSCertificate -Type $CSContent.Type -Thumbprint $CSContent.Thumbprint
            }
            else {
                $Cert = Get-PfxCertificate -FilePath $CSContent.PfxCertificate -ErrorAction Stop
            }

            if (Get-Member -InputObject $CSContent -Name $CredentialName -MemberType Properties) {
                $MessageParams = @{
                    Message = 'The given host already exists. Nothing to do here.'
                }
                Write-Warning @MessageParams
            }
            else {
                $RSAKey = Get-RandomAESKey

                $CredentialHash = [ordered]@{
                    User         = $Credential.UserName
                    Password     = ConvertFrom-SecureString -SecureString $Credential.Password -Key $RSAKey
                    Created      = $CurrentDate
                    LastChange   = $null
                    EncryptedKey = [Convert]::ToBase64String(
                        $Cert.PublicKey.Key.Encrypt(
                            $RSAKey,
                            [System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1
                        )
                    )
                }
                $MemberParams = @{
                    InputObject = $CSContent
                    Name        = $CredentialName
                    MemberType  = 'NoteProperty'
                    Value       = $CredentialHash
                }
                Add-Member @MemberParams
                try {
                    ConvertTo-Json -InputObject $CSContent | Out-File -FilePath $Path
                }
                catch {
                    $MessageParams = @{
                        Message     = 'Could not add item into credential store!'
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }
            }
        }
        else {
            $MessageParams = @{
                Message     = 'Please Provide at least a valid user!'
                ErrorAction = 'Stop'
            }
            Write-Error @MessageParams
        }
    }

    end {}

}


# srcFile: /drone/src/src/Item/Remove-CredentialStoreItem.ps1
function Remove-CredentialStoreItem {
    <#
    .SYNOPSIS
        Remove the given credentials from the credential store.
 
    .DESCRIPTION
        Use this CMDLet to completely remove an credential store item.
 
    .PARAMETER Path
        Define the store in which your given host entry already exists.
 
    .PARAMETER RemoteHost
        Specify the host you for which you would like to change the credentials.
 
    .PARAMETER Identifier
        Defaults to "". Specify a string, which separates two CredentialStoreItems for the
        same hostname.
 
    .PARAMETER Shared
        Switch to shared mode with this param. This enforces the command to work with a shared CredentialStore which
        can be decrypted across systems.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        Remove-CredentialStoreItem -RemoteHost "esx01.myside.local"
 
    .EXAMPLE
        Remove-CredentialStoreItem -Shared -RemoteHost "esx01.myside.local"
 
    .EXAMPLE
        Remove-CredentialStoreItem -Shared -Path "C:\TMP\mystore.json" -RemoteHost "esx01.myside.local"
 
    .EXAMPLE
        Remove-CredentialStoreItem -RemoteHost "esx01.myside.local" -Identifier svc
    #>


    [CmdletBinding(DefaultParameterSetName = 'Private')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions',
        '',
        Justification = 'Removes data from existing store.'
    )]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [string]$RemoteHost,

        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [string]$Identifier,

        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [switch]$Shared,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [ValidateNotNullOrEmpty()]
        [string]$Path
    )

    begin {
        # Set the CredentialStore for private, shared or custom mode.
        Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
        if ($PSCmdlet.ParameterSetName -eq 'Private') {
            $Path = Get-DefaultCredentialStorePath
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
            if (!($PSBoundParameters.ContainsKey('Path'))) {
                $Path = Get-DefaultCredentialStorePath -Shared
            }
        }
    }

    process {
        # Lets do a quick test on the given CredentialStore.
        if (-not(Test-CredentialStore -Shared -Path $Path)) {
            $MessageParams = @{
                Message     = 'Could not add anything into the given CredentialStore.'
                ErrorAction = 'Stop'
            }
            Write-Error @MessageParams
        }

        # Read the file content based on the given ParameterSetName
        $CSContent = Get-CredentialStore -Shared -Path $Path

        if ($Identifier -ne "") {
            $CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
        }
        else {
            $CredentialName = $RemoteHost
        }

        if (Get-Member -InputObject $CSContent -Name $CredentialName -MemberType NoteProperty) {
            # We need to use the .NET Method because there is no easier way in PowerShell.
            $CSContent.PSObject.Properties.Remove($CredentialName)
            ConvertTo-Json -InputObject $CSContent -Depth 5 | Out-File -FilePath $Path -Encoding utf8
        }
        else {
            $MessageParams = @{
                Message = 'The given CredentialStoreItem does not exist.'
            }
            Write-Warning @MessageParams
        }
    }

    end {

    }

}


# srcFile: /drone/src/src/Item/Set-CredentialStoreItem.ps1
function Set-CredentialStoreItem {
    <#
    .SYNOPSIS
        Changes the credentials for the given remote host in the store.
 
    .DESCRIPTION
        Use this function to update your already stored RemoteHost items.
 
    .PARAMETER Path
        Define the store in which your given host entry already exists.
 
    .PARAMETER RemoteHost
        Specify the host you for which you would like to change the credentials.
 
    .PARAMETER Identifier
        Defaults to "". Specify a string, which separates two CredentialStoreItems for the
        same hostname.
 
    .PARAMETER Shared
        Switch to shared mode with this param. This enforces the command to work with a shared CredentialStore which
        can be decrypted across systems.
 
    .PARAMETER Credential
        Provided the new credentials you want to update inside the RemoteHost item.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        Set-CredentialStoreItem -Path "C:\TMP\mystore.json" -RemoteHost "esx01.myside.local"
 
    .EXAMPLE
        Set-CredentialStoreItem -Path "C:\TMP\mystore.json" -RemoteHost "esx01.myside.local" -Identifier svc
    #>


    [CmdletBinding(DefaultParameterSetName = 'Private')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions',
        '',
        Justification = 'Updates existing credential object.'
    )]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [string]$RemoteHost,

        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [string]$Identifier,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [PSCredential]$Credential,

        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [switch]$Shared,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [ValidateNotNullOrEmpty()]
        [string]$Path
    )

    begin {
        # Set the CredentialStore for private, shared or custom mode.
        Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
        if ($PSCmdlet.ParameterSetName -eq 'Private') {
            $Path = Get-DefaultCredentialStorePath
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
            if (!($PSBoundParameters.ContainsKey('Path'))) {
                $Path = Get-DefaultCredentialStorePath -Shared
            }
        }
    }

    process {
        # Lets do a quick test on the given CredentialStore.
        if (-not(Test-CredentialStore -Shared -Path $Path)) {
            $MessageParams = @{
                Message     = 'Could not add anything into the given CredentailStore.'
                ErrorAction = 'Stop'
            }
            Write-Error @MessageParams
        }

        # Read the file content based on the given ParameterSetName
        $CSContent = Get-CredentialStore -Shared -Path $Path

        $CurrentDate = Get-Date -Format 'u'

        if ($Identifier -ne "") {
            $CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
        }
        else {
            $CredentialName = $RemoteHost
        }

        if (-not($Credential)) {
            $Credential = Get-Credential -Message $CredentialName
        }

        if ($Credential.UserName) {
            if ($null -eq $CSContent.PfxCertificate) {
                $Cert = Get-CSCertificate -Type $CSContent.Type -Thumbprint $CSContent.Thumbprint
            }
            else {
                $Cert = Get-PfxCertificate -FilePath $CSContent.PfxCertificate -ErrorAction Stop
            }

            if (Get-Member -InputObject $CSContent -Name $CredentialName -MemberType Properties) {
                $RSAKey = Get-RandomAESKey
                $CSContent.$CredentialName.User = $Credential.UserName
                $ConvertParams = @{
                    SecureString = $Credential.Password
                    Key          = $RSAKey
                }
                $CSContent.$CredentialName.Password = ConvertFrom-SecureString @ConvertParams
                $CSContent.$CredentialName.LastChange = $CurrentDate
                $CSContent.$CredentialName.EncryptedKey = [Convert]::ToBase64String(
                    $Cert.PublicKey.Key.Encrypt(
                        $RSAKey,
                        [System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1
                    )
                )
                ConvertTo-Json -InputObject $CSContent -Depth 5 | Out-File -FilePath $Path -Encoding utf8
            }
        }
        Else {
            $MessageParams = @{
                Message     = 'Please Provide at least a valid user!'
                ErrorAction = 'Stop'
            }
            Write-Error @MessageParams
        }
    }

    end {

    }
}


# srcFile: /drone/src/src/Item/Test-CredentialStoreItem.ps1
function Test-CredentialStoreItem {
    <#
    .SYNOPSIS
        Checks if the given RemoteHost identifier combination exists in the credential store.
 
    .DESCRIPTION
        Use this cmdlet for basic checks with a single item. Check the item first with this function before
        you try to interact with it.
 
        Breaking Change for `v1.1.0+`:
        Test-CredentialStoreItem will return `$false` even if the store doesn't exist. We removed the terminating
        error and replaced it with a warning message.
 
    .PARAMETER Path
        Define a custom credential store you try to read from. Without the `-Path` parameter
        `Test-CredentialStoreItem` tries to read from the default private store.
 
    .PARAMETER RemoteHost
        Specify the host, for which you would like to change the credentials.
 
    .PARAMETER Identifier
        Adds an optional identifier to the given RemoteHost. Makes it possible to store multiple credentials
        for a single host.
 
    .PARAMETER Shared
        Switch to shared mode with this param. This enforces the command to work with a shared CredentialStore which
        can be decrypted across systems.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        if (Test-CredentialStoreItem -RemoteHost "Default") {
            Get-CredentialStoreItem -RemoteHost "Default"
        }
        else {
            Write-Warning ("The given Remote Host {0} does not exist in the credential Store!" -f $RemoteHost)
        }
    #>


    [CmdletBinding(DefaultParameterSetName = 'Private')]
    [OutputType([bool])]
    param (
        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [string]$Path = "{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$RemoteHost,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$Identifier,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [switch]$Shared
    )

    begin {
        # Set the CredentialStore for private, shared or custom mode.
        Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
        if ($PSCmdlet.ParameterSetName -eq 'Private') {
            $Path = Get-DefaultCredentialStorePath
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
            if (!($PSBoundParameters.ContainsKey('Path'))) {
                $Path = Get-DefaultCredentialStorePath -Shared
            }
        }
    }

    process {
        if ($Identifier -ne "") {
            $CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
        }
        else {
            $CredentialName = $RemoteHost
        }

        if (Test-CredentialStore -Shared -Path $Path) {
            $CS = Get-CredentialStore -Shared -Path $Path
            $CSMembers = Get-Member -InputObject $CS
            if (($CSMembers.MemberType -eq 'NoteProperty') -and ($CSMembers.Name -contains $CredentialName)) {
                return $true
            }
            else {
                return $false
            }
        }
        else {
            $MsgParams = @{
                Message = "The given credential store ({0}) does not exist!" -f $Path
            }
            Write-Warning @MsgParams
            return $false
        }
    }

    end {}

}


# srcFile: /drone/src/src/Private/Get-CSPfxCertificate.ps1
function Get-CSPfxCertificate {
    <#
    .SYNOPSIS
        Returns the certificate object given by thumbprint.
 
    .DESCRIPTION
        You can use this function to get a stored certificate. Search for the object by its unique thumbprint.
 
    .PARAMETER Thumbprint
        Provide one or more thumbprints.
 
    .PARAMETER StoreName
        Select the store name in which you want to search the certificates.
 
    .PARAMETER StoreLocation
        Select between the both available locations CurrentUser odr LocalMachine.
 
    .INPUTS
        [string]
 
    .OUTPUTS
        [System.Security.Cryptography.X509Certificates.X509Certificate2[]]
 
    .EXAMPLE
        Get-CSPfxCertificate -Thumbprint '12345678' -StoreName 'My' -StoreLocation 'CurrentUser'
    #>


    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.X509Certificates.X509Certificate2])]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Thumbprint,

        [Parameter(Mandatory = $false)]
        [ValidateSet(
            'AddressBook',
            'AuthRoot',
            'CertificateAuthority',
            'Disallowed',
            'My',
            'Root',
            'TrustedPeople',
            'TrustedPublisher'
        )]
        [string]$StoreName = 'My',

        [Parameter(Mandatory = $false)]
        [ValidateSet(
            'CurrentUser',
            'LocalMachine'
        )]
        [string]$StoreLocation = 'CurrentUser'
    )

    begin {
        $Store = [System.Security.Cryptography.X509Certificates.X509Store]::New($StoreName, $StoreLocation)
        try {
            $Store.Open('ReadOnly')
        }
        catch {
            $_.Exception.Message | Write-Error -ErrorAction Stop
        }
    }

    process {
        foreach ($Thumb in $Thumbprint) {
            Write-Output $Store.Certificates | Where-Object { $_.Thumbprint -eq $Thumb }
        }
    }
    end {
        $Store.Close()
    }
}


# srcFile: /drone/src/src/Private/Get-DefaultCredentialStorePath.ps1
function Get-DefaultCredentialStorePath {
    <#
    .SYNOPSIS
        Returns the default CredentialStore path based on the current OS.
 
    .DESCRIPTION
        This is a low level helper function.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [string]
 
    .EXAMPLE
        $Path = Get-DefaultCredentialStorePath
    #>


    [CmdletBinding()]
    [OutputType([string])]
    param (
        [Parameter(Mandatory = $false)]
        [switch]$Shared
    )

    begin {}

    process {
        if ($Shared.IsPresent) {
            if ($IsLinux) {
                return Join-Path -Path '/var/opt' -ChildPath 'PSCredentialStore/CredentialStore.json'
            }
            if ($IsMacOS) {
                return Join-Path -Path '/var/opt' -ChildPath 'PSCredentialStore/CredentialStore.json'
            }
            elseif (
                ($isWindows) -or
                ($PSVersionTable.PSVersion.Major -lt 6) -or
                ($PSVersionTable.PSEdition -eq 'Desktop')
            ) {
                return Join-Path -Path $env:ProgramData -ChildPath 'PSCredentialStore/CredentialStore.json'
            }
        }
        else {
            if ($IsLinux) {
                return Join-Path -Path $Env:HOME -ChildPath 'CredentialStore.json'
            }
            if ($IsMacOS) {
                return Join-Path -Path $Env:HOME -ChildPath 'CredentialStore.json'
            }
            elseif (
                ($isWindows) -or
                ($PSVersionTable.PSVersion.Major -lt 6) -or
                ($PSVersionTable.PSEdition -eq 'Desktop')
            ) {
                return Join-Path -Path $env:AppData -ChildPath 'CredentialStore.json'
            }
        }
    }

    end {}
}


# srcFile: /drone/src/src/Private/Get-ModuleBase.ps1
function Get-ModuleBase {
    <#
    .SYNOPSIS
        Returns the base path of the current module.
 
    .DESCRIPTION
        This is just a wrapper for enabling pester tests.
 
    .OUTPUTS
        Returns the base path as string
    #>


    [CmdletBinding()]
    [OutputType([string])]
    param ()
    begin {}
    process {
        return $MyInvocation.MyCommand.Module.ModuleBase
    }
    end {}
}


# srcFile: /drone/src/src/Private/Get-RandomAESKey.ps1
function Get-RandomAESKey {
    <#
    .SYNOPSIS
        Generate a new 32-byte AES key.
 
    .DESCRIPTION
        Uses the System.Security.Cryptography namespace for random aes key generation.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [byte[]]
 
    .EXAMPLE
        .\Get-RandomAESKey
    #>


    [CmdletBinding()]
    [OutputType([byte[]])]
    param ()

    begin {}

    process {
        $key = [byte[]]::new(32)
        $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create()
        $rng.GetBytes($key)
        Write-Output $key
        if ($null -ne $key) {
            [array]::Clear($key, 0, $key.Length)
        }

    }
    end {}
}


# srcFile: /drone/src/src/Private/Get-TempDir.ps1
function Get-TempDir {
    <#
    .SYNOPSIS
        Returns the valid temp dir of the current OS
 
    .DESCRIPTION
        Returns the valid temp dir of the current OS.
 
    .INPUTS
        [None]
    .OUTPUTS
        [string]
 
    .EXAMPLE
        Get-TempDir
    #>


    [CmdletBinding()]
    [OutputType([string])]
    param ()

    begin {}

    process {
        if ($IsLinux) {
            return (Resolve-Path -Path '/tmp/').Path
        }
        if ($IsMacOS) {
            return (Resolve-Path -Path '/tmp/').Path
        }
        elseif (
            ($isWindows) -or
            ($PSVersionTable.PSVersion.Major -lt 6) -or
            ($PSVersionTable.PSEdition -eq 'Desktop')
        ) {
            return (Resolve-Path -Path $env:TEMP).Path
        }
    }

    end {}
}


# srcFile: /drone/src/src/Private/Import-CSPfxCertificate.ps1
function Import-CSPfxCertificate {
    <#
    .SYNOPSIS
        Adds a given pfx certificate file to current user's personal certificate store.
 
    .DESCRIPTION
        This function is used to import existing pfx certificate files. The Import-PFXCertificate cmdlet from the
        PKI module imports the certificate into a deprecated store. Thus you can't read the private key afterwards
        or using it for decrypting data.
 
    .PARAMETER Path
        Path to an existing *.pfx certificate file.
 
    .PARAMETER StoreName
        Additionally you change change the store where you want the certificate into.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        Import-CSPfxCertificate -Path (Join-Path -Path $Env:APPDATA -ChildPath '/PSCredentialStore.pfx')
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $false)]
        [ValidateSet(
            'AddressBook',
            'AuthRoot',
            'CertificateAuthority',
            'Disallowed',
            'My',
            'Root',
            'TrustedPeople',
            'TrustedPublisher'
        )]
        [string]$StoreName = 'My',

        [Parameter(Mandatory = $false)]
        [ValidateSet(
            'CurrentUser',
            'LocalMachine'
        )]
        [string]$StoreLocation = 'CurrentUser',

        [Parameter(Mandatory = $false)]
        [ValidateSet(
            'ReadOnly',
            'ReadWrite',
            'MaxAllowed',
            'OpenExistingOnly',
            'IncludeArchived'
        )]
        [string]$OpenFlags = 'ReadWrite'
    )

    begin {
        $Store = [System.Security.Cryptography.X509Certificates.X509Store]::new($StoreName, $StoreLocation)
        try {
            $Store.Open($OpenFlags)
        }
        catch {
            $_.Exception.Message | Write-Error -ErrorAction Stop
        }
    }

    process {
        try {
            $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(
                $Path,
                $null,
                (
                    [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bor
                    [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet
                )
            )

            if (Test-CSPfxCertificate -Thumbprint $cert.Thumbprint) {
                Write-Warning -Message (
                    'The certificate with thumbprint {0} is already present!' -f $cert.Thumbprint
                )
            }
            else {
                $Store.Add($cert)
            }
        }
        catch {
            $_.Exception.Message | Write-Error -ErrorAction Stop
            $ErrorParams = @{
                ErrorAction = 'Stop'
                Exception   = [System.Exception]::new(
                    'Could not read or add the pfx certificate!'
                )
            }
            Write-Error @ErrorParams
        }
    }
    end {
        $Store.Close()
    }
}


# srcFile: /drone/src/src/Private/Test-CSPfxCertificate.ps1
function Test-CSPfxCertificate {
    <#
    .SYNOPSIS
        Tests if the given certificate exists in a store.
 
    .DESCRIPTION
        Use this function to ensure if a certificate is already imported into a given store.
 
    .PARAMETER Thumbprint
        Provide one or more thumbprints.
 
    .PARAMETER StoreName
        Select the store name in which you want to search the certificates.
 
    .PARAMETER StoreLocation
        Select between the both available locations CurrentUser odr LocalMachine.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [bool]
 
    .EXAMPLE
        Test-CSPfxCertificate -Thumbprint '12345678' -StoreName 'My' -StoreLocation 'CurrentUser'
    #>


    [CmdletBinding()]
    [OutputType([bool])]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Thumbprint,

        [Parameter(Mandatory = $false)]
        [ValidateSet(
            'AddressBook',
            'AuthRoot',
            'CertificateAuthority',
            'Disallowed',
            'My',
            'Root',
            'TrustedPeople',
            'TrustedPublisher'
        )]
        [string]$StoreName = 'My',

        [Parameter(Mandatory = $false)]
        [ValidateSet(
            'CurrentUser',
            'LocalMachine'
        )]
        [string]$StoreLocation = 'CurrentUser'
    )

    begin {
        $Store = [System.Security.Cryptography.X509Certificates.X509Store]::New($StoreName, $StoreLocation)
        try {
            $Store.Open('ReadOnly')
        }
        catch {
            $_.Exception.Message | Write-Error -ErrorAction Stop
        }
    }

    process {
        # Script analyzer issue (unused var) workaround
        $null = $Thumbprint
        $Cert = $Store.Certificates | Where-Object { $_.Thumbprint -eq $Thumbprint }

        if ($null -eq $Cert) {
            return $false
        }
        else {
            return $true
        }
    }
    end {
        $Store.Close()
    }
}


# srcFile: /drone/src/src/Private/Test-Module.ps1
function Test-Module {
    <#
    .SYNOPSIS
        Tests if the given module exists on the local system.
 
    .DESCRIPTION
        Tests if the given module is installed on the local system. It returns a bool value as result.
 
    .PARAMETER Name
        Define a item name you need to test
 
    .PARAMETER Type
        Define the dependency type. This could be a Module or PSnapin.
 
    .PARAMETER MessagePattern
        You an optionally adjust the message pattern for the error message itself.
        The available placeholders are:
        - {0} : Type
        - {1} : Name
 
    .PARAMETER StopIfFails
        This switch forces the entire script to stop if the given dependency object fails.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [Bool]
 
    .EXAMPLE
        .\Test-Dependency -Name 'VMware.PowerCLI' -Type 'Module'
 
    .EXAMPLE
        .\Test-Dependency -Name 'VMware.PowerCLI' -Type 'Module' -StopIfFails
    #>


    [OutputType([bool])]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$MessagePattern = @"
Could not find the required {0} called {1}. Please install the required {0} to run this function!
"@
,
        [Parameter(Mandatory = $false)]
        [switch]$StopIfFails
    )

    begin {}

    process {
        $Message = $MessagePattern -f $Type, $Name
        Write-Debug $Message

        if (Get-Module -Name $Name -ListAvailable) {
            return $true
        }
        else {
            if ($StopIfFails) {
                Write-Error -Message $Message -ErrorAction Stop -Category NotInstalled
            }
            return $false
        }
    }

    end {}
}


# srcFile: /drone/src/src/Connection/Connect-To.ps1
function Connect-To {
    <#
    .SYNOPSIS
        Connects to the given host using the stored CredentialStoreItem.
 
    .DESCRIPTION
        Establish a connection to the selected host using a stored CredentialStoreItem.
 
    .PARAMETER RemoteHost
        Specify the host, for which you would like to change the credentials.
 
    .PARAMETER Identifier
        Defaults to "". Specify a string, which separates two CredentialStoreItems for the
        same hostname.
 
    .PARAMETER Type
        Specify the host type of the target. Currently implemented targets are: Possible connection values are:
 
        - CiscoUcs
        - CiscoUcsCentral
        - CisServer
        - ExchangeHTTP
        - ExchangeHTTPS
        - FTP
        - NetAppFAS
        - SCP
        - VMware
 
    .PARAMETER Credentials
        Use this parameter to bypass the stored credentials. Without this parameter Connect-To tries to read the
        needed credentials from the CredentialStore. If you provide this parameter you skip this lookup behavior.
        So you can use it to enable credentials without preparing any user interaction.
 
    .PARAMETER Path
        Define a custom path to a shared CredentialStore.
 
    .PARAMETER Shared
        Switch to shared mode with this param. This enforces the command to work with a shared CredentialStore which
        can be decrypted across systems.
 
    .PARAMETER PassThru
        Returns the value from the underlying connection type function.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        Connect-To -RemoteHost "ucs.myside.local" -Type CiscoUcs
 
    .EXAMPLE
       Connect-To -RemoteHost "ucscentral.myside.local" -Type 'CiscoUcsCentral'
 
    .EXAMPLE
        Connect-To -RemoteHost "ftp.myside.local" -Type FTP
 
    .EXAMPLE
        Connect-To -RemoteHost "fas.myside.local" -Type NetAppFAS
 
    .EXAMPLE
        Connect-To -RemoteHost "esx01.myside.local" -Type VMware
 
    .EXAMPLE
        Connect-To -RemoteHost "vCenter.myside.local" -Type CisServer
 
    .EXAMPLE
        Connect-To -RemoteHost "exchange01.myside.local" -Type ExchangeHTTP
 
    .EXAMPLE
        Connect-To -RemoteHost "exchange01.myside.local" -Type ExchangeHTTPS
    #>


    [CmdletBinding(DefaultParameterSetName = 'Private')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSAvoidGlobalVars',
        '',
        Justification = 'Wrapping existing var from WinSCP module.'
    )]
    param (
        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Private')]
        [string]$RemoteHost,

        [Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Private')]
        [string]$Identifier,

        [Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Private')]
        [ValidateSet(
            'CiscoUcs',
            'CiscoUcsCentral',
            'FTP',
            'NetAppFAS',
            'VMware',
            'CisServer',
            'ExchangeHTTP',
            'ExchangeHTTPS',
            'SCP'
        )]
        [string]$Type,

        [Parameter(Mandatory = $False, ParameterSetName = 'Shared')]
        [Parameter(Mandatory = $False, ParameterSetName = 'Private')]
        [PSCredential]$Credentials,

        [Parameter(Mandatory = $true, ParameterSetNAme = 'Shared')]
        [switch]$Shared,

        [Parameter(Mandatory = $False, ParameterSetName = 'Shared')]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $False, ParameterSetName = 'Private')]
        [Parameter(Mandatory = $False, ParameterSetName = 'Shared')]
        [switch]$PassThru
    )

    begin {
        # Set the CredentialStore for private, shared or custom mode.
        Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
        if ($PSCmdlet.ParameterSetName -eq 'Private') {
            $Path = Get-DefaultCredentialStorePath
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
            if (!($PSBoundParameters.ContainsKey('Path'))) {
                $Path = Get-DefaultCredentialStorePath -Shared
            }
        }

        switch ($Type) {
            "VMware" {
                # Disable the yellow certificate warning, since we haven't replaced the SSL certs for vCenter/ESXi
                $null = Set-PowerCLIConfiguration -Scope Session -InvalidCertificateAction Ignore -Confirm:$false

                # Disable connecting through proxy, since vCenter isn't somewhere we need a proxy for.
                $null = Set-PowerCLIConfiguration -Scope Session -ProxyPolicy NoProxy -Confirm:$false
            }
        }
    }

    process {
        if (-not ($Credentials)) {
            # Load the credential from the CredentialStore. If the credential doesn't exist, we need to
            # return 1, so a calling if statement can handle the failure detection.

            # Check if $Identifier has been defined, in which case we need to use different name for
            # the lookup of the CredentialStoreItem.
            try {
                if ($Identifier -ne "") {
                    $RemoteHostIdentifier = "{0}/{1}" -f $Identifier, $RemoteHost
                    $creds = Get-CredentialStoreItem -Shared -RemoteHost $RemoteHostIdentifier -Path $Path
                }
                else {
                    $creds = Get-CredentialStoreItem -Shared -RemoteHost $RemoteHost -Path $Path
                }
            }

            catch {
                $MessageParams = @{
                    Message     = (
                        "Unable to look up credential store item for RemoteHost " +
                        ("{0}/Identifier {1}!" -f $RemoteHost, $Identifier)
                    )
                    ErrorAction = 'Stop'
                }
                Write-Error @MessageParams
            }
        }
        else {
            $creds = $Credentials
        }

        if ($creds.UserName -eq "" -or $creds.Password.GetType().Name -ne 'SecureString') {
            $MessageParams = @{
                Message     = "Please provide valid credentials for RemoteHost {0}!" -f $RemoteHost
                ErrorAction = 'Stop'
            }
            Write-Error @MessageParams
        }
        else {
            switch ($Type) {
                "CiscoUcs" {
                    try {
                        $handle = Connect-Ucs -Name $RemoteHost -Credential $creds -ErrorAction 'Stop' -NotDefault
                        $ExecutionContext.SessionState.PSVariable.Set('DefaultUcs', $handle)
                    }

                    catch {
                        $MessageParams = @{
                            Message     = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                }
                "CiscoUcsCentral" {
                    try {
                        $handle = Connect-UcsCentral -Name $RemoteHost -Credential $creds -NotDefault
                        $ExecutionContext.SessionState.PSVariable.Set('DefaultUcsCentral', $handle)
                    }

                    catch {
                        $MessageParams = @{
                            Message     = ('Unable to connect to {0} using {1}' -f $RemoteHost, $Type)
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                }
                "FTP" {
                    # First establish the FTP session
                    $WinSCPConParams = @{
                        Credential = $creds
                        Hostname   = $RemoteHost
                        Protocol   = 'Ftp'
                        FtpMode    = 'Passive'
                    }
                    try {
                        $FTPSessionOption = New-WinSCPSessionOption @WinSCPConParams
                        $Global:WinSCPSession = New-WinSCPSession -SessionOption $FTPSessionOption
                    }
                    catch {
                        throw "Could not connect to {0} using {1} protocol!" -f $RemoteHost, $Type
                    }
                    # Check the Connection State
                    if (!($WinSCPSession.Opened)) {
                        # Check the connection state and find out if the session is still open.
                        $MessageParams = @{
                            Message     = (
                                ("Connection to {0} using Type {1} " -f $RemoteHost, $Type) +
                                "was established. But now it seems to be lost!"
                            )
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                }
                "NetAppFAS" {
                    try {
                        $null = Connect-NcController -Name $RemoteHost -Credential $creds -ErrorAction Stop -HTTPS
                    }

                    catch {
                        # Write a error message to the log.
                        $MessageParams = @{
                            Message     = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                }
                "VMware" {
                    try {
                        Connect-VIServer -Server $RemoteHost -Credential $creds -ErrorAction Stop | Out-Null
                    }

                    catch {
                        # Write a error message to the log.
                        $MessageParams = @{
                            Message     = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                }
                "CisServer" {
                    try {
                        if ($PassThru.IsPresent) {
                            Connect-CisServer -Server $RemoteHost -Credential $creds -ErrorAction Stop
                        }
                        else {
                            Connect-CisServer -Server $RemoteHost -Credential $creds -ErrorAction Stop | Out-Null
                        }

                    }

                    catch {
                        # Write a error message to the log.
                        $MessageParams = @{
                            Message     = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                }
                "ExchangeHTTP" {
                    try {
                        $ConnectionParams = @{
                            ConnectionURI     = "http://{0}/powershell" -f $RemoteHost
                            ConfigurationName = 'Microsoft.Exchange'
                            Credential        = $creds
                            ErrorAction       = 'Stop'
                        }
                        $Global:PSExchangeRemote = New-PSSession @ConnectionParams
                        # ScriptAnalyzer issue (unused var) workaround.
                        $null = $Global:PSExchangeRemote
                    }
                    catch {
                        # Write a error message to the log.
                        $MessageParams = @{
                            Message     = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                }
                "ExchangeHTTPS" {
                    try {
                        $ConnectionParams = @{
                            ConnectionURI     = "https://{0}/powershell" -f $RemoteHost
                            ConfigurationName = 'Microsoft.Exchange'
                            Credential        = $creds
                            ErrorAction       = 'Stop'
                        }
                        $Global:PSExchangeRemote = New-PSSession @ConnectionParams
                    }
                    catch {
                        # Write a error message to the log.
                        $MessageParams = @{
                            Message     = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                }
                "SCP" {
                    $WinSCPSessionParams = @{
                        Credential                           = $creds
                        Hostname                             = $RemoteHost
                        Protocol                             = 'Scp'
                        GiveUpSecurityAndAcceptAnySshHostKey = $True
                    }
                    try {
                        $SessionOption = New-WinSCPSessionOption @WinSCPSessionParams
                        $Global:WinSCPSession = New-WinSCPSession -SessionOption $SessionOption
                        Write-Verbose -Message (
                            "SCP Connection established with {0}" -f $Global:WinSCPSession.Hostname
                        )
                    }
                    catch {
                        # Write a error message to the log.
                        $MessageParams = @{
                            Message     = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                    # Check the Connection State
                    if (!($WinSCPSession.Opened)) {
                        # Check the connection state and find out if the session is still open.
                        $MessageParams = @{
                            Message     = (
                                ("Connection to {0} using Type {1} was established. " -f $RemoteHost, $Type) +
                                "But now it seems to be lost!"
                            )
                            ErrorAction = 'Stop'
                        }
                        Write-Error @MessageParams
                    }
                }
                default {
                    # Write a error message to the log.
                    $MessageParams = @{
                        Message     = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }
            }
        }
    }
}


# srcFile: /drone/src/src/Connection/Disconnect-From.ps1
function Disconnect-From {
    <#
    .SYNOPSIS
        Terminates a session established with Connect-To using a CredentialStoreItem.
 
    .DESCRIPTION
        Terminates a session established with Connect-To using a CredentialStoreItem.
 
    .PARAMETER RemoteHost
        Specify the remote endpoint, whose session you would like to terminate.
 
    .PARAMETER Identifier
        Defaults to "". Specify a string, which separates two CredentialStoreItems for the
        same hostname.
 
    .PARAMETER Type
        Specify the host type of the target. Currently implemented targets are:
 
        - CiscoUcs
        - CiscoUcsCentral
        - FTP
        - NetAppFAS
        - VMware
        - CisServer
        - ExchangeHTTP
        - ExchangeHTTPS
        - SCP
 
    .PARAMETER Force
        Force the disconnect, even if the disconnect would fail.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [None]
 
    .EXAMPLE
        Disconnect-From -RemoteHost "ucs.myside.local" -Type CiscoUcs
 
    .EXAMPLE
        Disconnect-From -RemoteHost "ftp.myside.local" -Type FTP
 
    .EXAMPLE
        Disconnect-From -RemoteHost "fas.myside.local" -Type NetAppFAS
 
    .EXAMPLE
        Disconnect-From -RemoteHost "esx01.myside.local" -Type VMware
 
    .EXAMPLE
        Disconnect-From -RemoteHost "esx01.myside.local" -Type VMware -Force:$True
 
    .EXAMPLE
        Disconnect-From -RemoteHost "vcenter.myside.local" -Type CisServer
 
    .EXAMPLE
        Disconnect-From -RemoteHost "exchange01.myside.local" -Type ExchangeHTTP
 
    .EXAMPLE
        Disconnect-From -RemoteHost "exchange01.myside.local" -Type ExchangeHTTPS
    #>


    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSAvoidGlobalVars',
        '',
        Justification = 'Wrapping existing global vars from external modules'
    )]
    param (
        [Parameter(Mandatory = $true)]
        [string]$RemoteHost,

        [Parameter(Mandatory = $true)]
        [ValidateSet(
            'CiscoUcs',
            'CiscoUcsCentral',
            'FTP',
            'NetAppFAS',
            'VMware',
            'CisServer',
            'ExchangeHTTP',
            'ExchangeHTTPS',
            'SCP'
        )]
        [string]$Type,

        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

    begin {}

    process {
        switch -Regex ($Type) {
            "VMware" {
                try {
                    if ($Force) {
                        Disconnect-VIServer -Server $RemoteHost -Confirm:$false -ErrorAction 'Stop' -Force:$true
                    }
                    else {
                        Disconnect-VIServer -Server $RemoteHost -Confirm:$false -ErrorAction 'Stop'
                    }
                }

                catch {
                    # Write a error message to the log.
                    $MessageParams = @{
                        Message     = "Unable to disconnect from {0} using Type {1}." -f $RemoteHost, $Type
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }
            }
            "CisServer" {
                try {
                    if ($Force) {
                        Disconnect-CisServer -Server $RemoteHost -Confirm:$false -ErrorAction 'Stop' -Force:$true
                    }
                    else {
                        Disconnect-CisServer -Server $RemoteHost -Confirm:$false -ErrorAction 'Stop'
                    }
                }

                catch {
                    # Write a error message to the log.
                    $MessageParams = @{
                        Message     = "Unable to disconnect from {0} using Type {1}." -f $RemoteHost, $Type
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }
            }
            # Check for an existing WinSCP Session var
            "FTP" {
                if ($Global:WinSCPSession.Opened) {
                    Remove-WinSCPSession -WinSCPSession $Global:WinSCPSession
                }
                else {
                    $MessageParams = @{
                        Message     = 'There is no open WinSCP Session'
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }
            }
            # DataONTAP doesn't have a CmdLet `Disconnect-NcController`.
            # So we go ahead and clear the CurrentNcController variable.
            "NetAppFAS" {
                try {
                    $MessageParams = @{
                        Message     = (
                            "Setting {0} to `$null, which will disconnect NetAppFAS" -f $Global:CurrentNcController
                        )
                        ErrorAction = 'Continue'
                    }
                    Write-Verbose @MessageParams
                    $Global:CurrentNcController = $null
                }

                catch {
                    # Write a error message to the log.
                    $MessageParams = @{
                        Message     = "Unable to disconnect from {0} using Type {1}." -f $RemoteHost, $Type
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }

            }
            "CiscoUcs" {
                try {
                    Disconnect-Ucs -Ucs $RemoteHost
                }

                catch {
                    # Write a error message to the log.
                    $MessageParams = @{
                        Message     = "Unable to disconnect from {0} using Type {1}." -f $RemoteHost, $Type
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }
            }
            "CiscoUCSCentral" {
                try {
                    $handle = Connect-UcsCentral -Name $RemoteHost -Credential $creds -NotDefault
                    $ExecutionContext.SessionState.PSVariable.Set('DefaultUcsCentral', $handle)
                }
                catch {
                    $MessageParams = @{
                        Message     = "Unable to disconnect from {0} using Type {1}." -f $RemoteHost, $Type
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }
            }

            "ExchangeHTTP*" {
                try {
                    Get-Variable -Name 'PSExchangeRemote' -Scope 'Global' -ErrorAction 'Stop'
                    Remove-PSSession -Session $Global:PSExchangeRemote -ErrorAction 'Stop'
                }
                catch {
                    $MessageParams = @{
                        Message     = "Unable to disconnect from {0} using Type {1}." -f $RemoteHost, $Type
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }
            }
            "SCP" {
                if ($Global:WinSCPSession.Opened) {
                    Remove-WinSCPSession -WinSCPSession $Global:WinSCPSession
                }
                else {
                    $MessageParams = @{
                        Message     = 'There is no open WinSCP Session'
                        ErrorAction = 'Stop'
                    }
                    Write-Error @MessageParams
                }
            }
            default {
                # Write a error message to the log.
                $MessageParams = @{
                    Message     = "Unable to disconnect from {0} using Type {1}." -f $RemoteHost, $Type
                    ErrorAction = 'Stop'
                }
                Write-Error @MessageParams
            }
        }
    }
}


# srcFile: /drone/src/src/Connection/Test-CSConnection.ps1
function Test-CSConnection {
    <#
    .SYNOPSIS
        Returns the connection state of a given type to the remote host.
 
    .DESCRIPTION
        Use this script to check a connection which was established with the `Connect-To` cmdlet.
 
    .PARAMETER RemoteHost
        Define the remote host you would like to check.
 
    .Parameter Type
        Define the connection type you would like to check. See the `Connect-To` documentation
        for valid type values.
 
    .INPUTS
        [None]
 
    .OUTPUTS
        [bool]
 
    .EXAMPLE
        Test-CMConnection -RemoteHost "vcr01.internal.net" -Type VMware
    #>


    [CmdletBinding()]
    [OutputType([bool])]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$RemoteHost,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet("CiscoUcs", "FTP", "NetAppFAS", "VMware")]
        [string]$Type
    )

    switch ($Type) {
        'VMware' {
            try {
                $Conn = Get-Variable -Name DefaultVIServer -Scope Global -ErrorAction Stop
            }
            catch [System.Management.Automation.ItemNotFoundException] {
                $MsgParams = @{
                    Message = "There is no open PowerCLI VMware connection bound to 'DefaultVIServer'."
                }
                Write-Verbose @MsgParams
                return $false
            }
            if ($Conn.Value.Name -eq $RemoteHost) {
                if ($Conn.Value.IsConnected) {
                    $MsgParams = @{
                        Message = "'DefaultVIServer' found. Connection to given remote host already established."
                    }
                    Write-Verbose @MsgParams
                    return $True
                }
                else {
                    $MsgParams = @{
                        Message = "'DefaultVIServer' found. RemoteHost matches but the connection is closed."
                    }
                    Write-Verbose @MsgParams
                    return $false
                }
            }
        }

        'CiscoUcs' {
            $MsgParams = @{
                ErrorAction = "Stop"
                Message     = "CiscoUCS connection test is not implemented yet!"
            }
            Write-Error @MsgParams
            return $false
        }

        'FTP' {
            $MsgParams = @{
                ErrorAction = "Stop"
                Message     = "FTP connection test is not implemented yet!"
            }
            Write-Error @MsgParams
            return $false
        }

        'NetAppFAS' {
            $MsgParams = @{
                ErrorAction = "Stop"
                Message     = "NetAppFAS connection test is not implemented yet!"
            }
            Write-Error @MsgParams
            return $false
        }

        # The Default section will never be shown as long as the powershell framework isn't broken.
        Default {
            $MsgParams = @{
                ErrorAction = "Stop"
                Message     = "Panic: There is an invalid type value! This error should never be thrown."
            }
            Write-Error @MsgParams
            return $false
        }
    }
}