1

I am new to Powershell scripts that parses XML and I just ran into a situation where I needed to make sure a line of XML got executed strictly before another.

To get there, I first searched on how I would be reading the XML file and this is what I got:

[XML]$Web = Get-Content *path_to_XML_file*

Using the dot notation, I travelled to the node that contained the line that needed to be validated (I still dont know how I will manage to find the line number to figure out if it came before or after the other).

The node I needed to iterate through was located at:

$Web.Configuration.location.'system.WebServer'.modules

Heres a console output when I entered that last line:

add
---
{HttpCacheModule, DynamicCompressionModule, IsapiFilterModule, ProtocolSupportModule...}

This node contained multiple instances of other nodes named 'add' but was of type System.Array.

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

Also, heres a copy of the modules nodes that are in the XML:

 <modules>
        <add name="HttpCacheModule" lockItem="true" />
            <add name="DynamicCompressionModule" lockItem="true" />   <--HERE
        <add name="IsapiFilterModule" lockItem="true" />
        <add name="ProtocolSupportModule" lockItem="true" />
        <add name="StaticFileModule" lockItem="true" />
        <add name="AnonymousAuthenticationModule" lockItem="true" />
        <add name="DefaultDocumentModule" lockItem="true" />
        <add name="CustomErrorModule" lockItem="true" />
        <add name="HttpRedirectionModule" lockItem="true" />
        <add name="RequestFilteringModule" lockItem="true" />
        <add name="IsapiModule" lockItem="true" />
        <add name="ConfigurationValidationModule" lockItem="true" />
            <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" preCondition="managedHandler" />
            <add name="Session" type="System.Web.SessionState.SessionStateModule" preCondition="managedHandler" />
            <add name="WindowsAuthenticationModule" lockItem="true" />
            <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" preCondition="managedHandler" />
            <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" preCondition="managedHandler" />
            <add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" preCondition="managedHandler" />
            <add name="RoleManager" type="System.Web.Security.RoleManagerModule" preCondition="managedHandler" />
            <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" preCondition="managedHandler" />
            <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" preCondition="managedHandler" />
            <add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" preCondition="managedHandler" />
            <add name="Profile" type="System.Web.Profile.ProfileModule" preCondition="managedHandler" />
            <add name="UrlMappingsModule" type="System.Web.UrlMappingsModule" preCondition="managedHandler" />
            <add name="BasicAuthenticationModule" lockItem="true" />
            <add name="HttpLoggingModule" lockItem="true" />
            <add name="StaticCompressionModule" lockItem="true" />
            <add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler,runtimeVersionv2.0" />
            <add name="ServiceModel-4.0" type="System.ServiceModel.Activation.ServiceHttpModule, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler,runtimeVersionv4.0" />
            <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler,runtimeVersionv4.0" />
            <add name="ScriptModule-4.0" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler,runtimeVersionv4.0" />
            <add name="ClientLoggingHandler" />
            <add name="AdvancedLoggingModule" />
            <add name="AppWarmupModule" />
            <add name="RewriteModule" />  <---- HERE
            <add name="FailedRequestsTracingModule" lockItem="true" />
            <add name="F5XFFHttpModule" />
    </modules>

I need to make sure the line <add name="DynamicCompressionModule" lockItem="true" /> absolutely gets executed before <add name="RewriteModule" /> and I don't know how to iterate through this System.Array to get there.

Could anyone help me with that, please?

1
  • I would try to write this using XPath. Then I could just use Select-Xml.
    – Bacon Bits
    Commented Apr 19, 2018 at 19:36

2 Answers 2

0

You can navigate the add nodes using the NextSibling property. Since your array is not inherently indexed we just need to index the items you care about, and move the second one to the end if its in the wrong place. I just use $i to count up as we loop through nodes, and if we find either DynamicCompressionModule or RewriteModule I get the index of it, and I'll get the RewriteModule node so I can move it later if needed. Then if the index of DynamicCompressionModule is greater than that of RewriteModule I add a clone of RewriteModule after the last node of the array, and delete the original.

$i=0
$CurrentNode = $Web.Configuration.location.'system.WebServer'.modules.FirstChild

Do{
    $i
    $CurrentNode
    Switch($CurrentNode){
        {$_.Name -eq 'DynamicCompressionModule'}{$DCM=$i;$DCMNode=$CurrentNode}
        {$_.Name -eq 'RewriteModule'}{$RM=$i;$RMNode=$CurrentNode}
    }
    $i++
    $CurrentNode = $CurrentNode.NextSibling

}Until($CurrentNode -eq $Web.Configuration.location.'system.WebServer'.modules.LastChild)

If($DCM -gt $RM){
    "Moving RewriteModule to end of list"
    #Add a clone of the RewriteModule node after the last item in the 'add' array
    $Web.Configuration.location.'system.WebServer'.modules.AppendChild($RMNode.Clone())
    #Remove the original RewriteModule node since we now have a clone of it at the end of the array
    $Web.Configuration.location.'system.WebServer'.modules.RemoveChild($RMNode)
}Else{"No change needed"}
4
  • Thank you for your answer. I tried to get the solution you proposed to work but I keep receiving and error saying that the line: $Web.Configuration.location.'system.WebServer'.modules.AppendChild($RMNode.Clone()) and $Web.Configuration.location.'system.WebServer'.modules.RemoveChild($RMNode) are null valued. Here's the error I get: You cannot call a method on a null-valued expression. What could possibly be the issue there? Thank you! Commented Apr 20, 2018 at 13:02
  • I tried to manually move the RewriteModule over the DynamicCompressionModule to see if the solution worked and received these errors. Commented Apr 20, 2018 at 13:12
  • Turned out that I had to specify the index[0] to Powershell to indicate that I was working with an XML object instead of a System.Array. The System.Array contained the XML object. Thanks to everyone ! :) Commented Apr 20, 2018 at 15:38
  • $Web.Configuration.location.'system.WebServer'.modules[0].AppendChild($RMNode.Clone()) $Web.Configuration.location.'system.WebServer'.modules[0].RemoveChild($RMNode) Commented Apr 20, 2018 at 16:04
0

You'll need to access the object.

($Web.Configuration.location.'system.WebServer'.modules.add.name) will give you a list.

You could then use a Foreach-Object or foreach loop. If you want to compare two objects you could use .IndexOf('')

($Web.Configuration.location.'system.WebServer'.modules.add.name).IndexOf('DynamicCompressionModule') #should return 1 since it is second in the list (and arrays start at zero)

($Web.Configuration.location.'system.WebServer'.modules.add.name).IndexOf('RewriteModule') #should return 34

This will iterate through the list and allow you to do something to each name

foreach ($a in $Web.Configuration.location.'system.WebServer'.modules.add.name) { $a }

Removing 'name' will show the 'lockItem' info.

foreach ($a in $Web.Configuration.location.'system.WebServer'.modules.add) {
    if ($a.lockItem -eq $true) { 
            $a 
    } 
}

This will list out all Module names with lockItem set to true.

1
  • ($Web.Configuration.location.'system.WebServer'.modules.add.name) does not give me a list. I get a string containing "Add". What is happening? Commented Apr 19, 2018 at 20:04

Not the answer you're looking for? Browse other questions tagged or ask your own question.