Wednesday, January 4, 2012

Updating BizTalk Config with Powershell

So, I was tasked with performing a simple update to a BizTalk application. This update would read values from the BizTalk configuration and leverage said values to determine best course of action in processing messages. Why you ask. Because we wanted to create a configurable process that would eliminate the need to recompile our BizTalk solutions.

In any case, I was able to add a library to the project, from which the config values would be read. Now, the next step was in deploying the application and appending the config value to BTSNTSvc.exe.config and BTSNTSvc64.exe.config. Since I already leverage Powershell to perform my deployments, I ultimately decided to include scripts to perform the config updates. Below follows the code to add a setting within the AppSettings section, as well as a comment to differentiate the new config setting with all others:

The first step was in generating functions to perform the actual procedures for inserting appSettings nodes and including comments in the appSettings section.

function Set-ApplicationSetting ([string]$fileName, [string]$filepath, [string]$name, [string]$value)
{
    # Load the config file up in memory
    [xml]$a = get-content $fileName;

    # Find the app settings item to change
    if($a.configuration.appSettings.selectsinglenode("add[@key='" + $name + "']") -ne $null)
    {
 $a.configuration.appSettings.selectsinglenode("add[@key='" + $name + "']").value = $value
    }
    else
    {
 $newElem = $a.CreateElement("add")
 $newKeyAttr = $a.CreateAttribute("key")
 $newValAttr = $a.CreateAttribute("value")

 $newKeyAttr.innerText = $name
 $newValAttr.innerText = $value

 $newElem.Attributes.Append($newKeyAttr) > $null
 $newElem.Attributes.Append($newValAttr) > $null
 
 $a.configuration.appSettings.appendChild($newElem)
    }

    # Save Changes
    $a.Save($filepath + $filename)
}

function InsertAppSettingsComment ([string]$fileName, [string]$filepath, [string]$value)
{
    # Load the config file up in memory
    [xml]$a = get-content $fileName;

    $comment = $a.CreateComment($value)

    # Find the app settings item to change
    $a.configuration.appSettings.InsertBefore($comment, $a.configuration.appSettings.LastChild)

    # Save Changes
    $a.Save($filepath + $filename)
}


Next up was discovering the appropriate folder location of the BizTalk config file and assigning said location to a variable.
# Check for correct Program Files directory
if(Test-Path (${env:ProgramFiles(x86)} + "\Microsoft BizTalk Server 2009\BTSNTSvc.exe.config"))
{
 $BTSConfigPath = ${env:ProgramFiles(x86)} + "\Microsoft BizTalk Server 2009\"
}
else
{
 $BTSConfigPath = "D:\Program Files (x86)\Microsoft BizTalk Server 2009\"
}

# Set Program Files directory as current location
Set-Location $BTSConfigPath


Now, I want to include code to backup the current config file before applying any changes.
$currentDate = (get-date).tostring("mm_dd_yyyy-hh_mm_s") # month_day_year - hours_mins_seconds
$backup86 = ".\BTSNTSvc.exe_" + $currentDate + "_config.bak"
$backup64 = ".\BTSNTSvc64.exe_" + $currentDate + "_config.bak"

Write-Output "Backup Prior BTS Config files (86 & 64)"

Copy-Item ".\BTSNTSvc.exe.config" $backup86 -Force
Copy-Item ".\BTSNTSvc64.exe.config" $backup64 -Force


Next, I want to write the new appSettings node to the BizTalk config file.
Write-Output "Updating BizTalk Application Setting"

# Change the app setting for the path to the backups
Set-ApplicationSetting "BTSNTSvc.exe.config" $BTSConfigPath "My Node Key" "My Node Value"
Set-ApplicationSetting "BTSNTSvc64.exe.config" $BTSConfigPath "My Node Key" "My Node Value"


Finally, I want to add some comments to the appSettings section just above my new appSettings node.
# Insert Comments
InsertAppSettingsComment "BTSNTSvc.exe.config" $BTSConfigPath "My Config Setting"
InsertAppSettingsComment "BTSNTSvc64.exe.config" $BTSConfigPath "My Config Setting"


Great! My new appSettings node is now available in the BizTalk config file. But how do I remove those values if need-be? No worries, follow the below steps:

The first step, again, is to include functions that perform the actual process.

function Remove-ApplicationSetting ([string]$fileName, [string]$filepath, [string]$name, [string]$value)
{
    # Load the config file up in memory
    [xml]$a = get-content $fileName;

    # Find the app settings item to change
    if($a.configuration.appSettings.selectsinglenode("add[@key='" + $name + "']") -ne $null)
    {
 $killChild = $a.configuration.appSettings.selectsinglenode("add[@key='" + $name + "']")
 $a.configuration.appSettings.RemoveChild($killChild)
    
 # Save Changes
 $a.Save($filepath + $filename)
    }
}

function RemoveAppSettingsComment ([string]$fileName, [string]$filepath, [string]$value, [string]$siblingValue )
{
    # Load the config file up in memory
    [xml]$a = get-content $fileName;


    if($a.configuration.appSettings.selectsinglenode("add[@key='" + $siblingValue + "']") -ne $null)
    {
 $sibChild = $a.configuration.appSettings.selectsinglenode("add[@key='" + $siblingValue + "']")
 $killChild = $sibChild.PreviousSibling

 if($killChild.innerText -eq $value)
 {
  $a.configuration.appSettings.RemoveChild($killChild)
 }
    
 # Save Changes
 $a.Save($filepath + $filename)
    }
}


We'll leverage the same logic from earlier to determine the proper folder location of the BizTalk config file, so I won't revisit that here. Which leads to the next step, we'll remove the comments first
Write-Output "Removing Application Setting"
# Insert Comments
RemoveAppSettingsComment "BTSNTSvc.exe.config" $BTSConfigPath "My Config Setting" "My Node Key"
RemoveAppSettingsComment "BTSNTSvc64.exe.config" $BTSConfigPath "My Config Setting" "My Node Key"


Finally, we remove the appSettings node.
# Change the app setting for the path to the backups
Remove-ApplicationSetting "BTSNTSvc.exe.config" $BTSConfigPath "My Node Key"
Remove-ApplicationSetting "BTSNTSvc64.exe.config" $BTSConfigPath "My Node Key"


Cheers!

No comments:

Post a Comment