MyRemoteManager.psm1
# # Script module for module 'MyRemoteManager' # #Requires -Version 7.1 #Requires -PSEdition Core using namespace System.Management.Automation #region Enum enum Scopes { # Scopes in which a connection can be invoked Undefined = 1 Console = 2 External = 3 } #endregion Enums #region Classes class Item { # Name [ValidateLength(1, 50)] [ValidatePattern("^([a-zA-Z0-9_\-]+)$")] [string] $Name # Description [string] $Description [hashtable] Splat() { $Hashtable = [ordered] @{} foreach ($p in $this.PSObject.Properties) { $this.PSObject.Properties | ForEach-Object -Process { $Hashtable[$p.Name] = $p.Value } } return $Hashtable } } class Client : Item { # Executable [string] $Executable # Command template [string] $TokenizedArgs # Default port [UInt16] $DefaultPort # Default connection scope [Scopes] $DefaultScope # Does this client require a user [bool] $RequiresUser # Tokens static [string] $HostToken = "<host>" static [string] $PortToken = "<port>" static [string] $UserToken = "<user>" Client( [string] $Name, [string] $Executable, [string] $TokenizedArgs, [UInt16] $DefaultPort, [Scopes] $DefaultScope, [string] $Description ) { $this.Name = $Name $this.Executable = $Executable [Client]::ValidateTokenizedArgs($TokenizedArgs) $this.TokenizedArgs = $TokenizedArgs $this.DefaultPort = $DefaultPort $this.DefaultScope = $DefaultScope $this.RequiresUser = [Client]::UserTokenExists($TokenizedArgs) $this.Description = $Description } hidden static [void] ValidateTokenizedArgs( [string] $TokenizedArgs ) { @( [Client]::HostToken, [Client]::PortToken ) | ForEach-Object -Process { if ($TokenizedArgs -notmatch $_) { throw "The argument line does not contain the following token: {0}." -f $_ } } } hidden static [bool] UserTokenExists([string] $TokenizedArgs) { return $TokenizedArgs -match [Client]::UserToken } [string] GenerateArgs( [string] $Hostname, [UInt16] $Port ) { return $this.TokenizedArgs.Replace( [Client]::HostToken, $Hostname ).Replace( [Client]::PortToken, $Port ) } [string] GenerateArgs( [string] $Hostname, [UInt16] $Port, [string] $User ) { return $this.GenerateArgs($Hostname, $Port).Replace( [Client]::UserToken, $User ) } [string] ToString() { return "{0}, Description: `"{1}`", Scope: {2}, Command: `"{3} {4}`"" -f ( $this.Name, $this.Description, $this.DefaultScope, $this.Executable, $this.TokenizedArgs.Replace( "<port>", "<port:{0}>" -f $this.DefaultPort ) ) } } class Connection : Item { # Hostname [ValidatePattern("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$")] [string] $Hostname # Port [UInt16] $Port # Default client [string] $DefaultClient # Default user [string] $DefaultUser Connection( [String] $Name, [String] $Hostname, [UInt16] $Port, [string] $DefaultClient, [string] $DefaultUser, [string] $Description ) { $this.Name = $Name $this.Hostname = $Hostname.ToLower() $this.Port = $Port $this.DefaultClient = $DefaultClient $this.DefaultUser = $DefaultUser $this.Description = $Description } [bool] IsDefaultPort() { return $this.Port -eq 0 } [string] ToString() { return "{0}, Description `"{1}`", Default client {2}, Target {3}:{4}" -f ( $this.Name, $this.Description, $this.DefaultClient, $this.Hostname, $( if ($this.IsDefaultPort()) { "default" } else { $this.Port.ToString() } ) ) } } class Inventory { # Title for the inventory file [string] $Title = "MyRemoteManager inventory" # description for the inventory file [string] $Description = "MyRemoteManager inventory file where the connections and clients are stored" # Version of the inventory file [string] $Version = "0.1.0" # Path to the inventory file [string] $Path = [Inventory]::GetPath() # Collection of Clients [Client[]] $Clients # Collection of Connections [Connection[]] $Connections # Encoding for inventory file static [string] $Encoding = "utf-8" # Name of the environement variable to use a custom path to the inventory file static [string] $EnvVariable = "MY_RM_INVENTORY" static [string] GetPath() { foreach ($Target in @("Process", "User", "Machine")) { $Value = [System.Environment]::GetEnvironmentVariable( [Inventory]::EnvVariable, [System.EnvironmentVariableTarget]::"$Target" ) if ($Value) { return $Value } } return Join-Path -Path $env:USERPROFILE -ChildPath "MyRemoteManager.json" } [void] ReadFile() { # Get content from the file $GetContentParams = @{ Path = $this.Path Raw = $true Encoding = [Inventory]::Encoding ErrorAction = "Stop" } try { $Items = Get-Content @GetContentParams | ConvertFrom-Json -AsHashtable } catch { throw "Cannot open inventory: {0}" -f $_.Exception.Message } # Check version of the inventory if ($Items.Version -ne $this.Version) { throw ( "Version of the inventory file is not supported.", "Current version: `"{0}`", Expected version: `"{1}`"" -f ( $Items.Version, $this.Version ) ) } # Add every Client to inventory object foreach ($c in $Items.Clients) { $this.Clients += New-Object -TypeName Client -ArgumentList @( $c.Name, $c.Executable, $c.TokenizedArgs, $c.DefaultPort, $c.DefaultScope, $c.Description ) } # Check if Client name duplicates exist if ($this.ClientNameDuplicatesExist()) { Write-Warning -Message ("Fix the inventory by renaming the duplicated client names in the inventory file: {0}" -f ( [Inventory]::GetPath() ) ) } # Add every Connection to inventory object foreach ($c in $Items.Connections) { $this.Connections += New-Object -TypeName Connection -ArgumentList @( $c.Name, $c.Hostname, $c.Port, $c.DefaultClient, $c.DefaultUser, $c.Description ) } # Check if Connection name duplicates exist if ($this.ConnectionNameDuplicatesExist()) { Write-Warning -Message ( "Fix the inventory by renaming the duplicated connection names in the inventory file: {0}" -f ( [Inventory]::GetPath() ) ) } } [void] SaveFile() { $Items = [ordered] @{ Title = $this.Title Description = $this.Description Version = $this.Version Clients = @() Connections = @() } foreach ($c in $this.Clients) { $Items.Clients += $c.Splat() } foreach ($c in $this.Connections) { $Connection = $c.Splat() $Items.Connections += $Connection } $Json = ConvertTo-Json -InputObject $Items -Depth 3 $BackupPath = "{0}.backup" -f $this.Path if (Test-Path -Path $this.Path -PathType Leaf) { Copy-Item -Path $this.Path -Destination $BackupPath -Force } Set-Content -Path $this.Path -Value $Json -Encoding ([Inventory]::Encoding) -Force } hidden [bool] ClientNameDuplicatesExist() { $Duplicates = $this.Clients | Group-Object -Property Name | Where-Object -Property Count -GT 1 if ($Duplicates) { $Duplicates | ForEach-Object -Process { Write-Warning -Message ("It exists more than one client named `"{0}`"." -f $_.Name) } return $true } return $false } hidden [bool] ConnectionNameDuplicatesExist() { $Duplicates = $this.Connections | Group-Object -Property Name | Where-Object -Property Count -GT 1 if ($Duplicates) { $Duplicates | ForEach-Object -Process { Write-Warning -Message ("It exists more than one connection named `"{0}`"." -f $_.Name) } return $true } return $false } [Client] GetClient([string] $Name) { return $this.Clients | Where-Object -Property Name -EQ $Name } [Connection] GetConnection([string] $Name) { return $this.Connections | Where-Object -Property Name -EQ $Name } [bool] ClientExists([string] $Name) { return $this.GetClient($Name).Count -gt 0 } [bool] ConnectionExists([string] $Name) { return $this.GetConnection($Name).Count -gt 0 } [void] AddClient([Client] $Client) { if ($this.ClientExists($Client.Name)) { throw "Cannot add Client `"{0}`" as it already exists." -f $Client.Name } $this.Clients += $Client } [void] AddConnection([Connection] $Connection) { if ($this.ConnectionExists($Connection.Name)) { throw "Cannot add Connection `"{0}`" as it already exists." -f $Connection.Name } $this.Connections += $Connection } [void] RemoveClient([string] $Name) { $this.Clients = $this.Clients | Where-Object -Property Name -NE $Name } [void] RemoveConnection([string] $Name) { $this.Connections = $this.Connections | Where-Object -Property Name -NE $Name } } class ValidateClientName : ValidateArgumentsAttribute { [void] Validate( [System.Object] $Argument, [System.Management.Automation.EngineIntrinsics] $EngineIntrinsics ) { $Inventory = Import-Inventory if (-not $Inventory.ClientExists($Argument)) { Throw "Client `"{0}`" does not exist." -f $Argument } } } class ValidateConnectionName : ValidateArgumentsAttribute { [void] Validate( [System.Object] $Argument, [System.Management.Automation.EngineIntrinsics] $EngineIntrinsics ) { $Inventory = Import-Inventory if (-not $Inventory.ConnectionExists($Argument)) { Throw "Connection `"{0}`" does not exist." -f $Argument } } } class ValidateSetClientName : IValidateSetValuesGenerator { [string[]] GetValidValues() { try { $Inventory = Import-Inventory return $Inventory.Clients | ForEach-Object -Process { $_.Name } } catch { Write-Warning -Message $_.Exception.Message } return $null } } class ValidateSetConnectionName : IValidateSetValuesGenerator { [string[]] GetValidValues() { try { $Inventory = Import-Inventory return $Inventory.Connections | ForEach-Object -Process { $_.Name } } catch { Write-Warning -Message $_.Exception.Message } return $null } } #endregion Classes #region Private functions function Import-Inventory { <# .SYNOPSIS Import inventory. .DESCRIPTION Creates inventory object, reads inventory file and returns the object. .INPUTS None. You cannot pipe objects to New-DefaultClients. .OUTPUTS Inventory. Import-Inventory returns the inventory object. .EXAMPLE PS> Import-Inventory (Inventory) #> [OutputType([Inventory])] param () process { $Inventory = New-Object -TypeName Inventory $Inventory.ReadFile() } end { $Inventory } } function New-DefaultClients { <# .SYNOPSIS Creates default clients. .DESCRIPTION Creates and returns Client objects with popular programs. .INPUTS None. You cannot pipe objects to New-DefaultClients. .OUTPUTS Client[]. New-DefaultClients returns an array of Client objects. .EXAMPLE PS> New-DefaultClients (Client[]) #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope = "Function", Target = "*")] [OutputType([Client[]])] param () begin { $DefaultClient = @() } process { # OpenSSH (Microsoft Windows feature) $DefaultClient += New-Object -TypeName Client -ArgumentList @( "OpenSSH", "C:\Windows\System32\OpenSSH\ssh.exe", "-l <user> -p <port> <host>", 22, [Scopes]::Console, "OpenSSH (Microsoft Windows feature)" ) # PuTTY using SSH protocol $DefaultClient += New-Object -TypeName Client -ArgumentList @( "PuTTY_SSH", "putty.exe", "-ssh -P <port> <user>@<host>", 22, [Scopes]::External, "PuTTY using SSH protocol" ) # Microsoft Remote Desktop $DefaultClient += New-Object -TypeName Client -ArgumentList @( "RD", "C:\Windows\System32\mstsc.exe", "/v:<host>:<port> /fullscreen", 3389, [Scopes]::External, "Microsoft Remote Desktop" ) } end { $DefaultClient } } #endregion Private functions #region Public functions function Add-MyRMClient { <# .SYNOPSIS Adds MyRemoteManager client. .DESCRIPTION Adds client entry to the MyRemoteManager inventory file. .PARAMETER Name Name of the client. .PARAMETER Executable Path to the executable program that the client uses. .PARAMETER Arguments String of Arguments to pass to the executable. The string should contain the required tokens. Please read the documentation of MyRemoteManager. .PARAMETER DefaultPort Network port to use if the connection has no defined port. .PARAMETER DefaultScope Default scope in which a connection will be invoked. .PARAMETER Description Short description for the client. .INPUTS None. You cannot pipe objects to Add-MyRMClient. .OUTPUTS System.Void. None. .EXAMPLE PS> Add-MyRMClient -Name SSH -Executable "ssh.exe" -Arguments "-l <user> -p <port> <host>" -DefaultPort 22 .EXAMPLE PS> Add-MyRMClient -Name MyCustomClient -Executable "client.exe" -Arguments "--hostname <host> --port <port>" -DefaultPort 666 -DefaultScope External -Description "My custom client" #> [OutputType([string])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Mandatory = $true, HelpMessage = "Name of the client." )] [ValidateNotNullOrEmpty()] [string] $Name, [Parameter( Mandatory = $true, HelpMessage = "Path to the executable to run as client." )] [ValidateNotNullOrEmpty()] [string] $Executable, [Parameter( Mandatory = $true, HelpMessage = "Arguments as a tokenized string. Please, read the documentation to get the list of tokens." )] [ValidateNotNullOrEmpty()] [string] $Arguments, [Parameter( Mandatory = $true, HelpMessage = "Default port to connect to on the remote host." )] [ValidateNotNullOrEmpty()] [UInt16] $DefaultPort, [Parameter( HelpMessage = "Default scope in which a connection will be invoked." )] [ValidateNotNullOrEmpty()] [Scopes] $DefaultScope = [Scopes]::Console, [Parameter( HelpMessage = "Short description of the client." )] [string] $Description ) begin { $ErrorActionPreference = "Stop" try { $Inventory = Import-Inventory } catch { Write-Error -Message ( "Error inventory: {0}" -f $_.Exception.Message ) } } process { try { $Client = New-Object -TypeName Client -ArgumentList @( $Name, $Executable, $Arguments, $DefaultPort, $DefaultScope, $Description ) } catch { Write-Error -Message ( "Cannot create new client: {0}" -f $_.Exception.Message ) } if ($PSCmdlet.ShouldProcess( "Inventory file {0}" -f $Inventory.Path, "Add Client {0}" -f $Client.ToString() ) ) { $Inventory.AddClient($Client) try { $Inventory.SaveFile() Write-Verbose -Message ( "Client `"{0}`" has been added to the inventory." -f $Name ) } catch { Write-Error -Message ( "Cannot save inventory: {0}" -f $_.Exception.Message ) } } } } function Add-MyRMConnection { <# .SYNOPSIS Adds MyRemoteManager connection. .DESCRIPTION Adds connection entry to the MyRemoteManager inventory file. .PARAMETER Name Name of the connection. .PARAMETER Hostname Name of the remote host. .PARAMETER Port Port to connect to on the remote host. If not set, it will use the default port of the client. .PARAMETER DefaultClient Name of the default client. .PARAMETER DefaultUser Default client to use to connect to the remote host. .PARAMETER Description Short description for the connection. .INPUTS None. You cannot pipe objects to Add-MyRMConnection. .OUTPUTS System.Void. None. .EXAMPLE PS> Add-MyRMConnection -Name myconn -Hostname myhost -DefaultClient SSH .EXAMPLE PS> Add-MyRMConnection -Name myrdpconn -Hostname myhost -DefaultClient RDP -Description "My RDP connection" .EXAMPLE PS> Add-MyRMConnection -Name mysshconn -Hostname myhost -Port 2222 -DefaultClient SSH -DefaultUser myuser -Description "My SSH connection" #> [OutputType([string])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Mandatory = $true, HelpMessage = "Name of the connection." )] [ValidateNotNullOrEmpty()] [string] $Name, [Parameter( Mandatory = $true, HelpMessage = "Name of the remote host." )] [ValidateNotNullOrEmpty()] [string] $Hostname, [Parameter( HelpMessage = "Port to connect to on the remote host." )] [UInt16] $Port, [Parameter( Mandatory = $true, HelpMessage = "Default client to use to connect to the remote host." )] [ValidateSet([ValidateSetClientName])] [ValidateClientName()] [string] $DefaultClient, [Parameter( HelpMessage = "Default user to use to connect to the remote host." )] [string] $DefaultUser, [Parameter( HelpMessage = "Short description of the connection." )] [string] $Description ) begin { $ErrorActionPreference = "Stop" try { $Inventory = Import-Inventory } catch { Write-Error -Message ( "Cannot open inventory: {0}" -f $_.Exception.Message ) } } process { try { $Connection = New-Object -TypeName Connection -ArgumentList @( $Name, $Hostname, $Port, $DefaultClient, $DefaultUser, $Description )# } catch { Write-Error -Message ( "Cannot create new connection: {0}" -f $_.Exception.Message ) } if ($PSCmdlet.ShouldProcess( "Inventory file {0}" -f $Inventory.Path, "Add Connection {0}" -f $Connection.ToString() ) ) { $Inventory.AddConnection($Connection) try { $Inventory.SaveFile() Write-Verbose -Message ( "Connection `"{0}`" has been added to the inventory." -f $Name ) } catch { Write-Error -Message ( "Cannot save inventory: {0}" -f $_.Exception.Message ) } } } } function Get-MyRMClient { <# .SYNOPSIS Gets MyRemoteManager clients. .DESCRIPTION Gets available clients from the MyRemoteManager inventory file. Clients can be filtered by their name. .PARAMETER Name Filters clients by name. .INPUTS None. You cannot pipe objects to Get-MyRMClient. .OUTPUTS PSCustomObject. Get-MyRMClient returns objects with details of the available clients. .EXAMPLE PS> Get-MyRMClient (objects) .EXAMPLE PS> Get-MyRMClient -Name "custom_*" (filtered objects) #> [OutputType([PSCustomObject[]])] [CmdletBinding()] param ( [Parameter( HelpMessage = "Filter by client name." )] [ValidateNotNullOrEmpty()] [string] $Name = "*" ) begin { $ErrorActionPreference = "Stop" try { $Inventory = Import-Inventory } catch { Write-Error -Message ( "Error import inventory: {0}" -f $_.Exception.Message ) } } process { $Clients = @() foreach ($c in $Inventory.Clients) { $Clients += [PSCustomObject] @{ Name = $c.Name Command = "{0} {1}" -f $c.Executable, $c.TokenizedArgs DefaultPort = $c.DefaultPort DefaultScope = $c.DefaultScope Description = $c.Description } } } end { $Clients | Where-Object -Property Name -Like $Name | Sort-Object -Property Name } } function Get-MyRMConnection { <# .SYNOPSIS Gets MyRemoteManager connections. .DESCRIPTION Gets available connections from the MyRemoteManager inventory file. connections can be filtered by their name and/or client name. .PARAMETER Name Filters connections by name. .INPUTS None. You cannot pipe objects to Get-MyRMConnection. .OUTPUTS PSCustomObject. Get-MyRMConnection returns objects with details of the available connections. .EXAMPLE PS> Get-MyRMConnection (objects) .EXAMPLE PS> Get-MyRMConnection -Name "myproject_*" -Hostname "*.mydomain" -Client "*_myproject" (filtered objects) #> [OutputType([PSCustomObject[]])] [CmdletBinding()] param ( [Parameter( HelpMessage = "Filter by connection name." )] [ValidateNotNullOrEmpty()] [string] $Name = "*", [Parameter( HelpMessage = "Filter by hostname." )] [ValidateNotNullOrEmpty()] [string] $Hostname = "*", [Parameter( HelpMessage = "Filter by client name." )] [ValidateNotNullOrEmpty()] [string] $Client = "*" ) begin { $ErrorActionPreference = "Stop" try { $Inventory = Import-Inventory } catch { Write-Error -Message ( "Error import inventory: {0}" -f $_.Exception.Message ) } } process { $Connections = @() foreach ($c in $Inventory.Connections) { $Connections += [PSCustomObject] @{ Name = $c.Name Hostname = $c.Hostname Port = if ($c.IsDefaultPort) { $Inventory.GetClient($c.DefaultClient).DefaultPort } else { $c.Port } DefaultClient = $c.DefaultClient DefaultUser = $c.DefaultUser Description = $c.Description } } } end { $Connections | Where-Object -Property Name -Like $Name | Where-Object -Property Hostname -Like $Hostname | Where-Object -Property DefaultClient -Like $Client | Sort-Object -Property Name } } function Get-MyRMInventoryInfo { <# .SYNOPSIS Gets MyRemoteManager inventory information. .DESCRIPTION Gets detailed information about the MyRemoteManager inventory. .INPUTS None. You cannot pipe objects to Get-MyRMInventoryInfo. .OUTPUTS PSCustomObject. Get-MyRMInventoryInfo returns an object with detailed information. .EXAMPLE PS> Get-MyRMInventoryInfo (objects) #> [OutputType([PSCustomObject])] [CmdletBinding()] param () begin { $Inventory = New-Object -TypeName Inventory $FileExists = $false } process { if (Test-Path -Path $Inventory.Path -PathType Leaf) { $Inventory.ReadFile() $FileExists = $true } $InventoryInfo = [PSCustomObject] @{ Path = $Inventory.Path EnvVariable = [Inventory]::EnvVariable FileExists = $FileExists NumberOfClients = $Inventory.Clients.Count NumberOfConnections = $Inventory.Connections.Count } } end { $InventoryInfo } } function Invoke-MyRMConnection { <# .SYNOPSIS Invokes MyRemoteManager connection. .DESCRIPTION Invokes MyRemoteManager connection which is defined in the inventory. .PARAMETER Name Name of the connection. .PARAMETER Client Name of the client to use to initiate the connection. .PARAMETER User Name of the user to connect with. .PARAMETER Scope Scope in which the connection will be invoked. .INPUTS None. You cannot pipe objects to Invoke-MyRMConnection. .OUTPUTS System.Void. None. .EXAMPLE PS> Invoke-MyRMConnection myconn .EXAMPLE PS> Invoke-MyRMConnection -Name myconn -Client SSH -User root -Scope Console #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Position = 0, Mandatory = $true, HelpMessage = "Name of the connection." )] [ValidateSet([ValidateSetConnectionName])] [ValidateConnectionName()] [string] $Name, [Parameter( HelpMessage = "Name of the client to use to initiate the connection." )] [ValidateSet([ValidateSetClientName])] [ValidateClientName()] [Alias("c")] [string] $Client, [Parameter( HelpMessage = "Name of the user to connect with." )] [Alias("u")] [string] $User, [Parameter( HelpMessage = "Scope in which the connection will be invoked." )] [Alias("x")] [Scopes] $Scope ) begin { $ErrorActionPreference = "Stop" try { $Inventory = Import-Inventory } catch { Write-Error -Message ( "Error import inventory: {0}" -f $_.Exception.Message ) } } process { $Invocation = @{} $Invocation.Connection = $Inventory.GetConnection($Name) Write-Debug -Message ("Invoke connection {0}" -f $Invocation.Connection.ToString()) $Invocation.Client = if ($Client) { $Inventory.GetClient($Client) } else { $Inventory.GetClient($Invocation.Connection.DefaultClient) } Write-Debug -Message ("Invoke connection with client {0}" -f $Invocation.Client.ToString()) $Invocation.Port = if ($Invocation.Connection.IsDefaultPort()) { $Invocation.Client.DefaultPort } else { $Invocation.Connection.Port } Write-Debug -Message ("Invoke connection on port {0}" -f $Invocation.Port) $Invocation.Executable = $Invocation.Client.Executable $Invocation.Arguments = if ($Invocation.Client.RequiresUser) { if ($User) { $Invocation.Client.GenerateArgs( $Invocation.Connection.Hostname, $Invocation.Port, $User ) } elseif ($Invocation.Connection.DefaultUser) { $Invocation.Client.GenerateArgs( $Invocation.Connection.Hostname, $Invocation.Port, $Invocation.Connection.DefaultUser ) } else { Write-Error -Message "Cannot invoke connection: A user must be specified." } } else { $Invocation.Client.GenerateArgs( $Invocation.Connection.Hostname, $Invocation.Port ) } $Invocation.Command = "{0} {1}" -f $Invocation.Executable, $Invocation.Arguments Write-Debug -Message ("Invoke connection with command `"{0}`"" -f $Invocation.Command) $Invocation.Scope = if ($Scope) { $Scope } else { $Invocation.Client.DefaultScope } Write-Debug -Message ("Invoke connection in scope `"{0}`"" -f $Invocation.Scope) if ($PSCmdlet.ShouldProcess($Invocation.Connection.ToString(), "Initiate connection")) { switch ($Invocation.Scope) { ([Scopes]::Console) { Invoke-Expression -Command $Invocation.Command } ([Scopes]::External) { Start-Process -FilePath $Invocation.Executable -ArgumentList $Invocation.Arguments } ([Scopes]::Undefined) { Write-Error -Message "Cannot invoke connection: Scope is undefined." } default { Write-Error -Message "Cannot invoke connection: Scope is unknown." } } } } } function New-MyRMInventory { <# .SYNOPSIS Creates MyRemoteManager inventory file. .DESCRIPTION Creates a new inventory file where MyRemoteManager saves items. .PARAMETER NoDefaultClients Does not add defaults clients to the new inventory. .PARAMETER Force Overwrites existing inventory file. .PARAMETER PassThru Indicates that the cmdlet sends items from the interactive window down the pipeline as input to other commands. .INPUTS None. You cannot pipe objects to New-MyRMInventory. .OUTPUTS System.Void. None. or if PassThru is set, System.String. New-MyRMInventory returns a string with the path to the created inventory. .EXAMPLE PS> New-MyRMInventory .EXAMPLE PS> New-MyRMInventory -NoDefaultClients .EXAMPLE PS> New-MyRMInventory -Force .EXAMPLE PS> New-MyRMInventory -PassThru C:\Users\MyUsername\MyRemoteManager.json .EXAMPLE PS> New-MyRMInventory -NoDefaultClients -Force -PassThru C:\Users\MyUsername\MyRemoteManager.json #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( HelpMessage = "Do not add defaults clients." )] [switch] $NoDefaultClients, [Parameter( HelpMessage = "Overwrite existing inventory file." )] [switch] $Force, [Parameter( HelpMessage = "Indicates that the cmdlet sends items from the interactive window down the pipeline as input to other commands." )] [switch] $PassThru ) begin { $ErrorActionPreference = "Stop" $Inventory = New-Object -TypeName Inventory } process { if ((Test-Path -Path $Inventory.Path -PathType Leaf) -and -not ($Force.IsPresent)) { Write-Error -ErrorAction Stop -Exception ( [System.IO.IOException] "Inventory file already exists. Use `"-Force`" to overwrite it." ) } if ($PSCmdlet.ShouldProcess($Inventory.Path, "Create inventory file")) { if (-not $NoDefaultClients.IsPresent) { New-DefaultClients | ForEach-Object -Process { $Inventory.AddClient($_) } } try { $Inventory.SaveFile() Write-Verbose -Message ( "Inventory file has been created: {0}" -f $Inventory.Path ) } catch { Write-Error -Message ( "Cannot save inventory: {0}" -f $_.Exception.Message ) } } } end { if ($PassThru.IsPresent) { Resolve-Path $Inventory.Path | Select-Object -ExpandProperty Path } } } function Remove-MyRMClient { <# .SYNOPSIS Removes MyRemoteManager client. .DESCRIPTION Removes client entry from the MyRemoteManager inventory file. .PARAMETER Name Name of the client. .INPUTS None. You cannot pipe objects to Remove-MyRMClient. .OUTPUTS System.Void. None. .EXAMPLE PS> Remove-MyRMClient SSH .EXAMPLE PS> Remove-MyRMClient -Name SSH #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Position = 0, Mandatory = $true, HelpMessage = "Name of the client." )] [ValidateSet([ValidateSetClientName])] [ValidateClientName()] [string] $Name ) begin { $ErrorActionPreference = "Stop" try { $Inventory = Import-Inventory } catch { Write-Error -Message ( "Cannot open inventory: {0}" -f $_.Exception.Message ) } } process { if ($PSCmdlet.ShouldProcess( "Inventory file {0}" -f $Inventory.Path, "Remove Client {0}" -f $Name ) ) { $Inventory.RemoveClient($Name) try { $Inventory.SaveFile() Write-Verbose -Message ( "Client `"{0}`" has been removed from the inventory." -f $Name ) } catch { Write-Error -Message ( "Cannot save inventory: {0}" -f $_.Exception.Message ) } } } } function Remove-MyRMConnection { <# .SYNOPSIS Removes MyRemoteManager connection. .DESCRIPTION Removes connection entry from the MyRemoteManager inventory file. .PARAMETER Name Name of the connection. .INPUTS None. You cannot pipe objects to Remove-MyRMConnection. .OUTPUTS System.Void. None. .EXAMPLE PS> Remove-MyRMConnection myconn .EXAMPLE PS> Remove-MyRMConnection -Name myconn #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Position = 0, Mandatory = $true, HelpMessage = "Name of the connection." )] [ValidateSet([ValidateSetConnectionName])] [ValidateConnectionName()] [string] $Name ) begin { $ErrorActionPreference = "Stop" try { $Inventory = Import-Inventory } catch { Write-Error -Message ( "Cannot open inventory: {0}" -f $_.Exception.Message ) } } process { if ($PSCmdlet.ShouldProcess( "Inventory file {0}" -f $Inventory.Path, "Remove Connection {0}" -f $Name ) ) { $Inventory.RemoveConnection($Name) try { $Inventory.SaveFile() Write-Verbose -Message ( "Connection `"{0}`" has been removed from the inventory." -f $Name ) } catch { Write-Error -Message ( "Cannot save inventory: {0}" -f $_.Exception.Message ) } } } } function Set-MyRMInventoryPath { <# .SYNOPSIS Sets MyRemoteManager inventory path. .DESCRIPTION Sets the specific environment variable to overwrite default path to the MyRemoteManager inventory file. .PARAMETER Name Path to the inventory file. This path is set in a environment variable. Pass an empty string or null to reset to the default path. .PARAMETER Target Target scope where the environment variable will be saved. .INPUTS None. You cannot pipe objects to Set-MyRMInventoryPath. .OUTPUTS System.Void. None. .EXAMPLE PS> Set-MyRMInventoryPath C:\MyCustomInventory.json .EXAMPLE PS> Set-MyRMInventoryPath -Path C:\MyCustomInventory.json #> [OutputType([void])] [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter( Position = 0, Mandatory = $true, HelpMessage = "Path to the inventory file." )] [AllowEmptyString()] [string] $Path, [Parameter( HelpMessage = "Target scope of the environment variable." )] [ValidateSet("Process", "User")] [string] $Target = "User" ) begin { $EnvVar = [Inventory]::EnvVariable } process { if ($PSCmdlet.ShouldProcess( ("{0} environment variable {1}" -f $Target, $EnvVar), "Set value {0}" -f $Path ) ) { [System.Environment]::SetEnvironmentVariable( $EnvVar, $Path, [System.EnvironmentVariableTarget]::"$Target" ) Write-Verbose -Message ("{0} environment variable `"{1}`" has been set to `"{2}`"." -f $Target, $EnvVar, $Path) } } } function Test-MyRMConnection { <# .SYNOPSIS Tests MyRemoteManager connection. .DESCRIPTION Tests MyRemoteManager connection which is defined in the inventory. .PARAMETER Name Name of the connection. .INPUTS None. You cannot pipe objects to Test-MyRMConnection. .OUTPUTS System.String. Test-MyRMConnection returns a string with the status of the remote host. .EXAMPLE PS> Test-MyRMConnection myconn (status) .EXAMPLE PS> Test-MyRMConnection -Name myconn (status) #> [OutputType([string])] [CmdletBinding()] param ( [Parameter( Position = 0, Mandatory = $true, HelpMessage = "Name of the connection." )] [ValidateSet([ValidateSetConnectionName])] [ValidateConnectionName()] [string] $Name ) begin { $Inventory = Import-Inventory $Status = "Unknown" } process { $Connection = $Inventory.GetConnection($Name) $Port = if ($Connection.IsDefaultPort()) { $Inventory.GetClient($Connection.DefaultClient).DefaultPort } else { $Connection.Port } $TestConnectionParams = @{ TargetName = $Connection.Hostname TcpPort = $Port TimeoutSeconds = 3 ErrorAction = "Stop" } try { if (Test-Connection @TestConnectionParams) { Write-Information -MessageData ( "Connection {0} is up on port {1}." -f $Connection.ToString(), $Port ) -InformationAction Continue $Status = "Online" } else { Write-Error -Message ( "Connection: {0} is down on port {1}." -f $Connection.ToString(), $Port ) $Status = "Offline" } } catch { Write-Error -Message $_.Exception.Message $Status = "CriticalFailure" } } end { $Status } } #endregion Public functions |