PwSh.Fw.Security.psm1

$Script:PROFILEDIR = $env:USERPROFILE
if ($IsLinux) { $Script:PROFILEDIR = $env:HOME }
if ($IsMacOS) { $Script:PROFILEDIR = $env:HOME }
if ($IsWindows) { $Script:PROFILEDIR = $env:USERPROFILE }

<#
.SYNOPSIS
Save credentials into a encrypted file
 
.DESCRIPTION
Sometime you need to save credentials to be used by scripts running unattended (in cron jobs for example).
This function lets you save an encrypted version in standard way to be easily re-used.
 
.PARAMETER Domain
Domain of the user. Optional
 
.PARAMETER Username
Username of the user.
 
.PARAMETER Password
Password of the user.
 
.PARAMETER Credentials
Full credentials of the user
 
.EXAMPLE
Save-Credentials -Username myuser
 
.EXAMPLE
$cred = Get-Credentials
Save-Credentials -Credentials $cred
 
.EXAMPLE
$cred = Get-Credentials
$cred | Save-Credentials
 
.NOTES
General notes
#>

function Save-Credentials {
    [CmdletBinding(DefaultParameterSetName = 'USERNAME')]
    [OutputType([Boolean])]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "", Justification="'Save' is a more intuitive verb for this function because it just... well... save your credentials into a file.")]
    Param (
        [Parameter(Mandatory = $false, ParameterSetName = 'USERNAME')][string]$Domain,
        [Parameter(Mandatory = $true, ParameterSetName = 'USERNAME')][string]$Username,
        [Parameter(Mandatory = $true, ParameterSetName = 'USERNAME')][SecureString]$Password,
        [Parameter(Mandatory = $false)][string]$TargetHost,
        [Parameter(Mandatory = $true, ParameterSetName = 'CREDENTIALS')][System.Management.Automation.PSCredential]$Credentials,
        [Parameter(Mandatory = $false)][string]$Path = $Script:PROFILEDIR
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'USERNAME' {
                if ($Domain) {
                    $Credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "$Domain\$Username",$Password
                } else {
                    $Credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "$Username",$Password
                }
            }
            'CREDENTIALS' {

            }
        }

        $Filename = "$Path/"
        if ($Domain) { $Filename += "$Domain_" }
        if ($TargetHost) { $Filename += "$TargetHost_" }
        $Filename += "$UserName.pwd"

        $Credentials.Password | ConvertFrom-SecureString | Out-File $Filename -encoding UTF8 -Force:$force
        if (Test-Path -PathType Leaf $Filename) {
            return $true
        }
        return $false
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Load credentials from a encrypted file
 
.DESCRIPTION
This function can load credentials previously saved by Save-Credentials
 
.PARAMETER Domain
Domain of the user. Optional
 
.PARAMETER Username
Username of the user.
 
.PARAMETER Filename
Path to the credential file if not in standard path.
 
.EXAMPLE
Load-Credentials -Username myuser
 
.EXAMPLE
Load-Credentials -Filename c:\path\to\user.pwd
 
.NOTES
General notes
#>

function Load-Credentials {
    [CmdletBinding(DefaultParameterSetName = 'USERNAME')]
    [OutputType([System.Management.Automation.PSCredential])]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "", Justification="'Load' is a more intuitive verb for this function because it just... well... load your credentials from a file.")]
    Param (
        [Parameter(Mandatory = $false, ParameterSetName = 'USERNAME')][string]$Domain,
        [Parameter(Mandatory = $true, ParameterSetName = 'USERNAME')][string]$Username,
        [Parameter(Mandatory = $false, ParameterSetName = 'USERNAME')][string]$TargetHost,
        [Parameter(Mandatory = $true, ParameterSetName = 'FILENAME')][string]$Filename,
        [Parameter(Mandatory = $false)][string]$Path = $Script:PROFILEDIR
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'USERNAME' {
                if ($USERNAME.Contains("\")) {
                    $Domain, $Username = $USERNAME.Split("\")
                }
                $Filename = "$Path/"
                if ($Domain) { $Filename += "$Domain_" }
                if ($TargetHost) { $Filename += "$TargetHost_" }
                $Filename += "$UserName.pwd"
            }
            'FILENAME' {
                $Item = Get-Item $Filename -ErrorAction stop
                if ($Item.Basename -match "_.*_") {
                    $Domain, $TargetHost, $Username = $Item.Basename -split '_'
                } elseif ($Item.Basename -match "_") {
                    $Domain, $Username = $Item.Basename -split '_'
                } else {
                    $Username = $Item.Basename
                }
            }
        }
        if (!($Username)) {
            return $null
        }
        if (!(Test-Path -path $Filename -PathType leaf)) {
            return $null
        }
        $Password = get-content $Filename | ConvertTo-SecureString
        if ($Domain) {
            $Credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "$Domain\$Username",$Password
        } else {
            $Credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "$Username",$Password
        }
        return $Credentials
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Display credentials in plain text
 
.DESCRIPTION
Sometime you need to see credentials in plain text to be sure it the good one.
This function just display it
 
.PARAMETER Domain
Domain of the user. Optional
 
.PARAMETER Username
Username of a user.
 
.PARAMETER Credentials
Full credentials of a user
 
.PARAMETER Filename
Path to the credential file if not in standard path.
 
.EXAMPLE
Show-Credentials -Username myuser
 
.EXAMPLE
$cred = Get-Credentials
Show-Credentials -Credentials $cred
 
.NOTES
General notes
#>

function Show-Credentials {
    [CmdletBinding(DefaultParameterSetName = 'USERNAME')]
    [OutputType([string])]
    Param (
        [Parameter(Mandatory = $false, ParameterSetName = 'USERNAME')][string]$Domain,
        [Parameter(Mandatory = $true, ParameterSetName = 'USERNAME')][string]$Username,
        [Parameter(Mandatory = $false, ParameterSetName = 'USERNAME')][string]$TargetHost,
        [Parameter(Mandatory = $true, ParameterSetName = 'FILENAME')][string]$Filename,
        [Parameter(Mandatory = $true, ParameterSetName = 'CREDENTIALS')][PSCredential]$Credentials,
        [Parameter(Mandatory = $false)][string]$Path = $Script:PROFILEDIR
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'USERNAME' {
                # $Filename = "$Path/"
                # if ($Domain) { $Filename = "$Domain_" }
                # if ($TargetHost) { $Filename = "$TargetHost_" }
                # $Filename += "$UserName.pwd"
                $Credentials = Load-Credentials -Domain $Domain -TargetHost $TargetHost -Username $Username -Path $Path
            }
            'FILENAME' {
                $Credentials = Load-Credentials -Filename $Filename
                # $Item = Get-Item $Filename -ErrorAction stop
                # if ($Item.Basename -match "_.*_") {
                # $Domain, $TargetHost, $Username = $Item.Basename -split '_'
                # } elseif ($Item.Basename -match "_") {
                # $Domain, $Username = $Item.Basename -split '_'
                # } else {
                # $Username = $Item.Basename
                # }
            }
            'CREDENTIALS' {
            }
        }
        $Username = $Credentials.UserName
        $Password = $Credentials.Password

        $Ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($Password)
        $result = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($Ptr)
        [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($Ptr)
        $Username, $result
    }

    End {
        Write-LeaveFunction
    }
}