Skip to content

Instantly share code, notes, and snippets.

@mu88
Last active June 4, 2018 14:04
Show Gist options
  • Save mu88/77763aa8ec934ba23983e6ea72877441 to your computer and use it in GitHub Desktop.
Save mu88/77763aa8ec934ba23983e6ea72877441 to your computer and use it in GitHub Desktop.
IIS - Create folders, application pools and websites according to a very specific pattern

Important: The PowerShell script has to be executed with a x64 PowerShell console, otherwise any attempt to configure IIS will fail.

The following configuration steps will be executed:

  1. Set permissions for root directory json['baseDirectory']
  • Full Control permission for Active Directory group json['adGroupAdministrator']
  • Modify permission for Active Directory user json['adUserBuildserver']
  1. Create website directory (e. g. D:\Web\Sites\myapp-production\htdocs) if it doesn't exist
  • The name and path of the website directory will be composed as <<BASE>>\Sites\<<NAME>>-<<ENVIRONMENT>> with:
    • <<Base>> is json['baseDirectory']
    • <<NAME>> is lowercase json['objects']['app']
    • <<ENVIRONMENT>> is lowercase json['objects']['environment']
  1. Set permissions for website directory
  • Full Control permission for Active Directory user json['objects']['appPoolAccount']
  1. Create network share for website directory
  • Delete existing network share if it exists
  • The name of the network share will be composed as <<NAME>>-<<ENVIRONMENT>>-htdocs with:
    • <<NAME>> is lowercase json['objects']['app']
    • <<ENVIRONMENT>> is lowercase json['objects']['environment']
  • Full Control permission for Active Directory group ['adGroupAdministrator']
  • Full Control permission for Active Directory user ['adUserBuildserver'] if json['objects']['shareForBuildserver'] is set to true
  • Full Control permission for additional Active Directory members json['objects']['fullControlMembers']
  • The default entry allowing everyone to access the network share will be removed. Depending on the platform of the OS, the alias has to be set via json['aliasEveryone']:
    • English: Everyone
    • German: Jeder
  1. Create application pool if it doesn't exist
  • The name of the application pool will be composed as <<NAME>>-<<ENVIRONMENT>> with:
    • <<NAME>> is lowercase json['objects']['app']
    • <<ENVIRONMENT>> is lowercase json['objects']['environment']
  • The .NET CLR Runtime Version will be set to either v4.0 or json['objects']['appPoolNetRuntimeVersion']
  1. Set application pool identity to json['objects']['appPoolAccount'] using json['objects']['appPoolPassword'] as password
  2. Create website if it doesn't exist
  • The name of the website will be composed as <<NAME>>-<<ENVIRONMENT>> with:
    • <<NAME>> is lowercase json['objects']['app']
    • <<ENVIRONMENT>> is lowercase json['objects']['environment']
  1. Set bindings for the website
  • Remove all existing bindings (When creating a new website with PowerShell, the default binding get's assigned. Since only one website can use the default binding, all existing bindings will be removed and only the specified ones will be set.)
  • Set bindings according to json['objects']['bindings']
  1. Start the website
  2. Copy existing code into website directory
  • The specific purpose of this script was to ease the migration from one server to a new one. Due to this, there was some pre-configured code (e. g. web.config) that will be simply copied into the website directory.
  • Copy everything from <<SOURCES>>\<<NAME>>-<<ENVIRONMENT>>\htdocs to <<DESTINATION>>\Sites\<<NAME>>-<<ENVIRONMENT>>\htdocs with:
    • <<NAME>> is lowercase json['objects']['app']
    • <<ENVIRONMENT>> is lowercase json['objects']['environment']
    • <<SOURCES>> is json['sources']
    • <<DESTINATION>> is json['baseDirectory']

Listed below the different configuration parameters of the JSON file:

  • json['baseDirectory']: The base directory of all websites.
  • json['sources']: The source code of the pre-configured applications that will be copied into the website directory.
  • json['adUserBuildserver']: The Active Directory user of the Buildserver. The user will always obtain Modify permissions on the base directory and on demand Full Control permissions on the network share (depending on json['objects']['shareForBuildserver']).
  • json['adGroupAdministrator']: The Active Directory group of the Administrators. This group will obtain Full Control permissions on the base directory and network share.
  • json['aliasEveryone']: The alias for Everyone that will be used to remove the default permission on a network share. It has to be set according to the OS platform, e. g. Jeder in German of Everyone in English.
  • json['objects']: Array of JSON objects with every object representing an application to configure.
  • json['objects']['app']: The name of the application. It will be used for the directory structure, name of the application pool and name of the website.
  • json['objects']['environment']: The environment (e. g. development, integration, staging or production).
  • json['objects']['appPoolAccount']: The Active Directory user being used for the application pool's identity. The user will obtain Full Control permissions on the website directory.
  • json['objects']['appPoolPassword']: The password of the application pool account.
  • json['objects']['bindings']: An array of strings containing all the bindings that will be configured for the website.
  • json['objects']['appPoolNetRuntimeVersion']: This parameter is optional. The .NET CLR Runtime version that will be used for the application pool. Default value is v4.0.
  • json['objects']['shareForBuildserver']: This parameter is optional. When set to true, the Active Directory user json['adUserBuildserver'] will obtain Full Control permissions to the network share of the website. Default value is false.
  • json['objects']['fullControlMembers']: This parameter is optional. An array of strings containing additional Active Directory users or groups that will obtain Full Control permissions on the website's network share.
Clear-Host
Import-Module WebAdministration
$definitionFile = "C:\temp\IIS_Definition.json"
# import JSON definition file
$definitions = Get-Content -Raw -Path $definitionFile | ConvertFrom-Json
$baseDirectory = $definitions.baseDirectory
$adUser_Buildserver = $definitions.adUserBuildserver
$adGroup_Administrators = $definitions.adGroupAdministrator
$websiteBaseDirectory = Join-Path $baseDirectory -ChildPath "Sites"
# set permissions in base directory
$Acl = Get-Acl $baseDirectory
$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($adGroup_Administrators, "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")
$Ar2 = New-Object System.Security.AccessControl.FileSystemAccessRule($adUser_Buildserver, "Modify", "ContainerInherit, ObjectInherit", "None", "Allow")
$Acl.SetAccessRule($Ar)
$Acl.SetAccessRule($Ar2)
Set-Acl $baseDirectory $Acl
# iterate over all definitions
foreach ($definition in $definitions.objects) {
# prepare some basic values
$currentAppNameLowerCase = $definition.app.ToLower()
$currentEnvironmentLowerCase = $definition.environment.ToLower()
$currentWebsiteBaseDirectory = Join-Path $websiteBaseDirectory -ChildPath "$currentAppNameLowerCase-$currentEnvironmentLowerCase" | Join-Path -ChildPath "htdocs"
$appPoolAccount = $definition.appPoolAccount
# create directory for IIS website (if not exists)
if ((Test-Path $currentWebsiteBaseDirectory) -eq $False) {
New-Item -Path $currentWebsiteBaseDirectory -ItemType "directory" | out-null
"Directory '$currentWebsiteBaseDirectory' created"
}
# set full control permissions to IIS website directory
$Acl = Get-Acl $currentWebsiteBaseDirectory
$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($appPoolAccount, "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")
$Acl.SetAccessRule($Ar)
"Granted full control to directory '$currentWebsiteBaseDirectory' for role '$appPoolAccount'"
# set additional full control members
if ($definition.fullControlMembers) {
foreach ($fullControlMember in $definition.fullControlMembers) {
$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($fullControlMember, "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")
$Acl.SetAccessRule($Ar)
"Granted full control to directory '$currentWebsiteBaseDirectory' for role '$fullControlMember'"
}
}
Set-Acl $currentWebsiteBaseDirectory $Acl
# share IIS website directory for Administrators and Buildserver (if requested)
$currentShareName = "$currentAppNameLowerCase-$currentEnvironmentLowerCase-htdocs"
# delete old share if it exists
if (Get-SMBShare -Name $currentShareName -ea 0) {
Remove-SmbShare -Name $currentShareName -Force
"Share '$currentShareName' removed"
}
New-SMBShare -Name $currentShareName -Path $currentWebsiteBaseDirectory | out-null
Revoke-SmbShareAccess -Name $currentShareName -AccountName $definitions.aliasEveryone -Force | out-null
"Share '$currentShareName' created"
Grant-SmbShareAccess -Name $currentShareName -AccountName $adGroup_Administrators -AccessRight Full -Force | out-null
"Granted full control to share '$currentShareName' for role '$adGroup_Administrators'"
if (($definition.shareForBuildserver) -AND ($definition.shareForBuildserver -eq $True)) {
Grant-SmbShareAccess -Name $currentShareName -AccountName $adUser_Buildserver -AccessRight Full -Force | out-null
"Granted full control to share '$currentShareName' for role '$adUser_Buildserver'"
}
# create an App Pool and set .NET runtime version
$appPoolName = "$currentAppNameLowerCase-$currentEnvironmentLowerCase"
$iisAppPoolPath = "IIS:\AppPools\$appPoolName"
$iisAppPoolAlreadyExists = (Get-ChildItem –Path IIS:\AppPools).Name -Contains $appPoolName
if ($iisAppPoolAlreadyExists -eq $False) {
New-Item –Path $iisAppPoolPath -Force | out-null
"Application pool '$appPoolName' created"
if ($definition.appPoolNetRuntimeVersion) {
$appPoolNetRuntimeVersion = $definition.appPoolNetRuntimeVersion
}
else {
$appPoolNetRuntimeVersion = "v4.0"
}
Set-ItemProperty -Path $iisAppPoolPath -Name managedRuntimeVersion -Value $appPoolNetRuntimeVersion
"Set .NET runtime version to '$appPoolNetRuntimeVersion' for Application pool '$appPoolName'"
}
# set App Pool identity
$username = $appPoolAccount
$password = $definition.appPoolPassword
Set-ItemProperty $iisAppPoolPath -name processModel -value @{userName="$username";password="$password";identitytype=3}
"Set identity to '$username' for Application pool '$appPoolName'"
# create website
$websiteName = "$currentAppNameLowerCase-$currentEnvironmentLowerCase"
$websiteAlreadyExists = (Get-Website).Name -Contains $websiteName
if ($websiteAlreadyExists -eq $False) {
New-Website –Name $websiteName –PhysicalPath $currentWebsiteBaseDirectory -ApplicationPool $appPoolName | out-null
"Website '$websiteName' created (AppPool '$appPoolName', Path '$currentWebsiteBaseDirectory')"
}
# remove default binding
Get-WebBinding -Name $websiteName | Remove-WebBinding
# add bindings
foreach ($binding in $definition.bindings) {
$existingBinding = (Get-WebBinding -Name $websiteName -HostHeader $binding)
if (-Not $existingBinding) {
New-WebBinding -Name $websiteName -IPAddress "*" -Port 80 -HostHeader $binding
"Binding '$binding' set for website '$websiteName'"
}
}
Get-Website –Name $websiteName | Start-Website
"Website '$websiteName' started"
# copy code into websites
$sources = Join-Path $definitions.sources -ChildPath "$currentAppNameLowerCase-$currentEnvironmentLowerCase-htdocs\"
get-childitem $sources | % {
copy-item $_.FullName -destination "$currentWebsiteBaseDirectory\$_" -recurse -Force
}
"Sources copied"
}
{
"baseDirectory": "C:\\\temp\\IIS\\Web",
"sources": "C:\\\temp\\IIS\\Original",
"adUserBuildserver": "MyDomain\\buildserver",
"adGroupAdministrator": "MyDomain\\admins",
"aliasEveryone": "Everyone",
"objects": [
{
"app": "app1",
"environment": "production",
"appPoolAccount": "MyDomain\\iis-app1",
"appPoolPassword": "secret1",
"bindings": [
"app1.com",
"app1.de"
]
},
{
"app": "app2",
"environment": "integration",
"appPoolAccount": "MyDomain\\iis-app2",
"appPoolPassword": "secret2",
"bindings": [
"app2.ch"
],
"appPoolNetRuntimeVersion": "v2.0"
},
{
"app": "app3",
"environment": "production",
"appPoolAccount": "MyDomain\\iis-app3",
"appPoolPassword": "secret3",
"bindings": [
"app3.de"
],
"fullControlMembers": [
"MyDomain\\user1",
"MyDomain\\user2"
],
"shareForBuildserver": true
},
{
"app": "app4",
"environment": "production",
"appPoolAccount": "MyDomain\\iis-app4",
"appPoolPassword": "secret4",
"bindings": [
"app4.com"
],
"shareForBuildserver": true
},
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment