Powershell: WMI Ping piped to variable - loops

I'm having some difficulties in getting my PowerShell script to work as I'd like it to and after much jiggery-pokery here I am.
My overall aim is fairly simple, unfortunately I'm somewhat of a PowerShell noob!
I'm trying to determine the name, manufacturer and model of all of the systems in our estate without having to walk around staring at lots of tin.
I've constructed the following based solely on my bad knowledge of scripting and I've hit a snag.
My idea was to pass DNS/IP information from a CSV into a variable which I can then use in turn to perform the WMI query based on the Ping results.
False Ping response = do not query
True Ping response = perform WMI query
Here is what I've got so far...
Test-connection -computername
foreach ($Ping in $Hosts)
{
test-connection -computername $Ping.IP -count 1 -quiet
if ($Ping.StatusCode -eq 0)
{Get-WmiObject win32_computersystem -computername $ip.Name | select Name,Manufacturer,Model } out-file c:\CSV\Test1.csv -ea SilentlyContinue}
else
{write-host $Hosts.Status Offline}
}

Assuming you have file C:\CSV\Hosts.csv with contents as described:
computer1.mydomain.com
computer2.mydomain.com
With the following script you'll get file C:\CSV\Results.csv:
$Hosts = Get-Content "C:\CSV\Hosts.csv"
foreach ($PingIP in $Hosts)
{
$alive = Test-Connection -ComputerName "$PingIP" -count 1 -quiet
if ($alive)
{
Get-WmiObject Win32_ComputerSystem -ComputerName "$PingIP" | Select Name, Manufacturer, Model | Out-File "C:\CSV\Results.csv" -ea SilentlyContinue
}
else
{
Write-Output "$PingIP offline"
}
}

Related

Problem when uninstall direct from msi location path & in for loop

I try to uninstall a msi file, but when I try this via array I get an error (cant find installation package)
When I do the same but not in array - it works
for ($i=0; $i -lt $msiArrayClean.length; $i++){
Write-Host $msiArrayClean[$i]
& msiexec.exe /x $msiArrayClean[$i]
}
here the output of Write Host
How i come to $msiArrayClean
$msiCache = get-wmiobject Win32_Product | Where-Object Name -like "*7-Zip*" | Format-Table LocalPackage -AutoSize -HideTableHeaders
$msiString = $msiCache | Out-String
$msiArrayWithEmptyLines = $msiString -split "`n"
$msiArray = $msiArrayWithEmptyLines.Split('', [System.StringSplitOptions]::RemoveEmptyEntries)
$msiArrayCleanString = $msiArray | Out-String
$msiArrayClean = $msiArrayCleanString -split "`n"
A few caveats up front:
Format-* cmdlets output objects whose sole purpose is to provide formatting instructions to PowerShell's output-formatting system - see this answer. In short: only ever use Format-* cmdlets to format data for display, never for subsequent programmatic processing.
The CIM cmdlets (e.g., Get-CimInstance) superseded the WMI cmdlets (e.g., Get-WmiObject) in PowerShell v3 (released in September 2012). Therefore, the WMI cmdlets should be avoided, not least because PowerShell (Core) (version 6 and above), where all future effort will go, doesn't even have them anymore. For more information, see this answer.
Use of the Win32_Product WMI class is discouraged, both for reasons of performance and due to potentially unwanted side effects - see this Microsoft article.
An alternative - available in Windows PowerShell only (not in PowerShell (Core) 7+) - is to use the following to get uninstall command lines and execute them via cmd /c:
Get-Package -ProviderName Programs -IncludeWindowsInstaller |
ForEach-Object { $_.meta.attributes['UninstallString'] }
If you need to stick with Win32_Product:
# Get the MSI package paths of all installed products, where defined.
$msiCache = (Get-CimInstance Win32_Product).LocalPackage -ne $null
foreach ($msiPackagePath in $msiCache) {
if (Test-Path -LiteralPath $msiPackagePath) {
# Note that msiexec.exe runs *asynchronously*.
# Use Start-Process -Wait to wait for each call to complete.
& msiexec.exe /x $msiPackagePath
} else {
Write-Warning "Package not found: $msiPackagePath"
}
}
I don't like reaching to WMI, since its perfomance is the issue. I prefer to do it via registry and it worked for me many times. Code explanation in comments.
$name = "7-zip"
#Get all items from registry
foreach ($obj in Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") {
#Get DisplayName property of registry
$dname = $obj.GetValue("DisplayName")
#Search for given name
if ($dname -like "*$name*") {
#Get uninstall string (it gets you msiexec /I{APPID})
$uninstString = $obj.GetValue("UninstallString")
foreach ($line in $uninstString) {
#Getting GUID from "{" to "}""
$found = $line -match '(\{.+\}).*'
if ($found) {
#If found - get GUID
$appid = $matches[1]
Write-Output "About to uninstall app $appid"
#Start uninstallation
Start-Process "msiexec.exe" -arg "/X $appid /qb" -Wait
}
}
}
}
Edit: Added solution with msi path after Nehat's comment as this works for me (I tried to minimize the code :))
$msiCache = get-wmiobject Win32_Product | Where-Object Name -like "*7-Zip*" | Format-Table LocalPackage -AutoSize -HideTableHeaders
foreach ($msi in $msiCache | Out-String) {
if ([string]::IsNullOrEmpty($msi)) {
continue
}
Write-Host $msi
Start-Process "msiexec.exe" -arg "/x $msi" -Wait
}

using mac address in filepath - file not found?

$test = #(gwmi win32_networkadapterconfiguration | select macaddress )
$test | ForEach-Object {
Write-Host $_.macaddress
$mac = $_.macaddress -replace ":", ""
$mac.Trim()
If (Test-Path "x:\$Mac") { $computer = $mac }
$Logfile = "x:\$Computer\$Computer.Log"
$File = "x:\$computer\$computer.ini"
$computer
$CompName = Get-Content $File | Select-Object -index 0
}
So the above script will not find the $file even though it is present. The x:\64006A849B90\64006A849B90.ini is present but i get this
ERROR: Get-Content : Cannot find path 'X:\64006A849B90\64006A849B90.ini' because it does not exist.
Anyone know why i cant use this - i know its something to do with the $mac value and making sure its a string but i have tried $mac.ToString() [String]$mac and trimming it and it will not see the path - any ideas? thanks
The strange thing is the value is being picked up hence the mac address being in the path but it wont find the path if that makes sense.
I think you might have other issues but assuming your files are named and exist where you expect the only problem you would have to deal with is potential nulls.
Do you have any adapters that do no have MAC Addresses? I have 3 right now. Using your code it will attempt to process those. If you were not aware of those I could see that being an issue. Easy to fix will a small code update
# Get the populated macs from all network adapters
$macs = Get-WmiObject win32_networkadapterconfiguration | Select-Object -ExpandProperty macaddress
ForEach($mac in $macs){
$mac = $mac.replace(":","")
$macFile = "x:\$mac\$mac.ini"
if(Test-Path $macFile){
# The ini file exists
$computer = Get-Content $macFile | Select-Object -Index 0
} else {
# Cant find the file
}
$computer
}
This could be simplified even further but I didn't want to do too much at once.
By using Select-Object -ExpandProperty macaddress we still get nulls but they are dropped by the pipeline so $macs would only contain strings of actual MACs.
The whole $computer = $mac should have worked but it was redundant so I removed that logic from your code.

wilcard searching in powershell

I work for a large organization that has many windows 2012 file servers where we re-direct AD users profile folders to.
Ever since Crypo-wall viruII have been plaguing us we run search looking for the various telltale signatures that someone's account is infected.
The last campaign left files with a HELP_YOUR_FILES* in there so was easy to look for using
Get-ChildItem -Recurse -Force $filePath -ErrorAction SilentlyContinue | Where-Object { ($_.PSIsContainer -eq $false) -and ( $_.Name -like "*$fileName*") } | Select-Object Name,Directory| Format-Table -AutoSize *
Now starting this week we've been hit with a new Cryto-wall campaing that leaves affected user files like this
<original_filename>.extension.mp3
only using a search parameter of *.mp3 or even ..mp3 (in an attempt to catch the double barrel file extensions just keeps returning all mp3 files which is too large a list to sort through and find any infection.
Does anyone have suggestions about how to find only
<original_filename>.extension.mp3
Thank you in advance
You could use a RegEx match to help with this. It would not be perfect, but it would limit the results quite a bit. I think I would do something like:
Get-ChildItem -Recurse -Force $filePath -ErrorAction SilentlyContinue | Where-Object { ($_.PSIsContainer -eq $false) -and ( $_.Name -match "\.[^\.]+\.mp3$") } | Select-Object Name,Directory| Format-Table -AutoSize *
That will search for a pattern where there's a period, at least one non-period character, another period, and then mp3 with nothing after it. Here's a RegEx101 Explaination.
As the RegEx101 example shows in the last line matches, this is not fool proof, and would require manual review, but it should reduce false positive results to a minimum.

Powershell Script using Invoke-SQL command,needed for SQL job, the SQL Server version of Powershell is somewhat crippled, is there a workaround?

Full Question: Have Powershell Script using Invoke SQL command, using snappins, I need them to be included in a SQL job, the SQL Server version of Powershell is somewhat crippled, does anyone know a workaround?
From what I have gathered, SQL Management Studio's version of powershell is underpowered, not allowing for the use of snappins, as such it does not recognize the cmdlets that I used in the script. I have tried running it in the job as a command line prompt rather than a Powershell script, which causes the code to work somewhat, however I check the history on the job and it says that invoke-sql is still not a recognized cmdlet. I speculate that because I am running the code on a remote server, with different credentials than my standard my profile with the snappins preloaded isn't being loaded, though this is somewhat doubtful.
Also, as I am a powershell rookie, any advice on better coding practices/streamlining my code would be much appreciated!
Code is as follows:
# define parameters
param
(
$file = "\\server\folder\file.ps1"
)
"invoke-sqlcmd -query """ | out-file "\\server\folder\file.ps1"
# retrieve set of table objects
$path = invoke-sqlcmd -query "select TableName from table WITH (NoLock)" -database db -server server
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
$so = New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
$so.DriPrimaryKey = $false
$so.Nocollation = $true
$so.IncludeIfNotExists = $true
$so.NoIdentities = $true
$so.AnsiPadding = $false
# script each table
foreach ($table in $path)
{
#$holder = $table
$table = get-item sqlserver:\sql\server\default\databases\database\tables\dbo.$($table.TableName)
$table.script($so) | out-file -append $file
}
(get-content "\\server\folder\file.ps1") -notmatch "ANSI_NULLS" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -notmatch " AS "| out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -notmatch "Quoted_" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -replace "\) ON \[PRIMARY\].*", ")" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -replace "\[text\]", "[nvarchar](max)" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -replace " SPARSE ", "" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -replace "COLUMN_SET FOR ALL_SPARSE_COLUMNS", "" | out-file "\\server\folder\file.ps1"
""" -database database -server server" | out-file "\\server\folder\file.ps1" -append
So I figured out the answer to my own question. Using this site: http://www.mssqltips.com/tip.asp?tip=1684 and
http://www.mssqltips.com/tip.asp?tip=1199
I figured out that he was able to do so using a SQL Server Agent Proxy, so I followed the yellow brick road, and basically I set up a proxy to my account and was able to use the external powershell through a feature. A note, you need to create a credential under the securities tab in object explorer prior to being able to select one when creating the proxy. Basically I ended up creating a proxy named powershell, using the powershell subsystem, and use my login info to create a credential. VOILA!
You have to add the snapins each time. In your editor you likely already have them loaded from another script/tab/session. In SQL Server you will need to add something like this to the beginning of the script:
IF ( (Get-PSSnapin -Name sqlserverprovidersnapin100 -ErrorAction SilentlyContinue) -eq $null )
{
Add-PsSnapin sqlserverprovidersnapin100
}
IF ( (Get-PSSnapin -Name sqlservercmdletsnapin100 -ErrorAction SilentlyContinue) -eq $null )
{
Add-PsSnapin sqlservercmdletsnapin100
}
I'm not sure the error you are trying to workaround - can you post that?
Have you tried this from a PowerShell prompt?
Add-PSSnapin SqlServerCmdletSnapin100

check RAM,page file, /PAE, /3GB, SQL server memory using powershell

I am a powershell novice.
After days of searching....
I have put together a small powershell script (as below) to check page file, /PAE switch, /3GB switch, SQL server max RAM, min RAM.
I am running this on 1 server.
If I want to run it on many servers (from a .txt) file, How can I change it ?
How can I change it to search boot.ini file's contents for a given server?
clear
$strComputer="."
$PageFile=Get-WmiObject Win32_PageFile -ComputerName $strComputer
Write-Host "Page File Size in MB: " ($PageFile.Filesize/(1024*1024))
$colItems=Get-WmiObject Win32_PhysicalMemory -Namespace root\CIMv2 -ComputerName $strComputer
$total=0
foreach ($objItem in $colItems)
{
$total=$total+ $objItem.Capacity
}
$isPAEEnabled =Get-WmiObject Win32_OperatingSystem -ComputerName $strComputer
Write-Host "Is PAE Enabled: " $isPAEEnabled.PAEEnabled
Write-Host "Is /3GB Enabled: " | Get-Content C:\boot.ini | Select-String "/3GB" -Quiet
# how can I change to search boot.ini file's contents on $strComputer
$smo = new-object('Microsoft.SqlServer.Management.Smo.Server') $strSQLServer
$memSrv = $smo.information.physicalMemory
$memMin = $smo.configuration.minServerMemory.runValue
$memMax = $smo.configuration.maxServerMemory.runValue
## DBMS
Write-Host "Server RAM available: " -noNewLine
Write-Host "$memSrv MB" -fore "blue"
Write-Host "SQL memory Min: " -noNewLine
Write-Host "$memMin MB "
Write-Host "SQL memory Max: " -noNewLine
Write-Host "$memMax MB"
Any comments how this can be improved?
Thanks in advance
In case you would like just to check the boot.ini file you could use Get-Content (in case you will not have problems with credentials)
# create a test file with server names
#"
server1
server2
"# | set-content c:\temp\serversso.txt
# read the file and get the content
get-content c:\temp\serversso.txt |
% { get-content "\\$_\c`$\boot.ini" } |
Select-String "/3GB" -Quiet
Later if you add some stuff that will be needed to run on remote computer then you will need to use remoting and basically Invoke-Command. Recently two resources appeared that touch remoting:
Administrator's Guid to Windows PowerShell Remoting
Series on PowerShell remoting by Ravikanth Chaganti that starts here

Resources