functions/Invoke-ServiceInfo.ps1

Using namespace Terminal.Gui

<#
Create a Terminal.GUI form that displays service information for
a specified computer.
#>


#region helper functions

#these functions could be imported from external files

Function resetForm {
    $txtComputer.Text = ''
    $TableView.Table = $null
    $txtUser.Text = ''
    $txtPass.Text = ''
    $radioGroup.SelectedItem = 0
    $txtComputer.SetFocus()
    $StatusBar.Items[0].Title = Get-Date -Format g
    $StatusBar.Items[3].Title = 'Ready'
    [Application]::Refresh()
}
function GetServiceInfo {
    Param(
        $computerName = $txtComputer.Text.ToString().ToUpper()
    )

    $splat = @{
        ClassName   = 'win32_service'
        ErrorAction = 'Stop'
    }

    #check for alternate credentials
    If ((-Not [string]::IsNullOrEmpty($txtUser.text.toString()) -AND (-Not [string]::IsNullOrEmpty($txtPass.text.toString())))) {
        $user = $txtUser.Text.ToString()
        $pass = $txtPass.Text.ToString() | ConvertTo-SecureString -AsPlainText -Force
        $cred = [PSCredential]::New($user, $pass)
        Try {
            $cs = New-CimSession -ComputerName $Computername -Credential $cred -ErrorAction Stop
            $splat['CimSession'] = $cs
        }
        Catch {
            [MessageBox]::ErrorQuery('Error!', "Failed to create a session to $Computername. $($_.Exception.Message)", 0, @('OK'))
            $StatusBar.Items[0].Title = Get-Date -Format g
            $StatusBar.Items[3].Title = 'Ready'
            $txtComputer.SetFocus()
            [Application]::Refresh()
            return
        }
    }
    elseif ((-Not [string]::IsNullOrEmpty($txtUser.text.toString()) -AND ([string]::IsNullOrEmpty($txtPass.text.toString())))) {
        [MessageBox]::Query('Alert!', 'Did you forget to enter a password for your alternate credential?')
    }
    else {
        $splat['Computername'] = $Computername
    }

    Switch ($radioGroup.SelectedItem) {
        1 { $splat['filter'] = "State='Running'" }
        2 { $splat['filter'] = "State='Stopped'" }
    }
    # Query services using Get-CimInstance
    Try {
        $script:services = Get-CimInstance @splat |
        Group-Object -Property Name -AsHashTable -AsString
        $TableView.Table = $script:services.GetEnumerator() |
        ForEach-Object { $_.value |
            Select-Object Name, State, StartMode, DelayedAutoStart, StartName
        } | Sort-Object -Property Name | ConvertTo-DataTable

        $StatusBar.Items[0].Title = "Updated: $(Get-Date -Format g)"
        $StatusBar.Items[3].Title = $script:services[$TableView.Table.Rows[$TableView.SelectedRow].Name].DisplayName
        $TableView.SetFocus()
    }
    Catch {
        [MessageBox]::ErrorQuery('Error!', "Failed to query services on $($txtComputer.text.ToString()). $($_.Exception.Message)", 0, @('OK'))
        $StatusBar.Items[0].Title = Get-Date -Format g
        $StatusBar.Items[3].Title = 'Ready'
    }
    Finally {
        $txtComputer.SetFocus()
        [Application]::Refresh()
    }

}
Function ExportJson {
    if ($script:services) {
        $ReportDate = Get-Date
        $SaveDialog = [SaveDialog]::New()
        [Application]::Run($SaveDialog)
        if ((-Not $SaveDialog.Canceled) -AND ($SaveDialog.FilePath.ToString() -match 'json$')) {

            $StatusBar.Items[3].Title = "Exported to $($saveDialog.FilePath.ToString())"

            $script:services.GetEnumerator() |
            ForEach-Object { $_.value |
                Select-Object Name, State, StartMode, DelayedAutoStart, StartName,
                @{Name = 'Computername'; Expression = { $txtComputer.Text.toString() } },
                @{Name = 'ReportDate'; Expression = { $ReportDate } }
            } | ConvertTo-Json | Out-File -FilePath $SaveDialog.FilePath.ToString()
            [MessageBox]::Query('Export', "Service data exported to $($saveDialog.FilePath.ToString())", 0, @('OK'))`

        }
    } #if service data is found
    Else {
        [MessageBox]::ErrorQuery('Alert!', "No services to export from $($txtComputer.Text.toString())", 0, @('OK'))
    }
    $txtComputer.SetFocus()
}
Function ExportCsv {
    if ($script:services) {
        $ReportDate = Get-Date
        $SaveDialog = [SaveDialog]::New()
        [Application]::Run($SaveDialog)
        if ((-Not $SaveDialog.Canceled) -AND ($SaveDialog.FilePath.ToString() -match 'csv$')) {

            $StatusBar.Items[3].Title = "Exported to $($saveDialog.FilePath.ToString())"

            $script:services.GetEnumerator() |
            ForEach-Object { $_.value |
                Select-Object -Property Name, State, StartMode, DelayedAutoStart, StartName,
                @{Name = 'Computername'; Expression = { $txtComputer.Text.toString() } },
                @{Name = 'ReportDate'; Expression = { $ReportDate } }
            } | Export-Csv -Path $SaveDialog.FilePath.ToString() -NoTypeInformation

            [MessageBox]::Query('Export', "Service data exported to $($saveDialog.FilePath.ToString())", 0, @('OK'))`

        }
    } #if service data is found
    Else {
        [MessageBox]::ErrorQuery('Alert!', "No services to export from $($txtComputer.Text.toString())", 0, @('OK'))
    }
    $txtComputer.SetFocus()
}
Function ExportCliXML {
    if ($script:services) {
        $ReportDate = Get-Date
        $SaveDialog = [SaveDialog]::New()
        [Application]::Run($SaveDialog)
        if ((-Not $SaveDialog.Canceled) -AND ($SaveDialog.FilePath.ToString() -match 'xml$')) {

            $StatusBar.Items[3].Title = "Exported to $($saveDialog.FilePath.ToString())"

            $script:services.GetEnumerator() |
            ForEach-Object { $_.value |
                Select-Object -Property Name, State, StartMode, DelayedAutoStart, StartName,
                @{Name = 'Computername'; Expression = { $txtComputer.Text.toString() } },
                @{Name = 'ReportDate'; Expression = { $ReportDate } }
            } | Export-Clixml -Path $SaveDialog.FilePath.ToString()

            [MessageBox]::Query('Export', "Service data exported to $($saveDialog.FilePath.ToString())", 0, @('OK'))`

        }
    } #if service data is found
    Else {
        [MessageBox]::ErrorQuery('Alert!', "No services to export from $($txtComputer.Text.toString())", 0, @('OK'))
    }
    $txtComputer.SetFocus()
}
Function ShowHelp {
    #define help information
    [CmdletBinding()]
    Param()

    $title = 'ServiceInfo Help'

        $help = @"
 
  The form defaults to the local computer name. You can also enter
  the name of a remote compute, including alternate credentials. The
  password will be masked.
 
  Next, select a filtering option. Finally, click 'Get Info` to
  retrieve service information and display in a table.
 
  If you change the filter, you need to get services again.
 
  Under Export, you can export the displayed data to the selected
  data format.
 
"@

    $dialog = [Terminal.Gui.Dialog]@{
        Title         = $title
        TextAlignment = 'Left'
        Width         = 75
        Height        = 30
        Text          = $help
    }
    $ok = [Terminal.Gui.Button]@{
        Text = 'OK'
    }
    $ok.Add_Clicked({ $dialog.RequestStop() })
    $dialog.AddButton($ok)
    [Terminal.Gui.Application]::Run($dialog)

}
#endregion

Function Invoke-ServiceInfo {
    [cmdletbinding()]
    [alias('ServiceInfo')]
    Param(
        [Parameter(Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Computername = $env:COMPUTERNAME
    )

    if ($IsMacOS -OR $IsLinux) {
        Write-Warning "This command requires a Windows platform."
        return
    }
    If ($host.name -ne 'ConsoleHost') {
        Write-Warning 'This should be run in a PowerShell console host.'
        Return
    }

    #region setup

    [Application]::Init()
    [Application]::QuitKey = 27 #Esc

    #get the module version to display in the status bar
    $Ver = (Get-Command $($MyInvocation.MyCommand)).Version.ToString()

    #endregion

    #region create the main window and status bar
    $window = [Window]@{
        Title = 'Service Report'
    }

    $StatusBar = [StatusBar]::New(
        @(
            [StatusItem]::New('Unknown', $(Get-Date -Format g), {}),
            [StatusItem]::New('Unknown', 'ESC to quit', {}),
            [StatusItem]::New('Unknown', "v$Ver", {}),
            [StatusItem]::New('Unknown', 'Ready', {})
        )
    )

    [Application]::Top.add($StatusBar)
    #endregion

    #region add menus
    $MenuItem0 = [MenuItem]::New('_Clear form', '', { resetForm })
    $MenuItem1 = [MenuItem]::New('_Quit', '', { [Application]::RequestStop() })
    $MenuBarItem0 = [MenuBarItem]::New('_Options', @($MenuItem0, $MenuItem1))

    $ExportCsvMenuItem = [MenuItem]::New('as Csv', '', { ExportCsv })
    $ExportJsonMenuItem = [MenuItem]::New('as JSON', '', { ExportJson })
    $ExportXmlMenuItem = [MenuItem]::New('as XML', '', { ExportCliXML })
    #this is what will be on the menu bar
    $MenuBarItem1 = [MenuBarItem]::New('_Export', @($ExportCsvMenuItem, $ExportJsonMenuItem, $ExportXmlMenuItem))

    $MenuItem3 = [MenuItem]::New('A_bout', '', { ShowAbout})
    $MenuItem4 = [MenuItem]::New('_Documentation', '', { ShowHelp })
    $MenuBarItem2 = [MenuBarItem]::New('_Help', @($MenuItem3, $MenuItem4))

    $MenuBar = [MenuBar]::New(@($MenuBarItem0, $MenuBarItem1, $MenuBarItem2))
    $Window.Add($MenuBar)
    #endregion

    #region Add a label and text box for the computer name
    $lblComputer = [Label]@{
        X    = 1
        Y    = 2
        Text = 'Computer Name:'
    }
    $window.Add($lblComputer)

    $txtComputer = [TextField]@{
        X        = 10
        Y        = 2
        Width    = 35
        Text     = $Computername.ToUpper()
        TabIndex = 0
    }

    #make the computername always upper case
    $txtComputer.Add_TextChanged({$txtComputer.Text = $txtComputer.Text.ToString().ToUpper()})

    $window.Add($txtComputer)
    #endregion

    #region alternate credentials
    $CredentialFrame = [FrameView]::New('Credentials')
    $CredentialFrame.x = 50
    $CredentialFrame.y = 1
    $CredentialFrame.width = 40
    $CredentialFrame.Height = 5

    $lblUser = [Label]@{
        Text = 'Username:'
        X    = 1
    }
    $CredentialFrame.Add($lblUser)

    $txtUser = [TextField]@{
        X        = $lblUser.Frame.Width + 2
        Width    = 25
        TabIndex = 1
    }
    $CredentialFrame.Add($txtUser)

    $lblPass = [Label]@{
        Text = 'Password:'
        X    = 1
        Y    = 2
    }
    $CredentialFrame.Add($lblPass)
    $txtPass = [TextField]@{
        X        = $lblUser.Frame.Width + 2
        Y        = 2
        Width    = 25
        Secret   = $True
        TabIndex = 2
    }
    $CredentialFrame.Add($txtPass)

    $Window.Add($CredentialFrame)
    #endregion

    #region Add a button to query services
    $btnQuery = [Button]@{
        X        = 1
        Y        = 4
        Text     = '_Get Info'
        TabIndex = 4
    }
    $btnQuery.Add_Clicked({
            Switch ($radioGroup.SelectedItem) {
                0 { $select = 'All' }
                1 { $select = 'Running' }
                2 { $select = 'Stopped' }
            }
            $StatusBar.Items[3].Title = "Getting $select services from $($txtComputer.Text.ToString().toUpper())"
            $StatusBar.SetNeedsDisplay()
            $tableView.RemoveAll()
            $tableView.Clear()
            $tableView.SetNeedsDisplay()
            [Application]::Refresh()
            GetServiceInfo
        })
    $window.Add($btnQuery)
    #endregion

    #region add radio group
    $RadioGroup = [RadioGroup]::New(15, 3, @('_All', '_Running', '_Stopped'), 0)
    $RadioGroup.DisplayMode = 'Horizontal'
    $RadioGroup.TabIndex = 3
    #put the radio group next to the Get Info button
    $RadioGroup.y = $btnQuery.y
    $Window.Add($RadioGroup)
    #endregion

    #region Add a table view to display the results
    $TableView = [TableView]@{
        X        = 1
        Y        = 6
        Width    = [Dim]::Fill()
        Height   = [Dim]::Fill()
        AutoSize = $True
    }
    #Keep table headers always in view
    $TableView.Style.AlwaysShowHeaders = $True

    $TableView.Add_SelectedCellChanged({
            $StatusBar.Items[3].Title = $script:services[$TableView.Table.Rows[$TableView.SelectedRow].Name].DisplayName
    })

    $window.Add($TableView)
    #endregion

    # Run the application
    $txtComputer.SetFocus()

    [Application]::Top.Add($window)
    [Application]::Run()
    [Application]::ShutDown()
}
#end of file