powershell to un-install muliple applications ? - powershell

I'm new to PowerShell and I'm looking for a way to un-install multiple applications. I have a list of applications in a text file that i want to Un-install. Here's the code i have so far:
# Retrieve names of all softwares to un-install and places in variable $app
$App = Get-Content "C:\temp\un-installApps.txt"
# Cycle through each of the softwares to un-install and store in the WMI variable
Foreach ($AppName in $App)
{
$AppTmp = Get-WmiObject -query "Select * from win32_product WHERE Name like" + $AppName
$AppNames = $AppNames + $AppTmp
}
foreach ($Application in $AppNames )
{
msiexec /uninstall $Application.IdentifyingNumber
}
The following lines causes the issues
$AppTmp = Get-WmiObject -query "Select * from win32_product WHERE Name like" + $AppName
$AppNames = $AppNames + $AppTmp"
Any ideas how i can get this to work?

I think it's because there is no space between like and the application name, and there needs to be single quotes around the application name. That part should look like like '" + $AppName + "'".
However, you could do the whole script more simply like this:
$App = Get-Content "C:\temp\un-installApps.txt"
gwmi win32_product|
where { $App -contains $_.Name }|
foreach { $_.Uninstall() } | out-null

Related

Optimizing Powershell Script to Query Remote Server OS Version

I want optimize a simple task: pull server OS version into a neat table. However, some servers in our environment have Powershell disabled. Below you fill find my script, which works! However, it takes about 20 seconds or so per server, since it waits for the server to return the results of the invoked command before moving onto the next server in the list. I know there's a way to asynchronously pull the results from a PS command, but is this possible when I need to resort to cmd line syntax for servers that can't handle PS, as shown in the catch statement?
$referencefile = "ps_servers_to_query.csv"
$export_location = "ps_server_os_export.csv"
$Array = #()
$servers = get-content $referencefile
foreach ($server in $servers){
#attempt to query the server with Powershell.
try{
$os_version = invoke-command -ComputerName $server -ScriptBlock {Get-ComputerInfo -Property WindowsProductName} -ErrorAction stop
$os_version = $os_version.WindowsProductName
} # If server doesnt have PS installed/or is disabled, then we will resort to CMD Prompt, this takes longer however.. also we will need to convert a string to an object.
catch {
$os_version = invoke-command -ComputerName $server -ScriptBlock {systeminfo | find "OS Name:"} # this returns a string that represents the datetime of reboot
$os_version = $os_version.replace('OS Name: ', '') # Remove the leading text
$os_version = $os_version.replace(' ','') # Remove leading spaces
$os_version = $os_version.replace('Microsoft ','') # Removes Microsoft for data standardization
}
# Output each iteration of the loop into an array
$Row = "" | Select ServerName, OSVersion
$Row.ServerName = $Server
$Row.OSVersion = $os_version
$Array += $Row
}
# Export results to csv.
$Array | Export-Csv -Path $export_location -Force
Edit: Here's what I'd like to accomplish. Send the command out to all the servers (less than 30) at once, and have them all process the command at the same time rather than doing it one-by-one. I know I can do this if they all could take PowerShell commands, but since they can't I'm struggling. This script takes about 6 minutes to run in total.
Thank you in advance!
If I got it right something like this should be all you need:
$referencefile = "ps_servers_to_query.csv"
$export_location = "ps_server_os_export.csv"
$ComputerName = Get-Content -Path $referencefile
$Result =
Get-CimInstance -ClassName CIM_OperatingSystem -ComputerName $ComputerName |
Select-Object -Property Caption,PSComputerName
$Result
| Export-Csv -Path $export_location -NoTypeInformation

Get Driver Install Dates

I am attempting to get a list of installed Drivers and the date they were installed via PowerShell. I have attempted the following:
Get-WmiObject Win32_PnPSignedDriver| select devicename, InstallDate
And the InstallDate property is blank for 100% of the items...
I also tried:
Get-WindowsDriver -Online -All | Sort-Object Date
There is a date there, but it is not the install date. Probably the date the driver was compiled is my assumption.
As a misc note: MMC is not an alternative in this case. Long story.
I found the InstallDate associated with the driver files and not with the driver objects from WMI.
Please find the code bellow for an example:
# Initializing parameters (modify the Output folder by changing the $TargetDirectory define in the first line)
$TargetDirectory = "C:\temp\DriversINFTest\"
$hostname = $ENV:COMPUTERNAME
# Get Third Party drivers used, that are not provided by Microsoft and presumably included in the OS
$drivers = Get-WmiObject Win32_PNPSignedDriver | where {$_.DriverProviderName -ne "Microsoft"}
# Initialize the list of detected driver packages as an array
$DriverFolders = #()
foreach ($d in $drivers) {
# We initialize the list of driver files for each driver
$DriverFiles = #()
# For each driver instance from WMI class Win32_PNPSignedDriver, we compose the related WMI object name from the other WMI driver class, Win32_PNPSignedDriverCIMDataFile
$ModifiedDeviceID = $d.DeviceID -replace "\\", "\\"
$Antecedent = "\\" + $hostname + "\ROOT\cimv2:Win32_PNPSignedDriver.DeviceID=""$ModifiedDeviceID"""
# Get all related driver files for each driver listed in WMI class Win32_PNPSignedDriver
$DriverFiles += Get-WmiObject Win32_PNPSignedDriverCIMDataFile | where {$_.Antecedent -eq $Antecedent}
if ($DriverFiles -ne $null) {
foreach ($i in $DriverFiles) {
# We elliminate double backslashes from the file paths
$path = $i.Dependent.Split("=")[1] -replace '\\\\', '\'
# We elliminate the trailing and ending quotes from the file paths
$path2 = $path.Substring(1,$path.Length-2)
# On the first pass, we only process the INF files as there is a very low chance of existing more than one driver package on the same machine, with the same INF file legth
if ($path2.Split("\\")[$path2.Split("\\").Length-1].split(".")[1] -eq "inf") {
# We get the file attributes for each INF file
$CurrentDeviceID = $d.DeviceID
Write-Host "Current Device ID is $CurrentDeviceID"
$filedetails = $i.Dependent
Write-Host "Current inf file is $filedetails"
$where = $filedetails.split("=")[1]
$query = "select * from CIM_DataFile where Name=$where"
$InstallDate = Get-WmiObject -Namespace "ROOT\CIMV2" -Query $query
$ReadableDate = $InstallDate.ConvertToDateTime($InstallDate.InstallDate)
Write-Host "Installation date is:"
$ReadableDate
} # End of if ($path2.Split("\\")[$path2.Split("\\").Length-1].split(".")[1] -eq "inf")
} # End of foreach ($i in $DriverFiles)
} # End of if ($DriverFiles -ne $null)
} # End of foreach ($d in $drivers)

Conditional Tags and Variables in PowerShell

A piece of software we work with outputs a JSON stream with the status of the software (if it's running on master or slave for example).
I'm using powershell to get this data, and from there I'll be either posting it to a database or something else, but that's not really relevant to this particular question.
I come from a web developer background so I'm familiar with conditional statements, variables etc. in PHP. However it appears to work differently for PowerShell by the looks of it. I'm pretty new to Powershell.
So the premise of the below snippet is that:
It reads a CSV containing the first three octets of an internal IP address (with the header IP) and the name of the location (with the header SITE).
Runs a foreach loop on the contents of the CSV and then runs conditional statement to first determine if the machine(s) are online using TEST-CONNECTION.
If the primary/master is online, display it's status, otherwise check the secondary/slave and display it's status, or if both are offline (the first two are taken from the JSON feed) simply display All Offline
At the end of each foreach, to simply write-host the variable $status
Currently it disregards the IF/ELSEIF/ELSE and just shows System Offline. Any ideas?
$sites = Import-CSV 'D:\Batch Files\fb.csv'
Foreach ($item in $sites) {
$ip = $($item.IP)
$primary = '$ip.205'
$secondary = '$ip.210'
$site = $($item.SITE)
If (Test-Connection $primary -count 2 -quiet) {
$request = 'http://' + $primary + ':32768/Console/SystemStatus.json'
Invoke-WebRequest $request | ConvertFrom-Json |select -expand PrimaryServer | select -expand Online -outvariable status
}
Elseif (Test-Connection $secondary -count 2 -quiet) {
$request = 'http://' + $secondary + ':32768/Console/SystemStatus.json'
Invoke-WebRequest $request | ConvertFrom-Json |select -expand SecondaryServer | select -expand Online -outvariable status
}
Else {
$status = 'System Offline'
}
Write-Host $status
}
CSV Contents:
IP,SITE
10.18.21,Name1
10.18.39,Name2
Now if I were to run the below, I do indeed get the correct output for $status however it's also displayed a second time probably due to something I'm missing to hide output from the select/expand.
$primary='10.18.21.205'
If (Test-Connection $primary -count 2 -quiet) {
$request = 'http://' + $primary + ':32768/Console/SystemStatus.json'
Invoke-WebRequest $request | ConvertFrom-Json |select -expand PrimaryServer | select -expand Online -outvariable status
Write-Host $status
}
Thank You
Your $primary and $secondary contain "$ip.205" / "$ip.210" at the time you try to test the connection. Variables inside of single quotes will not be interpreted.
If you change it to this it should work:
$primary="$ip.205"
$secondary = "$ip.210"
or
$primary = $ip + '.205'
$secondary = $ip + '.210'

Error in creating an if statement in powershell with the hostname as a variable

In the script attached I am trying to rename a PC if the PC has a certain hostname. However, the script is proceeding anyway and bypasses the if/else statement.
What am I doing wrong? I am kind of new with Windows Powershell.
Thanks!
# get current computername
$hostname = hostname.exe
#$env:computername
If ( $hostname = "CLNT3100" )
{
#Get all the computers with CLNT3* and sort them with the 'highest client' on top. Then put them in newlist.txt
Get-ADComputer -Filter 'SamAccountName -like "CLNT3*"' | Select -Exp Name | Sort-Object -Descending >> C:\newlist.txt
#Put the text file in a variable and show the top line
$Text = Get-Content -Path C:\newlist.txt
#$Text[0]
#Trim CLNT for numbering
$Text1 = $Text[0].TrimStart("CLNT")
#Add 1 number to the previous CLNT
$Text2 = 1 + $Text1
#Add CLNT again to the new variable
$Text3 = "CLNT" + $Text2
#Rename the computer
Rename-Computer –computername minint –newname $Text3
}
Else
{
Write-Host "Computernaam is niet minint!!!"
}
To compare if two values are equal in Powershell you have to use the -eqoperator.
Check the Powershell equality operators to see the others like -gt, -lt etc, or type man about_Comparison_Operators in the PS shell.
Also, to learn Powershell I found this free ebook to be very good.

Select option from Array

I am working on a side project and to make it easier for managment since almost all of out server names are 15 charactors long I started to look for an RDP managment option but none that I liked; so I started to write one and I am down to only one issue, what do I do to manage if the user types not enough for a search so two servers will match the Query. I think I will have to put it in an array and then let them select the server they meant. Here is what I have so far
function Connect-RDP
{
param (
[Parameter(Mandatory = $true)]
$ComputerName,
[System.Management.Automation.Credential()]
$Credential
)
# take each computername and process it individually
$ComputerName | ForEach-Object{
Try
{
$Computer = $_
$ConnectionDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=$computer)" -ErrorAction Stop | Select-Object -ExpandProperty DNSHostName
$ConnectionSearchDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=*$computer*)" | Select -Exp DNSHostName
Write-host $ConnectionDNS
Write-host $ConnectionSearchDNS
if ($ConnectionDNS){
#mstsc.exe /v ($ConnectionDNS) /f
}Else{
#mstsc.exe /v ($ConnectionSearchDNS) /f
}
}
catch
{
Write-Host "Could not locate computer '$Computer' in AD." -ForegroundColor Red
}
}
}
Basically I am looking for a way to manage if a user types server1
that it will ask does he want to connect to Server10 or Server11 since both of them match the filter.
Another option for presenting choices to the user is Out-GridView, with the -OutPutMode switch.
Borrowing from Matt's example:
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$IDX = 0
$(foreach ($item in $selection){
$item | select #{l='IDX';e={$IDX}},Name
$IDX++}) |
Out-GridView -Title 'Select one or more folders to use' -OutputMode Multiple |
foreach { $selection[$_.IDX] }
}
else {$Selection}
This example allows for selection of multiple folders, but can you can limit them to a single folder by simply switching -OutPutMode to Single
I'm sure what mjolinor has it great. I just wanted to show another approach using PromptForChoice. In the following example we take the results from Get-ChildItem and if there is more than one we build a collection of choices. The user would select one and then that object would be passed to the next step.
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$title = "Folder Selection"
$message = "Which folder would you like to use?"
# Build the choices menu
$choices = #()
For($index = 0; $index -lt $selection.Count; $index++){
$choices += New-Object System.Management.Automation.Host.ChoiceDescription ($selection[$index]).Name, ($selection[$index]).FullName
}
$options = [System.Management.Automation.Host.ChoiceDescription[]]$choices
$result = $host.ui.PromptForChoice($title, $message, $options, 0)
$selection = $selection[$result]
}
$selection
-Directory requires PowerShell v3 but you are using 4 so you would be good.
In ISE it would look like this:
In standard console you would see something like this
As of now you would have to type the whole folder name to select the choice in the prompt. It is hard to get a unique value across multiple choices for the shortcut also called the accelerator key. Think of it as a way to be sure they make the correct choice!