xmlpowershell

Building XML with PowerShell


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.


Solution

  • 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 }