Reputation: 35
We've been researching desired state configuration, and I was asked to set up a prototype using powershell DSC to configure an IIS app pool. I know all the steps to creating a configuration, I just am unsure of what I might have in my configuration. I plan to use the xWebAdministration resource because it has things like xWebAppPool and xWedbAdministration. Are there any suggestions on what else I might use to set this up?
Upvotes: 2
Views: 4934
Reputation: 110
*Realize this is quite old, but just stumbled across it and wanted to offer recommendations for the scenario your DSCs get big/unwieldly.
I'd start off by separating your your node definition file from your configuration file then map a reference. I keep my files side-by-side, so I'd reference like below. I won't go into encryption, good MS guidance on that.
$File = 'WebApp\NodeDefinitions.psd1'
$Parent = Split-Path -Parent $PSScriptRoot
$Path = Join-Path $Parent $File
WebApp -RunAs $RunAs -ConfigurationData "$Path" -OutputPath $localpath -verbose
Now to make things a little more dynamic you'd create arrays or hash table collections in your node definition file like below for folders, sites, etc. *I did have to alter xcertificatedsc to have it pass an array of accounts, which isn't difficult if you need an assist.
@{
NodeName = "Vm-Web-1"
Role = "DevWeb","Web","WS","SMP"
IUSRS = "Domain\User1$","Domain\User2$"
Folders = @(
("Dir1","F:\inetpub\wwwroot\SomeApp","Present"),
("Dir2","F:\inetpub\wwwroot\SomeApp2","Present"),
("Dir3","F:\inetpub\wwwroot\SomeApp2","Absent") #If something's moved or a mistake
)
Sites = @(
@("MyApp1","Domain\User1$","Present"),
@("MyApp2","Domain\User2$","Present")
)
CertPerms= @{
"[email protected]" = @("Domain\User1$","Domain\User2$")
}
},
Then I introduce non-node data in my configuration so that sites are more portable that we'll later join in the configuration ps1 file. I'll also show how I'd iterate folder creation.
@{
Name = "MyApp1"
PoolConfigName = "ApMyApp1"
PoolName = "ApMyApp1"
PoolRtVer = "v4.0"
SiteConfigName = "WaMyApp1"
SitePath = "ApMyApp1"
SiteName = "ApMyApp1"
SiteDepends = "[File]Dir1"
},
@{
Name = "MyApp2"
PoolConfigName = "ApMyApp2"
PoolName = "ApMyApp2"
PoolRtVer = "v4.0"
SiteConfigName = "WaMyApp2"
SitePath = "ApMyApp2"
SiteName = "ApMyApp2"
SiteDepends = "[File]Dir2"
},
To iterate over folders, you just reference elements of your array in the nodedefintion file
Foreach($Folder in $Node.Folders){
File $Folder[0]
{
Ensure = $Folder[2]
Type = "Directory"
DestinationPath = $Folder[1]
}
}
The site joining is a little more complex and I'm not overly happy with my current situation referencing the element instead of an easier to read name, but it works. After joining node/nonnode data, the next part of the script is for lesser used parameters. We don't introduce a lot of pools in always running mode for example (unless proper page initialization is validated in the web.config of the correlating app). If I'm introducing a site and the correlating service account isn't active on the domain, I'll make sure the correlating pool is stopped so it doesn't flood the iis worker process. Otherwise you should be able to map out what's set in the node section (array references) vs the non-node site data.
Foreach($SiteName in $Node.Sites){
$Site = $ConfigurationData.Sites.Where{$_.Name -eq $SiteName[0]}
if ([string]::IsNullOrWhiteSpace($Site.PoolIdleTO))
{
$PoolIdleTO = 20
}
else
{
$PoolIdleTO = $Site.PoolIdleTO
}
if ([string]::IsNullOrWhiteSpace($Site.PoolStartMode))
{
$PoolStartMode = "OnDemand"
}
else
{
$PoolStartMode = $Site.PoolStartMode
}
if ([string]::IsNullOrWhiteSpace($SiteName[3]))
{
$State = "Started"
}
else
{
$State = $SiteName[3]
}
xWebAppPool $Site.PoolConfigName
{
Name = $Site.PoolName
Ensure = $SiteName[2]
State = $State
autoStart = $true
enable32BitAppOnWin64 = $false
enableConfigurationOverride = $true
managedPipelineMode = "Integrated"
managedRuntimeVersion = $Site.PoolRtVer
startMode = $PoolStartMode
queueLength = 1000
cpuAction = "KillW3wp"
cpuLimit = 95000
cpuResetInterval = (New-TimeSpan -Minutes 1).ToString()
cpuSmpAffinitized = $false
cpuSmpProcessorAffinityMask = 4294967295
cpuSmpProcessorAffinityMask2 = 4294967295
identityType = 'SpecificUser'
Credential = New-Object System.Management.Automation.PSCredential($SiteName[1], (ConvertTo-SecureString $Node.GmsaPwd.ToString() -AsPlainText -Force))
idleTimeout = (New-TimeSpan -Minutes $PoolIdleTO).ToString()
idleTimeoutAction = 'Suspend'
loadUserProfile = $false
logEventOnProcessModel = 'IdleTimeout'
logonType = 'LogonBatch'
manualGroupMembership = $false
maxProcesses = 1
pingingEnabled = $true
pingInterval = (New-TimeSpan -Seconds 30).ToString()
pingResponseTime = (New-TimeSpan -Seconds 90).ToString()
setProfileEnvironment = $false
shutdownTimeLimit = (New-TimeSpan -Seconds 90).ToString()
startupTimeLimit = (New-TimeSpan -Seconds 90).ToString()
orphanActionExe = ''
orphanActionParams = ''
orphanWorkerProcess = $false
loadBalancerCapabilities = 'HttpLevel'
rapidFailProtection = $true
rapidFailProtectionInterval = (New-TimeSpan -Minutes 1).ToString()
rapidFailProtectionMaxCrashes = 5
autoShutdownExe = 'C:\Windows\System32\iisreset.exe'
autoShutdownParams = ''
disallowOverlappingRotation = $false
disallowRotationOnConfigChange = $false
logEventOnRecycle = 'Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory'
restartMemoryLimit = 3221225472
restartPrivateMemoryLimit = 8000000
restartRequestsLimit = 20000000
restartTimeLimit = (New-TimeSpan -Minutes 0).ToString()
restartSchedule = "00:00:00"
DependsOn = '[WindowsFeature]IIS'
}
<#!!!Imperative method (runs immediately) to ensure service accounts get IIS metabase access!!#
#Need to move this into the function with a flag, obviously there'll be looping challenges...
Invoke-Command -Session (New-PSSession -ComputerName $Node.NodeName -Credential $RunAs -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck)) -ScriptBlock {
param ([string] $User)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -ga $User
} -ArgumentList $SiteName[1]
#>
cIisAccess "IisMetabaseAccess$($Site.SiteConfigName + $SiteName[1])"
{
Account = $SiteName[1]
Ensure="Present"
}
xWebApplication $Site.SiteConfigName
{
Website = "Default Web Site"
Name = $Site.SiteName
WebAppPool = $Site.PoolName
PhysicalPath = $Node.DfSitePath + $Site.SitePath
Ensure = $SiteName[2]
PreloadEnabled = $true
DependsOn = "[xWebAppPool]$($Site.PoolConfigName)",$($Site.SiteDepends)
}
}
Where one thing to note is I use GMSAs so there's no password and a kerberos token is used. You'd still need a phony password reference due to PSCredential requirements, so you can just add something to your allnodes data like below for reference:
GmsaPwd = "none"
There's also lots of guidance on using roles out there, but a simple ref is below.
Node $AllNodes.Where{$_.Role -contains "Web"}.NodeName
{
#Embed site/folder iterators here if preferred
}
Upvotes: 1
Reputation: 171
If you have many sites that you are trying to bring under configuration control, you could use my DSC generator to produce the DSC for IIS Features, Web sites, app pools, and virtual directories.
I then use Octopus Deploy to deliver the DSC to the server and apply the DSC.
https://github.com/kevinsea/dsc-generator
Upvotes: 5
Reputation: 578
Some time ago I have been tasked with exactly the same challenge and I have created a prototype of DSC resource that would acomplish this. After initial tests this runs now in production.
Source code is on gihub (https://github.com/RafPe/cWebAdmin ) and all feedback would be more than welcome :)
Maybe this would give you an idea how to challenge this on your end
Upvotes: 1
Reputation: 47842
You would probably use the WindowsFeature
resource to install the Roles and Features needed (Web-Server
, etc.), you'd probably use the File
resource to create the directory and maybe copy the site's files, you might use the Registry
resource to enable the Web Management Service so that you can manage IIS remotely, then use the Service
resource to start that service.
Upvotes: 2