Retrieving real time info from VM's with PowerCLI - powershell

I have a couple of lines in a script that are giving me an issue:
Connect-VIServer "test-vcenter.test.com" -User user -Password pass
Get-VM -Name "test-vm" | Get-Stat -Stat cpu.ready.summation -Realtime | Select-Object -First 1 value | Format-List
When running this I receive this as output:
Operation is not valid due to the current state of the object.
At :line:0 char:0
If the second line is run a few seconds after the connection to vCenter is made I receive the output I expect. What I believe is happening is that my connection to vCenter hasn't completed before my second line has started. I am not sure of the best way to wait for or what to check for in a completed connection.

you might try using start-sleep command in PS
http://technet.microsoft.com/en-us/library/ee177002.aspx
however -- as with all "sleep" functions, this isn't necessarily the best way to fix a timing problem as your pushback never may never account for all possible latency issues.
a better solution would be to test for the completion of a command (and I'm not sure how to do that with the VMWare CLI)

Related

Powershell Script cycles through machines but hangs if one loses network temporarily

I have a powershell script that parses a txt file which is full of machine names, then one by one, it creates a session to the system, runs a few commands, and moves to the next system. The script usually take about 10-30 seconds to run on each system depending on the case encountered in the script.
Once in a while the system that is currently being checked will lose the network connection for some various reason. When this happens the console starts writing yellow warning messages about attempting to reconnect for 4 minutes and then disconnects the session when it cannot reconnect.
Even if it establishes the connection again within the 4 minutes, it doesn't do anything after that, it's like the script just freezes. It won't move on to the next system and it doesn't stop the script, I have to manually stop it, or if i manually run the script, i can hit control+c to break out of the current loop, and it then moves on to the next machine in the list.
Is there any way to break out of the current loop if a warning is encountered so it can move on to the next machine? That would be my ideal solution. thanks!
Script is simple..
foreach($server in Get-Content .\machines.txt) {
if($server -match $regex){
invoke-command $server -ErrorAction SilentlyContinue -ScriptBlock{
command1
command2
command3
}
}
this is what happens
PS C:\temp> .\script.ps1
machine1
machine2
machine3
machine4
machine5
WARNING: The network connection to machine5 has been interrupted. Attempting to reconnect for up to 4 minutes...
WARNING: Attempting to reconnect to machine5 ...
WARNING: Attempting to reconnect to machine5 ...
WARNING: Attempting to reconnect to machine5 ...
WARNING: The network connection to machine5 has been restored.
But it never goes on to machine6
When i work remotely with multiple machines i usually start the processes on the machines in parallel. So i have less impact when single machines are timing out. I use powershell 7 ForEach-Object -Parallel Feature for this https://devblogs.microsoft.com/powershell/powershell-foreach-object-parallel-
feature/
Try something like this:
$Credential=Get-Credential
#all Necessary parameters must be in the Object i give to ForEach Object
$myHosts = #(
#Hosts i want to connect to with values i want to use in the loop
#{Name="probook";cred=$Credential;param1="one_1";param2="two_1"}
#{Name="probook";cred=$Credential;param1="one_2";param2="two_2"}
)
$var1="one"
$var2="two"
$myHosts | ForEach-Object -Parallel {
#Variables outside of this "Parallel" Loop are not available. Because this is startet as separate SubProcess
#All Values come from the Object i piped in the ForEach-Object
$myHost=$_
#This is written to your local Shell
Write-Host ("Computer: "+ $env:Computername)
Write-Host $myHost.param1
Write-Host $myHost.param2
Write-Host $myHost.cred.UserName
Invoke-Command -ComputerName $myHost.Name -Credential $myHost.cred -ArgumentList #($myHost.param1,$myHost.param2) -ScriptBlock {
#Variables outside of of this Invoke Command Script Block are not available because this is a new Remote-Shell on the remote Host
#Parameters in Ordner of -Argument List
param($param1,$param2)
#Do your things on the Remote-Host here
#This is not Visbible -> it is only written on the "remote Shell"
Write-Host $env:Computername
#Here you get Back Values from the remote Shell
$env:Computername
$param1
$param2
}
} -ThrottleLimit 5
Hmm his is indeed a Problem.
You could experiment with:
Start-Job
(https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/start-job?view=powershell-7.1)
Get-Job (https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/get-job?view=powershell-7.1)
Receive-Job (https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/receive-job?view=powershell-7.1)
So you have more control what the processes do.
You start Background Jobs with Start-Job. Start-Job deliveres Job Objects Back -> save them in a array or variables
With Get-Job you see all Jobs currently Running
with Receive-Job you get back the output from a Job so far. You can use receive-Job to get back all PSObjects a Background Job has written.
Cannot explain in Detail, but this woul be another try i would do for this job.

Using Powershell to pull services on multiple domain computers

My code currently is:
$Workstationlist=get-adcomputer -filter * -searchbase 'OU=Workstations, DC=example, DC=com' -SearchScope 2 | foreach {$_.Name}
foreach($workstation in $Workstationlist){
get-service -ComputerName $workstation -name wauaserv
}
It seems to pulling service statuses from the array that is being parsed by the foreach method, but after a few values are returned it will spit this error message in between which seems to point at the location I saved the script
Running wuauserv Windows Update
Stopped wuauserv Windows Update
Running wuauserv Windows Update
Stopped wuauserv Windows Update
get-service : Cannot find any service with service name 'wuauserv'. At
\locationIsavedthescript
The idea is that I will stop the wauaserv service on all workstations in the domain, I was then going to add lines to delete everything from the \windowsdistrobution\ folder. And then restart the service. This effectively kills any downloading pending install update. I can drop $workstation into a ls snippet easy enough but pulling services doesn't seem to work quite as clean. Not sure why it is point the get-service to the location that I saved the script.
Any ideas and explanations as to how to accomplish what I'm shooting for a bit more cleanly or at least hide the erroneous output since the command IS running against the array.
I hope this made sense to someone out there.

Is there a way to find which user run what application on a server using Powershell

I am trying to find a way to find out who has ran an application (for example SQL) on a server, just to get some idea.
I tried Get-Process but this doesn't give me historic information, I want to get historical information
Get-Process -IncludeUserName *
what I want the return resule is "name of application", "user who ran it" and the last datetime it was ran by that user'
As for ...
I am trying to find a way to find out who has ran an application (for
example SQL) on a server, just to get some idea.
What you are asking for here is software metering.
SQL is a service that is always running once it is installed, so, no individual user is ever going to be running it. So, that is a bad example. MS Word for example would be a better example.
Yet there is nothing native in PowerShell that does this, software metering, but of course PowerShell can look at event logs. Yet if your auditing is not setup correctly then it's moot. This is better for a software metering tool, and there are several out there. So, why try and reinvent the wheel.
As for ...
I tried Get-Process but this doesn't give me historic information, I
want to get historical information
That is not what a process is nor what Get-Process is for. It, Get-Process only checks for and lists whatever process is currently running, regardless of what/who launched it.
As for...
what I want the return resule is "name of application", "user who ran
it" and the last datetime it was ran by that user'
As long as the process is running, you can get this, with that cmdlet.
However, what are you trying to accomplish by this?
Again, there are purpose built tools to meter software use.
https://learn.microsoft.com/en-us/sccm/apps/deploy-use/monitor-app-usage-with-software-metering
If you must go down this reinvent the wheel road, using scripting, then you need a task watcher on the target machines, which watches for the WinWord process to appear.
Get-Process -IncludeUserName |
Where ProcessName -EQ 'Winword'
... you then write those results to a file or database or your own event log each time you see that process.
Use PowerShell to Create and to Use a New Event Log
New-EventLog -LogName ScriptingGuys -Source scripts
When the command runs, no output appears to the Windows PowerShell console. To ensure the command actually created a new event log, I use
the Get-EventLog cmdlet with the –List parameter. Here is the command
and the associated output.
Write-EventLog -LogName ScriptingGuys -Source scripts -Message “Dude, it works … COOL!” -EventId 0 -EntryType information
Or just to a file
Get-Process -IncludeUserName |
Where ProcessName -EQ 'Winword' |
Select-Object -Property Name, StartTime, Username |
Export-Csv -Path 'F:\Temp\AppLaunchLog.csv' -Append
Import-Csv -Path 'F:\Temp\AppLaunchLog.csv'
# Results
Name StartTime UserName
---- --------- --------
WINWORD 5/23/2019 9:02:53 PM WS01\LabUser001

Powershell: Re-create RDS Remote Apps by Looping?

I'm stumped. I can usually take the output of one powershell command and use it as the input to another powershell command. For example:
Get-Mailbox | Set-Mailbox -MaxSendSize 40MB
This will loop through every mailbox and then set the maximum send size to 40 MB in Exchange 2007.
...but the same doesn't work with get-rdremoteapp and new-rdremoteapp.
Get-RDRemoteApp | new-rdremoteapp -collectionname APPSNEW -connectionbroker edge-1.mydom.local
The goal of this command is that we are preparing to Migrate from a Windows 2012 RDS environment on virtual servers to a Windows 2012 R2 environment on physical servers.
On the virtual 'edge' server, I should be able to get all the RD Remote Apps, loop through them, and then use the 'new-rdremoteapp' command to create them on the new 'edge-1' server.
What actually happens is the command runs and creates the 1st remote app, then exits without an error. It doesn't process the apps in the list.
I think I need to use foreach-object, but after reading the docs and playing around, I can't seem to get it to work.
I couldn't find an easy out. I had to specify a bunch of parameters like so:
Get-RDRemoteApp | foreach-object -process {new-rdremoteapp -collectionname APPSNEW -connectionbroker edge-1.mydom.local -displayname $_.displayname -filepath $_.filepath -alias $_.alias -commandlinesetting $_.commandlinesetting -usergroups $_.usergroups}
Time to find a job that has more bash scripting... ;)

powershell takes a long time to close connection?

I'm trying to run this powershell script on several remote network servers, but it takes a long time to complete the task and move to the next one.
Here's a basic script that will query a single remote server and return the data I want in a fast manner, but takes a long time to complete the entire script.
Get-EventLog -ComputerName WebServer1 -LogName System -EntryType Error -After ((Get-Date).Date.AddDays(-1))
I was running it through the Powershell ISE and from the command line in Powershell and both have the same behavior.
Any help is appreciated!
Thanks
As for the reason: mjolinor outlined that - Get-EventLog parses the whole logfile, and then filters the output to meet your parameters. What we want to do right now is to parse the event log one by one from the newest entries while the condition is true.
$i = 0
$EventParser = do
{
Get-EventLog -ComputerName Localhost -LogName System -EntryType Error -Newest $i; $i++
}
while
(
(Get-EventLog -ComputerName Localhost -LogName System -EntryType Error -Newest $i | Select -Last 1).TimeWritten -ge ((Get-Date).Date.AddHours(-24))
)
$EventParser|Where-Object { $_.TimeWritten -ge ((Get-Date).Date.AddHours(-24)) } | Sort-Object -Property Index -Descending | Get-Unique -OnType
I have measured your command and the loop I have written - here is the output for my machine
For $EventParser:
TotalMilliseconds : 634,6669
For your command (I changed webserver1 to localhost)
TotalSeconds : 14,3049668
I hope the script above will do the trick to speed up log parsing.
PS.
I have ran into one thing that I couldn't figure out. As you see, I am actually filtering the output twice for, I believe, the same condition (!). If I remove the last-line Where-Object statement, I receive 35 entries for my machine and it lists output even from 4 days ago ?? (Normally I receive 19 entries both from mine and original command). I have had to, to my surprise, re-apply the filter to receive the desired scope. Can you assist with that in a comment or should I post a question on that?
EDIT:
Now the problem doesn't occur. Edit: I have tweaked the code a little bit, I noticed that in some cases it may return duplicated entries.
Best regards,
AlexP