I have a Function invokes a command that starts a new ps session on a remote server. The invoke command has an Exit clause however this is not exiting?
Function CreateID{
Invoke-Command -Session $Script:sesh -ScriptBlock{
Set-Location c:\
Import-Module ActiveDirectory
Try
{
If (Get-ADGroupMember "$Using:IDGroup" | Where-Object Name -match
"$Using:Computer")
{
Write-Host "Already in $using:IDGroup Exiting Script"
Disconnect-PSSession -Session $Script:sesh
Exit-PSSession
Exit
}
}
Catch
{ }
Write-Host "Did not Exit"
}
}
The Get-AD command works fine so where it should not display "did not exit" it does - how can i exit from a scriptblock in a remote ps session?
I am trying the disconnect session and Exit-pssession to see if they would do the same as simply exit but none of those are working.
I have also tried Break and no luck.
Ok so i figured this out - the ps session and invoke-command are red herrings. The basis of this is that you cannot Exit a Try/Catch statement.
i had to do this to get it to work - now it Exits. I just cannot use a Try/ Catch - if anyone knows how to exit a Try/Catch let me know!
#Try
#{
If (Get-ADGroupMember "$Using:IDGroup" | Where-Object Name -match
"$Using:Computer")
{
Write-Host "Already in $using:IDGroup Exiting Script"
Disconnect-PSSession -Session $Script:sesh
Exit-PSSession
Exit
}
#}
#Catch
#{ }
I don't know if this works for PSSession or not, and I don't have an environment to test it, but you can use this to exit powershell within a try catch
[Environment]::Exit(0)
Try/Catch should work in an invoke-command.
I don't usually invoke-commands to sessions, rather I use -ComputerName.
This worked fine for me:
invoke-command -ComputerName "MyDomainController" -ScriptBlock {
try { get-aduser "ValidUser" } catch { "doh!" }
}
I just tried this as well and it also worked:
$sess1 = New-PSSession -ComputerName MyDomainController
invoke-command -Session $sess1 -ScriptBlock { try { get-aduser "ValidUser" } catch { "doh!" } }
If I change either of those "ValidUser" values to invalid users I see the "doh!" as well.
Perhaps it's because you're trying to end the session from within the session.
You should deal with the output of the invoke-command or the function and then handle the session based on that.
Like using my lame example...
$sess1 = New-PSSession -ComputerName MyDomainController
$DoStuff = invoke-command -Session $sess1 -ScriptBlock { try { get-aduser "ValidUser" } catch { "doh!" } }
If ($DoStuff -eq "doh!") { Remove-PSSession $sess1 } else { $DoStuff }
Related
My goal is to loop through all devices , stop a specific service for all of those devices ( in this case, IntenAudioService), then go kill speciifc tasks realted to that service ( let's just say task IntelX and task IntelY, if they exist)
Then just loop through again and re-start those services. can this be done all in 1 for loop? Is the syntax correct?
$devices= <<user can populate devices in this object. DeviceName or deviceID??>>
>Foreach ($device in $devices){
Invoke-Command -ComputerName $device {
net-stop IntelAudioService
taskkill /IM IntelX.exe /F
net start IntelAudioService
}
}
What if I wanted to also set a service for each device? Something like this?
foreach ($device in $devices){
Invoke-Command -ComputerName $device {
Set-Service -Name BITS -StartupType Automatic
}
}
Try with this, note that you can Invoke-Command to multiple hostnames at the same time. You can also create a New-PSession with multiple computers at the same time.
$ErrorActionPreference = 'Stop'
$devices = 'Hostname1','Hostname2'
$serviceName = 'IntelAudioService' # This can be an array
$processName = 'IntelX' # This can be an array
# Note: Looping through the devices an attempting to establish a
# PSSession like below is good if you're not sure if the remote host
# is up or if the device name is the right one, etc. Using a Try {} Catch {}
# statement in this case will let you know if you couldn't connect with a
# specific remote host and which one.
# You can also simply do: $session = New-PSSession $devices without
# any loop which will be a lot faster of course, however,
# if you fail to connect to one of the remote hosts
# you will get an error and the the PSSession cmdlet will stop.
$session = foreach($device in $devices)
{
try
{
New-PSSession $device
}
catch
{
Write-Warning $_
}
}
Invoke-Command -Session $session -ScriptBlock {
Get-Service $using:serviceName | Stop-Service -Force -Verbose
Get-Process $using:processName | Stop-Process -Force -Verbose
Start-Service $using:serviceName -Verbose
# Set-Service -Name $using:serviceName -StartupType Automatic
}
Remove-PSSession $session
I am working on developing PowerShell script to automate a task on a remote server by using Invoke-Command with WinRM.
The script will take the server IP, test WinRM and "Get-Credential" cmdlet to establish session and use Invoke-Command to run another script on remote server. I have made significant progress of what I want to achieve, however, I am having trouble on how to setup the code so that when I press the "Cancel" or "X" button on Get-Credential prompt it should abort the script and return to the regular PowerShell command line prompt.
Below is what I have so far, I have erased the comments and description of the code to keep the number of words less in here.
function SS
{
Add-Type -AssemblyName System.Windows.Forms
$BInput = [System.Windows.Forms.MessageBox]::Show('Do you want to proceed?', 'Confirmation',[System.Windows.Forms.MessageBoxButtons]::YesNo)
switch ($BInput)
{
"Yes" {
while ($true)
{
$server=Read-Host "Enter Server IP Address"
set-item -Path WSMan:\localhost\Client\TrustedHosts -Value "$server" -Force
if(Test-WSMan -ComputerName $server -ErrorAction SilentlyContinue)
{
Write-Host "$server is accessible, enter credentials to connect"
while ($true)
{
$creden=Get-Credential -Message "Please enter the server credentials that you want to connect"
$serversession = New-Pssession -ComputerName $server -Credential $creden -ErrorAction SilentlyContinue
if(-not($serversession))
{
write-warning "Credentials are not valild, please try again"
}
else
{
write-host "$server is connected, starting the workflow ......"
Invoke-Command -Session $serversession -FilePath "C:\Temp\XXX.ps1"
}
}
Break
}
else
{
write-host "Windows Remote Management (WinRM) protocol is not running, please check service and confirm."
}
}
Get-Pssession | Remove-PSSession
}
"No" {
Break
}
}
}
I understand I have to apply the changes / logic after this line
$creden=Get-Credential -Message "Please enter the server credentials that you want to connect"
But can't seem to find it yet. I looked online and have taken different approaches but no success so far. I would like to have opinions or recommendations on how to tackle this, appreciate your help.
Thanks
What i'm seeing is that you may be thinking too much into it. A simple if statement should do the trick, try:
$creden=Get-Credential -Message "Please enter the server credentials that you want to connect"
if(!$creden){break}
Continuing from my comment.
Try this refactor of your use case.
Point of note: Note fully tested since I do not have an environment at this time to test.
Function Start-WorkFlow
{
<#
.Synopsis
Execute a workflow
.DESCRIPTION
Sets up a WinRM session to a remote host to execute the defined workflow
.EXAMPLE
Start-WorkFlow
.EXAMPLE
swf
.INPUTS
Remote host IPAddress
Remove host credentials
.OUTPUTS
Resutls of teh workflow
.NOTES
V 0.0.1 - Prototype script. Clean-Up before production use
.COMPONENT
Stand-alone script
.ROLE
Administrative actions
.FUNCTIONALITY
Implemetned error logic for each code block
Restrict the user input to only be a proper IPAddress
Validate TCPIP state
Validate WSman state
Establish a new session
Process workflow
Exit session
#>
[cmdletbinding(SupportsShouldProcess)]
[Alias('swf')]
Param
(
)
If ((Read-Host -Prompt 'Do you want to proceed: [Yes/No]') -eq 'No' )
{Break}
Else
{
Do {$RemoteServerIPAddress = (Read-Host -Prompt 'Enter Server IP Address')}
Until ($RemoteServerIPAddress -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
Get-ChildItem -Path 'WSMan:\localhost\Client\TrustedHosts'
Try
{
(Test-Connection -ComputerName $RemoteServerIPAddress -Count 1 -ErrorAction Stop).IPV4Address
# Set-Item -Path 'WSMan:\localhost\Client\TrustedHosts' -Value $RemoteServerIPAddress -Force
Get-ChildItem -Path 'WSMan:\localhost\Client\TrustedHosts'
Try
{
Test-WSMan -ComputerName $RemoteServerIPAddress -ErrorAction Stop
"$RemoteServerIPAddress is accessible, enter credentials to connect"
Do
{
$Creds = $null
$CredMesssage = 'Please enter the remote server credentials that you want to connect.'
$CredMesssage = "$CredMesssage If credentials are not valid, you will be prompted to re-enter them."
$Creds = Get-Credential -Message $CredMesssage
if(-Not $creds)
{
Write-Warning -Message 'Credential request cancelled.'
Start-Sleep -Seconds 3
Exit
}
$NewPSSessionSplat = #{
ComputerName = $RemoteServerIPAddress
Credential = $Creds
Name = 'RemoteSessionName'
ErrorAction = 'Stop'
}
New-PSSession $NewPSSessionSplat
}
Until (Get-PSSession -Name 'RemoteSessionName')
"$RemoteServerIPAddress is connected, starting the workflow ......"
Invoke-Command -Session $RemoteServerSession -FilePath 'C:\Temp\XXX.ps1'
}
Catch
{
Write-Warning -Message 'Session connection results:'
$PSitem.Exception.Message
}
Finally
{
Get-PSSession |
Remove-PSSession -ErrorAction SilentlyContinue
}
}
Catch
{
Write-Warning -Message "
The remote server $RemoteServerIPAddress is not available
Exiting the session."
Start-Sleep -Seconds 3
Exit
}
}
}
Start-WorkFlow
I have a script that stops a service then renames a folder on the local computer, i would like to be able to make a run remotely by asking the command executer to enter the machine name of where it would like to run the script then execute.
try {
Stop-service wuauserv -force
} catch {
Write-error "Unable to stop the service WUAUSERV"
start-sleep -seconds 5
exit
}
try {
Rename-Item "C:\Windows\SoftwareDistribution" "C:\Windows\SoftwareDistribution.old" -force
} catch {
Write-error "Unable to rename the folder :("
start-service wuauserv
start-sleep -seconds 5
exit
}
start-service wuauserv
I would probably have the script take a parameter by adding this at the top
Param(
[Parameter(Mandatory)][string]$ComputerName
)
The Mandatory value will prompt the user to enter a value if it was not supplied.
Alternatively if you really want to manually ask the user you can use this:
$ComputerName = Read-Host "ComputerName"
This will also prompt the user to enter a variable.
Then use Invoke-Command to run your logic on the remote computer
Invoke-Command -ComputerName $ComputerName -ScriptBlock {
# Your code here..
}
Edit:
After your update, I notice you terminate the script by calling exit. I would advise you to use return instead. If someone runs this script from the commandline the script will kill the terminal, which is quite annoying.
Edit2: Here is a complete working example, with my recommendations:
Param(
[Parameter(Mandatory)][string]$ComputerName
)
Invoke-Command -ComputerName $ComputerName -ScriptBlock {
try {
Stop-Service wuauserv -Force
} catch {
Write-error "Unable to stop the service WUAUSERV"
Start-Sleep -Seconds 5
return
}
try {
Rename-Item "C:\Windows\SoftwareDistribution" "C:\Windows\SoftwareDistribution.old" -Force
} catch {
Write-error "Unable to rename the folder :("
Start-Service wuauserv
Start-Sleep -Seconds 5
return
}
Start-Service wuauserv
}
Currently trying to use PowerShell workflow to process some remote server stuff that I am doing in parallel.
I am having no luck trying to get the Invoke-Command to log onto the server to actually work. It is not throwing an error but there also is no output to the console like there usually is with a Write-Host statement. Not seeing anything that is helping me looking through older posts.
workflow test {
Param([System.Management.Automation.PSCredential]$cred)
InlineScript {
Write-Host $using:cred
Invoke-Command -Computer "server" -Credential $using:cred -ScriptBlock { Write-Host "hello world" } }
}
}
#Calling the function
test $cred
write-output instead of write-host works. Note that this runs in parallel too:
invoke-command computer1,computer2,computer3 { 'whatever' }
By the way, you have an extra curly bracket at the end.
Another way to do it:
workflow test {
InlineScript {
Write-Host 'hello world'
}
}
test -pscomputername server -pscredential $cred
There's a strange issue in my script.
I'm invoking a command on some servers. If I can't connect to the server (because it's offline or something) I still want to log that (Server is offline > log.txt)
Apparently, when an error occurs in Try block, the Catch block is not executed.
To test this, I wrote a value to the ComputerName parameter that doesn't exist.
Here's my code:
Try {
Invoke-Command -ComputerName hui -ScriptBlock $sb
}
Catch {
Write-Host "Hello"
}
But the Script never puts out "Hello"
The exception I get is a PSRemotingTransportException. I also tried to specify the exception, also didn't work
When I set a breakpoint at the Invoke-Command line, it also ignores the catch block. Why?
It's not a terminating error, so it will never be caught by try/catch. Add -ErrorAction Stop:
Try {
Invoke-Command -ComputerName hui -ScriptBlock $sb -ErrorAction Stop
}
Catch {
Write-Host "Hello"
}