I am a total novice when it comes to powershell. I would like to create a little script to save me a lot of time and after a little research, I am sure it can be achieved using Import-CSV command.
Basically I need to run a command on multiple PC's but the variable is different for each command. I wish to pull that variable from a comparision in a CSV file. So find current Hostname, then use that hostname to find the corresponding asset number in the CSV and then use that asset number as a variable in the final comamnd.
Looking at other examples on here, I have this so far:
$Asset = #()
$Host = #()
Import-Csv -Path "C:\hostnametoasset.csv" |`
ForEach-Object {
$Asset += $_.Asset
$Host += $_.Host
}
$Hostname = (Get-WmiObject -Class Win32_ComputerSystem -Property Name).Name
if ($Host -contains $Hostname)
{
C:\BiosConfigUtility64.exe /setvalue:"Asset Tracking Number","$Asset"
}
Section of the CSV:
Asset,Host
10756,PCD001
10324,PCD002
10620,PCD003
Any help would be greatly appreciated.
Couple of different points...
Importing a CSV results in an array of objects that you can filter on.
$Lines = Import-Csv -Path "C:\hostnametoasset.csv"
$Line = $Lines | ?{$_.host -match $ENV:COMPUTERNAME}
You can then use the filter results directly by accessing the member you need.
C:\BiosConfigUtility64.exe /setvalue:"Asset Tracking Number","$($Line.Asset)"
NOTE: I cannot test this directly right now so hopefully I got the syntax right.
Related
I have narrowed down a little more what exactly my end game is.
I have pre-created the file that I want the results to write to.
Here is a rough script of what I want to do:
$computers = Get-content "C:\users\nicholas.j.nedrow\desktop\scripts\lists\ComputerList.txt"
# Ping all computers in ComputerList.txt.
# Need WinEst_Computers.csv file created with 3 Columns; Computer Name | Online (Y/N) | Does File Exist (Y/N)
$output = foreach ($comp in $computers) {
$TestConn = Test-connection -cn $comp -BufferSize 16 -Count 1 -ea 0 -quiet
if ($TestConn -match "False")
{
#Write "N" to "Online (Y/N)" Column in primary output .csv file
}
if ($TestConn -match "True")
{
#Write "Y" to "Online (Y/N)" Column in primary output .csv file
}
#For computers that return a "True" ping value:
#Search for WinEst.exe application on C:\
Get-ChildItem -Path "\\$comp\c$\program files (x86)\WinEst.exe" -ErrorAction SilentlyContinue |
if ("\\$comp\c$\program files (x86)\WinEst.exe" -match "False")
{
#Write "N" to "Does File Exist (Y/N)" Column in primary output .csv file
}
if ("\\$comp\c$\program files (x86)\WinEst.exe" -match "True")
{
#Write "Y" to "Does File Exist (Y/N)" Column in primary output .csv file
}
Select #{n='ComputerName';e={$comp}},Name
}
$output | Out-file "C:\users\nicholas.j.nedrow\desktop\scripts\results\CSV Files\WinEst_Computers.csv"
What I need help with is the following:
How to get each result to either write to the appropriate line (I.e. computername, online, file exist?) or would it be easier to do one column at a time;
--Write all PC's to Column A
--Ping each machine and record results in Column B
--Search each machine for the .exe and record results.
Any suggestions? Sorry I keep changing things. Just trying to figure out the best way to do this.
You are using the foreach command, which has a syntax foreach ($itemVariable in $collectionVariable) { }. If $computer is your collection, then your current item cannot also be $computer inside your foreach.
Get-Item does not return a property computerName. Therefore you cannot explicitly select it with Select-Object. However, you can use a calculated property to add a new property to the custom object that Select-Object outputs.
If your CSV file has a row of header(s), it is simpler to use Import-Csv to read the file. If it is just a list of computer names, then Get-Content works well.
If you are searching for a single file and you know the exact path, then just stick with -Path or -LiteralPath and forget -Include. -Include is not intuitive and isn't explained well in the online documentation.
If you are piping output to Export-Csv using a single pipeline, there's no need for -Append unless you already have an existing CSV with data you want to retain. However, if you choose to pipe to Export-Csv during each loop iteration, -Append would be necessary to retain the output.
Here is some updated code using the recommendations:
$computers = Get-content "C:\users\nicholas.j.nedrow\desktop\scripts\lists\ComputerList.txt"
$output = foreach ($comp in $computers) {
Get-Item -Path "\\$comp\c$\program files (x86)\WinEst.exe" -ErrorAction SilentlyContinue |
Select #{n='ComputerName';e={$comp}},Name
}
$output | Export-Csv -Path "C:\users\nicholas.j.nedrow\desktop\scripts\results\CSV Files\WinEst_Computers.csv" -NoType
I have the following..
$output = #()
$servers =Get-Content "C:\Windows\System32\List3.txt"
foreach ($server in $servers)
{
trap [Exception] {continue}
Import-Module PSRemoteRegistry
$key="SOFTWARE\Microsoft\'Microsoft Antimalware'\'Signature Updates'"
$regkey=Get-RegBinary -ComputerName $server -Key $Key -Value SignatuesLastUpdated
#$regkey=(Get-Item HKLM:\SOFTWARE\Microsoft\'Microsoft Antimalware'\'Signature Updates').getValue('SignaturesLastUpdated')
#$regkey=[datetime]::ParseExact("01/02/03", "dd/MM/yy", $null) | Export-csv -path c:\temp\avinfo.csv -append
#$regkey
}
$output | Select $server , $Regkey | Export-Csv c:\temp\avinfo.csv -NoTypeInformation
I think it's pretty close but doesn't work as needed - can anyone tell me what I am doing wrong here - been reading a lot and managed to get this far, just need the help to finalise.
Thanks
Ok... so there is alot that needed to be changed to get this to work. I will update the answer frequently after this is posted.
$servers = Get-Content "C:\Windows\System32\List3.txt"
$key="SOFTWARE\Microsoft\Microsoft Antimalware\Signature Updates"
$servers | ForEach-Object{
$server = $_
Try{
Get-RegBinary -ComputerName $server -Key $Key -Value SignatuesLastUpdated -ErrorAction Stop
} Catch [exception]{
[pscustomobject]#{
ComputerName = $server
Data = "Unable to retrieve data"
}
}
} | Select ComputerName,#{Label=$value;Expression={If(!($_.Data -is [string])){[System.Text.Encoding]::Ascii.GetBytes($_.data)}Else{$_.Data}}} | Export-Csv c:\temp\avinfo.csv -NoTypeInformation
What the above code will do is more in line with your intentions. Take the list and for each item get the key data from that server. If there is an issue getting that data then we output a custom object stating that so we can tell in the output if there was an issue. The part that is up in the air is how you want to export the binary data to file. As it stands it should create a space delimited string of the bytes.
The issues that you did have that should be highlighted are
No need to import the module for every server. Moved that call out of the loop
You have declared the variable $output but do not populate it during your loop process. This is important for the foreach construct. You were, in the end, sending and empty array to you csv. My answer does not need it as it just uses standard output.
As #Meatspace pointed out you had a typo here: SignatuesLastUpdated
Get-RegBinary does not by default create terminating errors which are needed by try/catch blocks. Added -ErrorAction Stop. Don't think your code trap [Exception] {continue} would have caught anything.
The single quotes you have in your $key might have prevented the path from being parsed. You were trying to escape spaces and just need to enclose the whole string in a set of quotes to achieve that.
While Select can use variables they are there, in a basic form, to select property names. In short what you had was wrong.
I'm new to PowerShell and have a script which loops through Active Directory searching for certain computers. I get several variables and then run functions to check things like WMI and registry settings.
In the console, my script runs great and simple Write-Host command prints the data on the screen as I want. I know about Export-Csv when using the pipeline...but I'm not looking to print from the pipeline.
I want to write the variables to a text file, continue the loop, and check the next computer in AD...output the next iteration of the same variables on the next line. Here is my Write-Host:
Write-Host ($computer)","($Speed)","($Regcheck)","($OU)
Output file:
$computer,$Speed,$Regcheck | out-file -filepath C:\temp\scripts\pshell\dump.txt -append -width 200
It gives me the data, but each variable is on its own line. Why? I'd like all the variables on one line with comma separation. Is there a simple way to do this akin to VB writeline? My PowerShell version appears to be 2.0.
Use this:
"$computer, $Speed, $Regcheck" | out-file -filepath C:\temp\scripts\pshell\dump.txt -append -width 200
I usually construct custom objects in these loops, and then add these objects to an array that I can easily manipulate, sort, export to CSV, etc.:
# Construct an out-array to use for data export
$OutArray = #()
# The computer loop you already have
foreach ($server in $serverlist)
{
# Construct an object
$myobj = "" | Select "computer", "Speed", "Regcheck"
# Fill the object
$myobj.computer = $computer
$myobj.speed = $speed
$myobj.regcheck = $regcheck
# Add the object to the out-array
$outarray += $myobj
# Wipe the object just to be sure
$myobj = $null
}
# After the loop, export the array to CSV
$outarray | export-csv "somefile.csv"
You can concatenate an array of values together using PowerShell's `-join' operator. Here is an example:
$FilePath = '{0}\temp\scripts\pshell\dump.txt' -f $env:SystemDrive;
$Computer = 'pc1';
$Speed = 9001;
$RegCheck = $true;
$Computer,$Speed,$RegCheck -join ',' | Out-File -FilePath $FilePath -Append -Width 200;
Output
pc1,9001,True
$computer,$Speed,$Regcheck will create an array, and run out-file ones per variable = they get seperate lines. If you construct a single string using the variables first, it will show up a single line. Like this:
"$computer,$Speed,$Regcheck" | out-file -filepath C:\temp\scripts\pshell\dump.txt -append -width 200
The simple solution is to avoid creating an array before piping to Out-File. Rule #1 of PowerShell is that the comma is a special delimiter, and the default behavior is to create an array. Concatenation is done like this.
$computer + "," + $Speed + "," + $Regcheck | out-file -filepath C:\temp\scripts\pshell\dump.txt -append -width 200
This creates an array of three items.
$computer,$Speed,$Regcheck
FYKJ
100
YES
vs. concatenation of three items separated by commas.
$computer + "," + $Speed + "," + $Regcheck
FYKJ,100,YES
I was lead here in my Google searching. In a show of good faith I have included what I pieced together from parts of this code and other code I've gathered along the way.
# This script is useful if you have attributes or properties that span across several commandlets
# and you wish to export a certain data set but all of the properties you wish to export are not
# included in only one commandlet so you must use more than one to export the data set you want
#
# Created: Joshua Biddle 08/24/2017
# Edited: Joshua Biddle 08/24/2017
#
$A = Get-ADGroupMember "YourGroupName"
# Construct an out-array to use for data export
$Results = #()
foreach ($B in $A)
{
# Construct an object
$myobj = Get-ADuser $B.samAccountName -Properties ScriptPath,Office
# Fill the object
$Properties = #{
samAccountName = $myobj.samAccountName
Name = $myobj.Name
Office = $myobj.Office
ScriptPath = $myobj.ScriptPath
}
# Add the object to the out-array
$Results += New-Object psobject -Property $Properties
# Wipe the object just to be sure
$myobj = $null
}
# After the loop, export the array to CSV
$Results | Select "samAccountName", "Name", "Office", "ScriptPath" | Export-CSV "C:\Temp\YourData.csv"
Cheers
I'm trying to write a powershell script that returns only the rows that have a specific entry in one of the columns but struggling a bit.
The CSV has 2 relevant columns, the ID and the Error.
At the moment I have this:
$ImportFile = Import-csv X:\errorlist.csv
$Error = $userobject.Error
$ID = $userobject.ID
$ErrorMessage1 = "Error1"
foreach($test in $ImportFile)
{
$field1 = $test.Error
$field2 = $test.ID
Echo "$field1, $field2"
} Where-Object {$_.Error -like $ErrorMessage1}
However the echo still returns everything in the csv even with the Where-Object.
I'm trying to run a ForEach because ultimately where I have the echo I'm going to insert a SQL script that uses the ID information in the csv to fix specific errors. Therefore I think I need to keep the foreach to process each sql script in turn, unless anyone has a better idea?
Try this way, this will check for $ErrorMessage1 in the ID or Error columns:
Import-csv X:\errorlist.csv |
Where-Object {$_.Error -like "*$ErrorMessage1*" -or $_.ID -like "*$ErrorMessage1*"}
Filter the rows from the CSV before you run the SQL:
Import-Csv X:\errorlist.csv |
Where-Object { $_.Error -like $ErrorMessage } |
ForEach-Object {
# Don't use aliases in scripts, use the real cmdlet names. Makes maintenance easier.
Write-Host ('{0}, {1}' -f $_.Error,$_.ID)
# Run your SQL cmd here.
}
The foreach loop operates independent of the Where-object, so you all you're doing is dumping the contents of the CSV file to the console.
You can shorten this up a lot by just pumping the data through the pipeline. This will filter your CSV records down to just the ones that meet your criteria.
$ErrorMessage1 = "Error1"
$allerrors = Import-csv X:\errorlist.csv|where-object {$_.error -eq $ErrorMessage1}
$allerrors;
I have a serverlist which contains all server names on a single line. Each server has the exact path and file as listed in the following:
$Computers = get-content "c:\temp\serverlist.txt"
foreach ($Computer in $Computers) {
select-string "F:\xxx\SmarXXXX\yyyyyyations\xxxxx\servers\data-sources.xml" `
-pattern "thisiswhatimsearchingfor"
}
I edited the actual code with xxxxx since some of it may be considered sensitive. When I run this command I am given the string that I am looking for, but there is no link to which server returns this data. I essentially need to find out which servers from my serverlist do NOT contain the pattern requested.
Any ideas would be appreciated. Thanks for your patience.
EDIT:
I have come up with the following but it does not display the desired output. This returns just the strings, but not the servers that it is pulling them from. Thanks for the help. I attempted to add the where-object in order to see the associated server with each string. This failed.
$Computers = get-content "c:\temp\serverlist.txt"
foreach ($Computer in $Computers) {
Select-String "F:\xx\xxxxxxPath\applications\yyyyyyy\servers\data-xxxx.xml" -pattern "render" | where {($_ -match "render")
}
}
Use UNC paths and a filter to get a list of computers where the search pattern occurs in the file:
$file = 'F$\xxx\SmarXXXX\yyyyyyations\xxxxx\servers\data-sources.xml'
Get-Content "c:\temp\serverlist.txt" | ? {
(Get-Content "\\$_\$file") -match 'search pattern'
}
This might do it:
Get-Content c:\temp\serverlist.txt | where {
-not ( $_ -match "thisiswhatimsearchingfor" )
}