Home > OS >  Updating XML with Powershell
Updating XML with Powershell

Time:11-17

Looking to use PowerShell to Update Web Server IP addresses.
Multiple IPs on each host.
Imported Config file has incorrect IP Address for the new configuration, so I want to change the IP by creating list of Site Names and Associated bindings in an external "src" file.

Then have PowerShell parse the original config file and update relevant sites IP address with the correct details.

Sample of Config File used in script to generate the new config.

<appcmd>
  <SITE SITE.NAME="SiteA" SITE.ID="3" bindings="https/4.4.4.4:443:" state="Started">
    <site name="SiteA" id="3" serverAutoStart="true">
      <bindings>
        <binding protocol="https" bindingInformation="4.4.4.4:443:" sslFlags="0" />
      </bindings>
    </site>
  </SITE>
  <SITE SITE.NAME="SiteB" SITE.ID="4" bindings="https/32.32.32.32:443:" state="Started">
    <site name="SiteB" id="4" serverAutoStart="true">
      <bindings>
      <binding protocol="http" bindingInformation="32.32.32.32:80:" />
        <binding protocol="https" bindingInformation="32.32.32.32:443:" sslFlags="0" />
      </bindings>
    </site>
  </SITE>
</appcmd>

IP addresses from an external file, which is used to populate hash table.

Name,IPaddr
Site1,10.192.32.1
Site2,10.33.192.1
SiteA,10.2.2.2
Siteb,10.232.323.4

Script file called prompts for path to IP address file and Path to Config file.

Function checkexists {
    Param(
        # Parameter help description
        [Parameter(Mandatory = $false)]
        [string]$PathToFile
    )
    if (!(Test-Path $PathToFile)) {  
        Write-Host "File $PathToFile does not exist.  Exiting...." -ForegroundColor cyan
        start-sleep 3
        exit 1
    }
}

$csvfile = Read-Host -Prompt "Enter full Path IP Addressing File for WebServers.."
CheckExists -PathToFile $csvfile

$XMLfile = Read-Host -Prompt "Enter full Path to Configuration XML File for WebServers.."

CheckExists -PathToFile $XMLfile

$today = (Get-Date -Format mmhhddMMyy).ToString()
$expFile = (Split-Path -Parent $csvfile)   "\WebSites"   $today   ".xml"

If (!(Test-Path -Path $XMLFile)) {
    Write-Host "Exiting as the file does not exist." -ForegroundColor Red
    Exit 1
}

$sites = @{}
import-CSV $csvFile | ForEach-Object { $sites[$_.Name] = $_.IPaddr }  

[XML]$xml = Get-Content -Path $XMLfile

foreach ($element in $xml.AppCmd.Site) {    
    $siteIP = $sites.($element.Site.Name)
    Write-Host "Planned IP for $($element.Site.Name) is : $siteIp "
 
    $element.bindings =  $element.bindings -replace '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', $siteIP
    $element.site.Bindings.binding.bindingInformation = $element.site.Bindings.binding.bindingInformation -replace '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', $siteIP
}
  
$xml.Save($expFile)

On running it populates the new config file, if single node values exist in the BindingInformation node/element.

I am at a loss as to how to update both elements if web server is supporting more than one endpoint.

Planned IP for SiteB is : 10.232.323.4
InvalidOperation: C:\somescript.ps1:58:8
Line |
  58 |         $element.site.Bindings.binding.bindingInformation = $element.s …
     |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The property 'bindingInformation' cannot be found on this object. Verify that the property exists and can be set.

I am trying to learn my way through the many options of attempting this, and seemed to be having some success until multiple IP popped up. I appreciated this all may be incorrect, but could do with a pointer (at layman level)

Any assistance is appreciated.

CodePudding user response:

The root cause is that member-access enumeration only supports getting property values, not setting them (assigning to them) - see this answer for an in-depth explanation.

Therefore, because $element.site.Bindings.binding can situationally return multiple elements, you cannot directly assign to (update) their .bindingInformating properties and must instead process the elements individually, such as with the intrinsic .ForEach() method:

foreach ($element in $xml.AppCmd.Site) {
  # Note: 'SITE.NAME' is a *single* property name must therefore be quoted.
  $siteIP = $sites.($element.'SITE.NAME')
  Write-Host "Planned IP for $($element.Site.Name) is : $siteIp "
  $element.bindings = $element.bindings -replace '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', $siteIP
  # !! Because $element.site.Bindings.binding can return an ARRAY of
  # !! elements and you want to modify a property of EACH, you must
  # !! process them individually, such as with .ForEach()
  $element.site.Bindings.binding.ForEach({
    $_.bindingInformation = $_.bindingInformation -replace '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', $siteIP
  })
}
  • Related