I've inherited this script that builds xml with users and their roles. It doesn't handle the situation where one user can have multiple roles.
Here is an excerpt from the script:
$somePATH = "C:\wherever"
"Dummy Header" | Set-Content $somePATH
$data = @(
[pscustomobject]@{Type='P';ID='Fred';Role='Operator';Domain='Home'}
[pscustomobject]@{Type='P';ID='Fred';Role='Admin';Domain='Home'}
[pscustomobject]@{Type='P';ID='Wilma';Role='Operator';Domain='Home'}
[pscustomobject]@{Type='P';ID='Barney';Role='Admin';Domain='Work'}
[pscustomobject]@{Type='N';ID='Betty';Role='Whatev';Domain='Home'}
)
Function BuildXML
{
param($Type, $ID, $Role, $Domain)
$CC = "1234"
$Account = "SNOW_CC$CC" + "_BNK0000_BLAH"
if ($Type -eq "N") {
@”
<account id="$Account">
<name><![CDATA[$ID@$Domain]]></name>
<endPoint>UNC</endPoint>
<domain>UNC</domain>
<comments/>
<attributes>
<attribute name="appUserName">
<attributeValues>
<attributeValue><value><![CDATA[$ID]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="CostCentre">
<attributeValues>
<attributeValue><value><![CDATA[$CC]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Bank_Number">
<attributeValues>
<attributeValue><value><![CDATA[0000]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Directory">
<attributeValues>
<attributeValue><value><![CDATA[BLAH]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=$Role"/></attributeValues>
</attribute>
</attributes>
</account>
"@ | Add-Content $somePATH
} elseif ($Type -eq "P") {
@"
<account id="$ID">
<name><![CDATA[$ID@$Domain]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[$ID]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=$Role"/></attributeValues>
</attribute>
</attributes>
</account>
"@ | Add-Content $WORKPATH$FEEDFILENAME
}
} # End Function
foreach ($d in $data) {BuildXML $d.Type $d.ID $d.Role $d.Domain}
Showing results just for the 'Fred' user:
<account id="Fred">
<name><![CDATA[Fred@Home]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[Fred]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=Operator"/></attributeValues>
</attribute>
</attributes>
</account>
<account id="Fred">
<name><![CDATA[Fred@Home]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[Fred]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=Admin"/></attributeValues>
</attribute>
</attributes>
</account>
What I'm after is something like this, where 'Fred' would only have one entry, but it reflects both 'Roles':
<account id="Fred">
<name><![CDATA[Fred@Home]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[Fred]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=Operator"/></attributeValues>
<attributeValues><attributeValueRef id="Role=Admin"/></attributeValues>
</attribute>
</attributes>
</account>
I'm at a loss as to how to get the multiple roles reflected in a single user entry.
Appreciate any help.
I've inherited this script that builds xml with users and their roles. It doesn't handle the situation where one user can have multiple roles.
Here is an excerpt from the script:
$somePATH = "C:\wherever"
"Dummy Header" | Set-Content $somePATH
$data = @(
[pscustomobject]@{Type='P';ID='Fred';Role='Operator';Domain='Home'}
[pscustomobject]@{Type='P';ID='Fred';Role='Admin';Domain='Home'}
[pscustomobject]@{Type='P';ID='Wilma';Role='Operator';Domain='Home'}
[pscustomobject]@{Type='P';ID='Barney';Role='Admin';Domain='Work'}
[pscustomobject]@{Type='N';ID='Betty';Role='Whatev';Domain='Home'}
)
Function BuildXML
{
param($Type, $ID, $Role, $Domain)
$CC = "1234"
$Account = "SNOW_CC$CC" + "_BNK0000_BLAH"
if ($Type -eq "N") {
@”
<account id="$Account">
<name><![CDATA[$ID@$Domain]]></name>
<endPoint>UNC</endPoint>
<domain>UNC</domain>
<comments/>
<attributes>
<attribute name="appUserName">
<attributeValues>
<attributeValue><value><![CDATA[$ID]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="CostCentre">
<attributeValues>
<attributeValue><value><![CDATA[$CC]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Bank_Number">
<attributeValues>
<attributeValue><value><![CDATA[0000]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Directory">
<attributeValues>
<attributeValue><value><![CDATA[BLAH]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=$Role"/></attributeValues>
</attribute>
</attributes>
</account>
"@ | Add-Content $somePATH
} elseif ($Type -eq "P") {
@"
<account id="$ID">
<name><![CDATA[$ID@$Domain]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[$ID]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=$Role"/></attributeValues>
</attribute>
</attributes>
</account>
"@ | Add-Content $WORKPATH$FEEDFILENAME
}
} # End Function
foreach ($d in $data) {BuildXML $d.Type $d.ID $d.Role $d.Domain}
Showing results just for the 'Fred' user:
<account id="Fred">
<name><![CDATA[Fred@Home]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[Fred]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=Operator"/></attributeValues>
</attribute>
</attributes>
</account>
<account id="Fred">
<name><![CDATA[Fred@Home]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[Fred]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=Admin"/></attributeValues>
</attribute>
</attributes>
</account>
What I'm after is something like this, where 'Fred' would only have one entry, but it reflects both 'Roles':
<account id="Fred">
<name><![CDATA[Fred@Home]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[Fred]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues><attributeValueRef id="Role=Operator"/></attributeValues>
<attributeValues><attributeValueRef id="Role=Admin"/></attributeValues>
</attribute>
</attributes>
</account>
I'm at a loss as to how to get the multiple roles reflected in a single user entry.
Appreciate any help.
Share Improve this question asked Feb 15 at 14:39 surge3333surge3333 2074 silver badges9 bronze badges2 Answers
Reset to default 1Consolidate the
[pscustomobject]
instances in your$data
array by combining all instances that have the same.ID
property value into a single instance whose.Role
property is the aggregation of all roles, which you can do via theGroup-Object
cmdlet.Operating on each consolidated instance, use
$(...)
, the subexpression operator to embed code in your expandable here-string that iterates over potentially multiple roles and creates an XML element for each.
The following puts it all together; note that, for demonstration purposes, the BuildXml
function outputs the generated XML to the display by default:
# Note the renaming of $Role to $Roles.
# Look for the $($Roles.ForEach({...})) lines below.
Function BuildXML {
param($Type, $ID, $Roles, $Domain)
$CC = '1234'
$Account = "SNOW_CC$CC" + '_BNK0000_BLAH'
if ($Type -eq 'N') {
@”
<account id="$Account">
<name><![CDATA[$ID@$Domain]]></name>
<endPoint>UNC</endPoint>
<domain>UNC</domain>
<comments/>
<attributes>
<attribute name="appUserName">
<attributeValues>
<attributeValue><value><![CDATA[$ID]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="CostCentre">
<attributeValues>
<attributeValue><value><![CDATA[$CC]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Bank_Number">
<attributeValues>
<attributeValue><value><![CDATA[0000]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Directory">
<attributeValues>
<attributeValue><value><![CDATA[BLAH]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
$($Roles.ForEach({
" <attributeValues><attributeValueRef id=`"Role=$_`"/></attributeValues>"
}) -join [Environment]::NewLine)
</attribute>
</attributes>
</account>
"@ # | Add-Content $somePATH
}
elseif ($Type -eq 'P') {
@"
<account id="$ID">
<name><![CDATA[$ID@$Domain]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[$ID]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
$($Roles.ForEach({
" <attributeValues><attributeValueRef id=`"Role=$_`"/></attributeValues>"
}) -join [Environment]::NewLine)
</attribute>
</attributes>
</account>
) </attribute>
</attributes>
</account>
"@ # | Add-Content $WORKPATH$FEEDFILENAME
}
} # End Function
#"# Your original sample data.
$data = @(
[pscustomobject]@{Type = 'P'; ID = 'Fred'; Role = 'Operator'; Domain = 'Home' }
[pscustomobject]@{Type = 'P'; ID = 'Fred'; Role = 'Admin'; Domain = 'Home' }
[pscustomobject]@{Type = 'P'; ID = 'Wilma'; Role = 'Operator'; Domain = 'Home' }
[pscustomobject]@{Type = 'P'; ID = 'Barney'; Role = 'Admin'; Domain = 'Work' }
[pscustomobject]@{Type = 'N'; ID = 'Betty'; Role = 'Whatev'; Domain = 'Home' }
)
# Consolidate all instances that have the same .ID value into
# a single instance each that collects all .Role values, using
# Group-Object.
$consolidatedData =
$data | Group-Object Id | ForEach-Object {
if ($_.Count -gt 1) {
# Use *member-access enumeration* to collect the .Role property values
# from all members of the group.
$_.Group[0].Role = $_.Group.Role
}
$_.Group[0]
}
# Build the XML for each potentially consolidated object.
foreach ($d in $consolidatedData) { BuildXML $d.Type $d.ID $d.Role $d.Domain }
Why not go for a result like this?
<attribute name="Role">
<attributeValues>
<attributeValueRef>"Operator"</attributeValueRef>
<attributeValueRef>"Admin"</attributeValueRef>
</attributeValues>
</attribute>
Anyway, since you are building the xml using Here-String templates, I would suggest:
Change the data by adding a single value or an array of values for the Role
property like
$data = @(
[pscustomobject]@{Type='P';ID='Fred';Role='Operator', 'Admin';Domain='Home'}
...
)
Then rewrite the function
function BuildXML {
param(
[char]$Type,
[string]$ID,
[string[]]$Role, # string array or single value
[string]$Domain
)
$CC = "1234"
$Account = "SNOW_CC$CC" + "_BNK0000_BLAH"
# Change the templates by adding a placeholder text you can replace later
$typeN = @"
<account id="$Account">
<name><![CDATA[$ID@$Domain]]></name>
<endPoint>UNC</endPoint>
<domain>UNC</domain>
<comments/>
<attributes>
<attribute name="appUserName">
<attributeValues>
<attributeValue><value><![CDATA[$ID]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="CostCentre">
<attributeValues>
<attributeValue><value><![CDATA[$CC]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Bank_Number">
<attributeValues>
<attributeValue><value><![CDATA[0000]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Directory">
<attributeValues>
<attributeValue><value><![CDATA[BLAH]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues>
@@ROLES@@
</attributeValues>
</attribute>
</attributes>
</account>
"@
$typeP = @"
<account id="$ID">
<name><![CDATA[$ID@$Domain]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[$ID]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues>
@@ROLES@@
</attributeValues>
</attribute>
</attributes>
</account>
"@
if ($Type -eq "N") { $xmlTemplate = $typeN }
else { $xmlTemplate = $typeP }
# Replace the @@ROLES@@ placeholder
# adjust this spacer to the number of tabs in your actual xml do it looks nice
$spacer = "`t" * 5 # 5 TABS in this example
$roleAttribs = $(foreach ($userRole in $Role) {
'{0}<attributeValueRef id="Role={1}"/>' -f $spacer, $userRole
}) -join [environment]::NewLine
# save to file
$xmlTemplate -replace '@@ROLES@@', $roleAttribs | Add-Content $WORKPATH$FEEDFILENAME
}
# for demo:
BuildXML -Type 'P' -ID 'Fred' -Role 'Admin', 'Operator' -Domain 'whatever'
The result would be
<account id="Fred">
<name><![CDATA[[email protected]]]></name>
<endPoint>ABC</endPoint>
<domain>ABC</domain>
<comments/>
<attributes>
<attribute name="AppBoRID">
<attributeValues>
<attributeValue><value><![CDATA[Fred]]></value></attributeValue>
</attributeValues>
</attribute>
<attribute name="Role">
<attributeValues>
<attributeValueRef id="Role=Admin"/>
<attributeValueRef id="Role=Operator"/>
</attributeValues>
</attribute>
</attributes>
</account>
I would also suggest you rename your function into something using Approved Verbs.
Perhaps Format-UserXml
would be an idea?