by Eurisko at 2013-01-26 13:10:22
I am fairly new to PowerShell, but I have been writing a large number of functions to utilize a specific products WebAPI via Soap requests/responses.by ps_gregg at 2013-01-26 14:15:02
So far, things were great, I was able to reference the nodes the way I needed, make use of them, and output what I wanted.
Until I ran into output that didn’t have nodes in the way I’m used to, and I was quickly over my head. I’m hoping there are some built in functions to handle this data, or some pre-done routines to handle it, as I don’t have a clue how to even begin to manipulate it.
The listed respose is:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetUserListResponse xmlns="http://archer-tech.com/webservices/">
<GetUserListResult>string</GetUserListResult>
</GetUserListResponse>
</soap:Body>
</soap:Envelope>
If I drill down into the response: $SoapResponse.Envelope.Body.GetUserListResponse.GetUserListResult I get this blurb:
<Return>
<User>
<row_num>1</row_num>
<ID>186</ID>
<LastName>Norris</LastName>
<MiddleName/>
<FirstName>Chuck</FirstName>
<AccountStatus>Active</AccountStatus>
<ParamName>Speical Security</ParamName>
<SecurityID>7</SecurityID>
<username>cnorris</username>
<SysAdmin>False</SysAdmin>
<DistinguishedName>Norris, Chuck</DistinguishedName>
<ReturnDomain/>
<TimeZone>Central Standard Time</TimeZone>
<Locale />
<IsLoggedIn>False</IsLoggedIn>
</User>
<User>
<row_num>2</row_num>
<ID>205</ID>
<LastName>Kent</LastName>
<MiddleName/>
<FirstName>Clark</FirstName>
<AccountStatus>Active</AccountStatus>
<ParamName>Special Security</ParamName>
<SecurityID>7</SecurityID>
<username>superman1</username>
<SysAdmin>False</SysAdmin>
<DistinguishedName>Kent, Clark</DistinguishedName>
<ReturnDomain/>
<TimeZone>Central Standard Time</TimeZone>
<Locale />
<IsLoggedIn>False</IsLoggedIn>
</User>
<User>
<row_num>3</row_num>
<ID>221</ID>
<LastName>Flintstone</LastName>
<MiddleName>P.</MiddleName>
<FirstName>Fred</FirstName>
<AccountStatus>Active</AccountStatus>
<ParamName>General User</ParamName>
<SecurityID>1</SecurityID>
<username>fflintstone</username>
<SysAdmin>False</SysAdmin>
<DistinguishedName>Flintstone, Fred</DistinguishedName>
<ReturnDomain>bedrock.com</ReturnDomain>
<TimeZone>Central Standard Time</TimeZone>
<Locale>2</Locale>
<IsLoggedIn>True</IsLoggedIn>
</User>
</Return>
If I output the whole response to a file, it looks more like this sample:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="Error; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetUserListResponse xmlns="somedomain.com
<GetUserListResult><Return>
<User>
<row_num>1</row_num>
<ID>186</ID>
<LastName>Admin</LastName>
<MiddleName></MiddleName>
<FirstName>Speical</FirstName>
<AccountStatus>Active</AccountStatus>
<ParamName>Speical Admin Security Parameter</ParamName>
<SecurityID>7</SecurityID>
<username>Speicaladmin</username>
<SysAdmin>False</SysAdmin>
<DistinguishedName>Admin, Speical</DistinguishedName>
<ReturnDomain></ReturnDomain>
<TimeZone>Central Standard Time</TimeZone>
<Locale />
<IsLoggedIn>False</IsLoggedIn>
</User>
<User>
<row_num>2</row_num>
<ID>205</ID>
<LastName>Admin2</LastName>
<MiddleName></MiddleName>
<FirstName>Speical</FirstName>
<AccountStatus>Active</AccountStatus>
<ParamName>Speical Admin Security Parameter</ParamName>
<SecurityID>7</SecurityID>
<username>Speicaladmin2</username>
<SysAdmin>False</SysAdmin>
<DistinguishedName>Admin2, Speical</DistinguishedName>
<ReturnDomain></ReturnDomain>
<TimeZone>Central Standard Time</TimeZone>
<Locale />
<IsLoggedIn>False</IsLoggedIn>
</User>
<User>
<row_num>3</row_num>
<ID>221</ID>
<LastName>Admin3</LastName>
<MiddleName></MiddleName>
<FirstName>Speical</FirstName>
<AccountStatus>Active</AccountStatus>
<ParamName>Speical Admin Security Parameter</ParamName>
<SecurityID>7</SecurityID>
<username>Speicaladmin3</username>
<SysAdmin>False</SysAdmin>
<DistinguishedName>Admin3, Speical</DistinguishedName>
<ReturnDomain></ReturnDomain>
<TimeZone>Central Standard Time</TimeZone>
<Locale />
<IsLoggedIn>False</IsLoggedIn>
</User>
</Return></GetUserListResult>
</GetUserListResponse>
</soap:Body>
</soap:Envelope>
I had pumped out about 80 or so of these functions I made for handling these methods and everything was nice and simple, now I completely went over my head on this one.
Here is what I’m trying to do:
I basically need to be able to extract a <User> "chunk" at a time to be able to pass it off into another function (that I could just call in a foreach loop). My other function will do some lookups based on those values, manipulate them then output the results to a file.
What’s the best way to pass those <User> sections one at a time to a function, AND, what’s the best format to send them in? (ie, send it as an XML chunk and manipulate the nodes in the sub function to get what I want, or send them each as paramaters?)
Previously I did a chunk like this, but that was just one simple CSV.
Import-CSV $DataFile | ForEach {Set-UserAccountStatus -Token $TokenValue -FromScript $true -ResultsLog $ResultsLog -username $.username -accountStatus $accountStatus}
Any suggestions or recommendations are greatly appreciated. Thanks!
Hi Euriskoby Eurisko at 2013-01-26 15:59:14
Looks like you just need to drill down one more level. I saved your whole response to a file and did the following:
$file_xml = [xml](Get-Content test.xml)
$file_xml.Envelope.Body.GetUserListResponse.GetUserListResult.Return | % {$.User}
Which returns XmlElement objects that look like this:
row_num : 1
ID : 186
LastName : Admin
MiddleName :
FirstName : Speical
AccountStatus : Active
ParamName : Speical Admin Security Parameter
SecurityID : 7
username : Speicaladmin
SysAdmin : False
DistinguishedName : Admin, Speical
ReturnDomain :
TimeZone : Central Standard Time
Locale :
IsLoggedIn : False
row_num : 2
ID : 205
LastName : Admin2
MiddleName :
FirstName : Speical
AccountStatus : Active
ParamName : Speical Admin Security Parameter
SecurityID : 7
username : Speicaladmin2
SysAdmin : False
DistinguishedName : Admin2, Speical
ReturnDomain :
TimeZone : Central Standard Time
Locale :
IsLoggedIn : False
row_num : 3
ID : 221
LastName : Admin3
MiddleName :
FirstName : Speical
AccountStatus : Active
ParamName : Speical Admin Security Parameter
SecurityID : 7
username : Speicaladmin3
SysAdmin : False
DistinguishedName : Admin3, Speical
ReturnDomain :
TimeZone : Central Standard Time
Locale :
IsLoggedIn : False
I would take those objects and using your example command, I would pass the object properties as parameter values to my other commands – it would look something like this:
$file_xml = [xml](Get-Content test.xml)
$users = $file_xml.Envelope.Body.GetUserListResponse.GetUserListResult.Return
$users | ForEach { Set-UserAccountStatus -Token $TokenValue -FromScript $true -ResultsLog $ResultsLog -username $.User.username -accountStatus $AccountStatus }
This can be consolidated into one less line, but I figured more lines were better for explaination purposes.
$.User.username will get the username property of each of the XmlElement objects as they go through the Foreach loop.
I have also seen it written as ($_.User).username which will work too.
Hope that helps.
Cheers
-Gregg
That appears to have worked perfectly!by Eurisko at 2013-01-26 17:34:43
Thank you again so much!
One final piece for my project stumps me. Some of the values witn a single XML node.by Eurisko at 2013-01-26 18:29:39
Drilling down in my variable that I stored the Soap response:
$GetUser.Envelope.Body.GetUserResponse.GetUserResult
Which contains this:<user><name first="Fred" middle="" last="Flintstone" /><system userName="fflintstone" userId="293" status="Active" securityParameter="1" lastLogin="" forcePasswordChange="False" company="Bedrock Rubble Company" title="Cheif Dino Operator" locale="" /><timeZone id="Central Standard Time" /><address><html><head></head><p style="margin: 0px">123 Bedrock Lane,</p><p style="margin: 0px">Bedrock, FL 68513</p></html></address><notes/><contactItems><item id="231" type="EMail" value="fred@bedrock.com" default="True" /></contactItems><roles><role id="1" name="General User" /><role id="2" name="Default Administrator" /><role id="25" name="Access Control Administrator" /><role id="40" name="EM: Admin" /><role id="2" name="Default Administrator" /><role id="51" name="CM: Admin" /></roles><groups><group id="38" name="Risk Management" /><group id="54" name="CM: Admin" /></groups></user>
I have a bad feeling this is much more difficult to extract my values from? Ideally, what I need to do is be able to access each of these as a variable, or convert them to variables. I have to be able to evaluate each value in this block, so I can compare it to other values being passed to this function to make comparisons and selections based upon my findings.
Is this as ugly as I fear?
To add to it, I realize it has that as a string, and not an xml element like I thought. (when using Get-Member -Force, it sees that section as Type System.String)by Eurisko at 2013-01-26 19:21:28
After trying every advanced trick I could find, I simply cast the string I got once I drilled down as xml, then I could again navigate that section. ($XMLGetUser = [xml]$GetUser.Envelope.Body.GetUserResponse.GetUserResult )