I'm trying to automate the creation of a server farm in PowerShell. Through manual creation I've got the following XML:
<webFarms>
<webFarm name="alwaysup" enabled="true">
<server address="alwaysup-blue" enabled="true">
<applicationRequestRouting httpPort="8001" />
</server>
<server address="alwaysup-green" enabled="true">
<applicationRequestRouting httpPort="8002" />
</server>
<applicationRequestRouting>
<healthCheck url="http://alwaysup/up.html" interval="00:00:05" responseMatch="up" />
</applicationRequestRouting>
</webFarm>
<applicationRequestRouting>
<hostAffinityProviderList>
<add name="Microsoft.Web.Arr.HostNameRoundRobin" />
</hostAffinityProviderList>
</applicationRequestRouting>
</webFarms>
Trying to do this via PS proves troublesome however: as far as I can tell there is no dedicated API to do this through (WebFarmSnapin is meant for an older version).
I have shifted my attention to IIS Administration Cmdlets but only got it half working.
The code that I have:
#####
# Overwriting the server farm
#####
Write-Host "Overwriting the server farm $($webFarmName)"
$webFarm = @{};
$webFarm["name"] = 'siteFarm'
Set-WebConfiguration "/webFarms" -Value $webFarm
#####
# Adding the servers
#####
Write-Host "Adding the servers"
$blueServer = @{}
$blueServer["address"] = 'site-blue'
$blueServer["applicationRequestRouting"] = @{}
$greenServer = @{}
$greenServer["address"] = 'site-green'
$greenServer["applicationRequestRouting"] = @{}
$servers = @($blueServer, $greenServer)
Add-WebConfiguration -Filter "/webFarms/webFarm[@name='siteFarm']" -Value $servers
#####
# Adding routing
#####
Write-Host "Adding the routing configurations"
$blueServerRouting = @{}
$blueServerRouting["httpPort"] = "8001"
Add-WebConfiguration -Filter "/webFarms/webFarm[@name='siteFarm']/server[@address='site-blue']" -Value $blueServerRouting
This generates
<webFarms>
<webFarm name="siteFarm">
<server address="site-blue" />
<server address="site-green" />
</webFarm>
<applicationRequestRouting>
<hostAffinityProviderList>
<add name="Microsoft.Web.Arr.HostNameRoundRobin" />
</hostAffinityProviderList>
</applicationRequestRouting>
</webFarms>
As you can see it's missing the port related to the routing. And I haven't even started with trying to add the healthcheck at this point.
What am I doing wrong? Is there some Cmdlet that I haven't found which makes this easier?
Related but without much of a useful answer (the PowerShell tab with generated code stays empty).
A web farm is a group of two or more web servers (or nodes) that host multiple instances of an app. When requests from users arrive to a web farm, a load balancer distributes the requests to the web farm's nodes.
The Microsoft Web Farm Framework (WFF) 2.0 for IIS 7 and above simplifies the provisioning, scaling, and management of multiple servers for administrators and hosting providers. Administrators can seamlessly provision multiple servers, deploy content to them, and use them to enable elastic scale.
When a web application is hosted on multiple web servers and access based on the load on servers, it is called Web Farm. In web farm, a single application is hosted on multiple IIS server and these IIS servers are connected with the VIP (Virtual IP) with load balancer.
It looks like you figured out how to do this by modifying the XML configuration file itself. Despite the feeling that this doesn't quite seem like the "right way", changing the config file directly is a perfectly valid solution. In essence, you created a template, and configuration templates provide a fast and readable approach to producing maintainable and repeatable configuration.
We can improve upon this approach by extracting the template text from the script into a separate text file. Then, scripts can read (or source) the template and swap out any placeholder values as needed. This is similar to how we separate concerns in a web application by decoupling our HTML templates from the code.
To more directly answer the question, let's take a look at how to do this using PowerShell and the IIS Administration API (you're correct—Web Farm Framework is no longer supported for recent versions if IIS and Windows). The original code in the question is a good start. We just need to distinguish between manipulating configuration collections (using *-WebConfiguration
cmdlets) and configuration items (using *-WebConfigurationProperty
cmdlets). Here's a script that will set the configuration values based on the example in the question:
$farmName = 'siteFarm'
Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter 'webFarms' `
-Name '.' `
-Value @{ name = $farmName; enabled = $true }
Add-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']" `
-Value @(
@{ address = 'site-blue'; enabled = $true },
@{ address = 'site-green'; enabled = $true }
)
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/server[@address='site-blue']" `
-Name 'applicationRequestRouting' `
-Value @{ httpPort = 8001 }
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/server[@address='site-green']" `
-Name 'applicationRequestRouting' `
-Value @{ httpPort = 8002 }
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/applicationRequestRouting" `
-Name 'healthCheck' `
-Value @{
url = 'http://mySite/up.html'
interval = '00:00:05'
responseMatch = 'up'
}
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/applicationRequestRouting" `
-Name 'protocol' `
-Value @{ reverseRewriteHostInResponseHeaders = $true }
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' `
-Filter "webFarms/webFarm[@name='$farmName']/applicationRequestRouting/protocol" `
-Name 'cache' `
-Value @{ enabled = $false; queryStringHandling = 'NoCaching' }
This may be a bit more verbose than necessary, but I wanted to clearly illustrate the intention in each step. This implementation uses XPath queries for the -Filter
arguments to select the appropriate XML nodes. We could, perhaps, refactor the script to to reduce repetitive tasks, such as by defining an Add-FarmServer
function that takes a server name and port and then adds the appropriate directives. We may also need Remove-WebConfigurationLock
if we encounter locked configuration issues.
Whether we choose to use a template or a programmatic approach depends on the project and team preference. The API becomes much more attractive when we have many similar items to configure, such as if we have hundreds of servers to add to a web farm. On the other hand, templates are simple to understand don't require that other team members learn a new (and maybe somewhat confusing) API.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With