#Filename: Get-PC.ps1
#This is the primary function for get-pc. All functionality is accessed through this function
#See documentation for how to use this module

#region Module Import Block

$ErrorActionPreference = 'SilentlyContinue'
#DevStage can take either Dev or Prod as values
$devStage = 'Dev'
#Locations for dev build and prod build
$getPCDevBuildPath = '\\int.samhealth.net\files\Tech2k\Apps\SHS Custom Desktop Apps\Get-PC\Get-PC\Get-PC.psd1'
$getPCProdBuildPath = "\\shscm01\packages\SHS Custom\Get-PC\Prod\Get-PC\Get-PC.psd1"
#grabs the version number out of the module manifest and compares it to the current production version
$modulemanifest = Join-Path (get-item $PSScriptRoot).Parent.FullName 'Get-PC.psd1'
$Version = (Test-ModuleManifest $modulemanifest).version

if ($devStage -eq "Prod") {
    $deployedVersion = (Test-ModuleManifest $getPCProdBuildPath).Version
}
elseif ($devStage -eq "Dev") {
    $deployedVersion = (Test-ModuleManifest $getPCDevBuildPath).Version
}

$getPCWrappedPath = '\\int.samhealth.net\files\Tech2k\Apps\SHS Custom Desktop Apps\Get-PC-Wrapped\backlog.txt'

$SparkQueryTimeoutSec = 5

#lets user know when get-pc has been imported and what version they are running
Write-Host "`nGet-PC Module Version $Version [$devStage] Loaded." -ForegroundColor Green
if ($Version -ne $deployedVersion) {
    Write-Host 'New version of Get-PC is available. Please run Get-PC -Update' -ForegroundColor Yellow
    if ($devStage -eq "Prod") {
        if ($getPCProdBuildPath -match '^(.*)\\[^\\]+$') { $getPCPatchNotesPath = "$($Matches[1])\patchnotes.txt" }
    }
    elseif ($devStage -eq "Dev") {
        if ($getPCDevBuildPath -match '^(.*)\\[^\\]+$') { $getPCPatchNotesPath = "$($Matches[1])\patchnotes.txt" }
    }
    #Print out the most recent patch notes entry
    $patchNotes = Get-Content $getPCPatchNotesPath 
    $msg = ''
    foreach ($line in $patchNotes) {
        #Matches an empty line that may contain whitespace
        if ($line -match '^\s*$') { break }
        $msg += "   $line`n"
    }
    Write-Host "$msg"
}
Write-Host "Enter 'Help Get-PC' for available commands.`n" -ForegroundColor Green
#endregion

Function Get-PC {
    <#
.Synopsis
    Get-PC is a custom Powershell module that contains many useful features for querying details about workstations and also performing many useful/common functions on those computers remotely.
.DESCRIPTION
    Using get-pc hostname will query a workstation (or multiple workstations, comma separated) for information such as model, SSO client, currently logged in user, OS version, RAM, installed printers, etc…

    Expanded functionality can be accessed using these flags
    -Apps               | shows the installed applications on the computer
    -AppDiff            | shows the installed applications that are not in the standard image
    -Bypass             | used in conjuction with remote viewer will bypass user prompt
    -ClearCCMCache      | clears CCM cache, may fix software center
    -CMDB               | only queries CMDB for info
    -Devices            | shows connected monitors and usb devices
    -DevicesUnplugged   | shows unplugged devices like scanners and printers
    -Enable             | re-enable computer account in AD
    -EventLog           | pulls up errors in the event log for the last x days
    -Excel              | exports collected data to csv and opens it with excel
    -Filesystem         | access the file system
    -GPUpdate           | runs a gpupdate
    -InstallNet35       | installs .Net 3.5
    -LogOffUser         | remotely logs off user
    -Monitor            | monitors computers for changes in status
    -NextPrinterName    | generates next open printer name
    -Orion              | opens orion to the mac address of the pc
    -PatchNotes         | read the patch notes
    -PCCleanup          | removes temp files and inactive user profiles
    -PCReboot           | reboots computer
    -PCRename           | renames computer
    -RemoteDesktopApp   | remote into computer using RDP
    -ResetRepository    | remotely resets repository
    -Resources          | more details about system resources
    -PrinterPurge       | removes printer from all online computers
    -SCCM               | queries sccm for the computer information
    -SHSPrinter         | pulls data about a printer using snmp
    -SHSPrinterweb      | pulls data about a printer and opens web interface and orion
    -SHSUser            | get data about a user from AD, can accept username or employee number
    -Tableview          | output the results in a table
    -UninstallProgram   | remotely uninstall a program from a pc
    -Usb                | shows installed usb devices
    -UserProfileBackup  | backups and restores user profiles
    -ViewerRemote       | remote into the computer using RemoteViewer
    -Wake               | attempts to wake on LAN
    -WinProfileRebuild  | rebuilds user profiles

    For more detailed help please see the documentation on AppDoc

.EXAMPLE
   Get-PC [Hostname], [Hostname], [Hostname]
.EXAMPLE
   Get-PC [Hostname] -TableView -Apps
.EXAMPLE
   Get-PC [Hostname] -FileSystem
#>	
    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [Switch]$Apps,
        [Switch]$AppDiff,
        [Switch]$Bypass,
        [Switch]$ClearCCMCache,
        [Switch]$CMDB,
        [Switch]$CMDBWeb,
        [switch]$Devices,
        [switch]$DevicesUnplugged,
        [switch]$Enable,
        [Switch]$EventLog,
        [switch]$Excel,
        [Switch]$FileSystem,
        [Switch]$GPUpdate,
        [Switch]$HostnamesByPrinter,
        [switch]$InstallNet35,
        [switch]$Jobsprinters,
        [Switch]$LogOffUser,
        [Switch]$Monitor,
        [Switch]$NextPrinterName,
        [switch]$Orion,
        [Switch]$PatchNotes,
        [Switch]$PCCleanup,
        [Switch]$PCReboot,
        [Switch]$PCRename,
        [Switch]$Quiet,
        [Switch]$RemoteDesktopApp,
        [Switch]$ResetRepository,
        [Switch]$Resources,
        [Switch]$PrinterPurge,
        [Switch]$SCCM,
        [Switch]$SHSPrinter,
        [switch]$SHSPrinterWeb,
        [Switch]$SHSUser,
        [Switch]$TableView,
        [Switch]$UninstallProgram,
        [switch]$Update,
        [switch]$Usb,
        [Switch]$UserProfileBackup,
        [Switch]$ViewerRemote,
        [Switch]$Wake,
        [Switch]$WinProfileRebuild
    )

    #Data collection for Get-PC Wrapped TM
    $invokeData = @{
        Hostname = $env:COMPUTERNAME
        Username = $env:USERNAME
        Datetime = Get-Date -Format 'yyyy-MM-ddThh:mm:ss.fffffff'
        Parameters = $MyInvocation.BoundParameters
    }
    Add-Content -Path $getPCWrappedPath -Value $(ConvertTo-Json -Compress $invokeData)

    ## Define which Spark Record Properties are Returned
     $Spark_Property_Return = @(
        "Name", 
        "ivnt_assetfulltype", 
        "ivnt_location", 
        "SHS_LocationDetails", 
        "AssetTag", 
        "SerialNumber", 
        "Owner", 
        "Status"
    )
    
    <#
    if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        Write-Warning "Get-PC requires powershell to be run as administrator. Please re-launch powershell as administrator." 
        break
    }
    #>

    # Stuff only run once
    if ($Update) {
        #updates get pc
        Update-GetPC
        return 
    }

    if ($PatchNotes) {
        $scriptparent = (get-item $PSScriptRoot ).parent.FullName
        Write-Host "`n"
        Get-Content $scriptparent\patchnotes.txt 
        break
    }
            
    if ($Jobsprinters) {
        #runs the printer query as a set of jobs
        $outPutArray = Get-PCBatchJob($ComputerName) -printers
        Write-Output $outPutArray
        return
    } 

    if($locationSearch){
        Write-Host 'Please enter a partial location like a room number.'
        if ($ComputerName[0]) { $searchInput = $ComputerName[0] }
        else { $searchInput = Read-Host 'Location' }
        Write-Host "Searching CMDB for $searchInput ..."
        if($searchInput.ToCharArray().Length -lt 3){
            Write-Host 'Location searches much contain more than 2 characters' -ForegroundColor Yellow
            return 
        }


        $searchResults = Search-CMDB -Location $searchInput | Sort-Object -Property Hostname 
        if($null -eq $searchResults){
            Write-Host 'No results for given search' -ForegroundColor Yellow
            return 
        }


        Write-Output $searchResults
        return

    }

    #Generates an available printer hostname. Useful for supplying a new hostname in new printer requests
    if ($NextPrinterName) {
        Find-NextPrinterName $ComputerName[0]
        break
    }


    $charA = $ComputerName.ToCharArray()
    if($charA -contains '*'){
        if($charA -lt 4){
            Write-Host "Wildcard searches need to be at least 4 characters long" -ForegroundColor Red
            return 
        }
        Write-Host "Starting CMDB Wildcard Search..."
        # $searchResults = Search-CMDB -hostname $ComputerName.Replace('*','') | Sort-Object -Property Hostname 

        $output = @()
        foreach ($comp in $ComputerName) {
            $searchResults = Find-ISMBO -BusinessObject "cis" -SearchQuery $comp | Sort-Object -Property Name 

            $output += Write-Output $searchResults | Select-Object $Spark_Property_Return
        }
        #$searchResults = Find-ISMBO -BusinessObject "cis" -SearchQuery $ComputerName.Replace('*','') | Sort-Object -Property Name 
        if($TableView){
           $output | Out-GridView -Title 'Get-PC Wildcard Search'
        }
        return $output
        
    }

    $getPCComputers = @() #List of computers that will get a batch query
    $outPutArray = @() #For use near the end of the script to output to users screen in a specified format
    $PCID = 0 #This is a helper variable for the progress bar

    $NumberofComputers = $ComputerName.Count
    foreach ($comp in $ComputerName) {
        $PCID++
        Write-Progress -Activity "Get-PC flags processing" -Status "$comp ($PCID/$NumberofComputers)" -PercentComplete ($PCID*100/$NumberOfComputers) -Id 1
        #Pulls user data from AD
        if ($SHSUser) {
            $user = Get-SHSUser $comp
            $outPutArray += $user
            continue            
        }

        $comp,$msg = Get-Hostname $comp
        Write-Progress -Activity "Get-PC flags processing" -Status "$comp ($PCID/$NumberofComputers)" -PercentComplete ($PCID*100/$NumberOfComputers) -Id 1
        if ($msg -and -not $SHSPrinter) {
            if ($SoundEnabled) {
                $oopsSoundPath = Join-Path (get-item $PSScriptRoot).Parent.FullName 'Data\gamblecore.wav'
                (New-Object Media.SoundPlayer $oopsSoundPath).Play()
            }

            Write-Host "`n$comp`n$msg" -ForegroundColor Red

            $props = [Ordered]@{
                Hostname       = "$comp"
                Status         = "$msg"
                'Current User' = ""
                'Last User(s)' = ""
                'IP | MAC'     = "" 
                Model          = ""
                'OS'           = ""
                'OS Build'     = ""
                'BIOS Ver'     = ""
                Encryption     = ""
                'Free Space'   = "" 
                RAM            = ""
                'SSO Client'   = ""
                'Kiosk Role'   = ""
                'Citrix Ver'   = ""
                'Asset Tag'    = ""
                'Service Tag'  = ""
                'Last Reboot'  = ""
                'TPM Status'   = ""
                'MBAM GPO'     = ""
                Printers       = ""                 
            }
            $obj = New-Object -TypeName PSObject -Property $props
            
            $outPutArray += $obj
            
            continue
        }

        if ($CMDB) {
            $outputArray += Get-CMDBFallback $comp
            continue
        }
        if ($CMDBWeb) {
            Invoke-CMDBWebLaunch $comp
            break
        }

                #DREW SUCKS

        #Pulls basic SNMP Data from printer $comp
        if ($SHSPrinter) {   
            $printer = Get-SHSPrinter $comp              
            $outPutArray += $printer
            $PCID++
            continue
        }
        
        if ($SHSPrinterWeb) {
            $printer = Get-SHSPrinter $comp
            Write-Output $printer
            Start-SHSPrinterWeb $printer
            break
        }
        
        #Grabs all hostnames that have the requested printer installed on it
        if ($HostnamesByPrinter) {
            Write-Progress -Activity "Hostnames by printer for $comp" -Status "Querying SCCM" -PercentComplete 50 -Id 1
            $printers = HostnamesByPrinter $comp
            $out = @()
            foreach ($printer in $printers) {
                if ($printer -notlike '*EPIC*') {
                    $out += $printer
                }
            }
            
            if ($TableView) {
                $out | Out-GridView -Title "Workstations Associated with $comp"
            }
            else {
                Write-Output $out
            }
            Write-Progress -Activity "Hostnames by printer for $comp" -Completed
            continue
        }

        if ($PrinterPurge) {
            Invoke-PrinterPurge $comp
            continue
        }
        
        if ($Wake) {
            Invoke-Wake $comp
            continue
        }

        if ($Enable) {
            if (-not ( Get-Module -ListAvailable -Name 'ActiveDirectory') ) {
                Write-Warning 'Active Drirectory Thick Client is required for this function'
                break
            }
            Invoke-EnableComputer $comp
            continue
        }

        
        #PING HERE - All commands after here either require the computer to be online or need to know
        #------------------------------------------------------------------------------------------------------------------------------------------------------------------#
        Write-Progress -Activity "Get-PC flags processing" -Status "Connecting to $comp ($PCID/$NumberofComputers)" -PercentComplete ($PCID*100/$NumberOfComputers) -Id 1
        $Connection = Test-Connection -ComputerName $comp -Count 1
        #Write-Progress -Activity "Connecting to computers" -Status "$comp ($PCID/$NumberofComputers)" -PercentComplete (($PCID / $NumberofComputers) * 100)
        
        if ($Connection) {
            $compStatus = 'Online'
        }
        else {
            $compStatus = 'Offline'
        }

        #grabs some basic usb info from the wmi
        if ($Usb) {
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }
            Get-USB $comp
            break
        }
        
        #grabs info on attached monitors and usb devices
        if ($Devices) {
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus skipping monitor query"
            }
            
            $out = Get-Devices $comp $compStatus
            if ($null -eq $out) {
                Write-Warning "Unable to get device info for $comp"
                continue
            }
            
            $outPutArray += $out
            continue
        }

        if ($DevicesUnplugged) {
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to query unplugged devices"
                continue
            }
            $outPutArray += Get-DevicesUnplugged $comp
            continue
        }
        
        #Pulls the event log for the past x days
        if ($EventLog) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }
            
            Get-PCEventLog $comp
            break
        }

        #Will remove selected users profile from C:\Users\ and cleans the registry of their profile
        if ($WinProfileRebuild) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }
            
            Get-WinProfileRebuild $comp
            break
        }

        if ($ResetRepository) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                continue
            }

            Get-ResetRepository $comp
            continue
        }

        #Backs up user profile to specified destination
        if ($UserProfileBackup) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }

            Get-UserProfileBackup $comp
            break 
        }

        #Remotely renames and restarts the computer. 
        if ($PCRename) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }

            Get-PCRename $comp
            break
        }
        
        #removes temp, cache files, and inactive user profiles
        if ($PCCleanup) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }

            Get-PCCleanup $comp
            Get-PCProfileCleanup $comp
            break   
        }

        #Will reboot the users selected PC
        if ($PCReboot) {
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }

            Get-PCReboot $comp
            break         
        }
        
        #Pulls brief overview of system resources
        if ($Resources) {
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                continue
            }
            get-resources $comp
            continue
        }
        
        #Pulls a list of installed programs from SCCM and Get-Package
        if ($Apps) {
            Get-Apps $comp $TableView
            continue 
        }

        if ($AppDiff) {
            Get-AppDiff $comp $TableView
            continue
        }

        if ($UninstallProgram) {
            Invoke-UninstallProgram $comp
            break
        }

        if ($GPUpdate) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                continue
            }
            
            Get-GPUpdate $comp
            continue
        }
        
        #Will not notify the user that another user is about to remote onto their PC
        if ($Bypass) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }

            Get-Bypass $comp
        }

        #Removes any indicator that another user is remoted onto their PC (like the green bar)
        if ($Quiet) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }

            Invoke-Quiet $comp
        }

        #Runs Remote Viewer, Thick Client Only
        if ($ViewerRemote) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }

            Get-ViewerRemote $comp
            break  
        }
        
        #Runs Remote Desktop Connection Application
        if ($RemoteDesktopApp) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }

            Get-RemoteDesktopApp $comp
            break
        }
        
        #Same as C$ into a workstation
        if ($FileSystem) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                continue 
            }

            Get-FileSystem $comp
            continue
        }

        if ($ClearCCMCache) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                continue 
            }
            Invoke-ClearCCMCache $comp
            continue
        }
        
        #Will log off the current logged in user for the designated PC
        #Adapted from Josh Gobens Logoff Script
        if ($LogOffUser) {
            
            if ($compStatus -ne 'Online') {
                Write-Warning "$comp is $compStatus unable to proccess command"
                break
            }
            
            Get-LogOffUser $comp
            break
        }

        if ($InstallNet35) {
            Get-InstallNet35 $comp
            continue
        }

        #Checks if there is a WinRM service error
        #TODO Re-evaluate where we want the WSNetwork test
        #$WSNetworkTest = Test-WSMan -ComputerName $comp
        #-or ($null -eq $WSNetworkTest)
        
        #checks to see if the computer is in AD or the disabled computers OU and warns the user
        if (get-module -ListAvailable -Name 'ActiveDirectory') {
            Write-Progress -Activity "Get-PC flags processing" -Status "Looking up $comp in AD ($PCID/$NumberofComputers)" -PercentComplete ($PCID*100/$NumberOfComputers) -Id 1
            try { 
                $adTest = ((Get-ADComputer $comp).DistinguishedName -match "Disabled Computers") 
                if ($adTest) {
                    Write-Warning "$comp is disabled"
                    Write-Warning "Run 'Get-PC $comp -Enable' to attempt to re-enable"
                }
            } catch { 
                Write-Warning "$comp is off the domain"
                if ($SoundEnabled) {
                    $oopsSoundPath = Join-Path (get-item $PSScriptRoot).Parent.FullName 'Data\xfiles.wav'
                    (New-Object Media.SoundPlayer $oopsSoundPath).Play()
                }
            }
        }
        
        #Direct SCCM Queries
        if ( $SCCM ) {
            $result = Get-SCCMQuery $comp $NumberofComputers $PCID
            $outPutArray += $result
            continue
        }
            
        #Checks if local computer
        if ($comp -eq $env:COMPUTERNAME) {
            
            $result = Get-PCLocal $comp $NumberofComputers $PCID
            $outPutArray += $result
        }
        else {
            $getPCComputers += $comp
        } 
        
        if ($Orion) {
            break
        }

    }
    Write-Progress -Activity "Get-PC flags processing" -Completed -Id 1

    # Monitor list of PCs for change in online status or 
    if ($Monitor) {
        Invoke-PCMonitor $getPCComputers
        return
    }
    
    #This is the get-pc of get-pc. It collects all the data from the computers and puts it in an array to be outputed
    if ($getPCComputers) { $outPutArray += Get-PCBatchInvoke($getPCComputers) }
    
    if ($Orion) {

        $mac = $outPutArray[0].'IP | MAC' -split " "
        if ($mac) {
            $mac = $mac[2] -split ","
            $mac = $mac[0]
            Write-Host "Launching Orion page to $mac"
            Start-Process "https://azporion1/Orion/UDT/EndpointDetails.aspx?NetObject=UE-MAC:VAL=$mac"
        }
        else {
            Write-Host "Unable to validate MAC"
        }
        
    }
    
    Write-Output $outPutArray
    
    #Creats an out grid view of all the data collected
    if ($TableView) {
        $outPutArray | Out-GridView -Title "Get-PC Grid"
    }
    
    #Takes the outputed data and exports it to a csv in the user's temp folder. Then opens that file with default csv viewer. It should be excel for most users
    if ($Excel) {
        ExportToExcel $outPutArray
    }
               
}
