Public/SCJBServerTeamTools.ps1

function SCJBServerTeamTools {
    <#
    .LINK
        https://www.powershellgallery.com/packages/SCJBServerTeamTools
    .EXAMPLE
        SCJBServerTeamTools
        Open to the home window
    .EXAMPLE
        SCJBServerTeamTools -QuickLaunch ADLookups
        Open to the ADLookups window
    .EXAMPLE
        SCJBServerTeamTools -QuickLaunch RSOP
        Open to the RSOP window
    .EXAMPLE
        STT -QuickLaunch SCCMDeployments
        Open using the alias to the SCCM deployments window
    .EXAMPLE
        STT -ShowSettings
        Outputs the tools settings file in a PS object.
    #>


    # MAIN TODO
    # - function for creating windows (leave out variable creation) or a foreach of each .xml in the assets folder.
    # - help window
    # - users - member of sccm collection (probably doesn't matter, we have like 10 user collections)
    # - IntuneDeviceDetails needs to exit cleaner back to the home window when there is an error, such as failing to login to graph.
    # - _UpdateSCCMDevices use API instead
    # - SCCM Collection members
    # - datagrid header padding between columns
    # - hide first run output of LoganShell folder creation
    # - getobjectsplat doesnt update when settings are changed. modify these things in the _loadsettings function?
    # - move 'constants' to this function.
    # - ComputerStats CPU round to 0 or 2 decimal places
    # - figure out how to resort datagrids
    # - user tab - actions with a verify messagebox
    # - computer tab - install updates
    # - computer tab - schedule reboot
    # - verify that the settingssaved variable works correctly
    # - add user or computer to a group with out-gridview
    # - computerstats - make filter stuff work on datagrid
    # - use the $Session variable for stuff
    # - options - default domain and add that to _SaveSettings

    [CmdletBinding()]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Blackholes are fun.')]

    param (
        [Parameter(Mandatory = $false)]
        [ValidateSet('ADLookups', 'RSOP', 'SCCMDeployments', 'ComputerStats', 'Passphrase')]
        [string]
        $QuickLaunch,

        [Parameter(Mandatory = $false, HelpMessage = 'This will just output the contents of the settings file.')]
        [switch]
        $ShowSettings
    )

    # WPF framework
    Add-Type -AssemblyName PresentationFramework

    # Windows Form framework (message boxes)
    Add-Type -AssemblyName System.Windows.Forms
    # Add-Type -AssemblyName System.Drawing

    #region Pre-run checks and constants
    # Testing for the AD module manifest since workstations and servers use different methods to check the installation of RSAT tools and this seems faster.
    if (Test-Path 'C:\WINDOWS\System32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.psd1') {
        Write-Warning 'RSAT AD Tools must be installed prior to use.'
        break
    }

    try {
        $Script:PDC = (Get-ADDomain).PDCEmulator
    }
    catch {
        Write-Warning 'Unable to contact a domain controller.'
        break
    }

    $Script:Module = Get-Module SCJBServerTeamTools
    $Script:ModuleName = 'SCJBServerTeamTools'

    #endregion

    #region First Run
    Write-Verbose 'Checking for first run'
    # TODO move this to it's own function with _InitialSettings
    if (!(Test-Path -Path $env:APPDATA\LoganShell\settings.json)) {
        # Settings file doesn't exist
        Write-Verbose 'First Run, creating directory and saving settings.'
        if (!(Test-Path -Path $env:APPDATA\LoganShell\)) {
            $Blackhole = New-Item -Path $env:APPDATA -Name 'LoganShell' -ItemType Directory
        }
        Write-Verbose 'Prompting for initial input'

        $userinput = Read-Host -Prompt 'Do you want to enable SCCM functionality? (Y/N)'
        switch ($userinput) {
            'Y' {
                $EnableSCCM = $true
                $SCCMServerInput = Read-Host -Prompt 'What is the hostname for the SCCM server?'
            }
            'N' {
                $EnableSCCM = $false
            }
            default {
                Write-Host 'Invalid option. Run again.'
                break
            }
        }

        $userinput = Read-Host -Prompt 'Do you want to enable Exchange on-premise functionality? (Y/N)'
        switch ($userinput) {
            'Y' {
                $EnableExchange = $true
            }
            'N' {
                $EnableExchange = $false
            }
            default {
                Write-Host 'Invalid option. Run again.'
                break
            }
        }

        Write-Verbose 'Setting temporary storage location'
        $ObjectFolder = 'C:\temp\Loganshell\'
        if (!(Test-Path $ObjectFolder)) {
            Write-Verbose 'Creating path.'
            New-Item -Path $ObjectFolder -ItemType Directory -Force -ErrorAction Stop
        }

        try {
            # Save default settings
            $InitialSplat = @{
                Path         = "$env:APPDATA\LoganShell\"
                SCCM         = $EnableSCCM
                SCCMServer   = $SCCMServerInput
                Exchange     = $EnableExchange
                DefaultDC    = $PDC
                ObjectFolder = $ObjectFolder
            }
            _InitialSettings @InitialSplat -ErrorAction Stop
        }
        catch {
            throw $_
        }
        Write-Verbose 'Done setting up for first run.'
    }
    #endregion

    #region Loading Settings
    Write-Verbose 'Loading Settings'
    try {
        $Script:STTSettings = _LoadSettings -File $env:APPDATA\LoganShell\Settings.json -ErrorAction Stop
    }
    catch {
        throw $_
    }

    if ($ShowSettings) {
        Write-Output $STTSettings
        break
    }



    if ($STTSettings.DCSettings.UseAlternateCredentials) {
        _LoadCredential
        $Script:SessionSplat = @{
            Credential = $StoredCredential
        }
    }
    else {
        $Script:SessionSplat = @{
        }
    }

    if ($STTSettings.DCSettings.DomainController) {
        if ($STTSettings.DCSettings.UseAlternateCredentials) {
            $Script:GetObjectSplat = @{
                Server     = $STTSettings.DCSettings.DomainController
                # TODO the stored credential has '\' at the start and that's making it mad.
                # _StoreCredentials was still using the domain box, removed that
                Credential = $StoredCredential
            }
        }
        else {
            $Script:GetObjectSplat = @{
                Server = $STTSettings.DCSettings.DomainController
            }
        }
    }
    else {
        $Script:GetObjectSplat = @{
        }
    }

    Write-Verbose ($SessionSplat | ConvertTo-Json)
    Write-Verbose ($GetObjectSplat | ConvertTo-Json)

    if ($STTSettings.Theme) {
        switch ($STTSettings.Theme) {
            'Light' {
                $ThemeFile = "$ModuleRoot\Assets\LightTheme.xaml"
            }
            'Dark' {
                $ThemeFile = "$ModuleRoot\Assets\DarkTheme.xaml"
            }
            'Violet' {
                $ThemeFile = "$ModuleRoot\Assets\VioletTheme.xaml"
            }
            Default {
                $ThemeFile = "$ModuleRoot\Assets\LightTheme.xaml"
            }
        }
    }
    else {
        # Old settings file
        $ThemeFile = "$ModuleRoot\Assets\LightTheme.xaml"
    }
    #endregion

    #region RSOP
    $xamlFile = "$ModuleRoot\Assets\RSOP.xaml"

    #create window
    $inputXML = Get-Content $xamlFile -Raw
    $inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace 'x:N', 'N' -replace '^<Win.*', '<Window'
    [XML]$XAML = $inputXML
    $xaml.window.'Window.Resources'.ResourceDictionary.'ResourceDictionary.MergedDictionaries'.ResourceDictionary.Source = $ThemeFile

    #Read XAML
    $reader = (New-Object System.Xml.XmlNodeReader $xaml)
    try {
        $RSOP = [Windows.Markup.XamlReader]::Load( $reader )
    }
    catch {
        Write-Warning $_.Exception
        throw
    }

    $xaml.SelectNodes('//*[@Name]') | ForEach-Object {
        try {
            Set-Variable -Name "RSOP_$($_.Name)" -Value $RSOP.FindName($_.Name) -ErrorAction Stop
        }
        catch {
            throw
        }
    }
    #endregion

    #region ADLookups
    $xamlFile = "$ModuleRoot\Assets\ADLookups.xaml"

    #create window
    $inputXML = Get-Content $xamlFile -Raw
    $inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace 'x:N', 'N' -replace '^<Win.*', '<Window'
    [XML]$XAML = $inputXML
    $xaml.window.'Window.Resources'.ResourceDictionary.'ResourceDictionary.MergedDictionaries'.ResourceDictionary.Source = $ThemeFile

    #Read XAML
    $reader = (New-Object System.Xml.XmlNodeReader $xaml)
    try {
        $ADLookups = [Windows.Markup.XamlReader]::Load( $reader )
    }
    catch {
        Write-Warning $_.Exception
        throw
    }

    $xaml.SelectNodes('//*[@Name]') | ForEach-Object {
        try {
            Set-Variable -Name "ADLookups_$($_.Name)" -Value $ADLookups.FindName($_.Name) -ErrorAction Stop
        }
        catch {
            throw
        }
    }
    #endregion

    #region SCCMDeployments
    $xamlFile = "$ModuleRoot\Assets\SCCMDeployments.xaml"

    #create window
    $inputXML = Get-Content $xamlFile -Raw
    $inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace 'x:N', 'N' -replace '^<Win.*', '<Window'
    [XML]$XAML = $inputXML
    $xaml.window.'Window.Resources'.ResourceDictionary.'ResourceDictionary.MergedDictionaries'.ResourceDictionary.Source = $ThemeFile

    #Read XAML
    $reader = (New-Object System.Xml.XmlNodeReader $xaml)
    try {
        $SCCMDeployments = [Windows.Markup.XamlReader]::Load( $reader )
    }
    catch {
        Write-Warning $_.Exception
        throw
    }

    $xaml.SelectNodes('//*[@Name]') | ForEach-Object {
        try {
            Set-Variable -Name "SCCMDeployments_$($_.Name)" -Value $SCCMDeployments.FindName($_.Name) -ErrorAction Stop
        }
        catch {
            throw
        }
    }
    #endregion

    #region ComputerStats
    $xamlFile = "$ModuleRoot\Assets\ComputerStats.xaml"

    #create window
    $inputXML = Get-Content $xamlFile -Raw
    $inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace 'x:N', 'N' -replace '^<Win.*', '<Window'
    [XML]$XAML = $inputXML
    $xaml.window.'Window.Resources'.ResourceDictionary.'ResourceDictionary.MergedDictionaries'.ResourceDictionary.Source = $ThemeFile

    #Read XAML
    $reader = (New-Object System.Xml.XmlNodeReader $xaml)
    try {
        $Global:ComputerStats = [Windows.Markup.XamlReader]::Load( $reader )
    }
    catch {
        Write-Warning $_.Exception
        throw
    }

    $xaml.SelectNodes('//*[@Name]') | ForEach-Object {
        try {
            Set-Variable -Name "ComputerStats_$($_.Name)" -Value $ComputerStats.FindName($_.Name) -Scope Global -ErrorAction Stop
        }
        catch {
            throw
        }
    }
    #endregion

    #region Passphrases
    $xamlFile = "$ModuleRoot\Assets\Passphrases.xaml"

    #create window
    $inputXML = Get-Content $xamlFile -Raw
    $inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace 'x:N', 'N' -replace '^<Win.*', '<Window'
    [XML]$XAML = $inputXML
    $xaml.window.'Window.Resources'.ResourceDictionary.'ResourceDictionary.MergedDictionaries'.ResourceDictionary.Source = $ThemeFile

    #Read XAML
    $reader = (New-Object System.Xml.XmlNodeReader $xaml)
    try {
        $Passphrases = [Windows.Markup.XamlReader]::Load( $reader )
    }
    catch {
        Write-Warning $_.Exception
        throw
    }

    $xaml.SelectNodes('//*[@Name]') | ForEach-Object {
        try {
            Set-Variable -Name "Passphrases_$($_.Name)" -Value $Passphrases.FindName($_.Name) -ErrorAction Stop
        }
        catch {
            throw
        }
    }
    #endregion

    #region Options
    $xamlFile = "$ModuleRoot\Assets\Options.xaml"

    #create window
    $inputXML = Get-Content $xamlFile -Raw
    $inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace 'x:N', 'N' -replace '^<Win.*', '<Window'
    [XML]$XAML = $inputXML
    $xaml.window.'Window.Resources'.ResourceDictionary.'ResourceDictionary.MergedDictionaries'.ResourceDictionary.Source = $ThemeFile

    #Read XAML
    $reader = (New-Object System.Xml.XmlNodeReader $xaml)
    try {
        $Options = [Windows.Markup.XamlReader]::Load( $reader )
    }
    catch {
        Write-Warning $_.Exception
        throw
    }

    $xaml.SelectNodes('//*[@Name]') | ForEach-Object {
        try {
            Set-Variable -Name "Options_$($_.Name)" -Value $Options.FindName($_.Name) -ErrorAction Stop
        }
        catch {
            throw
        }
    }
    #endregion

    #region HomeWindow
    $xamlFile = "$ModuleRoot\Assets\HomeWindow.xaml"

    #create window
    $inputXML = Get-Content $xamlFile -Raw
    $inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace 'x:N', 'N' -replace '^<Win.*', '<Window'
    [XML]$XAML = $inputXML
    $xaml.window.'Window.Resources'.ResourceDictionary.'ResourceDictionary.MergedDictionaries'.ResourceDictionary.Source = $ThemeFile

    #Read XAML
    $reader = (New-Object System.Xml.XmlNodeReader $xaml)
    try {
        $HomeWindow = [Windows.Markup.XamlReader]::Load( $reader )
    }
    catch {
        Write-Warning $_.Exception
        throw
    }

    $xaml.SelectNodes('//*[@Name]') | ForEach-Object {
        try {
            Set-Variable -Name "HomeWindow_$($_.Name)" -Value $HomeWindow.FindName($_.Name) -ErrorAction Stop
        }
        catch {
            throw
        }
    }
    #endregion

    _RSOPWindowUIEvents
    _ADLookupsUIEvents
    _SCCMDeploymentsUIEvents
    _ComputerStatsUIEvents
    _OptionsUIEvents
    _HomeWindowUIEvents
    _PassphrasesUIEvents

    #region Update Check
    Write-Verbose 'Update Check'
    $UpdateCheck = _CheckForUpdate
    if ($UpdateCheck.Outdated) {
        $HomeWindow_UpdateStatusBarItem.Content = 'Update Found!'
        $MessageSplat = @{
            MessageText  = "An update was found.`nCurrent version: $($UpdateCheck.Current)`nNew version: $($UpdateCheck.Newest)`nDo you want to update now?`n`nRelease Notes: $($UpdateCheck.ReleaseNotes)"
            MessageIcon  = 'Asterisk'
            ButtonType   = 'YesNo'
            MessageTitle = 'Update Found'
        }
        $Answer = _ShowMessageBox @MessageSplat
        switch ($Answer) {
            Yes {
                Update-Module $ModuleName
                Write-Host 'Module Updated. Open a new powershell session to load the new version.'
                break
            }
            No {
            }
            Default {
            }
        }
    }
    #endregion

    # Get-Variable Options_* | Select-Object Name

    switch ($QuickLaunch) {
        'ADLookups' {
            Write-Output 'Loading ADLookups....'
            $null = $ADLookups.ShowDialog()
        }
        'RSOP' {
            Write-Output 'Loading RSOP....'
            $null = $RSOP.ShowDialog()
        }
        'SCCMDeployments' {
            Write-Output 'Loading SCCM Deployments....'
            $null = $SCCMDeployments.ShowDialog()
        }
        'ComputerStats' {
            Write-Output 'Loading Computer Stats....'
            $null = $ComputerStats.ShowDialog()
        }
        'Passphrase' {
            Write-Output 'Loading Passphrases....'
            $null = $Passphrases.ShowDialog()
        }
        Default {
            Write-Output 'Loading ServerTeamTools....'
            $null = $HomeWindow.ShowDialog()
        }
    }
}