Nesting foreach state - powershell

I have the following script to list the running vms on hyper-V servers:
$VirtualHosts = Get-content "C:\scripts\Hosts.txt"
ForEach ($Guest in $VirtualHosts)
{Get-VM -ComputerName $Guest | Where State -eq Running | FT Name}
I want to add the functionality of rebooting the computers using the Restart-Computer cmdlet. To do this, I plan on using a nested foreach statement. Can you help me with the nested statement?

Nesting two loop constructs is pretty straight-forward in PowerShell - just make sure the inner/nested loop is entirely contained within the body of the outer loop:
$VirtualHosts = Get-content "C:\scripts\Hosts.txt"
foreach($vHost in $VirtualHosts)
{
foreach($runningVM in Get-VM -ComputerName $vHost | Where State -eq Running)
{
$runningVM |Restart-VM
}
}
You can also skip the inner loop completely and just pipe the output from Get-VM directly to Restart-VM:
foreach($vHost in $VirtualHosts)
{
Get-VM -ComputerName $vHost | Where State -eq Running | Restart-VM
}

Related

Using Get-Job to test-connection, but quite different [duplicate]

This question already has answers here:
If using Test-Connection on multiple computers with -Quiet how do I know which result is for which computer?
(2 answers)
Closed 2 years ago.
It's my first post here, I'm tring to write scripts on PS on my own, now my target is to write script that checks if computer is online at network, for example: test-Connection 192.168.0.1, 2, 3 etc. Doing this one by one on loop for takes some time if some computers are offline, I've found some tutorials on this site to use -AsJob param, but I'm not really Sure how could it work. I mean I'd like to output every checked PC to excel, so i need if operator. eg:
if (Job1 completed successfull (computer pings)){
do smth}...
I need to get output from Job boolean (true/false), but one by one. I'm taking my first steps in PS, I've made program that checks it one by one in for loop, but as i said it take some time till my excel file fill...
I can see, that AsJob makes working more effective and I think it's important to understand it
Thanks and sorry for bad text formatting, by the time I'll go on with this!
In your example, in the Start-Job scriptblock you are trying to access $_ which is not available in the codeblock scope. If you replace $_ with $args[0] it should work since you are passing in the $ip value as an argument
Your Example
$ipki = Get-Content 'C:\Users\pchor\Desktop\ipki.txt'
foreach ($ip in $ipki) {
Start-Job -Name "$ip" -ScriptBlock {
Test-Connection $_ -Count 1 # <---- replace $_ with $args[0]
} -ArgumentList $_ # <----- change $_ to $ip
}
You'll probably also want to wait for all the jobs to finish. I recommend something like this
$computers = #(
'www.google.com'
'www.yahoo.com'
)
$jobs = $computers |
ForEach-Object {
Start-Job -ScriptBlock {
[pscustomobject]#{
Computer = $using:_
Alive = Test-Connection $using:_ -Count 1 -Quiet
}
}
}
# Loop until all jobs have stopped running
While ($jobs |
Where-Object { $_.state -eq 'Running' }) {
"# of jobs still running $( ($jobs | Where-Object {$_.state -eq 'Running'}).Count )";
Start-Sleep -Seconds 2
}
$results = $jobs | Receive-Job | Select-Object Computer, Alive
$results | Format-Table
Output
Computer Alive
-------- -----
www.google.com True
www.yahoo.com True
To modify the properties to what you want there are different ways of doing this. Easiest in this case is probably to use a calculated property
$newResults = $results |
Select-Object Computer,
#{Label = 'State'; Expression = { if ($_.Alive) { 'Online' } else { 'Offline' } } }
Objects will now look like this (I added another fake address to illustrate offline state)
Computer State
-------- -----
www.google.com Online
www.yahoo.com Online
xxx.NotAValidAddress.xxx Offline
You can then export the objects to csv using Export-csv
$newResults | Export-Csv -Path c:\temp\output.csv

How to get VM VID partition of Running VM's only

A simple syntax question please. I am not getting it right. Please help:
This lists the VM VID for both Running and Paused VM's
Get-Counter '\Hyper-V VM VID Partition(*)\*'
This lists all the running VM's:
$g = Get-VM | Where-Object {$_.State -eq 'Running'} | Select-Object -Property Name
My guess is something like Get-Counter '\Hyper-V $g VID Partition(*)\*' might work but am not getting the syntax right.
How can I combine the two and get the VM VID for Running VM's only please?
I think you may need to use ForEach-Object. Something like:
$g = Get-VM | Where-Object {$_.State -eq 'Running'} |
ForEach-Object { Get-Counter -ComputerName $_.Name -Counter '\Hyper-V VM VID Partition(*)\*'}
Cannot try this myself..
I have finally realized there is no way to do this in a single command. So this is how I solved it:
pPipe1 = _wpopen(L"powershell -Command \"Get-Counter '\\Hyper-V VM VID Partition(*)\\*'\"", L"rt")
This will get me the full list of all Running and Paused VM socketID. Then I do list of Paused VM's.
pPipe2 = _wpopen(L"powershell -Command \"Get-VM | Where-Object {$_.State -eq 'Paused'};\" ", L"rt" )
Using both the pipe I filter out the Paused and I have the list of Running VM's with their socket ID.

Can't figure out Powershell Where-Object. SSAS Cube Processed State via Powershell,

I've created a powershell script to list the Processed State of a number of SSAS Cubes.
I'm puzzled why the Where-Object only produces the correct results when I use -like. I can't seem to use -eq. Anyone got any ideas ?
I'm using the Where-Object to zero in on the production Database.
The code i'm using is this..
Import-Module SQLASCmdlets -DisableNameChecking
$SSASServer = New-Object Microsoft.AnalysisServices.Server
$instanceName = "SSASSVR01"
$SSASServer.connect($instanceName)
$ProdCubes = $SSASServer.Databases.Cubes
$ProdCubes | Select Parent , State , Name `
| Where-Object { $_.Parent -like "DWProd*" }
I'd expect that -eq would work, but it doesn't
I don't get any response if the last line is written as...
$ProdCubes | Select Parent , State , Name `
| Where-Object { $_.Parent -eq "DWProd" }
I suspect it's whitespace, but how do you filter out the whitespace in powershell.

Trying to find and kill a process by PowerShell script

I have the following script to find the process "dotnet.exe". In my system, I have many dotnet.exe processes running. But I want to kill the "dotnet.exe" which has command line argument "MyService\Web\argument". I'm trying to do it by the following script. But it doesn't find anything, although I see the process in the Task Manager.
$process = Get-WmiObject Win32_Process | select name, commandline
foreach ($p in $process)
{
if ($p.name -contains "dotnet.exe" -and $p.commandline -contains "web")
{
$kp = Get-Process $p;
$kp.CloseMainWindow();
if (!$kp.HasExited)
{
$kp | Stop-Process -Force
}
}
else
{
Write-Host name: $p.name and param: $p.commandline;
}
}
All you need to do is filter the process list directly via Get-WmiObject and then terminate the matching process(es):
$fltr = "name like '%dotnet.exe%' and commandline like '%web%'"
Get-WmiObject Win32_Process -Filter $fltr | ForEach-Object {
$_.Terminate()
}
You could also call Terminate() directly on the output of Get-WmiObject like this:
(Get-WmiObject Win32_Process -Filter $fltr).Terminate()
However, there are situations where this could fail, e.g. if Get-WmiObject doesn't return any results, or if you're using PowerShell v2 or earlier and Get-WmiObject returns more than one result (passing a method call to the members of an array requires member enumeration, which was introduced with PowerShell v3). Using a ForEach-Object loop is both more robust and backwards-compatible.
The Get-WmiObject cmdlet returns quite useful objects, but you have stripped off everything by selecting only the Name and CommandLine parameters:
$process = Get-WmiObject Win32_Process | select name, commandline
If you remove the | select name, commandline part, you can still loop through each process but also make use of methods like Terminate() that will still be available.
You could do it in one shot, as per #ansgar-wiechers comment, or still make use of the loop and add in more logging, etc. if you wanted:
$process = Get-WmiObject Win32_Process
foreach($p in $process){
if($p.Name -eq "*dotnet.exe" -and $p.CommandLine -like "*web*"){
$p.Terminate()
# and so on...
}
}
Note also the comment from #TheIncorrigible1 about the use of comparison operators. I have used -eq for the process name and -like for the command line.

Taking input from one PSSession and sending it to another

Like many others, my background is in Linux with no powershell experience. So this object oriented programming is messing me up.
I need to search through VMware Horizon for VMs with users assigned to them, then check if they are disabled in AD. If they are disabled in AD I want to recycle the VM.
At the moment I am pulling the SIDs for the users from VMware Horizon, but when I try to use these in an invoke-command against AD I receive the following error
"Object reference not set to an instance of an object"
The Script so far
function getlist() {
$temp=Invoke-Command -ComputerName $vdiserver -ScriptBlock { add-pssnapin vmware.view.broker; get-desktopvm | select user_sid }
$list=$temp | Select-Object user_sid
#$list
}
$vdi1="server1"
$vdi2="server2"
$test=Test-Connection -ComputerName $vdi1 -Quiet
$test2=Test-Connection -ComputerName $vdi2 -Quiet
if ($test -eq "True"){
$vdiserver=$vdi1
getlist
}
elseif ($test2 -eq "True"){
$vdiserver=$vdi2
getlist
}
else {echo "No servers to connect to"}
ForEach ($user in $list) #{
#echo $user
#sleep 1
#}
{Invoke-Command -ComputerName domaincontroller -ScriptBlock {param($p1) get-aduser -identity $p1 } -argumentlist $user}
So this object oriented programming is messing me up.
So you're trying to revert to shell script, and writing twice as much code to do achieve half as much work.
The most important bit you're missing is to imagine an object as a collection of things - like, imagine you're working with /etc/passwd and each line has a user ID and a group ID and a home directory and a login shell.. and you're passing the entire line around at once, that's your analogous object.
An object has many properties, just like that (but overall more capable).
When you Select user_sid you're choosing that field to stay in the 'line', but the line is still something like :::user_sid:::: with the other fields now empty. (Approximately). But they're still there and in the way. To work with it directly, you have to get it out of the 'line' entirely - throw the container away and just have the user_sid outside of it.
get-desktopvm | select user_sid
->
get-desktopvm | select -expandproperty user_sid
which makes "sid1", "sid2", "sid3", but no containers for each sid.
This
function getlist() {
$temp=Invoke-Command -ComputerName $vdiserver -ScriptBlock { add-pssnapin vmware.view.broker; get-desktopvm | select user_sid }
$list=$temp | Select-Object user_sid
}
is essentially saying
function getlist() {
#do any amount of work here, and throw it all away.
}
Because the function returns nothing, and it doesn't change any data on disk or anything, so when the function finishes, the variables are cleared out of memory, and you can't use them afterwards.
This:
if ($test -eq "True"){
is a bit of a nonsense. It might work, but it's not working how you expect because it's happenstance that "a string with content" compared to a boolean True is True, regardless of the string containing the English word "True" or not. But it's also redundant - $test is itself true or false, you don't need to compare True with anything. if ($test). Or even if (Test-Connection -ComputerName $vdi -Quiet)
But stillll, so much work. Just connect to them all, and let it fail for the ones it can't contact. Maybe add -ErrorAction SilentlyContinue if you don't want to see the error.
$VMs = Invoke-Command -ComputerName Server1,Server2 -ScriptBlock {
Add-PsSnapin vmware.view.broker
Get-DesktopVm
}
Now you have all the VMs, get the user enabled/disabled state
foreach ($VM in $VMs) {
$Sid = $VM.user_sid
$AdEnabled = Invoke-Command -ComputerName domaincontroller -ScriptBlock {
(Get-AdUser -Identity $using:Sid).Enabled
}
$VM| Add-Member -NotePropertyName 'AdEnabled' -NotePropertyValue $AdEnabled
}
Now you should ideally have $VM as an array of objects, each one having all the VM Desktop properties - and also the True/False state of the AD Enabled property for that user account.
$VM | Out-Gridview
or
$VM | Export-Csv Report.csv
or
$VM | Where-Object { -not $_.AdEnabled }