lib/TMD.PowerShell.ps1
function Test-TMDIsRunningAction { $Global:TmdRunningAction ?? $false } Function Import-TMDProvider { param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)][String]$Provider ) ## Get the PS module Root folder $ModuleFolder = (Get-Module -ListAvailable TMD.Common).path | Split-Path -Parent $Provider = $Provider.Replace(' ', '-') $extension = '.ps1' $FilePath = Join-Path $ModuleFolder 'Providers' ($Provider + $extension) if (Test-Path -Path $FilePath) { . $FilePath } } Function Invoke-WindowsPowerShell { param( [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 0)][PSObject]$Params, [Parameter(Mandatory = $true, ValueFromPipeline = $false, Position = 0)][scriptblock]$ScriptBlock ) $WpsSession = New-PSSession -ComputerName ($env:COMPUTERNAME + '.' + $env:USERDNSDOMAIN) -ConfigurationName TMD51 -EnableNetworkAccess -Authentication Kerberos $StdOut = Invoke-Command -ScriptBlock $ScriptBlock -Session $WpsSession -ArgumentList $Params if ($null -ne $StdOut) { $ReturnObjects = $StdOut | Select-Object -Last 1 | ConvertFrom-Json -Depth 100 ## Create each of the items as a Variable object with the name foreach ($obj in $ReturnObjects.PSObject.Properties) { New-Variable -Name $obj.Name -Value $obj.Value -Force -Scope Global # $obj } } } #======================================================================== # Created By: Anders Wahlqvist # Website: DollarUnderscore (http://dollarunderscore.azurewebsites.net) #======================================================================== function Connect-PSSessionWhenReady { [CmdletBinding()] param ( [Parameter(Mandatory = $True, ValueFromPipeline)][System.Management.Automation.Runspaces.PSSession]$Session, [Parameter(Mandatory = $False, ValueFromPipeline)][Int]$MaxRetries = 51 ) ## Start a loop $ConnectionAttempts = 0 $SessionStateReady = $false While ($SessionStateReady -eq $false) { ## Set a Max Connection attempt limit if ($ConnectionAttempts -eq $MaxRetries) { $Session = New-TMDPSSession -Connect return # throw ('TMD is unable to connect to the TMD PowerShell Session ' + $Session.InstanceId + '. Please restart TMD and try again.') } ## Open the session or report the status if unable. This is a combination of Session STATE and AVAILABILITY switch ($Session.State) { "Opened" { if ($Session.Availability -eq 'Available') { $SessionStateReady = $True break } } "Disconnected" { switch ($Session.Availability) { "Available" { Connect-PSSession $Session -ErrorAction 'SilentlyContinue' | Out-Null if ($Session.Availability -eq 'Available') { $SessionStateReady = $true } break } "None" { Connect-PSSession $Session -ErrorAction 'SilentlyContinue' | Out-Null if ($Session.Availability -eq 'Available') { $SessionStateReady = $true } break } "Busy" { break } Default { break } } } "Broken" { ## The session is broken and is no longer functioning Remove it and create a new one Remove-PSSession $Session $Session = New-TMDPSSession $SessionStateReady = $True break } Default { Write-Verbose ("TMD-PS-Watcher: Session State is " + $Session.State) break } } ## If SessionReadyState is not ready if (-Not $SessionStateReady) { ## Start a Sleep Delay $SleepTime = (50..150 | Get-Random) Start-Sleep -Milliseconds $SleepTime $ConnectionAttempts++ ## Log Debugging Connecting attempts if ($ConnectionAttempts -eq 5) { Write-Host "SessionManager unable to connect 5 times" } if ($ConnectionAttempts -eq 50) { Write-Host "SessionManager unable to connect 50 times" } # ## The loop above should have connected to PowerShell, but if it didn't throw an error # if ($ConnectionAttempts -eq $MaxRetries) { # # throw ('Unable to connect to PowerShell Session' + $Session.InstanceId) # } } } } ## Provide a simple interface to get/create a TMD_<username> session on the local computer Function Get-TMDPSSession { [CmdletBinding()] param ( [Parameter()][Switch]$Connect, [Parameter()][Switch]$Disconnect, [Parameter()][Switch]$Enter ) <# # Stopping and starting TMD is unpredicitble since the PowerShell session could be affected by MANY circumstances like: # Are there still active running jobs # Did the computer hybernate # Change in network connectivity (VPN changes, blah blah) # etc. #> if ($IsWindows) { $SessionManagerInstanceId = Get-ItemProperty -Path 'HKCU:\SOFTWARE\TransitionManager' | Select-Object -ExpandProperty 'SessionManagerInstanceId' ## Get any existing PS Session started earlier $TmdUserSession = Get-PSSession -ConfigurationName 'TMD' -ComputerName 'localhost' -InstanceId $SessionManagerInstanceId -ErrorAction 'SilentlyContinue' ## There may be a broken session. If there is, remove it and create a new one. if ($TmdUserSession.State -eq 'Broken') { ## The session is broken and is no longer functioning Remove it and create a new one Remove-PSSession $TmdUserSessions $TmdUserSession = New-TMDPSSession } ## If there is no TMD session Found, try creating one if (-Not $TmdUserSession) { $TmdUserSession = New-TMDPSSession } ## Check again if to see if there was a failure above if (-Not $TmdUserSession) { Throw 'Unable to create a PowerShell Session.' } ## Handle Switches to interact with the session, or just return it if ($Connect) { Write-Host "Connecting to Session" -NoNewline Connect-PSSessionWhenReady $TmdUserSession | Out-Null Write-Host '...Connected' } if ($Disconnect) { Disconnect-PSSession $TmdUserSession | Out-Null Write-Host "Session Disconnected" } elseif ($Enter) { Write-Host "Entering Session" Connect-PSSessionWhenReady $TmdUserSession | Out-Null Enter-PSSession $TmdUserSession } else { ## If the session wasn't Connected, Disconnected or Entered, Return the Session return $TmdUserSession } } ## Get a Mac Session if ($IsMacOS) { ## Get any existing PS Session started earlier $KeyFilePath = (Join-Path $userPaths.credentials '.ssh' 'id_rsa') $TmdUserSession = New-PSSession -HostName 'localhost' -UserName $ENV:USER -KeyFilePath $KeyFilePath ## There may be a broken session. If there is, remove it and create a new one. if ($TmdUserSession.State -eq 'Broken') { ## The session is broken and is no longer functioning Remove it and create a new one Remove-PSSession $TmdUserSessions $TmdUserSession = New-TMDPSSession } ## If there is no TMD session Found, try creating one if (-Not $TmdUserSession) { $TmdUserSession = New-TMDPSSession } ## Check again if to see if there was a failure above if (-Not $TmdUserSession) { Throw 'Unable to create a PowerShell Session.' } ## Handle Switches to interact with the session, or just return it if ($Connect) { Write-Host "Connecting to Session" -NoNewline Connect-PSSessionWhenReady $TmdUserSession | Out-Null Write-Host '...Connected' } if ($Disconnect) { Disconnect-PSSession $TmdUserSession | Out-Null Write-Host "Session Disconnected" } elseif ($Enter) { Write-Host "Entering Session" Connect-PSSessionWhenReady $TmdUserSession | Out-Null Enter-PSSession $TmdUserSession } else { ## If the session wasn't Connected, Disconnected or Entered, Return the Session return $TmdUserSession } } } function New-TMDPSSession { [CmdletBinding()] param ( [Parameter()][Switch]$Disconnect, [Parameter()][Switch]$Enter ) ## Connect a Windows Session if ($IsWindows) { ## Create Session Name and create the session with environment defaults $TmdSessionConfigurationName = 'TMD' $TmdUserSession = New-PSSession -ConfigurationName $TmdSessionConfigurationName -ComputerName localhost -EnableNetworkAccess # Attempt Kerberos Authentication if (-not $TmdUserSession) { $TmdUserSession = New-PSSession -ConfigurationName $TmdSessionConfigurationName -ComputerName localhost -EnableNetworkAccess -ErrorAction SilentlyContinue -Authentication Kerberos | Out-Null } ## Attempt Negotiating Authentication if (-not $TmdUserSession) { $TmdUserSession = New-PSSession -ConfigurationName $TmdSessionConfigurationName -ComputerName localhost -EnableNetworkAccess -ErrorAction SilentlyContinue -Authentication Negotiate | Out-Null } ## Revert to Explicit Default Authentication, in case the user has a custom configuration if (-not $TmdUserSession) { $TmdUserSession = New-PSSession -ConfigurationName $TmdSessionConfigurationName -ComputerName localhost -EnableNetworkAccess -ErrorAction SilentlyContinue -Authentication 'Default' | Out-Null } ## If a TMD User Session isn't connected yet, throw an error. if (-not $TmdUserSession) { Write-Host 'PSSessionManager||Status||Session Creation Failed, Restart WinRM' Throw "Unable to create a TMD User Session. Please restart TMD." } ## Record the SessionInstanceID in the Registry for other processes to use Set-ItemProperty -Path "HKCU:\Software\TransitionManager" -Name "SessionManagerInstanceId" -Value $TmdUserSession.InstanceId ## While Connected, ensure the Session has a SessionCache Variable Invoke-Command -Session $TmdUserSession -ScriptBlock { ## Import TMD Import-Module TMD.Common ## Import TransitionManager Module Import-Module TransitionManager ## Import PoshRSJobs Import-Module PoshRSJob } ## Handle Switches to interact with the session, or just return it if ($Disconnect) { Disconnect-PSSession $TmdUserSession | Out-Null Write-Host "Session Disconnected" } elseif ($Enter) { Write-Host "Entering Session" Enter-PSSession $TmdUserSession } else { ## Return the new session return $TmdUserSession } } ## Connect on a Mac if ($IsMacOS) { ## Create an SSH based session to PowerShell on the SSH endpoint $KeyFilePath = (Join-Path $env:HOME '.ssh' 'id_rsa') $TmdUserSession = New-PSSession -HostName 'localhost' -UserName $ENV:USER -KeyFilePath $KeyFilePath if (-not $TmdUserSession) { Write-Host 'PSSessionManager||Status||Session Creation Failed, Restart TMD and Try again.' Throw "Unable to create a TMD User Session. Restart TMD" } ## While Connected, ensure the Session has a SessionCache Variable Invoke-Command -Session $TmdUserSession -ScriptBlock { ## Import TMD Import-Module TMD.Common ## Import TransitionManager Module Import-Module TransitionManager ## Import PoshRSJobs Import-Module PoshRSJob } ## Return the new session return $TmdUserSession } } |