CISPowerShell.psm1

<#
.SYNOPSIS
    Save Putty
 
.DESCRIPTION
    Save the x64 or x86 version of the latest version of Putty to a
    specified file path location
 
.EXAMPLE
    Save-Putty -FilePath C:\
     
    This example will determine if you need the x86 or x64 version of Putty
    and save the executable file to c:\putty.exe
 
.EXAMPLE
    Save-Putty -FilePath C:\users\Me\NewFolder
 
    This example will determine if you need the x86 or x64 version of Putty
    and save the executable file to c:\users\Me\NewFolder. If NewFolder doesn't
    exist, the cmdlet will create the folder for you
#>

function Save-Putty
{ 
    [cmdletbinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [string]$FilePath
    )

    begin
    {
        $timer = New-Object -TypeName System.Diagnostics.StopWatch
        $cmdletName = $PSCmdlet.MyInvocation.MyCommand.Name
        Write-Verbose -Message ('{0} : Begin execution : {1}' -f $cmdletName, (Get-Date))
        $timer.Start()
    }

    process
    {
        try
        {
            if (Test-Connection google.com -Count 1)
            {
                Write-Verbose -Message ('{0} : Creating destination filepath : {1}' -f $cmdletName, $FilePath)
                If (!(Test-Path -Path $FilePath))
                {
                    New-Item -Path $FilePath -ItemType Directory -Force | Out-Null
                    Write-Verbose -Message ('{0} : Created destination filepath complete' -f $cmdletName)
                }
                Write-Verbose -Message ('{0} : Detecting operating system architecture' -f $cmdletName)
                $uri = switch ((Get-WmiObject -Class Win32_OperatingSystem -Property OSArchitecture).OSArchitecture)
                {
                    '32-bit'
                    {
                        Write-Verbose -Message ('{0} : Detected x86 operating system' -f $cmdletName)
                        'https://the.earth.li/~sgtatham/putty/latest/w32/putty.exe'
                    }

                    '64-bit'
                    {
                        Write-Verbose -Message ('{0} : Detected x64 operating system' -f $cmdletName)
                        'https://the.earth.li/~sgtatham/putty/latest/w64/putty.exe'
                    }
                }
                Write-Verbose -Message ('{0} : Download URL : {1}' -f $cmdletName, $uri)
                Invoke-WebRequest -Uri $uri -OutFile (Join-Path -Path $FilePath -ChildPath 'putty.exe')
                Write-Verbose -Message ('{0} : Download complete' -f $cmdletName)
                '{0} : Download complete. FilePath : {1}\putty.exe' -f $cmdletName, $FilePath | Write-Output
            }
            else
            {
                'No access to internet! Unable to download Putty!' | Write-Warning
            }
        }
        catch
        {
            throw
        }
    }

    end
    {
        $timer.Stop()
        Write-Verbose -Message ('{0} : End execution' -f $cmdletName)
        Write-Verbose -Message ('Total execution time: {0} ms' -f $timer.Elapsed.TotalMilliseconds)
    }
}


<#
.SYNOPSIS
    Get a computer's public IP address
 
.DESCRIPTION
    Retrieve a computer's public IP address by using Amazon's CheckIP REST service
 
.EXAMPLE
    Get-PublicIP
     
    This example will retrieve the local machine's public IP address
 
.EXAMPLE
    Get-PublicIP -ComputerName Computer1, Computer2, Computer3
 
    This example will retrieve the specified computers public IP addresses using
    default credentials
 
.EXAMPLE
    Get-PublicIP -ComputerName (Get-Content c:\list.txt) -Credential (Get-Credential)
 
    This example will retrieve public IP addresses for the list of computer names
    in the file c:\list.txt (one computername per line) using the specified credentials
#>

function Get-PublicIP
{
    [cmdletbinding()]
    param
    (
        [parameter(ValueFromPipeline = $true,
                   ValueFromPipelineByPropertyName = $true)]
        [string[]]$ComputerName = $env:COMPUTERNAME,
        
        [parameter()]
        [pscredential]$Credential   
    )

    begin
    {
        $timer = New-Object -TypeName System.Diagnostics.StopWatch
        $cmdletName = $PSCmdlet.MyInvocation.MyCommand.Name
        Write-Verbose -Message ('{0} : Begin execution : {1}' -f $cmdletName, (Get-Date))
        $timer.Start()
    }

    process
    {
        foreach ($computer in $ComputerName)
        {
            try
            {
                Write-Verbose -Message ('{0} : {1} : Begin execution' -f $cmdletName, $computer)
                $sessionParams = @{ComputerName = $computer
                                   ErrorAction  = 'Stop'}
                if ($Credential -ne $null)
                {
                    $sessionParams.Add('Credential', $Credential)
                    Write-Verbose -Message ('{0} : {1} : Using supplied credentials' -f $cmdletName, $computer)
                }
                else
                {
                    Write-Verbose -Message ('{0} : {1} : Using default credentials' -f $cmdletName, $computer)
                }
                $session = $null
                $session = New-PSSession @sessionParams
                if ($session)
                {
                    $ip = $null
                    $ip = Invoke-Command -Session $session -ScriptBlock {
                        Invoke-RestMethod -Uri http://checkip.amazonaws.com
                    }
                    $props = @{ComputerName = [string]$computer
                               PublicIP     = [ipaddress]$ip.Trim()}
                    New-Object -TypeName System.Management.Automation.PSObject -Property $props | Write-Output
                }
                else
                {
                    '{0} : {1} : Unable to create PSSession. If not supplied, try including the -Credential parameter' -f $cmdletName, $computer | Write-Warning
                }
            }
            catch
            {
                throw
            }
        }
    }

    end
    {
        $timer.Stop()
        Write-Verbose -Message ('{0} : End execution' -f $cmdletName)
        Write-Verbose -Message ('Total execution time: {0} ms' -f $timer.Elapsed.TotalMilliseconds)
    }
}


<#
.SYNOPSIS
    Change a Server Core instance's default shell from CMD.exe to PowerShell.exe
 
.DESCRIPTION
    Set registry key to specify PowerShell as default shell
    HKLM:\Software\Microsoft\Windows NT\CurrentVersion\WinLogon\Shell = "PowerShell.exe -NoExit"
 
    * ONLY APPLICABLE TO SERVER CORE INSTALLATIONS *
 
.EXAMPLE
    Set-PowerShellDefaultShell
 
    This example sets PowerShell as the default shell on the local machine using default
    credentials and then warns the user that the local machine must still be rebooted for
    the changes to take effect
 
.EXAMPLE
    Set-PowerShellDefaultShell -ComputerName Server1, Server2 -Restart
 
    This example sets PowerShell as the default shell on the specified computer names (Server1
    and Server2) using default credentials and then reboots the computers automatically when
    complete.
 
.EXAMPLE
    Set-PowerShellDefaultshell -ComputerName (Get-Content c:\list.txt) -Credential (Get-Credential) -Restart
 
    This example sets Powershell as the default shell on the specified computer names read
    from the file specified (1 computername per line) using the supplied credentials of
    Get-Credential and then reboots the computers automatically when complete
#>

function Set-PowerShellDefaultShell
{
    [cmdletbinding()]
    param
    (
        [parameter(ValueFromPipeline = $true,
                   ValueFromPipelineByPropertyName = $true)]
        [string[]]$ComputerName = $env:COMPUTERNAME,
        
        [parameter()]
        [pscredential]$Credential,

        [parameter()]
        [alias('Reboot')]
        [switch]$Restart
    )

    begin
    {
        $timer = New-Object -TypeName System.Diagnostics.StopWatch
        $cmdletName = $PSCmdlet.MyInvocation.MyCommand.Name
        Write-Verbose -Message ('{0} : Begin execution : {1}' -f $cmdletName, (Get-Date))
        $timer.Start()

        $keyPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\winlogon'
        Write-Verbose -Message ('{0} : KeyPath : {1}' -f $cmdletName, $keyPath)
    }

    process
    {
        foreach ($computer in $ComputerName)
        {
            Write-Verbose -Message ('{0} : {1} : Begin execution' -f $cmdletName, $computer)
            $sessionParams = @{ComputerName = $computer
                               ErrorAction  = 'Stop'}
            if ($Credential -ne $null)
            {
                $sessionParams.Add('Credential', $Credential)
                Write-Verbose -Message ('{0} : {1} : Using supplied credentials' -f $cmdletName, $computer)
            }
            else
            {
                Write-Verbose -Message ('{0} : {1} : Using default credentials' -f $cmdletName, $computer)
            }
            $session = $null
            $session = New-PSSession @sessionParams
            if ($session)
            {
                try
                {
                    $result = Invoke-Command -Session $sessionParams -ArgumentList $keyPath, $Restart -ScriptBlock {
                        try
                        {
                            Set-ItemProperty -Path $args[0] -Name 'Shell' -Value 'PowerShell.exe -NoExit' -Force
                            $true | Write-Output
                            if ($args[1])
                            {
                                Restart-Computer -Force
                            }
                        }
                        catch
                        {
                            throw
                        }
                    }
                    New-Object -TypeName System.Management.Automation.PSCustomObject -Property @{ComputerName = $computer
                                                                                                 Result       = $result} |
                        Write-Output
                    if (!$Restart)
                    {
                        '{0} : {1} : Successfully set PowerShell as default shell - reboot is required to make setting active' -f $cmdletName, $computer | Write-Warning
                    }
                }
                catch
                {
                    throw
                }
            }
            else
            {
                '{0} : {1} : Unable to create PSSession' -f $cmdletName, $computer | Write-Warning
                if (!$Credential)
                {
                    'Try including the -Credential parameter!' | Write-Warning
                }
            }
        }
    }

    end
    {
        $timer.Stop()
        Write-Verbose -Message ('{0} : End execution' -f $cmdletName)
        Write-Verbose -Message ('Total execution time: {0} ms' -f $timer.Elapsed.TotalMilliseconds)
    }
}


<#
.SYNOPSIS
    Get a computers uptime
 
.DESCRIPTION
    Calculate a computer's uptime by retrieving the LastBootUpTime property from the Win32
    OperatingSystem WMI class and subtracting that from the current date and time.
 
.EXAMPLE
    Get-UpTime
 
    This example retrieves the local machine's uptime using default credentials
 
.EXAMPLE
    Get-UpTime -ComputerName Server1, Server2 -Restart
 
    This example retrieves the specified computer's uptime using default credentials
 
.EXAMPLE
    Get-UpTime -ComputerName (Get-Content c:\list.txt) -Credential (Get-Credential)
 
    This example retrieves the computer names found in the file (one computer name per line)
    uptime using the specified credentials
#>

function Get-Uptime
{
    [cmdletbinding()]
    param
    (
        [parameter(ValueFromPipeline = $true,
                   ValueFromPipelineByPropertyName = $true)]
        [string[]]$ComputerName = $env:COMPUTERNAME,
        
        [parameter()]
        [pscredential]$Credential   
    )

    begin
    {
        $timer = New-Object -TypeName System.Diagnostics.StopWatch
        $cmdletName = $PSCmdlet.MyInvocation.MyCommand.Name
        Write-Verbose -Message ('{0} : Begin execution : {1}' -f $cmdletName, (Get-Date))
        $timer.Start()
    }

    process
    {
        foreach ($computer in $ComputerName)
        {
            try
            {
                Write-Verbose -Message ('{0} : {1} : Begin execution' -f $cmdletName, $computer)
                $sessionParams = @{ComputerName = $computer
                                   ErrorAction  = 'Stop'}
                if ($Credential -ne $null)
                {
                    $sessionParams.Add('Credential', $Credential)
                    Write-Verbose -Message ('{0} : {1} : Using supplied credentials' -f $cmdletName, $computer)
                }
                else
                {
                    Write-Verbose -Message ('{0} : {1} : Using default credentials' -f $cmdletName, $computer)
                }
                $session = $null
                $session = New-PSSession @sessionParams
                if ($session)
                {
                    $result = $null
                    $result = Invoke-Command -Session $session -ScriptBlock {
                        [DateTime]::Now - [Management.ManagementDateTimeConverter]::ToDateTime((Get-WmiObject Win32_OperatingSystem).LastBootUpTime)
                    }
                    if ($result)
                    {
                        $props = @{
                            ComputerName = $computer
                            UpTime       = ('{0}:{1}:{2}:{3}:{4}' -f $result.Days, $result.Hours, $result.Minutes, $result.Seconds, $result.Milliseconds)
                            TotalDays    = $result.TotalDays
                            TotalHours   = $result.TotalHours
                            TotalMinutes = $result.TotalMinutes
                            TotalSeconds = $result.TotalSeconds
                        }
                        New-Object -TypeName System.Management.Automation.PSObject -Property $props | Write-Output
                    }
                    else
                    {
                        '{0} : {1} : Unable to retrieve Win32_Operatingsystem.LastBootupTime informantion!' -f $cmdletName, $computer | Write-Warning
                    }
                }
                else
                {
                    $message = '{0} : {1} : Unable to create PSSession. Ensure PSremoting to {1} is enabled.' -f $cmdletName, $computer
                    if ($Credential -eq $null)
                    {
                        $message += ' Try including the -Credential parameter.'
                    }
                    $message | Write-Warning
                }
            }
            catch
            {
                throw
            }
        }
    }

    end
    {
        $timer.Stop()
        Write-Verbose -Message ('{0} : End execution' -f $cmdletName)
        Write-Verbose -Message ('Total execution time: {0} ms' -f $timer.Elapsed.TotalMilliseconds)
    }
}


# Kaseya credentials helper function
function Save-KCredentials
{
    param
    (
        [parameter(mandatory = $true)]
        [string]$UserName,

        [parameter(mandatory = $true)]
        [securestring]$Password
    )
    try
    {
        $filePath = (Join-Path $env:LOCALAPPDATA -ChildPath ('{0}_{1}_kcredentials.xml' -f $env:USERNAME, $env:COMPUTERNAME))
        Remove-Item -Path $filePath -Force -ErrorAction SilentlyContinue
        Get-Credential -Credential ([PScredential]::new($UserName, $Password)) -ErrorAction Stop |
            Export-Clixml -Path $filePath -Force -ErrorAction Stop
        Test-Path -Path $filePath | Write-Output
    }
    catch
    {
        $false | Write-Output
    }
}


# Kaseya Connect PowerShell Script generator
function Save-KConnect {
try
{
$FilePath = (Join-Path -Path $env:LOCALAPPDATA -ChildPath 'kconnect.ps1')
Remove-Item -Path $FilePath -ErrorAction SilentlyContinue
$contents = @'
$creds = (Import-Clixml -Path (Join-Path -Path $env:LOCALAPPDATA -ChildPath ('{0}_{1}_kcredentials.xml' -f $env:USERNAME, $env:COMPUTERNAME))).GetNetworkCredential()
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Visible = $true
$ie.Navigate2('https://assist.customis.com/vsapres/web20/core/login.aspx')
$sw = '
[DllImport("user32.dll")]
public static extern int ShowWindow(int hwnd, int nCmdShow);
'
$type = Add-Type -Name ShowWindow2 -MemberDefinition $sw -Language CSharpVersion3 -Namespace Utils -PassThru
$type::ShowWindow($ie.hwnd, 3)
while ($ie.Busy) { Start-Sleep -Seconds 1 }
$user = $ie.Document.IHTMLDocument3_getElementById('UsernameTextbox')
if ($user) { $user.Value = $creds.UserName }
$pass = $ie.Document.IHTMLDocument3_getElementById('PasswordTextbox')
if ($pass) { $pass.Value = $creds.Password }
$button = $ie.Document.IHTMLDocument3_getElementById('SubmitButton')
if ($button) { $button.click() }
'@

New-Item -Path $FilePath -Value $contents -Force | Out-Null
$true | Write-Output
}
catch
{
    $false | Write-Output
}
}


<#
.SYNOPSIS
    Create a new Kaseya enhanced desktop shortcut
 
.DESCRIPTION
    Create a new desktop shortcut that launches Internet Explorer, navigates to CIS's Kaseya
    VSA, programmatically inputs the login credentials, logs in, and maximizes the Internet
    Explorer window
 
.EXAMPLE
    New-KaseyaShortcut (First Time)
 
    IF THIS IS THE FIRST TIME EVER this script is running, you will be prompted to enter in
    your Kaseya username and password. This information is securely encrypted and saved in your
    LocalAppData directory. A PowerShell script to control the interactions with Internet
    Explorer will also be saved to this directory. A desktop shortcut called "Connect Kaseya"
    will be created on your desktop. This desktop shortcut file may be moved to any other
    file system location.
     
    NOTE! Moving credential.xml and KConnect.ps1 files will result in this function asking for
    your credentials and saving these 2 files in the LocalAppData directory again.
 
.EXAMPLE
    Get-UpTime -ComputerName Server1, Server2 -Restart
 
    This example retrieves the specified computer's uptime using default credentials
 
.EXAMPLE
    Get-UpTime -ComputerName (Get-Content c:\list.txt) -Credential (Get-Credential)
 
    This example retrieves the computer names found in the file (one computer name per line)
    uptime using the specified credentials
#>

function New-KaseyaShortcut
{
    [cmdletbinding()]
    param
    (
        [string]$FilePath = (Join-Path -Path ([Environment]::GetFolderPath('Desktop')) -ChildPath 'Connect Kaseya.lnk'),
        [switch]$Force
    )
    
    begin
    {
        $cmdletName = $PSCmdlet.MyInvocation.MyCommand.Name
        $timer      = New-Object -TypeName System.Diagnostics.StopWatch
        Write-Verbose -Message ('{0} : {1} : Begin execution' -f $cmdletName, (Get-Date))
        $timer.Start()

        $CredPath = Join-Path -Path $env:LOCALAPPDATA -ChildPath ('{0}_{1}_kcredentials.xml' -f $env:USERNAME, $env:COMPUTERNAME)
        $connPath = Join-Path -Path $env:LOCALAPPDATA -ChildPath 'kconnect.ps1'
        if (!$Force)
        {
            Write-Verbose -Message ('{0} : Force not detected' -f $cmdletName)
            Write-Verbose -Message ('{0} : Checking for encrypted Kaseya credential file' -f $cmdletName)
            if (!(Test-Path $CredPath))
            {
                Write-Verbose -Message ('{0} : Could not find encrypted Kaseya credential file. FilePath = {1}' -f $cmdletName, $CredPath)
                ('Could not find encrypted credential file for {0}\{1}!' -f $env:COMPUTERNAME, $env:USERNAME) | Write-Warning
                $u = Read-Host -Prompt 'Kaseya user name'
                $p = Read-Host -Prompt 'Kaseya password' -AsSecureString
                if (Save-KCredentials -UserName $u -Password $p)
                {
                    Write-Verbose -Message ('{0} : Successfully saved encrypted Kaseya credential file. FilePath ={0}' -f $cmdletName, $CredPath)
                    ('{0} : Successfully saved encrypted Kaseya credential file.' -f $cmdletName)
                }
                else
                {
                    'ERROR! Unable to save credential file to {0}' -f $CredPath | Write-Error
                }
            }
            else
            {
                Write-Verbose -Message ('{0} : Detected encrypted Kaseya credential file.' -f $cmdletName)
            }
            Write-Verbose -Message ('{0} : Checking for KConnect PowerShell script' -f $cmdletName)
            if (!(Test-Path $connPath))
            {
                Write-Verbose -Message ('{0} : Could not find Kaseya Connect PowerShell script. ConnPath = {0}' -f $cmdletName, $connPath)
                if (Save-KConnect)
                {
                    Write-Verbose -Message ('{0} : Successfully saved Kaseya Connect PowerShell script. ConnPath = {0}' -f $cmdletName, $connPath)
                    ('{0} : Successfully saved Kaseya Connect PowerShell script.' -f $cmdletName)
                }
                else
                {
                    'ERROR! Unable to save Kaseya Connect PowerShell script to {0}' -f $connPath | Write-Error
                }
            }
            else
            {
                Write-Verbose -Message ('{0} : Kaseya Connect PowerShell script already exists.' -f $cmdletName)
            }
        }
        else
        {
            Write-Verbose -Message ('{0} : Force detected. Generating new credentials file and Kaseya Connect PowerShell script' -f $cmdletName)
            Remove-Item -Path $CredPath -Force -ErrorAction SilentlyContinue
            Remove-Item -Path $connPath -Force -ErrorAction SilentlyContinue
            Remove-Item -Path $FilePath -Force -ErrorAction SilentlyContinue
            $u = Read-Host -Prompt 'Kaseya user name'
            $p = Read-Host -Prompt 'Kaseya password' -AsSecureString
            if (Save-KCredentials -UserName $u -Password $p)
            {
                Write-Verbose -Message ('{0} : Successfully saved encrypted Kaseya credential file. FilePath ={0}' -f $cmdletName, $CredPath)
                ('{0} : Successfully saved encrypted Kaseya credential file.' -f $cmdletName)
            }
            else
            {
                'ERROR! Unable to save credential file to {0}' -f $CredPath | Write-Error
            }
            if (Save-KConnect)
            {
                Write-Verbose -Message ('{0} : Successfully saved Kaseya Connect PowerShell script. ConnPath = {0}' -f $cmdletName, $connPath)
                ('{0} : Successfully saved Kaseya Connect PowerShell script.' -f $cmdletName)
            }
            else
            {
                'ERROR! Unable to save Kaseya Connect PowerShell script to {0}' -f $connPath | Write-Error
            }
        }
    }

    process
    {
        try
        {
            $shell = New-Object -ComObject Wscript.Shell
            $shortcut = $shell.CreateShortcut($FilePath)
            $shortcut.TargetPath = $('C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe')
            $shortcut.Arguments = (' -noprofile -command "& {0}"' -f (Join-Path -Path $env:LOCALAPPDATA -ChildPath 'kconnect.ps1'))
            $shortcut.WindowStyle = 7
            $shortcut.Save()
        }
        catch
        {
            throw
        }
    }

    end
    {
        $timer.Stop()
        Write-Verbose -Message ('{0} : {1} : End execution' -f $cmdletName, (Get-Date))
        'Total execution time: {0} ms' -f $timer.ElapsedMilliseconds
    }
}


# Portal credentials helper function
function Save-PortalCredentials
{
    param
    (
        [parameter(mandatory)]
        [string]$UserName,

        [parameter(mandatory)]
        [securestring]$Password
    )
    try
    {
        $filePath = (Join-Path $env:LOCALAPPDATA -ChildPath ('{0}_{1}_portalcredentials.xml' -f $env:USERNAME, $env:COMPUTERNAME))
        Remove-Item -Path $filePath -Force -ErrorAction SilentlyContinue
        Get-Credential -Credential ([PSCredential]::new($UserName, $Password)) -ErrorAction Stop |
            Export-Clixml -Path $filePath -Force -ErrorAction Stop
        Test-Path -Path $filePath | Write-Output
    }
    catch
    {
        $false | Write-Output
    }
}

# Portal Connect PowerShell Script generator
function Save-PortalConnect {
try
{
$filePath = (Join-Path -Path $env:LOCALAPPDATA -ChildPath 'portalconnect.ps1')
Remove-Item -Path $filePath -ErrorAction SilentlyContinue
$contents = @'
$creds = (Import-Clixml -Path (Join-Path -Path $env:LOCALAPPDATA -ChildPath ('{0}_{1}_portalcredentials.xml' -f $env:USERNAME, $env:COMPUTERNAME))).GetNetworkCredential()
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Visible = $true
$ie.Navigate2('https://portal.customis.com')
$sw = '
[DllImport("user32.dll")]
public static extern int ShowWindow(int hwnd, int nCmdShow);
'
$type = Add-Type -Name ShowWindow2 -MemberDefinition $sw -Language CSharpVersion3 -Namespace Utils -PassThru
$type::ShowWindow($ie.hwnd, 3)
while ($ie.Busy){Start-Sleep -Seconds 1}
try {
    $username = $ie.Document.IHTMLDocument3_getElementById('os_username')
    if ($username) { $username.Value = $creds.UserName }
    $password = $ie.Document.IHTMLDocument3_getElementById('os_password')
    if ($password) { $password.Value = $creds.Password }
    $button = $ie.Document.IHTMLDocument3_getElementById('loginbutton')
    if ($button) { $button.click() }
} catch { throw }
'@

New-Item -Path $filePath -Value $contents -Force | Out-Null
Test-Path -Path $filePath | Write-Output
} catch { $false | Write-Output }
}


function New-PortalShortcut
{
    [cmdletbinding()]
    param
    (
        [string]$FilePath = (Join-Path -Path ([Environment]::GetFolderPath('Desktop')) -ChildPath 'Connect Portal.lnk'),
        [switch]$Force
    )
    
    begin
    {
        $cmdletName = $PSCmdlet.MyInvocation.MyCommand.Name
        $timer      = New-Object -TypeName System.Diagnostics.StopWatch
        Write-Verbose -Message ('{0} : {1} : Begin execution' -f $cmdletName, (Get-Date))
        $timer.Start()

        $CredPath = Join-Path -Path $env:LOCALAPPDATA -ChildPath ('{0}_{1}_portalcredentials.xml' -f $env:USERNAME, $env:COMPUTERNAME)
        $connPath = Join-Path -Path $env:LOCALAPPDATA -ChildPath 'portalconnect.ps1'
        if (!$Force)
        {
            Write-Verbose -Message ('{0} : Force not detected' -f $cmdletName)
            Write-Verbose -Message ('{0} : Checking for encrypted Portal credential file' -f $cmdletName)
            if (!(Test-Path $CredPath))
            {
                Write-Verbose -Message ('{0} : Could not find encrypted Portal credential file. FilePath = {1}' -f $cmdletName, $CredPath)
                ('Could not find encrypted credential file for {0}\{1}!' -f $env:COMPUTERNAME, $env:USERNAME) | Write-Warning
                $u = Read-Host -Prompt 'Portal user name'
                $p = Read-Host -Prompt 'Portal password' -AsSecureString
                if (Save-KCredentials -UserName $u -Password $p)
                {
                    Write-Verbose -Message ('{0} : Successfully saved encrypted Portal credential file. FilePath ={0}' -f $cmdletName, $CredPath)
                    ('{0} : Successfully saved encrypted Portal credential file.' -f $cmdletName)
                }
                else
                {
                    'ERROR! Unable to save credential file to {0}' -f $CredPath | Write-Error
                }
            }
            else
            {
                Write-Verbose -Message ('{0} : Detected encrypted Portal credential file.' -f $cmdletName)
            }
            Write-Verbose -Message ('{0} : Checking for PortalConnect PowerShell script' -f $cmdletName)
            if (!(Test-Path $connPath))
            {
                Write-Verbose -Message ('{0} : Could not find Portal Connect PowerShell script. ConnPath = {0}' -f $cmdletName, $connPath)
                if (Save-KConnect)
                {
                    Write-Verbose -Message ('{0} : Successfully saved Portal Connect PowerShell script. ConnPath = {0}' -f $cmdletName, $connPath)
                    ('{0} : Successfully saved Portal Connect PowerShell script.' -f $cmdletName)
                }
                else
                {
                    'ERROR! Unable to save Portal Connect PowerShell script to {0}' -f $connPath | Write-Error
                }
            }
            else
            {
                Write-Verbose -Message ('{0} : Portal Connect PowerShell script already exists.' -f $cmdletName)
            }
        }
        else
        {
            Write-Verbose -Message ('{0} : Force detected. Generating new credentials file and Portal Connect PowerShell script' -f $cmdletName)
            Remove-Item -Path $CredPath -Force -ErrorAction SilentlyContinue
            Remove-Item -Path $connPath -Force -ErrorAction SilentlyContinue
            Remove-Item -Path $FilePath -Force -ErrorAction SilentlyContinue
            $u = Read-Host -Prompt 'Portal user name'
            $p = Read-Host -Prompt 'Portal password' -AsSecureString
            if (Save-PortalCredentials -UserName $u -Password $p)
            {
                Write-Verbose -Message ('{0} : Successfully saved encrypted Portal credential file. FilePath ={0}' -f $cmdletName, $CredPath)
                ('{0} : Successfully saved encrypted Portal credential file.' -f $cmdletName)
            }
            else
            {
                'ERROR! Unable to save credential file to {0}' -f $CredPath | Write-Error
            }
            if (Save-PortalConnect)
            {
                Write-Verbose -Message ('{0} : Successfully saved Portal Connect PowerShell script. ConnPath = {0}' -f $cmdletName, $connPath)
                ('{0} : Successfully saved Portal Connect PowerShell script.' -f $cmdletName)
            }
            else
            {
                'ERROR! Unable to save Portal Connect PowerShell script to {0}' -f $connPath | Write-Error
            }
        }
    }

    process
    {
        try
        {
            $shell = New-Object -ComObject Wscript.Shell
            $shortcut = $shell.CreateShortcut($FilePath)
            $shortcut.TargetPath = $('C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe')
            $shortcut.Arguments = (' -noprofile -command "& {0}"' -f (Join-Path -Path $env:LOCALAPPDATA -ChildPath 'portalconnect.ps1'))
            $shortcut.WindowStyle = 7
            $shortcut.Save()
        }
        catch
        {
            throw
        }
    }

    end
    {
        $timer.Stop()
        Write-Verbose -Message ('{0} : {1} : End execution' -f $cmdletName, (Get-Date))
        'Total execution time: {0} ms' -f $timer.ElapsedMilliseconds
    }
}