I'm trying to loop through a list of Windows machines in the domain to call for Disk Cleanup however, because the way the list is returned it's not executing the ForEach statement with the name substituted. Can anyone provide some guidance on how to make this work?
Get-ADComputer -Filter {OperatingSystem -Like "*windows*"} -Property * | Format-Table Name | foreach {Echo "psexec \\$ cleanmgr /sagerun:1"}
something like this
$computers = get-Content c:\computers.txt
foreach ($computer in $computers){
if(!(Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{write-host "cannot reach $computer" -f red}
else {& \\$computer\C$\Windows\System32\cleanmgr.exe /sagerun:1}}
I got it working exactly the way I had wanted and with specific OU.
ForEach ($COMPUTER in (Get-ADComputer -Filter * | Where-Object {$_.DistinguishedName -Like "*OU=Employee's Computers,DC=company,DC=com"} | Select-Object -ExpandProperty Name)) { .\psexec \\$COMPUTER cleanmgr /sagerun:1 }
Related
The majority of this code was pulled from a blog online, but I think it's exactly the way I need to be tackling this. I want to get the top 4 machines from an OU based on uptime, and run a script that lives on each of the top 4 machines. I know that the problem involves the Array losing access to the Get-ADComputer properties, but I'm unsure of how to pass these new properties back to their original objects. This works as expected until it gets to the foreach loop at the end.
$scriptBlock={
$wmi = Get-WmiObject -Class Win32_OperatingSystem
($wmi.ConvertToDateTime($wmi.LocalDateTime) – $wmi.ConvertToDateTime($wmi.LastBootUpTime)).TotalHours
}
$UpTime = #()
Get-ADComputer -Filter 'ObjectClass -eq "Computer"' -SearchBase "OU=***,OU=***,OU=***,DC=***,DC=***" -SearchScope Subtree `
| ForEach-Object { $Uptime += `
(New-Object psobject -Property #{
"ComputerName" = $_.DNSHostName
"UpTimeHours" = (Invoke-Command -ComputerName $_.DNSHostName -ScriptBlock $scriptBlock)
}
)
}
$UpTime | Where-Object {$_.UpTimeHours -ne ""} | sort-object -property #{Expression="UpTimeHours";Descending=$true} | `
Select-Object -Property ComputerName,#{Name="UpTimeHours"; Expression = {$_.UpTimeHours.ToString("#.##")}} | Select-Object -First 4 |`
Format-Table -AutoSize -OutVariable $Top4.ToString()
foreach ($Server in $Top4.ComputerName) {
Invoke-Command -ComputerName $Server -ScriptBlock {HOSTNAME.EXE}
}
I'm not married to Invoke-Command in the last foreach but am having the same issues when I try to use psexec. Also, I'm running hostname.exe as a check to make sure I'm looping through the correct machines before I point it at my script.
Here's a streamlined version of your code, which heeds the comments on the question:
# Get all computers of interest.
$computers = Get-ADComputer -Filter 'ObjectClass -eq "Computer"' -SearchBase "OU=***,OU=***,OU=***,DC=***,DC=***" -SearchScope Subtree
# Get the computers' up-times in hours.
# The result will be [double] instances, but they're also decorated
# with .PSComputerName properties to identify the computer of origin.
$upTimes = Invoke-Command -ComputerName $computers.ConputerName {
((Get-Date) - (Get-CimInstance -Class Win32_OperatingSystem).LastBootUpTime).TotalHours
}
# Get the top 4 computers by up-time.
$top4 = $upTimes | Sort-Object -Descending | Select-Object -First 4
# Invoke a command on all these 4 computers in parallel.
Invoke-Command -ComputerName $top4.PSComputerName -ScriptBlock { HOSTNAME.EXE }
I have a script that can have a list of AD servers (with Get-ADComputer) and the results goes to a TXT file. I don't know how to only have Online Servers only. I only need their names.
I tried to do some IF {} Else{} with the cmdlet Test-Connection -CN $Server but it doesn't work (I'm probably doing it wrong). Here is my code :
$TXTFile = "C:\Scripts\Serv.txt"
$TXTOutput = #()
Write-Host "INFO: Finding servers from AD"
$Servers = Get-ADComputer -Filter {OperatingSystem -like "*server*" -and Enabled -eq $true} | SORT Name
Write-Host "INFO:"$Servers.Count"Records found"
ForEach ($Server in $Servers) {
$ServerHash = $NULL
$ServerHash = [ordered]#{
"Servers Name" = $Server.Name
}
$TXTOutput += New-Object PSObject -Property $ServerHash
}
$TXTOutput
I want, if possible, to have all of my AD Online Servers name in a TXT file. For now I only have all of my servers (Online and Offline). It's my first post so sorry if I made it wrong !
You can use -Quiet parameter with Test-Connection cmdlet in order to get just True or False and then make a decision based on that result.
$TXTFile = "C:\Temp\Serv.txt"
$TXTOutput = #()
$servers=Get-ADComputer -Filter {OperatingSystem -like "*server*" -and Enabled -eq $true} | select -expandproperty Name
ForEach ($Server in $Servers) {
if ((Test-Connection $Server -Count 2 -Quiet) -eq $true) {
$TXTOutput += $Server
}
}
$TXTOutput | Out-File $TXTFile
You can pipe $TXTOutput to sort if you want. Keep in mind that this might take a while since you are basically pinging each server twice -Count 2.
Hi I have an issue in powershell where the Do Until Condition is true, but the loop doesn't stop. If I change the -eq to 0. It will stop... Basically what this should do is get the number of computers in the text file. Store that number in $count. Then restart the service for each computer in the list until it reaches the last one.
$computers = gc C:\temp\computers.txt
$count = $computers.count
Do {
foreach($computer in $computers){
$readCount = $computer.ReadCount
gwmi win32_service -ComputerName $computer | where {$_.name -like "*was*"} | Restart-Service
}
}
Until (($count - $readCount) -eq 1)
You don't need a Do-Until loop here since you can just iterate over the computers. To skip the last computer, use the Select-Object cmdlet with the -SkipLast 1 parameter:
Get-Content 'C:\temp\computers.txt' | Select-Object -SkipLast 1 | Forach-Object {
gwmi win32_service -ComputerName $computer |
where {$_.name -like "*was*"} |
Restart-Service
}
I have the following short script to grab serial numbers of computers and monitors in an OU, which works fine:
Import-Module ActiveDirectory
$searchbase = "OU=some,OU=organisational,OU=units,DC=somedomain,DC=local"
Write-Host ""
Write-Host "Serial Numbers for Computers and Monitors in" $searchbase
Write-Host "--"
Get-ADComputer -SearchBase $searchbase -Filter '*' | `
Select-Object -Expand Name | %{Write-Host ""; echo $_ ; Get-WMIObject -Class Win32_BIOS -ComputerName $_ | Select-Object -Expand SerialNumber; `
$monitor = gwmi WmiMonitorID -Namespace root\wmi -computername $_; ($monitor.SerialNumberID | foreach {[char]$_}) -join ""};
This script doesn't check to see if the computer is online before attempting to fetch the WMIObject, so if a computer is offline it takes ages before the RPC call times out.
I tried to modify the script to use the Test-Connection cmdlet before trying to get the WMIObject:
Import-Module ActiveDirectory
$searchbase = "OU=some,OU=organisational,OU=units,DC=somedomain,DC=local"
Write-Host ""
Write-Host "Serial Numbers for Computers and Monitors in" $searchbase
Write-Host "--"
Get-ADComputer -SearchBase $searchbase -Filter '*' | `
Select-Object -Expand Name | `
if (Test-Connection -ComputerName $_ -Quiet) {
%{Write-Host ""; echo $_ ; Get-WMIObject -Class Win32_BIOS -ComputerName $_ | Select-Object -Expand SerialNumber; `
$monitor = gwmi WmiMonitorID -Namespace root\wmi -computername $_; ($monitor.SerialNumberID | foreach {[char]$_}) -join ""};}
}
else {
Write-Host ""; Write-Host $_ "is offline";
}
I'm sure I'm doing something syntactically stupid. Can someone point me in the right direction?
You can't pipe directly to an if statement, only to cmdlets.
Put the if statement inside the ForEach-Object block (% is an alias for ForEach-Object):
... | Select-Object -Expand Name | `
%{
if (Test-Connection -ComputerName $_ -Quiet) {
# Get-WmiObject in here
}
else {
Write-Host ""; Write-Host $_ "is offline";
}
}
If you don't care about writing each machine's status to the host, you could also filter out offline computers with Where-Object(alias ?):
... | Select-Object -Expand Name | ?{
Test-Connection $_ -Quiet
} | % {
Get-WmiObject -ComputerName $_
}
In addition to the answer from #Mathias R. Jessen, you can get rid of the backticks for line continuation.
They are not needed if the end of the line infers there is another block of code required for the statement. Like | or { or (.
"foo", "bar" |
% {$_}
works just fine...
I'm new to PowerShell and I'm attempting to write a script that will query AD for machine names, check which ones are responding and write the output into a file. So far I have this:
$computers = Get-ADComputer -filter {(Name -like "PC*")} | Select-Object -Property Name
foreach ($computer in $computers) {
if((Test-Connection -Cn $computer -BufferSize 16 -Count 1 -Ea 0 -Quiet)) {
"Machine $computer connected." | Out-File "out.txt" -Append
} else {
"Machine $computer not connected." | Out-File "out.txt" -Append
} #end if
} #end foreach
What I get in the output text file looks like the following:
...
Machine #{Name=PC-0649} not connected.
Machine #{Name=PC-1541} not connected.
Machine #{Name=PC-1574} not connected.
...
I think my problem lies with the Select-Object -Property Name part of the first line. Running the debugger, it looks like PowerShell is formatting each iteration of $computer to include the header line.
[DBG]: PS Y:\>> $computer
Name
----
PC-0649
What's the best way for me to strip out everything except the PC-#### part in this situation?
I think your problem is that you still have a list of (truncated) computer objects in $computers. Verify this by doing $computers[0].GetType(). If you don't see String, it's not a string. :) Try this instead:
$computers = Get-ADComputer -filter {(Name -like "PC*")} |
Select-Object -ExpandProperty Name