Powershell reading from a XML - powershell

I am attempting to read from an XML file and make an output of the information.
<?xml version="1.0" encoding="utf-8"?>
<ModelList>
<Model name="ThinkCentre M715Q">
<Types>
<Type>10M4</Type>
<Type>10RA</Type>
<Type>10RB</Type>
<Type>10M5</Type>
<Type>10RC</Type>
<Type>10M2</Type>
<Type>10RD</Type>
<Type>10M3</Type>
</Types>
</Model>
</ModelList>
I managed to get the output of the model attribute with the following powershell code.
[xml]$xmlFile = Get-Content -Path C:\Temp\data.xml
$xmlFile.GetType().Attributes
$xmlFile.ModelList.Model | Format-Table
Output information with the current code above:
name Types
---- -----
ThinkCentre M715Q Types
But... As you can see, the Types attribute is just types. I also want to be able to read the nested information inside of the ModelList. I want the output to be more like this:
name Types
---- -----
ThinkCentre M715Q 10M4, 10RA, 10RB, 10M5, 10RC...
I am stuck here. I need guidance to just simply bind the attributes. That Types attribute knows it is associated with the Model of ThinkCentre. Any help is appreciated! Thanks.

Since your XML could contain more models and types, you would need to loop over the nodes.
Also, there is a better way to load the xml than using Get-Content that automatically takes the documents encoding into account:
# load the xml file. This way, you are ensured to get the file encoding correct
$xml = [System.Xml.XmlDocument]::new()
$xml.Load('C:\Temp\data.xml')
foreach ($model in $xml.ModelList.Model) {
[PsCustomObject]#{
Model = $model.Name
Types = $model.Types.Type -join ', '
}
}
Output:
Model Types
----- -----
ThinkCentre M715Q 10M4, 10RA, 10RB, 10M5, 10RC, 10M2, 10RD, 10M3

Related

How namespaces in XML and FreeMarker template work together?

I have read a few relevant topics on here and now have a basic understanding of the namespace concept. But I still have some difficulties getting my template to work with my XML data.
When there is no namespace involved, this template and this XML works fine:
ftl:
${pp.doc.user1.name}
XML:
<?xml version="1.0" encoding="UTF-8" ?>
<user1>
<name>Jack</name>
</user1>
However, if there is namespace defined in the XML file, like this:
<?xml version="1.0" encoding="UTF-8" ?>
<user1 xmlns="https://example.com/xyz">
<name>Jack</name>
</user1>
I got error messages:
Error when processing this file: data\test1.xml
FreeMarker template error: For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), or "template output" , but this has evaluated to a sequence+hash (wrapper: f.e.dom.NodeListModel):
==> pp.doc.user1.name [in template "renderer/test.ftlh" at line 1, column 3]
----
Tip: This XML query result can't be used as string because for that it had to contain exactly 1 XML node, but it contains 0 nodes. That is, the constructing XML query has found no matches.
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${pp.doc.user1.name} [in template "renderer/test.ftlh" at line 1, column 1]
----
My current understanding is that in the ftl, I need to use <#ftl ns_prefixes={"ns": "https://example.com/xyz"}> or something like this, but none of the things I tried have worked. Please kindly help. Thank you!
In pp.doc.user1.name you aren't using any namespace prefix, so the elements are assumed to belong to the default namespace, which is by default nothing. To set it, use <#ftl ns_prefixes={"D": "https://example.com/xyz"}>. D is a prefix reserved for this purpose.

fetch particular column in PowerShell

I am creating a PowerShell Script wherein I have a csv file which consist of few parameters wherein there is a parameter called as status .The ask is whenever the status is Fail ,I want to get the corresponding row. I am using Import-Csv cmdlet in order to fetch the csv file and checking the status if the status is fail or not and based on that I am fetching the corresponding details using the split function but when I am using the split method it is giving me error as
Method invocation failed because
[System.Management.Automation.PSCustomObject] does not contain a
method named 'split'.
Using the below code
$Report=(Import-Csv "C:\Users\Documents\Optim_Config_Report_20210216170900.csv")
foreach($i in $Report)
{
if($i.Status -eq "Fail")
{
$RULE_ID= $i.split(',')[0]
Write-output $RULE_ID
}
}
Can someone please help me how can I get the corresponding Row details for which the Status is fail?
Report is something like this in a csv format
Rule,Id,Category,Sub_Category,System_Value,Risk,Status
1,Operations,Access,Login,High,Pass
2,Operations,Logging,AccessControl,Medium,Pass
3,Operations,encryptions,certificate,High,Fail,
4,Security,Encryption,protcolo,High,Fail
Thanks in Advance!
When you Import-CSV, you are creating an array of [PSCustomObject]s, each of which has properties corresponding to the names of the columns in the CSV. For your example, those properties will be Rule, ID, Category, Sub_Category, System_Value, Risk, and Status.
It is not clear from your example what information you wish to report in the event of a status of Fail, but all you need to do is reference the properties that contain that information - for example, if I wanted to report the Category and Sub_Category, I might use
...
if ($i.status -eq "Fail") {
Write-Host $i.Category, $i.Sub_Category
}
...
Which would, for your sample data, output
encryptions certificate
Encryption protcolo
For a CSV file named "test.csv" with content like this:
"Rule","Id","Category","Sub_Category","System_Value","Risk","Status"
1,Operations,Access,Login,1,High,Pass
2,Operations,Logging,AccessControl,1,Medium,Pass
3,Operations,encryptions,certificate,1,High,Fail
4,Security,Encryption,protcolo,1,High,Fail
This script:
Import-Csv -Path ./test.csv | Where-Object Status -eq Fail | Format-Table
Returns:
Rule Id Category Sub_Category System_Value Risk Status
---- -- -------- ------------ ------------ ---- ------
3 Operations encryptions certificate 1 High Fail
4 Security Encryption protcolo 1 High Fail

Import-Excel only data without header in string - PowerShell

I have installed Import-Excel Module for PowerShell by dfinke which has a great functionality but I'm facing some troubles with the headers.
I would like to insert only the text into a string array, but instead, it comes with the header even when -NoHeader is declared. According to the documentation it's not its function not insert the header in the variable but I'm looking for a way to do it. So far I came with a newbie solution of $xlsxArray | Format-Table -HideHeaders | Out-File C:\temp\info.txt and then remove the spaces with .Trim() so the file doesn't get written #{P1=ContentofTheCell}.
Is there a better way to accomplish it?
Thank you so far.
You didn't give enough detail about the desired output, but I'll try to give guidance.
Import-Excel will return objects. Normally the column headers become the property names on the objects. When you use -NoHeader, the property names are simply named P1, P2 etc... An object's properties must have names. If you want the data from those properties you may have to process differently. You can access the properties like any other object collection:
$ExcelData = Import-Excel "C:\Temp\Some.xlsx"
$ExcelData.PropertyName
The PropertyName would be the column header from the file. So let's say I had a colum named Balance in that file, then the example would something like:
$ExcelData = Import-Excel "C:\Temp\Some.xlsx"
$ExcelData.balance
Output:
7254.74
4268.16
3051.32
64.77
323.22
146.62
14798.83
Note: these are pretty simple examples. Obviously things can get more complex.

Powershell not displaying rss feed

I am trying to parse an RSS feed using powershell, however when i use Invoke-RestMethod this is the only output that i get:
xml RDF
--- ---
version="1.0" encoding="UTF-8" RDF
I have had this issue with multiple rss feeds where nothing gets displayed, how can i get it working so that the RSS feed is actually displayed when I use Invoke-RestMethod?
$url = 'http://www.aero-news.net/news/rssfeed.xml'
Invoke-RestMethod -Uri $url
Thanks.
The output you're getting implies that Invoke-RestMethod worked as intended: it returned a [xml] (System.Xml.XmlDocument) instance that is an XML DOM (document object model) of the XML text returned from the site.
Unfortunately, the default display formatting for [xml] instances, as shown in your question, isn't very helpful[1], but all the information is there, which you can simply verify by accessing the .OuterXml property:
# Get the XML DOM object parsed from the website's XML text output.
$xml = Invoke-RestMethod 'http://www.aero-news.net/news/rssfeed.xml'
# Output its text representation.
$xml.OuterXml
The above prints a string such as:
<?xml version="1.0" encoding="iso-8859-1"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/">
<channel rdf:about="http://www.aero-news.net">
<title>Aero-News Network</title>
<description>Daily, Real-Time news and information critical to aviation and aerospace personnel the world over. Aero-News provides daily newsletter summaries, RSS feeds, and numerous personal and professional syndication and news distribution options to insure that aviators, the world over, are kept up to date on information of critical concern.</description>
<link>http://www.aero-news.net</link>
...
You can therefore work with the [xml] (XmlDocument) instance as usual:
Using PowerShell's convenient adaptation of the XML DOM via property-based dot-notation; e.g., $xml.RDF.channel.about returns string http://www.aero-news.net, which is the text content of the about attribute of the element whose path (from the document root) is /RDF/channel, irrespective of namespaces[2]; see this answer for more information.
Using the [xml] type's native properties and methods, such as the XPath-based .SelectNodes() method for extracting information from the XML document; however, this is less convenient if XML namespaces are involved (such as in your case), because they require explicit management; see this answer for more information.
If you want to pretty-print the XML text:
The [xml] (System.Xml.XmlDocument) type doesn't have built-in support for pretty-printing its text content.
While it's possibly to use a System.Xml.XmlWriter instance, doing so is verbose and cumbersome; however, it does offer you control over the specifics of the pretty-printing format.
A pragmatic, much simpler solution is to use the System.Xml.Linq.XDocument type instead (for which PowerShell does not provide dot notation, unfortunately), whose .ToString() method pretty-prints by default, using indentation with two space characters, as the following example demonstrates:
# Create a sample XmlDocument instance, as would be returned
# from an Invoke-RestMethod call (from a site returning XML text):
$xml = [xml] ('<?xml version="1.0"?><catalog><book id="bk101"><title>De Profundis</title></book></catalog>')
# Cast to [System.Xml.Linq.XDocument] via .OuterXml; the former's
# .ToString() method then pretty-prints automatically.
([System.Xml.Linq.XDocument] $xml.OuterXml).ToString()
The above yields the following string:
<catalog>
<book id="bk101">
<title>De Profundis</title>
</book>
</catalog>
Note that the XML declaration is not included, but you can easily prepend it yourself:
$xd = [System.Xml.Linq.XDocument] $xml.OuterXml
$xd.Declaration.ToString() + "`n" + $xd.ToString()
The following Format-Xml convenience function wraps this functionality:
function Format-Xml {
param(
[Parameter(ValueFromPipeline)]
[xml] $Xml
)
process {
$xd = [System.Xml.Linq.XDocument] $Xml.OuterXml
if ($xd.Declaration) {
$str = $xd.ToString()
$newline = ("`n", "`r`n")[$str.Contains("`r`n")]
$xd.Declaration.ToString() + $newline + $str
}
else {
$xd.ToString()
}
}
}
Now you can use the following to pretty-print the original $xml variable (obtained via Invoke-RestMethod):
# Outputs a pretty-printed version of the document's XML text.
$xml | Format-Xml
[1] What is being shown is the content of the document's XML declaration as property .xml, and the name of the document (root) element as a property named for itself. Printing any given element in the document works as follows: if the element has neither attributes nor child elements, its text content (text child node), if any, is printed. Otherwise, its attributes and their values are printed, followed by properties named for the child elements, each represented by their name as the property value too, if they have attributes and/or child elements themselves, otherwise by their text content, if any.
[2] An example command that processes all feed items whose title contains a given word and transforms them into custom objects.
$userTerm = 'Quote'
$xml.RDF.Item | ? Title -like "*$userTerm*" | % {
[PSCustomObject]#{
Source = "aero"
Title = $_.Title
Link = $_.Link
Description = $_.description
}
}
Invoke-WebRequest parses the RSS feed data as xml. You can just access the data like an ordinary object. A demo:
$feed = [xml]( invoke-webrequest "https://arminreiter.com/feed/" )
$feed.rss.channel.item | Select-Object #{Name="Id";Expression={$_."post-id".InnerText}}, title, link, pubDate

Convert String from XML to Array

I am setting up a unattended installation of a software with PowerShell.
If I use the following code to set an argument ($Servers) for the installer, the installation is OK:
$Servers = "Server1.$($env:USERDNSDOMAIN)","Server2.$($env:USERDNSDOMAIN)"
But I want to get the settings from an external XML file like this one:
<?xml version="1.0" encoding="utf-8"?>
<Settings>
<Servers>"Server1.$($env:USERDNSDOMAIN)","Server2.$($env:USERDNSDOMAIN)"</Servers>
</Settings>
I use the following code to retrieve the data in Powershell:
[Xml]$xmlConfigFile = Get-Content -Path "C:\Temp\Settings.xml"
$Servers = $ExecutionContext.InvokeCommand.ExpandString($xmlConfigFile.Settings.Servers)
This does NOT work. The install fails.
I think this is because I get a string back from the XML and not an array like in the first example above.
What is the best method to workaround the issue?
I thought about .Split(","), but the quotes are a problem. I also tried ConvertFrom-Csv, but this didn't work either.
EDIT:
The output of the variable $Servers when it's working is:
Server1.domain.local
Server2.domain.local
The ouput of the variable $Server when coming from XML is:
"Server1.domain.local","Server2.domain.local"
Yes, the value you get from the XML data is a single string, not an array, so you need to split it to go from string to array. You could remove the double quotes before splitting the string (PowerShell allows you to daisy-chain method calls that return a value):
$Servers = $Servers.Replace('"', '').Split(',')
However, a much better approach would be to fix your input data by putting the values in separate nodes:
<?xml version="1.0" encoding="utf-8"?>
<Settings>
<Servers>
<Server>Server1.$($env:USERDNSDOMAIN)</Server>
<Server>Server2.$($env:USERDNSDOMAIN)</Server>
</Servers>
</Settings>
so you can process the data like this:
$Servers = $xmlConfigFile.Settings.Servers.Server | ForEach-Object {
$ExecutionContext.InvokeCommand.ExpandString($_)
}
or like this:
$Servers = $xmlConfigFile.SelectNodes('//Server') | ForEach-Object {
$ExecutionContext.InvokeCommand.ExpandString($_.'#text')
}