Powershell to present 'Net View' data - powershell

happy Easter!
I am trying to write a script in Powershell that takes a list of hosts from a txt (or csv) and then for each does a "net view /all" on it, returning the presented shares in a csv.
I got something working but I need a column to show the host its looking at for each row otherwise I cant map them back.
Attempt 1 returns the data and the host but looks VERY messy and is proving difficult to dissect in Excel:
$InputFile = 'M:\Sources\Temp\net_view_list.txt'
$addresses = get-content $InputFile
foreach($address in $addresses) {
$sharedFolders = (NET.EXE VIEW $address /all)
foreach ($item in $sharedfolders)
{
$str_list = $address + "|" + $item
$obj_list = $str_list | select-object #{Name='Name';Expression={$_}}
$obj_list | export-csv -append M:\sources\temp\netview.csv -notype
}
}
Attempt 2 works better but cant get the hostname listed, plus the comments seem to appear in the "Used as" section (only using for one host to test the theory (didnt work!)):
$command = net view hostname #/all
$netview = $command -split '\n'
$comp = $netview[0].trim().split()[-1]
$result = $netview -match '\w' | foreach {
convertfrom-string $_.trim() -delim '\s{2,}' -propertynames 'Share','Type', 'Used as', 'Comment'
}
$result[0] = $null
$result | format-table 'Share', 'Type', 'Used as', 'Comment' -hidetableheaders
Also neither of these accounts for issues where the host either isn't accessible or has 0 shares.
I have literally spent all day on these - grateful for any guidance!

I will provide the way to get what you want in the your 1st example. The main reason it is not appearing like you are expecting it to is because you are not dealing with a PowerShell object. You are getting the raw output from an external command. What you need to do is take the data and create a PS Custom object then you can use it as you will. Below is the code that you should add after you have the $SharedFolder populated heavily commented to explain what each part is for.
# Create Array to hold PSCustom Object and variable to tell when the DO loop is done
$share_list = #()
$completed = $false
# Loop through each line in the output
for($x=0;$x -lt $sharedFolders.count;$x++){
$next_line = $x + 1
# If the line is a bunch of - then we know the next line contains the 1st share name.
if($sharedFolders[$x] -like "*-------*"){
# Here we will loop until we find the end of the list of shares
do {
# Take the line and split it in to an array. Note when you
# use -split vs variable.split allows you to use regular
# expressions. the '\s+' will consider x number of spaces as one
# the single quotes are important when using regex. Double
# quotes use variable expansion. Single quotes don't
$content = $sharedFolders[$next_line] -split '\s+'
$share_name = $content[0].Trim()
# Create a PS Custom Object. This is a bit over kill for one item
# but shows you how to create a custom Object. Note the Object last
# just one loop thus you create a new one each go round then add it to
# an Array before the loop starts over.
$custom_object = new-object PSObject
$custom_object | add-member -MemberType NoteProperty -name 'Share Name' -Value $share_name
# Add the Custom Object to the Array
$share_list += $custom_object
# This exits the Do loop by setting $completed to true
if($sharedFolders[$next_line] -like "*command completed*"){
$completed = $true
}
# Set to the next line
$next_line++
} until ($completed)
}
}
$share_list

Related

How to pull a sentence from a log file on multiple remote computers

I am needing to pull a specific sentence from a log file on multiple remote computers. I have all of the computer names already but I do not know how to go about pulling contents of a file from them and copying all of it to a file so that I can reference the sentence from each computer with its machine name. Basically each machine has a specific number unique to itself that we need.
Before explaining, I assume powershell is the tool to use for this.
There are about 1800 machines and I have a variable for all of those. Then I assume I have to make a loop of some kind that runs on each of those machines.
the loop would pull the text from that file that I need and save it all to a file. I am basically pretty new in my Net Admin position with not a lot of PowerShell experience and I wondered if anyone could help.
$computers = ***list of computers***
$computers | ForEachObject{
Add-Content -Path C:\Users\Public\Activant\Eagle\3log.log -Value "Terminal information for ***need the info that is here***"
}
Get-Content -Path .\TERMINAL NUMBERS.txt
this seems to do what you want. [grin] it builds a scriptblock that does the work, hands that off to Invoke-Command with a list of systems to run it on, gathers the results, creates a list of $Non-Responders, removes unwanted properties added by the I-C cmdlet, and finally shows the two collections.
#requires -RunAsAdministrator
# fake reading in a text file
# in real life, use Get-Content
$ComputerNameList = #'
LocalHost
10.0.0.1
127.0.0.1
BetterNotBeThere
'# -split [System.Environment]::NewLine
$IC_ScriptBlock = {
$TargetFileName = 'C:\Temp\Grouping-Strings-List_2019-07-31.log'
# the " \b\w+\b \b\w+\b " is two words delimited by spaces
# so this will find any line that has two words between the listed phrases
$LinePattern = '^Acid Drum \b\w+\b \b\w+\b Psychedelic$'
# the next line is a no-match patern for testing
#$LinePattern = '^Acid Drum \b\w+\b$'
$Line = (Get-Content -LiteralPath $TargetFileName |
Select-String -Pattern $LinePattern).Line
if ([string]::IsNullOrEmpty($Line))
{
$Line = '__Not Found__'
}
[PSCustomObject]#{
ComputerName = $env:COMPUTERNAME
LineText = $Line
}
}
$IC_Params = #{
ComputerName = $ComputerNameList
ScriptBlock = $IC_ScriptBlock
# comment out the next line to see any errors in the I-C call
ErrorAction = 'SilentlyContinue'
}
$Responders = Invoke-Command #IC_Params
$Non_Responders = $ComputerNameList.Where({$_ -notin $Responders.PSComputerName})
# the next line removes unwated properties added by "Invoke-Command"
$Responders = $Responders |
Select-Object -Property * -ExcludeProperty PSComputerName, PSShowComputerName, RunspaceId
$Responders
'=' * 40
$Non_Responders
output ...
ComputerName LineText
------------ --------
[MySysName] Acid Drum Instrumental Live Psychedelic
[MySysName] Acid Drum Instrumental Live Psychedelic
========================================
10.0.0.1
BetterNotBeThere
if needed, you can create a single collection from the two above fairly directly. [grin]
I think what you are trying to do is to READ the line from a file all computers in your list should have, located at C:\Users\Public\Activant\Eagle\3log.log
In that case, something like below should work:
# use UNC naming for the remote file path
$inputFile = 'C$\Users\Public\Activant\Eagle\3log.log' # I'm guessing this is the file you want to read
$outputFile = 'C:\TERMINAL NUMBERS.txt'
$computers = ***list of computers*** # the array of computer names
$result = $computers | ForEach-Object {
# test if the computer is online
if (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
# create the full UNC path by prepending the common file path with the computer name
$file = '\\{0}\{1}' -f $_, $inputFile
# test if the file can be found or not
if (Test-Path -LiteralPath $file -PathType Leaf) {
# using non regex string search
$line = (Select-String -LiteralPath $file -Pattern "whatever you're looking for" -SimpleMatch).Line
if (!$line) {
# the file is there, but the pattern was not found
$line = "Pattern not found"
}
}
else {
$line = "File '$inputFile' not found."
}
}
else {
$line = 'Computer is Off-Line'
}
# Finally, add this info to your text file
Add-Content -Path $outputFile -Value "$_ --> $line"
# And emit an object for the $result collection. This will display nicely on screen,
# but also allow for the creation of a CSV file which might be better as output.
[PsCustomObject]#{
'Computer' = $_
'LogInfo' = $line
}
}
Afterwards you can read the output text file or (better I think) use the $result collection for output:
On screen:
$result | Format-Table -AutoSize
To CSV file
$result | Export-Csv -Path 'C:\TERMINAL NUMBERS.csv' -NoTypeInformation -Force

Extract word from powershell output Search-String

Hi All I'm trying to extract a word from a text output. It should be pretty easy but I've already spent so much time on it. Right now I can extract the line but not just the word.
For example
w32tm /query /status | Select-String -pattern "CMOS"
outputs the line
"Source: Local CMOS Clock"
I only want to extract "Local CMOS Clock"
$var1=w32tm /query /status | Select-String -pattern "CMOS"
$var2=($var1 -split ':')[1] | Out-String
I was able to come up with the above it seems to work I'm not sure if there's a better way, I'm trying to evaluate it through a true/false seem to always pass as true though
For example
if($var2 = "Local CMOS Clock"){
Write-Output "True";
}Else{
Write-Output "False";
}
Always true: even when the condition is wrong
thanks in advance.
I'm not entirely sure of your motives, but here's a cleaner way to get to the answer you're looking for:
Build a PSObject containing the output
The PSObject will contain the output of w32tm. The code works by piping the command output through a loop, at the beginning we make a HashTable and then this is used to build a PowerShell object which is easier to manipulate:
# Pipe the w32tm command through a foreach
# Build a hashtable object containing the keys
# $_ represents each entry in the command output, which is then split by ':'
$w32_obj = w32tm /query /status | ForEach-Object -Begin {$w32_dict = #{}} -Process {
# Ignore blank entries
if ($_ -ne '') {
$fields = $_ -split ': '
# This part sets the elements of the w32_dict.
# Some rows contain more than one colon,
# so we combine all (except 0) the split output strings together using 'join'
$w32_dict[$fields[0]] = $($fields[1..$($fields.Count)] -join ':').Trim()
}
} -End {New-Object psobject -Property $w32_dict}
View the PSObject
Simply run this to display the new PSObject that has been created:
$w32_obj
Now check the 'Source'
Now we can ask for the 'Source' object from $w32_obj by using the dot-notation: $w32_obj.Source:
if($w32_obj.Source -eq "Local CMOS Clock"){
Write-Output "True";
}Else{
Write-Output "False";
}
Further reading
This shows the conversion from HashTable to PSobject and vice-versa
PSCustomObject to Hashtable
here's yet another way to get the False/True from w32tm. my system does not have "cmos" in the output, so i use 'system clock', but the idea will work for your situation.
[bool]((w32tm /query /status) -match 'system clock')
the above returns a $True on my system. that seems a tad more direct than the method you used. [grin]
take care,
lee

Adding multiple rows to CSV file at once through PowerShell

Background
I've been looking through several posts here on Stack and can only find answers to "how to add one single row of data to a CSV file" (notably this one). While they are good, they only refer to the specific case of adding a single entry from memory. Suppose I have 100,000 rows I want to add to a CSV file, then the speed of the query will be orders of magnitude slower if I for each row write it to file. I imagine that it will be much faster to keep everything in memory, and once I've built a variable that contains all the data that I want to add, only then write it to file.
Current situation
I have log files that I receive from customers containing about half a million rows. Some of these rows begin with a datetime and how much memory the server is using. In order to get a better view of how the memory usage looks like, I want to plot the memory usage over time using this information. (Note: yes, the best solution would be to ask the developers to add this information as it is fairly common we need this, but since we don't have that yet, I need to work with what I got)
I am able to read the log files, extract the contents, create two variables called $timeStamp and $memoryUsage that finds all the relevant entries. The problem occurs when I occurs when I try to add this to a custom PSObject. It would seem that using a $csvObject += $newRow only adds a pointer to the $newRow variable rather than the actual row itself. Here's the code that I've got so far:
$header1 = "Time Stamp"
$header2 = "Memory Usage"
$csvHeaders = #"
$header1;$header2
"#
# The following two lines are a workaround to make sure that the $csvObject becomes a PSObject that matches the output I'm trying to achieve.
$csvHeaders | Out-File -FilePath $csvFullPath
$csvObject = Import-Csv -Path $csvFullPath -Delimiter ";"
foreach ($TraceFile in $traceFilesToLookAt) {
$curTraceFile = Get-Content $TraceFile.FullName
Write-Host "Starting on file: $($TraceFile.Name)`n"
foreach ($line in $curTraceFile) {
try {
if (($line.Substring(4,1) -eq '-') -and ($line.Substring(7,1) -eq '-')) {
$TimeStamp = $line.Split("|",4)[0]
$memoryUsage = $($line.Split("|",4)[2]).Replace(",","")
$newRow = New-Object PSObject -Property #{
$header1 = $TimeStamp;
$header2 = $memoryUsage
}
$reorderedRow = $newRow | Select-Object -Property $header1,$header2
$reorderedRow | Export-Csv -Path $csvFullPath -Append -Delimiter ";"
}
} catch {
Out-Null
}
This works fine as it appends the row each time it finds one to the CSV file. The problem is that it's not very efficient.
End goal
I would ideally like to solve it with something like:
$newRow = New-Object PSObject -Property #{
$header1 = $TimeStamp;
$header2 = $memoryUsage
}
$rowsToAddToCSV += $newRow
And then in the final step do a:
$rowsToAddToCSV | Export-Csv -Path $csvFullPath -Append -Delimiter ";"
I have not been able to create any form of workaround for this. Among other things, PowerShell tells me that op_Addition is not part of the object, that the object I'm trying to export (the collection of rows) doesn't match the CSV file etc.
Anything that appends thousands of items to an array in a loop is bound to perform poorly, because each time an item is appended, the array will be re-created with its size increased by one, all existing items are copied, and then the new item is put in the new free slot.
Any particular reason why you can't simply do something like this?
$traceFilesToLookAt | ForEach-Object {
Get-Content $_.FullName | ForEach-Object {
if ($_.Substring(4, 1) -eq '-' -and $_.Substring(7, 1) -eq '-') {
$line = $_.Split('|', 4)
New-Object PSObject -Property #{
'Time Stamp' = $line[0]
'Memory Usage' = $line[2].Replace(',', '')
}
}
}
} | Export-Csv -Path $csvFullPath -Append -Delimiter ";"
A regular expression match might be an even more elegant approach to extracting timestamp and memory usage from the input files, but I'm going to leave that as an exercise for you.

Output PowerShell variables to a text file

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

Powershell: Search data in *.txt files to export into *.csv

First of all, this is my first question here. I often come here to browse existing topics, but now I'm hung on my own problem. And I didn't found a helpful resource right now. My biggest concern would be, that it won't work in Powershell... At the moment I try to get a small Powershell tool to save me a lot of time. For those who don't know cw-sysinfo, it is a tool that collects information of any host system (e.g. Hardware-ID, Product Key and stuff like that) and generates *.txt files.
My point is, if you have 20, 30 or 80 server in a project, it is a huge amount of time to browse all files and just look for those lines you need and put them together in a *.csv file.
What I have working is more like the basic of the tool, it browses all *.txt in a specific path and checks for my keywords. And here is the problem that I just can use the words prior to those I really need, seen as follow:
Operating System: Windows XP
Product Type: Professional
Service Pack: Service Pack 3
...
I don't know how I can tell Powershell to search for "Product Type:"-line and pick the following "Professional" instead. Later on with keys or serial numbers it will be the same problem, that is why I just can't browse for "Standard" or "Professional".
I placed my keywords($controls) in an extra file that I can attach the project folders and don't need to edit in Powershell each time. Code looks like this:
Function getStringMatch
{
# Loop through the project directory
Foreach ($file In $files)
{
# Check all keywords
ForEach ($control In $controls)
{
$result = Get-Content $file.FullName | Select-String $control -quiet -casesensitive
If ($result -eq $True)
{
$match = $file.FullName
# Write the filename according to the entry
"Found : $control in: $match" | Out-File $output -Append
}
}
}
}
getStringMatch
I think this is the kind of thing you need, I've changed Select-String to not use the -quiet option, this will return a matches object, one of the properties of this is the line I then split the line on the ':' and trim any spaces. These results are then placed into a new PSObject which in turn is added to an array. The array is then put back on the pipeline at the end.
I also moved the call to get-content to avoid reading each file more than once.
# Create an array for results
$results = #()
# Loop through the project directory
Foreach ($file In $files)
{
# load the content once
$content = Get-Content $file.FullName
# Check all keywords
ForEach ($control In $controls)
{
# find the line containing the control string
$result = $content | Select-String $control -casesensitive
If ($result)
{
# tidy up the results and add to the array
$line = $result.Line -split ":"
$results += New-Object PSObject -Property #{
FileName = $file.FullName
Control = $line[0].Trim()
Value = $line[1].Trim()
}
}
}
}
# return the results
$results
Adding the results to a csv is just a case of piping the results to Export-Csv
$results | Export-Csv -Path "results.csv" -NoTypeInformation
If I understand your question correctly, you want some way to parse each line from your report files and extract values for some "keys". Here are a few lines to give you an idea of how you could proceede. The example is for one file, but can be generalized very easily.
$config = Get-Content ".\config.txt"
# The stuff you are searching for
$keys = #(
"Operating System",
"Product Type",
"Service Pack"
)
foreach ($line in $config)
{
$keys | %{
$regex = "\s*?$($_)\:\s*(?<value>.*?)\s*$"
if ($line -match $regex)
{
$value = $matches.value
Write-Host "Key: $_`t`tValue: $value"
}
}
}