This data is store in a file
AUTOM01-AYEHU1:No Updates Available
AUTOM01-AYEHU2:No Updates Available
AUTOM01-AYEHU3:No Updates Available
AUTOM01-AYEHU4:No Updates Available
AUTOM01-AYEHU5:No Updates Available
AUTOM01-AYEHU6:No Updates Available
I have a above dataset in a file i need to create 2 powershell custom object with the name of (SERVERNAME,STATUS) and put respective data into it .
before : is servername and rest is status
This is the answer if you really want 2 objects
$fileLines = #("AUTOM01-AYEHU1:No Updates Available","AUTOM01-AYEHU2:No Updates Available")
$serverArray = [System.Collections.ArrayList]::new()
$statusArray = [System.Collections.ArrayList]::new()
foreach($fileLine in $fileLines)
{
$splittedLine = $fileLine.split(":")
$serverArray.Add([PSCustomObject]#{ServerName=$splittedLine[0]})
$statusArray.add([PsCustomobject]#{Status=$splittedLine[1]})
}
find them in $serverArray and $statusArray
As commented in your previous question, you can simply do
$result = (Get-Content -Path 'thefile.txt' -Raw) -replace ':', '=' | ConvertFrom-StringData
ConvertFrom-StringData returns a Hashtable, in which by default the items are unordered.
As you would like to keep the order as in the input file, you may want to use this instead:
$result = Get-Content -Path 'D:\Test\thefile.txt' | ConvertFrom-String -Delimiter ':' -PropertyNames Server, Status
To return an array of PSCustomObjects:
Server Status
------ ------
AUTOM01-AYEHU1 No Updates Available
AUTOM01-AYEHU2 No Updates Available
AUTOM01-AYEHU3 No Updates Available
AUTOM01-AYEHU4 No Updates Available
AUTOM01-AYEHU5 No Updates Available
AUTOM01-AYEHU6 No Updates Available
Related
So I am trying to parse a show tech config file from a cisco router.
The config file is huge so I need to capture everything in between two strings... For Example:
------------------ show running-config ------------------
everything here
------------------ show redundancy history ------------------
I've been able to do it via REGEX:
$TechFile = "filepath"
$Cisco = Get-Content $TechFile
$ShowRunningCfgPattern = "------------------ show running-config ------------------(.*?)------------------ show redundancy history ------------------"
$Results = [regex]::Match($Cisco,$ShowRunningCfgPattern).Groups[1].Value
However this is not an ideal solution as it just clusters everything together making it parsing this information any further impossible
Anyone know a way around this?
You could get the linenumber/index of the particular strings first, then use select-object to only select the lines within those ranges.
See below code example of this, tested on my machine with success:
$startSTR = "------------------ show running-config ------------------"
$endSTR = "------------------ show redundancy history ------------------"
$testfile = "C:\Temp\test.txt"
$content = get-content $testfile
$startIndex = $content.indexof($startSTR) + 1
$endIndex = $content.indexof($endSTR) -1
$desiredContent = $content | select-object -index ($startIndex..$endIndex)
$desiredContent
I am trying to parse a server monitoring page which doesnt have any class name . The HTML file looks like this
<div style="float:left;margin-right:50px"><div>Server:VIP Owner</div><div>Server Role:ACTIVE</div><div>Server State:AVAILABLE</div><div>Network State:GY</div>
how do i parse this html content to a variable like
$Server VIP Owner
$Server_Role Active
$Server_State Available
Since there is no class name.. i am struggling to get this extracted.
$htmlcontent.ParsedHtml.getElementsByTagName('div') | ForEach-Object {
>> New-Variable -Name $_.className -Value $_.textContent
While you are only showing us a very small part of the HTML, it is very likely there are more <div> tags in there.
Without an id property or anything else that uniquely identifies the div you are after, you can use a Where-Object clause to find the part you are looking for.
Try
$div = ($htmlcontent.ParsedHtml.getElementsByTagName('div') | Where-Object { $_.InnerHTML -like '<div>Server Name:*' }).outerText
# if you're on PowerShell version < 7.1, you need to replace the (first) colons into equal signs
$result = $div -replace '(?<!:.*):', '=' | ConvertFrom-StringData
# for PowerShell 7.1, you can use the `-Delimiter` parameter
#$result = $div | ConvertFrom-StringData -Delimiter ':'
The result is a Hashtable like this:
Name Value
---- -----
Server Name VIP Owner
Server State AVAILABLE
Server Role ACTIVE
Network State GY
Of course, if there are more of these in the report, you'll have to loop over divs with something like this:
$result = ($htmlcontent.ParsedHtml.getElementsByTagName('div') | Where-Object { $_.InnerHTML -like '<div>Server Name:*' }) | Foreach-Object {
$_.outerText -replace '(?<!:.*):', '=' | ConvertFrom-StringData
}
Ok, so the original question did not show what we are dealing with..
Apparently, your HTML contains divs like this:
<div>=======================================</div>
<div>Service Name:MysqlReplica</div>
<div>Service Status:RUNNING</div>
<div>Remarks:Change role completed in 1 ms</div>
<div>=======================================</div>
<div>Service Name:OCCAS</div>
<div>Service Status:RUNNING</div>
<div>Remarks:Change role completed in 30280 ms</div>
To deal with blocks like that, you need a whole different approach:
# create a List object to store the results
$result = [System.Collections.Generic.List[object]]::new()
# create a temporary ordered dictionary to build the resulting items
$svcHash = [ordered]#{}
foreach ($div in $htmlcontent.ParsedHtml.getElementsByTagName('div')) {
switch -Regex ($div.InnerText) {
'^=+' {
if ($svcHash.Count) {
# add the completed object to the list
$result.Add([PsCustomObject]$svcHash)
$svcHash = [ordered]#{}
}
}
'^(Service .+|Remarks):' {
# split into the property Name and its value
$name, $value = ($_ -split ':',2).Trim()
$svcHash[$name] = $value
}
}
}
if ($svcHash.Count) {
# if we have a final service block filled. This happens when no closing
# <div>=======================================</div>
# was found in the HTML, we need to add that to our final array of PSObjects
$result.Add([PsCustomObject]$svcHash)
}
# output on screen
$result | Format-Table -AutoSize
# output to CSV file
$result | Export-Csv -Path 'X:\services.csv' -NoTypeInformation
Output on screen using the above example:
Service Name Service Status Remarks
------------ -------------- -------
MysqlReplica RUNNING Change role completed in 1 ms
OCCAS RUNNING Change role completed in 30280 ms
I have a PowerShell script pulling data in from a CSV. What I am trying to do is "replace" the data in the Account column based on the value. For example, Account 001 = Hardware, Account 002 = Software, etc. The data in the CSV is being pulled from a SQL database so if it would be easier for me to change it in the SQL script, I can do that easily. The Account column in the CSV has 001, 002, etc. I want to change those values to Hardware, Software, etc. Thanks for the help.
$Results = import-csv Expenses.csv
$Array = #()
Foreach($R in $Results)
{
$Object = [pscustomobject][ordered] #{
Account = $R.Account
Vendor = $R.Desc1
Item = $R.Desc2
Amount = $R.Amount
}
$Array += $Object
}
$Array
If your CSV looks anything like this:
Account,Vendor,Item,Amount
001,Some Vendor,Something expensive, 1
002,Another Vendor,Something cheapish,26
you can update without a loop:
# create a lookup hashtable where you combine the account values with the wanted replacement
$lookup = #{
'001' = 'Hardware'
'002' = 'Software'
# etcetera
}
# import the csv and update the `Account` column
$Results = Import-Csv D:\Test\Expenses.csv | Select-Object #{Name = 'Account'; Expression = {$lookup[$_.Account]}}, * -ExcludeProperty Account
# display on screen
$Results
# output to (new) csv file
$Results | Export-Csv -Path D:\Test\Expenses_Updated.csv -NoTypeInformation
Result:
Account Vendor Item Amount
------- ------ ---- ------
Hardware Some Vendor Something expensive 1
Software Another Vendor Something cheapish 26
As per the comment of not2qubit some explanation about the Select-Object statement used.
Because the result should reflect all fields in the csv, where the existing field value named Account needs to be replaced, the code uses a Calculated property to set the Account field values using whatever was stored in the lookup Hashtable.
This is done with #{Name = 'Account'; Expression = {$lookup[$_.Account]}}
Next, all other fields contained in the csv are selected unchanged using the asteriks *.
Because we're overwriting the Accound field, but keep its name, the line ends with -ExcludeProperty Account in order to remove the original Account field in the output.
If we don't do that, PowerShell will show an error: Select-Object : The property cannot be processed because the property "Account" already exists.
If I have understood what you require correctly, you just want to change "001" to "Hardware" and so on in the object imported by the Import-Csv cmdlet. You can create a ScriptBlock with a switch that will return a value based off the value you have searched for. I could have recommended a Hashtable here too, but the benefit of a switch over a Hashtable, in this case, is that you can return the value using the default option if it is not specified. For example:
$Lookup = {
Param ([string]$Value)
switch ($Value) {
"001" { "Hardware" }
"002" { "Software" }
default { $Value }
}
}
$Results = Import-Csv Expenses.csv
foreach($R in $Results)
{
# Invoke the scriptblock with the named parameter.
$R.Account = & $Lookup -Value $R.Account
}
# Do stuff with $Results
I am attempting to put the output of compare-object. I am new to Powershell and unfortunately don't know the ins and outs yet.
My command is as follows:
Compare-Object -referenceObject $(Get-Content "c:\temp\mek\123-first.txt") -differenceObject $(Get-Content "c:\temp\mek\123-second.txt") | %{$_.Inputobject} | sort-object | out-file "c:\temp\mek\results.txt"
The contents of my files are as follows (simply comparing Windows services):
systemname name state startmode
---------- ---- ----- ---------
D7MCYP AdobeARMservice Stopped Auto
D7MCYP AdobeFlashPlayerUpdateSvc Stopped Manual
D7MCYP AeLookupSvc Stopped Manual
My results of the compare-object are as follows:
BL3C4V wudfsvc Stopped Auto
BL3C4V wudfsvc Stopped Manual
D7MCYP AdobeARMservice Running Auto
D7MCYP AdobeARMservice Stopped Auto
Now if anyone could help output to keep the first 2 columns per server and the different values of columns 3,4 to new columns (5,6). It would also be nice if I get titles too. For example:
Server Service Before State Before Mode After State After Mode
BL3C4V wudfsvc Stopped Auto Stopped Manual
D7MCYP AdobeARMservice Running Auto Stopped Auto
Note: The code below is an exercise in parsing plain-text data into objects for more robust, flexible handling.
Ideally, however, processing should start out with objects rather than plain text,
which is why starting with PowerShell cmdlets such as Get-Service rather than the text output from external utilities is preferable.
Assuming that all entries in each input file have a matching server + service-name entry in the respective other file:
$f1, $f2 = "c:\temp\mek\123-first.txt", "c:\temp\mek\123-second.txt"
Compare-Object (Get-Content $f1) (Get-Content $f2) | ForEach-Object {
$i = 0; $ht = #{}; $vals = -split $_.InputObject
foreach($col in 'Server', 'Service', 'State', 'Mode') {
$ht.$col = $vals[$i++]
}
$ht.Before = $_.SideIndicator -eq '<='
[pscustomobject] $ht
} | Group-Object Server, Service | ForEach-Object {
$ndxBefore, $ndxAfter = if ($_.Before) { 0, 1 } else { 1, 0 }
[pscustomobject] #{
Server = $_.Group[0].Server
Service = $_.Group[0].Service
'State Before' = $_.Group[$ndxBefore].State
'Mode Before' = $_.Group[$ndxBefore].Mode
'State After' = $_.Group[$ndxAfter].State
'Mode After' = $_.Group[$ndxAfter].Mode
}
} | Sort-Object Server, Service |
Format-Table
Note:
The above formats the output for display (using Format-Table), without sending it to a file.
You can append | Out-File "c:\temp\mek\results.txt" to save the same representation to a file.
However, note that the command - before Format-Table is applied - returns objects with individual properties, so you can output to a file in a variety of formats, such as by using Export-Csv, for instance.
Example output:
Server Service State Before Mode Before State After Mode After
------ ------- ------------ ----------- ----------- ----------
D7MCYP AdobeFlashPlayerUpdateSvc Stopped Manual Stopped Auto
D7MCYP AeLookupSvc Stopped Manual Started Manual
Explanation:
A single, long pipeline is used, which makes the code concise and memory-efficient.
The pipeline breaks down as follows:
Comparison:
Compare-Object compares the array of lines from the two input files returned by the Get-Content calls, and outputs [pscustomobject] instances representing the differences found, with string property .SideIndicator indicating whether the line at hand (accessible via .InputObject) is unique to the LHS (the 1st input file) - <= - or the RHS (2nd input file) - >=
Transformation to custom objects:
The script block ({ ... }) passed to ForEach-Object is executed for each input object (represented as $_).
-split $_.InputObject splits the "difference line" at hand into fields by runs of whitespace and stores the resulting fields as an array in $vals.
$ht is an auxiliary hashtable that is used to map the field values to field names.
$ht.Before adds a Boolean entry to indicate whether the difference line at hand is from the "before file" (the 1st input file) or not.
[pscustomobject] $ht converts the aux. hashtable into a custom object and outputs it (sends it through the pipeline).
Grouping:
Group-Object is used to group the resulting objects can by shared Server and Service property values, resulting in a [Microsoft.PowerShell.Commands.GroupInfo] instance representing each grouping.
Transformation to combined custom objects:
Again, ForEach-Object is used to perform per-input object processing.
[pscustomobject] #{ ... } is used to construct each combined output object, again using an auxiliary hashtable.
$_.Group contains the input objects that form each group - in our case, $_.Group[0] and $_.Group[1] are the converted-to-objects input lines representing a given server-service combination.
By definition, both input objects have the same .Server and .Service values, so blindly using $_.Group[0]'s values for the combined output object will do.
By contrast, the * Before and * After properties the appropriate input object (whether from the 1st or 2nd file), which is why the array indices $ndxBefore and $ndxAfter are chosen accordingly, via the previously added .Before property
Sorting:
Sort-Object sorts the resulting objects by the specified properties.
Output formatting:
Output-formatting cmdlet Format-Table ensures that the sorted objects are presented as a table.
I am trying to automate the process of managing WSUS reports. I managed to
I) report the updates that I approve to WSUS console.
II) run a cleanup Process for the superseeding
So the script I use to list approved updated updates is:
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updatescope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::LatestRevisionApproved
$updatescope.FromArrivalDAte = [datetime]"10/08/2013"
$wsusgroup = $wsus.GetComputerTargetGroups() | Where {$_.Name -eq "PCM_WSUS_spec"}
$updatescope
$updatescope.gettype()
$updatescope.count
$updateScope.ApprovedComputerTargetGroups.add($wsusgroup)
$wsus.GetUpdates($updatescope) | Select KnowledgebaseArticles,Title
$Updates = $wsus.GetUpdates($updatescope) | Select KnowledgebaseArticles
What I really need is a function to list of the updates went superseded based on the aboce list; updates that got approved after the given date.
Any ideas?
To build a list of superseded updates will require you to know the current update. Superseded updates are maintained as a list of UpdateIDs associated with the current update. (The list of superseding updates is built by traversing that list backwards.)
In the actual package XML, it looks like this:
<sdp:SupersededPackages xmlns:sdp="http://schemas.microsoft.com/wsus/2005/04/CorporatePublishing/SoftwareDistributionPackage.xsd">
<sdp:PackageID>b87cc8c1-b03d-4548-aa53-f0138ec6e2a3</sdp:PackageID>
<sdp:PackageID>7d8fcd7b-49d1-440d-8140-d6c33ce2cb80</sdp:PackageID>
<sdp:PackageID>76f68136-436f-42a5-9029-560b23702416</sdp:PackageID>
<sdp:PackageID>64b8c9d0-ecbc-4711-90de-b190d2ee7ee1</sdp:PackageID>
<sdp:PackageID>6bd77c33-1b2d-450d-91c6-259e101e57bb</sdp:PackageID>
<sdp:PackageID>94642c22-d70e-45b8-a70f-c09b86e2c4f5</sdp:PackageID>
</sdp:SupersededPackages>
But I do not know how it's actually accessed via the API (if even possible), or where in the database schema to find it.
Well I achieved to get a list using the following :
'[reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")
$WSUS = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer('wsupdates',$false,80)
#creating the update scope. Different parameters can be used each time for different reports needed
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updatescope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::LatestRevisionApproved
$updatescope.FromArrivalDAte = [datetime]"10/08/2013"
$wsusgroup = $wsus.GetComputerTargetGroups() | Where {$_.Name -eq "PCM_WSUS_spec"}
$updateScope.ApprovedComputerTargetGroups.add($wsusgroup)
$updatescope
$updates = $wsus.GetUpdates($updatescope) | Select Title,UpdateClassificationTitle,SupersededUpdates | Sort Title |Export-csv 'C:\WSUS\test_approved.csv' -notype
$updates = $wsus.GetUpdates($updatescope)
$updates
$updates | ForEach-Object {
$temp =$_
Write-Output ' temp is'
$temp
$temp | Select Title| Export-CSV 'C:\wsus\superseded.csv' -Append -Notype -Force
$SupersededOnes = $_.GetRelatedUpdates(([Microsoft.UpdateServices.Administration.UpdateRelationship]::UpdatesSupersededByThisUpdate)) |Select Title
Write-Output 'after finding out superseded'
$SupersededOnes
Start-Sleep 8
$SupersededOnes | Export-CSV 'C:\wsus\superseded.csv' -Append -Notype -Force
}'
My issue is it appends the superseded beneath the approved, I need them to the column besides the associating superseding update.