I have a PowerShell that would uninstall a program from remote computers but it takes a long time since it goes through all the computers in the list. I just need help from you to modify it so it first checks if the program exists on the remote computer or not and then uninstalls it:
$comps = gc "C:\Computers.txt"
$appname = gc "C:\appname.txt"
foreach($comp in $comps){
foreach ($appname in $appname){
$prod=gwmi -computer $comp win32_product | ?{$_.name -eq "$appname"}
$prod.uninstall()
}
}
Try using foreach –parallel instead of just foreach. Official docs here.
workflow uninstallstuff {
sequence {
$comps = gc "C:\Computers.txt"
$appname = gc "C:\appname.txt"
foreach -parallel ($comp in $comps){
foreach ($appname in $appname){
$prod=gwmi -computer $comp win32_product | ?{$_.name -eq "$appname"}
$prod.uninstall()
}
}
}
}
That should run each computer in parallel but each app for that computer will be uninstalled sequentially.
edit: rewrote as a workflow. I haven't tested it yet.
Got it! thanks
$comps= gc "C:\Computers.txt"
$appname = gc "C:\appname.txt"
foreach($comp in $comps){
$program = gwmi -computer $comp Win32_Product | sort-object Name | select Name | where { $_.Name -match “$appname”}
if($program -eq $null)
{
Write-host "Does Not"
}
else
{
$prod=gwmi -computer $comp win32_product | ?{$_.name -eq "$appname"}
$prod.uninstall()
}
}
Related
Below Function is not running the lines "If ($script:AADServersTable)" down... I have put a write-host and that is working, however the variables are not working below that
Function get-AADVariables
{
$script:AADServersTable = New-Object 'System.Collections.Generic.List[System.Object]'
$scriptBlockaadinstalled = {
Get-WmiObject -Class Win32_Product |
where name -eq "Microsoft Azure AD Connect synchronization services" | select Name, Version}
##$scriptBlockaadinstalled = {hostname}
Import-Module ActiveDirectory
$servers = Get-ADComputer -Filter 'operatingsystem -like "*server*" -and enabled -eq "true"' | Select-Object -Property Name -ExcludeProperty Release
## just a single invoke-command
foreach ($server in $servers) {
$aadinstalledresults = Invoke-Command -ComputerName $server.name -ScriptBlock $scriptBlockaadinstalled ##-HideComputerName
$aadinstalledresults = $aadinstalledresults.PSComputerName
$script:AADServersTable.Add($aadinstalledresults)
}
If ($script:AADServersTable) {
Write-Host 'working'
$AADServers = ($script:AADServersTable | Group-Object -NoElement).Name | Get-Unique
$AADServers = $AADServers -split "`n"
$AADServer = $AADServers[0]
$StandByAADServer = $AADServers[1]
$SecondStandByAADServer = $AADServers[2]
}
}
I have some requirement where I have to find out the current version and update details of an installed application (highlighted):
And I have this PowerShell snippet for modification:
$server="XXXXXXXXXX"
$ServiceInfo = Get-WmiObject win32_service -ComputerName $server -ExpandProperty Version | Where-Object {$_.Name -eq "VSTTAgent"}
if($ServiceInfo.State -eq "Running")
{
$userAccount = $ServiceInfo.DisplayName.ToString()
Write-Host ("VSTTAgent service is Running on $server and $userAccount ")
}
To get the product version, you can use the Get-Item cmdlet using the PathName property of your $ServiceInfo object:
$ServiceInfo.PathName.Trim('"') | Get-Item | select -expand VersionInfo | select ProductVersion
Essentially the same as #MartinBrandl, but the WMI-only version.
Get-WmiObject win32_service -Filter 'Name="VSTTAgent"' -ComputerName $server | ForEach-Object {
$filter = 'Name="{0}"' -f $_.PathName -replace '\\', '\\'
$version = (Get-WmiObject CIM_DataFile -Filter $filter -ComputerName $server).Version
if ($_.State -eq 'Running') {
$userAccount = $ServiceInfo.DisplayName.ToString()
Write-Host ("VSTTAgent ($version) service is Running on $server and $userAccount")
}
}
im new beginner of powershell, now i have two script, one is get remote server's IPs, another one is get remote server's specific service start time, i need to show remote server's IP and specific service start time, can someone guide me how to merge these two script.
below is my two script.
$servers = gc -path D:\Ted\Computers.txt
$Job = get-wmiobject win32_networkadapterconfiguration -computer $servers -filter "IPEnabled='True'" -asjob
$results = $job | receive-job
$results
get-job | wait-job
receive-job job* | select IPAddress
and another one for get service start time is
$servers = gc -path D:\Ted\Computers.txt
$check = get-wmiobject win32_process -computer $servers -Filter "Name='aspnet_state.exe'" -asjob
$results = $check | receive-job
$results
get-job | wait-job
receive-job job* | Select-Object name, processId, #{Name="StartTime"; Expression={ $_.ConvertToDateTime( $_.CreationDate )}}
at last i need know one thing, If I use asjob to this script, that means it is multi-threaded execution?
sorry for my poor english, thank you for your kindly help.
There is probably a cleaner way to do this, but here is my take on your problem. It looks as if you need some way to correlate each computer to the output of the two WMI queries. If it is a requirement to run this in parallel using jobs, it will take a bit more work, but here is a serial version.
Get-Content -Path D:\Ted\Computers.txt | ForEach-Object {
$ip = Get-WmiObject Win32_NetworkAdapterConfiguration -Computer $_ -Filter "IPEnabled='True'" | Select-Object IPAddress
$process = Get-WmiObject Win32_Process -Computer $_ -Filter "Name='aspnet_state.exe'" | Select-Object Name, ProcessId, #{ Name="StartTime"; Expression = { $_.ConvertToDateTime($_.CreationDate) } }
#{
Computer = $_
Ip = $ip
Name = $process.Name
ProcessId = $process.ProcessId
StartTime = $process.StartTime
}
}
A parallel version would be something along the lines of this:
# A collection that stores all the jobs
$AllJobs = #()
# A collection that stores jobs correlated with the computer
$ComputerJobs = Get-Content -Path D:\Ted\Computers.txt | ForEach-Object {
$ipJob = Get-WmiObject Win32_NetworkAdapterConfiguration -Computer $_ -Filter "IPEnabled='True'" -AsJob
$AllJobs += $ipJob
$processJob = Get-WmiObject Win32_Process -Computer $_ -Filter "Name='aspnet_state.exe'"
$AllJobs += $processJob
#{
Computer = $_
IpJob = $ipJob
ProcessJob = $processJob
}
}
# Wait for everything to complete
Wait-Job -Job $AllJobs
# Iterate the correlated collection and expand the results
$ComputerJobs | ForEach-Object {
$ip = Receive-Job -Job $_.IpJob | Select-Object IPAddress
$process = Receive-Job -Job $_.ProcessJob | Select-Object Name, ProcessId, #{ Name="StartTime"; Expression = { $_.ConvertToDateTime($_.CreationDate) } }
#{
Computer = $_.Computer
Ip = $ip
Name = $process.Name
ProcessId = $process.ProcessId
StartTime = $process.StartTime
}
}
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...
(Get-WmiObject -Class Win32_Product -ComputerName $PCNumber -ErrorAction SilentlyContinue | Where-Object { $_.Name -match "$softwareName" }).Uninstall() | Out-Null
I have following code which works perfectly. The only problem is that I wont to know if the software has been removed or not.This doesn't tells me but the code below does.
This way works for me.
$software = Get-WmiObject -Class Win32_Product -ComputerName $PCNumber -ErrorAction SilentlyContinue | Where-Object { $_.Name -match "$softwareName" }
$soft = $software.Uninstall();
$n = $software.ReturnValue;
if ( $n -eq 0 ){
SOFTWARE HAS BEEN REMOVED.
}
my question is that how do i tell if the software has been removed or not.
using this code.
(Get-WmiObject -Class Win32_Product -ComputerName $PCNumber -ErrorAction SilentlyContinue | Where-Object { $_.Name -match "$softwareName" }).Uninstall() | Out-Null
You have to check the ReturnValue property. When you pipe to Out-Null you are suppressing the output of the operation and there's no way to tell what happened, unless you issue a second call to find if it returns the software in question.
I recommend using the Filter parameter (instead of using Where-Object) to query the software on the server. To be safe you should also pipe the results to the Foreach-Object cmdlet, you never know how many software objects you get back due to the match operation (and you call the Uninstall method as if the result is one object only):
Get-WmiObject -Class Win32_Product -ComputerName $PCNumber -Filter "Name LIKE '%$softwareName%'" | Foreach-Object {
Write-Host "Uninstalling: $($_.Name)"
$rv = $_.Uninstall().ReturnValue
if($rv -eq 0)
{
"$($_.Name) uninstalled successfully"
} # Changed this round bracket to a squigly one to prperly close the scriptblock for "if"
else
{
"There was an error ($rv) uninstalling $($_.Name)"
}
}