{"id":225,"date":"2018-04-10T00:01:52","date_gmt":"2018-04-10T08:01:52","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/koryt\/?p=225"},"modified":"2019-10-10T04:08:11","modified_gmt":"2019-10-10T12:08:11","slug":"inserting-new-elements-into-xml-files","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/inserting-new-elements-into-xml-files\/","title":{"rendered":"Inserting new elements into XML files"},"content":{"rendered":"<h2>The Goal:<\/h2>\n<p><span style=\"font-size: small;\">Insert nodes into a specific place in <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.xml.xmldocumenttype(v=vs.110).aspx\" target=\"_blank\" rel=\"noopener noreferrer\">XML<\/a> config files <\/span><\/p>\n<h2>The Motivation:<\/h2>\n<p><span style=\"font-size: small;\">I had a coworker a while back working with App Fabric. He needed to insert a particular chunk of XML into a specific spot inside of the config file. This had to be done on a bunch of different machines, but the kicker was that the config files might look different on all of them. <\/span><\/p>\n<p><span style=\"font-size: small;\">We knew the node that needed to come before our new node, but that might be in a different spot in each file. <\/span><\/p>\n<p><span style=\"font-size: small;\">When the configSections node ended ( &lt;\/configSections&gt; ) we needed to insert:<\/span><\/p>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:7f42d5d1-48cb-4151-a7d8-3493326bf014\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:default decode:true\">&lt;appSettings&gt;\r\n&lt;add key=\"backgroundGC\" value=\"true\"\/&gt;\r\n&lt;\/appSettings&gt;<\/pre>\n<\/div>\n<p><span style=\"font-size: small;\">This is the perfect place for PowerShell automation! We can write a script that searches for that node, inserts our new node in place and once we know it works we can just remote it out to all the servers that need it.<\/span><\/p>\n<p>here is the file we are working with (MSDN wouldn&#8217;t let me upload it directly)<\/p>\n<pre class=\"lang:default decode:true \">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\r\n&lt;configuration&gt;\r\n&lt;configSections&gt;\r\n&lt;!-- Microsoft.ApplicationServer.Caching.Core assembly name is hard-coded --&gt;\r\n\r\n&lt;section name=\"dataCacheConfig\" type=\"Microsoft.ApplicationServer.Caching.DataCacheConfigSection, Microsoft.ApplicationServer.Caching.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" \/&gt;\r\n\r\n&lt;section name=\"fabric\" type=\"Microsoft.Fabric.Common.ConfigFile, Microsoft.WindowsFabric.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" allowLocation=\"true\" allowDefinition=\"Everywhere\" \/&gt;\r\n\r\n&lt;section name=\"dataCache\" type=\"Microsoft.ApplicationServer.Caching.DataCacheSection, Microsoft.ApplicationServer.Caching.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" \/&gt;\r\n\r\n&lt;section name=\"uri\" type=\"System.Configuration.UriSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" \/&gt;\r\n&lt;\/configSections&gt;\r\n&lt;dataCacheConfig cacheHostName=\"AppFabricCachingService\"&gt;\r\n&lt;log location=\"C:\\ProgramData\\Microsoft\\AppFabric\\Runtime\" logLevel=\"-1\" \/&gt;\r\n&lt;clusterConfig provider=\"SPDistributedCacheClusterProvider\" connectionString=\"Data Source=SQL;Initial Catalog=SharePoint_Config;Integrated Security=True;Enlist=False\" \/&gt;\r\n&lt;\/dataCacheConfig&gt;\r\n&lt;fabric&gt;\r\n\r\n&lt;section name=\"param\" path=\"\"&gt;\r\n&lt;key name=\"VersionInfoClass\" value=\"Microsoft.ApplicationServer.Caching.ServerVersionInfo, Microsoft.ApplicationServer.Caching.Server\" \/&gt;\r\n&lt;key name=\"DroppedReplicaKeepDuration\" value=\"0\" \/&gt;\r\n&lt;key name=\"ClusterStableNodeUpInterval\" value=\"10\" \/&gt;\r\n&lt;key name=\"RPFederationCloseTimeout\" value=\"15\" \/&gt;\r\n&lt;key name=\"ReplicationQueueCapacity\" value=\"128\" \/&gt;\r\n&lt;key name=\"CopyQueueCapacity\" value=\"2\" \/&gt;\r\n&lt;key name=\"ReplicationTempListCapacity\" value=\"1024\" \/&gt;\r\n&lt;key name=\"ReplicationTempListInitialSize\" value=\"128\" \/&gt;\r\n&lt;key name=\"ReplicationRetryInterval\" value=\"12\" \/&gt;\r\n&lt;key name=\"ThrowOnAssert\" value=\"true\" \/&gt;\r\n&lt;key name=\"KeepOperationOnSecondary\" value=\"false\" \/&gt;\r\n&lt;key name=\"ExternalRingStateUpdateTimeout\" value=\"480\" \/&gt;\r\n&lt;key name=\"ExternalStoreUpdateRetry\" value=\"8\" \/&gt;\r\n&lt;\/section&gt;\r\n\r\n&lt;\/fabric&gt;\r\n&lt;dataCache size=\"Small\"&gt;\r\n&lt;hosts&gt;\r\n&lt;host replicationPort=\"22236\" arbitrationPort=\"22235\" clusterPort=\"22234\" hostId=\"1739552749\" size=\"1228\" leadHost=\"true\" account=\"NT AUTHORITY\\NETWORK SERVICE\" name=\"localhost\" cacheHostName=\"AppFabricCachingService\" cachePort=\"22233\" \/&gt;\r\n&lt;\/hosts&gt;\r\n&lt;\/dataCache&gt;\r\n&lt;uri&gt;\r\n&lt;iriParsing enabled=\"true\" \/&gt;\r\n&lt;\/uri&gt;\r\n&lt;runtime&gt;\r\n&lt;gcServer enabled=\"true\" \/&gt;\r\n&lt;\/runtime&gt;\r\n&lt;startup&gt;\r\n&lt;supportedRuntime version=\"v4.0.30319\" \/&gt;\r\n&lt;supportedRuntime version=\"v4.0\" \/&gt;\r\n&lt;\/startup&gt;\r\n&lt;\/configuration&gt;<\/pre>\n<p>&nbsp;<\/p>\n<h2>The Adventure:<\/h2>\n<p><span style=\"font-size: small;\">There are a lot of different ways we could approach this problem, but I had seen type casting to [XML] before so I figured that was the best place to start.<\/span><\/p>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:c61cbd23-ebb5-4ac5-881c-9181660c98aa\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:ps decode:true \">$pathToConfig = \"$PSScriptRoot\\DistributedCacheService.exe.config\" #put your path here\r\n\r\n#force the config into an XML\r\n$xml = [xml](get-content $pathToConfig)\r\n\r\n$xml | GM<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:04591448-ec07-4ede-a588-3855693a801a\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:default decode:true \">TypeName: System.Xml.XmlDocument\r\n\r\nName MemberType Definition\r\n---- ---------- ----------\r\nToString CodeMethod static string XmlNode(psob...\r\nAppendChild Method System.Xml.XmlNode AppendC...\r\nClone Method System.Xml.XmlNode Clone()...\r\nCloneNode Method System.Xml.XmlNode CloneNo...\r\n...\r\nImportNode Method System.Xml.XmlNode ImportN...\r\nInsertAfter Method System.Xml.XmlNode InsertA...\r\nInsertBefore Method System.Xml.XmlNode InsertB...\r\nLoad Method void Load(string filename)...\r\nLoadXml Method void LoadXml(string xml)\r\n...\r\nItem ParameterizedProperty System.Xml.XmlElement Item...\r\nconfiguration Property System.Xml.XmlElement conf...\r\nxml Property string xml {get;set;}<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<p><span style=\"font-size: small;\">Here I noticed two really interesting things:<\/span><\/p>\n<ol>\n<li><span style=\"font-size: small;\">The InsertAfter() method sounds like it will do exactly what I need if I can first find the &lt;ConfigSections&gt; node. <\/span><\/li>\n<li><span style=\"font-size: small;\">The &#8220;configuration&#8221; property seemed a little odd. When viewing the actual XML file, it became clearer that it managed to make the &lt;configuration&gt; node into this property. <\/span><\/li>\n<\/ol>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:74cb2840-9246-4da5-8121-9dd82b0f503d\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:default decode:true \">&lt;configSections&gt;\r\n&lt;!-- Microsoft.ApplicationServer.Caching.Core assembly name is hard-coded --&gt;\r\n\r\n&lt;section name=\"dataCacheConfig\" type=\"Microsoft.ApplicationServer.Caching.DataCacheConfigSection, Microsoft.ApplicationServer.Caching.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" \/&gt;\r\n\r\n&lt;section name=\"fabric\" type=\"Microsoft.Fabric.Common.ConfigFile, Microsoft.WindowsFabric.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" allowLocation=\"true\" allowDefinition=\"Everywhere\" \/&gt;\r\n\r\n&lt;section name=\"dataCache\" type=\"Microsoft.ApplicationServer.Caching.DataCacheSection, Microsoft.ApplicationServer.Caching.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" \/&gt;\r\n\r\n&lt;section name=\"uri\" type=\"System.Configuration.UriSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" \/&gt;\r\n&lt;\/configSections&gt;<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<p><span style=\"font-size: small;\">So playing around a bit more I decided to try to dig down into that configuration element and try to locate the children. <\/span><\/p>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:79691f52-db7c-466a-93b1-2ad0d238f839\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<p>&nbsp;<\/p>\n<pre class=\"lang:ps decode:true \">$xml.configuration.configsections<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:e891975f-e05c-47a7-9705-1b3e521e1973\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:default decode:true \">#comment section\r\n-------- -------\r\nMicrosoft.ApplicationServer.Caching.Core assembly name is hard-coded {dataCacheConf..<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<p><span style=\"font-size: small;\">Looking at the node in the file I can see a comment with that text and I can see a bunch of sections, the first being the truncated one. So now I know I&#8217;ve got the right element.<\/span><\/p>\n<p><span style=\"font-size: small;\">InsertAfter() takes in the new child, and the reference child, so I built the new node and tried it:<\/span><\/p>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:f168eaa3-81a2-4064-9e76-3ffc2d2c7e27\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:ps decode:true \">$newNode = [xml]@\"\r\n&lt;appSettings&gt;\r\n&lt;add key=\"backgroundGC\" value=\"true\"\/&gt;\r\n&lt;\/appSettings&gt;\r\n\"@\r\n\r\n#add new node AFTER the configsections node\r\n\r\n$xml.configuration.InsertAfter($newNode,$foundNode)<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:9013aac4-48cb-4ad4-a19e-891f9a213ea9\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:default decode:true \">Exception calling \"InsertAfter\" with \"2\" argument(s): \"The specified node cannot be\r\n\r\ninserted as the valid child of this node, because the specified node is the wrong type.\"<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<p><span style=\"font-size: small;\">Doing a little searching on this lead me to find similar issues from C#, which are caused because the node you&#8217;re trying to insert is &#8220;from a different document&#8221;. It doesn&#8217;t already live in the file, and it needs to be &#8220;Imported&#8221; into the XML document before it can be inserted. I looked around in the documentation and found <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.xml.xmldocument.importnode(v=vs.110).aspx\" target=\"_blank\" rel=\"noopener noreferrer\">ImportNode()<\/a> <\/span><span style=\"font-size: small;\">which needs a node and a bool for deep copy. <\/span><\/p>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:48f82c17-5696-47c1-b861-d6f8ffe1cd17\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<p>&nbsp;<\/p>\n<pre class=\"lang:ps decode:true \">$xml.ImportNode($newNode,$true)<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:a854d901-08b1-41a2-9a62-b75fd475f5bd\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:default decode:true \">Exception calling \"ImportNode\" with \"2\" argument(s): \"Cannot import nodes of type document\"<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<p><span style=\"font-size: small;\">Closer, but apparently [XML] made my $newNode a document, luckily we can just grab the root element though. Since ImportNode() returns the node it creates we also need to grab a reference to it.<\/span><\/p>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:bdd34ebb-a9d2-44e1-8ec2-eb594ebc937c\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:ps decode:true \">$newNode = $xml.ImportNode($newNode.appSettings,$true)\r\n\r\n$xml.configuration.InsertAfter($newNode,$foundNode)<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<p><span style=\"font-size: small;\">This seems to work, but gives some annoying output, so we&#8217;ll take care of that and then save the file and take a look<\/span><\/p>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:63bc2acd-1a0d-43de-b1b3-338bc2537771\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:ps decode:true\">$xml.configuration.InsertAfter($newNode,$foundNode) |out-null\r\n\r\n$xml.Save(\"$pathToConfig.CHANGED.XML\")<\/pre>\n<\/div>\n<blockquote><p><span style=\"font-size: small;\">we did it!<\/span><\/p><\/blockquote>\n<h2>The Treasure:<\/h2>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:29ae7544-d8db-44fd-b33b-e83e31aa86da\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:ps decode:true \">$pathToConfig = \"$PSScriptRoot\\DistributedCacheService.exe.config\" #put your path here\r\n\r\n#force the config into an XML\r\n$xml = [xml](get-content $pathToConfig)\r\n\r\n#find the node to insert after\r\n$foundNode = $xml.configuration.configsections\r\n\r\n#build new node by hand and force it to be an XML object\r\n$newNode = [xml]@\"\r\n&lt;appSettings&gt;\r\n&lt;add key=\"backgroundGC\" value=\"true\"\/&gt;\r\n&lt;\/appSettings&gt;\r\n\"@\r\n\r\n#add new node AFTER the configsections node\r\n$newNode = $xml.ImportNode($newNode.appSettings,$true)\r\n$xml.configuration.InsertAfter($newNode,$foundNode) |out-null\r\n\r\n#save file\r\n$xml.Save(\"$pathToConfig.CHANGED.XML\")<\/pre>\n<p>&nbsp;<\/p>\n<\/div>\n<p><span style=\"font-size: small;\">Here is a slightly shorter version as well:<\/span><\/p>\n<div id=\"scid:C89E2BDB-ADD3-4f7a-9810-1B7EACF446C1:3eeedacf-b469-42aa-8118-50e6549331ec\" class=\"wlWriterEditableSmartContent\" style=\"margin: 0px; padding: 0px; float: none;\">\n<pre class=\"lang:ps decode:true\">$pathToConfig = \"$PSScriptRoot\\DistributedCacheService.exe.config\" #put your path here\r\n\r\n#force the config into an XML\r\n$xml = [xml](get-content $pathToConfig)\r\n\r\n#build new node by hand and force it to be an XML object\r\n[xml]$newNode = @\"\r\n&lt;appSettings&gt;\r\n&lt;add key=\"backgroundGC\" value=\"true\"\/&gt;\r\n&lt;\/appSettings&gt;\r\n\"@\r\n\r\n#add new node AFTER the configsections node\r\n$xml.configuration.InsertAfter($xml.ImportNode($newNode.appSettings, $true), $xml.configuration.configsections) | out-null\r\n\r\n#save file\r\n$xml.Save($pathToConfig)<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p><span style=\"font-size: small;\"><a href=\"https:\/\/github.com\/Sambardo\/PowerShell-XML-add-node\">Here is the project on GitHub.<\/a><\/span><\/p>\n<p><span style=\"font-size: small;\">That&#8217;s all for now, hopefully this was valuable for you to follow along with and see how I approach these kinds of problems. There are a lot of powerful tools built right in for us to take advantage of! If you enjoyed this content don&#8217;t forget to like, rate and share \ud83d\ude42<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Goal: Insert nodes into a specific place in XML config files The Motivation: I had a coworker a while back working with App Fabric. He needed to insert a particular chunk of XML into a specific spot inside of the config file. This had to be done on a bunch of different machines, but [&hellip;]<\/p>\n","protected":false},"author":7300,"featured_media":87096,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1738],"tags":[2221,2125],"class_list":["post-225","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-kory-thacher","tag-koryt"],"acf":[],"blog_post_summary":"<p>The Goal: Insert nodes into a specific place in XML config files The Motivation: I had a coworker a while back working with App Fabric. He needed to insert a particular chunk of XML into a specific spot inside of the config file. This had to be done on a bunch of different machines, but [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/225","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/7300"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=225"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/225\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=225"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=225"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=225"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}