CISPowerShell.psm1

<#
.SYNOPSIS
    Download Putty
 
.DESCRIPTION
    Download the x64 or x86 version of the latest version of Putty to a
    specified file path location
 
.EXAMPLE
    Download-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-d00mPutty -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 Download-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')
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
}
}

function New-KaseyaShortcut
{
    [cmdletbinding()]
    param
    (
        [string]$FilePath = (Join-Path -Path ([Environment]::GetFolderPath('Desktop')) -ChildPath 'Connect Kaseya.lnk')   
    )
    
    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)
        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)
        }

        $connPath = Join-Path -Path $env:LOCALAPPDATA -ChildPath 'kconnect.ps1'
        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)
        }
    }

    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 = 0
            $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
    }
}