Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Register-Websites.psm1
<#
This script will set up sites in IIS, pointing them at their respective directories on your machine.
By convention, the site name, host name and application pool name are all the same. For example, local.careers.stackoverflow.com.
It will also update the hosts file to point host names to 127.0.0.1 (localhost)
Prerequisites:
- IIS is already installed, including ASP.Net 4.5
Troubleshooting:
- Out-File : The process cannot access the file 'C:\Windows\System32\drivers\etc\hosts' because it is being used by another process
- Try to re-run the script several times. Try to end the explorer.exe task and re-run the script. If nothing works, reboot and try again.
Example Websites config:
@(
@{
Directory = "StackOverflow\StackOverflow";
Site = "local.mse.com";
Aliases = "discuss.local.area51.lse.com";
Databases = "StackExchange.Meta", "Area51.Meta";
},
@{
Directory = "StackOverflow\StackExchange.Website";
Site = "local.lse.com";
Databases = "StackExchange";
},
@{
Directory = "StackOverflow\StackAuth";
Site = "local.stackauth.com";
Databases = "Sites";
},
@{
Directory = "StackOverflow\StackOverflow.Api.V2";
Site = "api.local.lse.com";
},
@{
Directory = "Area51\StackOverflow.Proposals";
Site = "local.area51.lse.com";
Databases = "Area51";
}
)
#>
function Register-Websites {
Param(
[Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)] $Websites
)
Write-Host "Registering IIS Websites..." -ForegroundColor "Magenta"
# Grab the web admin and HOSTS module
Import-Module WebAdministration
# DLL blocking on Windows, yay fun!
Get-ChildItem "$PSScriptRoot\Includes\PsHosts" | Unblock-File
Import-Module "$PSScriptRoot\Includes\PsHosts"
# Unlock the serverRuntime section of system.webServer.
Set-WebConfiguration "//system.WebServer/serverRuntime" "machine/webroot/apphost" -Metadata overrideMode -Value Allow
# Set some common variables
Set-WebConfigurationProperty "/system.webServer/httpCompression/scheme[@name='gzip']" -PSPath IIS:\ -name dynamicCompressionLevel -value 4
# Remove default website and application pool - we don't want it for any setups
Get-Website -Name "Default Web Site" | Remove-Website
# Remove DefaultAppPool app pool
if (Test-Path "IIS:\AppPools\DefaultAppPool")
{
Remove-WebAppPool -Name "DefaultAppPool"
}
# First, check for existence of all folders
$missing = $false
foreach ($website in $Websites) {
$webPath = Join-Path $Env:CODEROOT $website.Directory
if (!(Test-Path $webPath))
{
$missing = $true
Write-Error "Could not find folder $webPath"
Write-Error ""
}
}
# Let's not fail partially; go get everything
if ($missing -eq $true) {
Write-Warning "Go back and run the repos step."
Write-Warning ""
return
}
$iis_iusrs = New-Object System.Security.Principal.NTAccount BUILTIN\IIS_IUSRS
$acl_iis_iusrs = Get-AccessRule $iis_iusrs
# Remove & create sites in IIS
foreach ($website in $Websites) {
$baseHost = $website.Site
Write-Host "Creating IIS Website & AppPool for $baseHost..." -ForegroundColor "Magenta"
# Host name, site name and app pool name are identical by convention
$rootPath = Join-Path $env:CODEROOT $website.Directory
Write-Host " Website directory: $rootPath"
$acl = Get-Acl $rootPath
if (!(Find-AccessRule $acl $iis_iusrs)) {
Write-Host " Granting access to $rootPath for IIS_IUSRS"
$acl.AddAccessRule($acl_iis_iusrs)
Set-Acl $rootPath $acl
}
# Remove Existing site and pool
Get-Website -Name $baseHost | Remove-Website
if (Test-Path "IIS:\AppPools\$baseHost")
{
Remove-WebAppPool -Name $baseHost
}
Write-Host " Creating AppPool for $baseHost"
$appPool = New-WebAppPool -Name $baseHost
$appPool | Set-ItemProperty -Name ProcessModel.loadUserProfile -Value false
$appPool | Set-ItemProperty -Name managedRuntimeVersion -Value v4.0
# Create new site
Write-Host " Creating Website for $baseHost"
$iisWebsite = New-WebSite -Name $baseHost -ApplicationPool $baseHost -PhysicalPath $rootPath -HostHeader $baseHost
# Add hosts file entry
Write-Host " Creating HOSTS entry for $baseHost to 127.0.0.1"
Get-HostEntry | Where { $_.Name -eq $baseHost } | Remove-HostEntry
Add-HostEntry -Name $baseHost -Address 127.0.0.1 -Comment "auto-generated by dev-local-setup" | Out-Null
# If requested, SSL self-signed certs
if ($website.Certificate)
{
New-IISSelfSignedCertificate $baseHost $baseHost
}
foreach ($hostAlias in $website.Aliases) {
Write-Host " Alias: Creating HOSTS entry for $hostAlias to 127.0.0.1"
Get-HostEntry | Where { $_.Name -eq $hostAlias } | Remove-HostEntry
Add-HostEntry -Name $hostAlias -Address 127.0.0.1 -Comment "auto-generated by dev-local-setup" | Out-Null
Write-Host " Alias: Adding website binding for $hostAlias"
New-WebBinding -Name $baseHost -IPAddress "*" -Port 80 -HostHeader $hostAlias
if ($website.Certificate)
{
# Need to add a cert for each...
New-IISSelfSignedCertificate $baseHost $hostAlias
}
}
# Print
Write-Host "IIS Website & AppPool for $baseHost created." -ForegroundColor "Green"
}
Write-Host "IIS Websites registered." -ForegroundColor "Green"
}
function Find-AccessRule ($acl, $account) {
foreach ($item in $acl.Access) {
if ($item.IdentityReference -eq $account) {
return $item
}
}
}
function Get-AccessRule ($account) {
New-Object System.Security.AccessControl.FileSystemAccessRule ($account,
[System.Security.AccessControl.FileSystemRights]::Read,
([System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit),
[System.Security.AccessControl.PropagationFlags]::None,
[System.Security.AccessControl.AccessControlType]::Allow)
}
function New-IISSelfSignedCertificate ($baseHost, $host) {
Write-Host " SSL Certificate requested. Creating self-signed cert for $host."
# Recreate self-signed SSL cert
Push-Location cert:\LocalMachine\My
Get-ChildItem -DnsName $host | Remove-Item
$cert = New-SelfSignedCertificate -DnsName $host
Pop-Location
# Re-add it to the Root store (needed for the green padlock)
Push-Location cert:\LocalMachine\Root
Get-ChildItem -DnsName $host | Remove-Item
$rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList Root, LocalMachine
$rootStore.Open("MaxAllowed")
$rootStore.Add($cert)
$rootStore.Close()
Pop-Location
Write-Host " SSL Certificate added to local root store."
Write-Host " Removing any existing WebBindings for $host..." -NoNewline
Get-WebBinding -Name $baseHost -Protocol 'https' -Port 443 -HostHeader $host | Remove-WebBinding
Write-Host "removed."
# Recreate SSL bindings
Write-Host " Adding a new WebBinding for $host..." -NoNewline
New-WebBinding -Name $baseHost -Protocol 'https' -Port 443 -HostHeader $host -SslFlags 1 # SNI enabled w/o IIS Central Cert Store
Write-Host "added."
# Required for SNI
$existsCheck = netsh http show sslcert hostnameport=$host:443
if ($existsCheck -Like "*$host:443*") {
Write-Host " Existing netsh sslcert found for $host, removing..." -NoNewline
netsh http delete sslcert hostnameport=$($host):443
}
Write-Host " Adding netsh sslcert binding..." -NoNewline
netsh http add sslcert hostnameport=$($host):443 certhash=$($cert.Thumbprint) appid='{00000000-0000-0000-0000-000000000000}' certstorename=MY
Write-Host " SNI thumbprint added."
Write-Host " SSL Certificate setup complete."
Write-Host ""
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment