├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.md ├── examples ├── 01-CreateSite-Simple │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── 02-CreateSite-Advanced │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── 03-CreateVDir │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── 04-CreateAppPool │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── 05-AssigningAppPools │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── 06-CheckExists │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── 07-ChangePaths │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── 08-ChangeAuth │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── 09-ChangeLogging │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── 10-SpecificUser │ ├── IIS.ps1 │ ├── README.md │ └── Web.ps1 ├── IIS-Change-Handler-Mapping-EditFeaturePermissions.ps1 ├── _Setup.IIS.ps1 ├── _Setup.Web.ps1 ├── _Teardown.IIS.ps1 └── _Teardown.Web.ps1 ├── run.cmd └── src ├── ExampleRunner.sln ├── ExampleRunner.sln.DotSettings └── ExampleRunner ├── App.config ├── ExampleRunner.csproj ├── Generation ├── Example.cs ├── ExampleParser.cs └── ExampleWriter.cs ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── TeamCitySink.cs ├── Testing ├── ScriptRunner.cs └── SilentProcessRunner.cs └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | out.md 254 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Octopus Deploy and contributors. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShell-IIS-Examples 2 | Examples showing how to do everything with IIS and PowerShell 3 | -------------------------------------------------------------------------------- /examples/01-CreateSite-Simple/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | 11 | # ----------------------------------------------------------------------------- 12 | # Example 13 | # ----------------------------------------------------------------------------- 14 | 15 | Import-Module IISAdministration 16 | 17 | New-IISSite -Name "Website1" -BindingInformation "*:80:" -PhysicalPath "C:\Sites\Website1" 18 | 19 | # Examples of -BindingInformation: 20 | # "*:80:" - Listens on port 80, any IP address, any hostname 21 | # "10.0.0.1:80:" - Listens on port 80, specific IP address, any host 22 | # "*:80:myhost.com" - Listens on port 80, specific hostname 23 | 24 | # ----------------------------------------------------------------------------- 25 | # Assert 26 | # ----------------------------------------------------------------------------- 27 | 28 | if ((Get-IISSite -Name "Website1") -eq $null) { Write-Error "Website1 not found" } 29 | 30 | # ----------------------------------------------------------------------------- 31 | # Clean up 32 | # ----------------------------------------------------------------------------- 33 | 34 | . ..\_Teardown.IIS.ps1 35 | 36 | Remove-IISSite -Name "Website1" -Confirm:$false 37 | -------------------------------------------------------------------------------- /examples/01-CreateSite-Simple/README.md: -------------------------------------------------------------------------------- 1 | ### Creating sites (simple) 2 | 3 | -------------------------------------------------------------------------------- /examples/01-CreateSite-Simple/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | clear 7 | 8 | . ..\_Setup.Web.ps1 9 | 10 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 11 | Remove-Item -Path "IIS:\Sites\Website1" -Recurse -Force -ErrorAction SilentlyContinue 12 | 13 | # ----------------------------------------------------------------------------- 14 | # Example 15 | # ----------------------------------------------------------------------------- 16 | 17 | Import-Module WebAdministration 18 | 19 | New-Website -Name "Website1" -Port 80 -IPAddress "*" -HostHeader "" -PhysicalPath "C:\Sites\Website1" 20 | 21 | # ----------------------------------------------------------------------------- 22 | # Assert 23 | # ----------------------------------------------------------------------------- 24 | 25 | if ((Test-Path -Path "IIS:\Sites\Website1") -eq $false) { Write-Error "Couldn't find Website1" } 26 | 27 | # ----------------------------------------------------------------------------- 28 | # Clean up 29 | # ----------------------------------------------------------------------------- 30 | 31 | . ..\_Teardown.Web.ps1 32 | 33 | Remove-Item -Path "IIS:\Sites\Website1" -Recurse -Force 34 | -------------------------------------------------------------------------------- /examples/02-CreateSite-Advanced/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | 11 | # ----------------------------------------------------------------------------- 12 | # Example 13 | # ----------------------------------------------------------------------------- 14 | 15 | Import-Module IISAdministration 16 | 17 | $manager = Get-IISServerManager 18 | $site = $manager.Sites.Add("Website1", "http", "*:8022:", "C:\Sites\Website1") 19 | $site.Id = 4 20 | $site.Bindings.Add("*:8023:", "http") 21 | $site.Bindings.Add("*:8024:", "http") 22 | $manager.CommitChanges() 23 | 24 | # ----------------------------------------------------------------------------- 25 | # Assert 26 | # ----------------------------------------------------------------------------- 27 | 28 | if ((Get-IISSite -Name "Website1") -eq $null) { Write-Error "Website1 not found" } 29 | 30 | # ----------------------------------------------------------------------------- 31 | # Clean up 32 | # ----------------------------------------------------------------------------- 33 | 34 | . ..\_Teardown.IIS.ps1 35 | 36 | Remove-IISSite -Name "Website1" -Confirm:$false 37 | -------------------------------------------------------------------------------- /examples/02-CreateSite-Advanced/README.md: -------------------------------------------------------------------------------- 1 | ### Creating sites (advanced) 2 | 3 | Most likely, you'll want to specify a few extra settings when creating a real site. To do that, you can get the site after creating it, and add the extra settings. Since we are making multiple changes, we can use delayed commits to write them all to applicationHost.config at once. 4 | 5 | Here we'll add an additional binding, and set the site ID. 6 | -------------------------------------------------------------------------------- /examples/02-CreateSite-Advanced/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.Web.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | 11 | # ----------------------------------------------------------------------------- 12 | # Example 13 | # ----------------------------------------------------------------------------- 14 | 15 | Import-Module WebAdministration 16 | 17 | New-Item -Path "IIS:\Sites" -Name "Website1" -Type Site -Bindings @{protocol="http";bindingInformation="*:8021:"} 18 | Set-ItemProperty -Path "IIS:\Sites\Website1" -name "physicalPath" -value "C:\Sites\Website1" 19 | Set-ItemProperty -Path "IIS:\Sites\Website1" -Name "id" -Value 4 20 | New-ItemProperty -Path "IIS:\Sites\Website1" -Name "bindings" -Value (@{protocol="http";bindingInformation="*:8022:"}, @{protocol="http";bindingInformation="*:8023:"}) 21 | 22 | Start-Website -Name "Website1" 23 | 24 | # ----------------------------------------------------------------------------- 25 | # Assert 26 | # ----------------------------------------------------------------------------- 27 | 28 | if ((Get-WebSite -Name "Website1") -eq $null) { Write-Error "Website1 not found" } 29 | 30 | # ----------------------------------------------------------------------------- 31 | # Clean up 32 | # ----------------------------------------------------------------------------- 33 | 34 | . ..\_Teardown.Web.ps1 35 | 36 | Remove-Item -Path "IIS:\Sites\Website1" -Recurse -Force -------------------------------------------------------------------------------- /examples/03-CreateVDir/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\MyApp" -ErrorAction SilentlyContinue 11 | 12 | New-IISSite -Name "Website1" -BindingInformation "*:8022:" -PhysicalPath "C:\Sites\Website1" 13 | 14 | # ----------------------------------------------------------------------------- 15 | # Example 16 | # ----------------------------------------------------------------------------- 17 | Import-Module IISAdministration 18 | 19 | $manager = Get-IISServerManager 20 | $app = $manager.Sites["Website1"].Applications.Add("/MyApp", "C:\Sites\MyApp") 21 | $manager.CommitChanges() 22 | 23 | # ----------------------------------------------------------------------------- 24 | # Assert 25 | # ----------------------------------------------------------------------------- 26 | 27 | # ----------------------------------------------------------------------------- 28 | # Clean up 29 | # ----------------------------------------------------------------------------- 30 | 31 | . ..\_Teardown.IIS.ps1 32 | 33 | Remove-IISSite -Name "Website1" -Confirm:$false 34 | -------------------------------------------------------------------------------- /examples/03-CreateVDir/README.md: -------------------------------------------------------------------------------- 1 | ### Creating applications in virtual directories 2 | 3 | Most of the time, when people think of deploying a .NET app to a "virtual directory", they mean creating an *application* underneath a web site. The example below creates an application that would be viewable at `http://site/MyApp`. We also assign an application pool. 4 | -------------------------------------------------------------------------------- /examples/03-CreateVDir/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.Web.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\MyApp" -ErrorAction SilentlyContinue 11 | 12 | New-Website -Name "Website1" -Port 80 -IPAddress "*" -HostHeader "" -PhysicalPath "C:\Sites\Website1" 13 | 14 | # ----------------------------------------------------------------------------- 15 | # Example 16 | # ----------------------------------------------------------------------------- 17 | 18 | Import-Module WebAdministration 19 | 20 | New-Item -Type Application -Path "IIS:\Sites\Website1\MyApp" -physicalPath "C:\Sites\MyApp" 21 | 22 | # ----------------------------------------------------------------------------- 23 | # Assert 24 | # ----------------------------------------------------------------------------- 25 | 26 | if ((Test-Path IIS:\Sites\Website1\MyApp) -eq $false) { Write-Error "App not found" } 27 | 28 | # ----------------------------------------------------------------------------- 29 | # Clean up 30 | # ----------------------------------------------------------------------------- 31 | 32 | . ..\_Teardown.Web.ps1 33 | 34 | Remove-Item -Path "IIS:\Sites\Website1" -Recurse -Force 35 | -------------------------------------------------------------------------------- /examples/04-CreateAppPool/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | # ----------------------------------------------------------------------------- 10 | # Example 11 | # ----------------------------------------------------------------------------- 12 | Import-Module IISAdministration 13 | 14 | $manager = Get-IISServerManager 15 | $pool = $manager.ApplicationPools.Add("My Pool") 16 | 17 | # Older applications may require "Classic" mode, but most modern ASP.NET 18 | # apps use the integrated pipeline 19 | $pool.ManagedPipelineMode = "Integrated" 20 | 21 | # What version of the .NET runtime to use. Valid options are "v2.0" and 22 | # "v4.0". IIS Manager often presents them as ".NET 4.5", but these still 23 | # use the .NET 4.0 runtime so should use "v4.0". For a "No Managed Code" 24 | # equivalent, pass an empty string. 25 | $pool.ManagedRuntimeVersion = "v4.0" 26 | 27 | # If your ASP.NET app must run as a 32-bit process even on 64-bit machines 28 | # set this to $true. This is usually only important if your app depends 29 | # on some unmanaged (non-.NET) DLL's. 30 | $pool.Enable32BitAppOnWin64 = $false 31 | 32 | # Starts the application pool automatically when a request is made. If you 33 | # set this to false, you have to manually start the application pool or 34 | # you will get 503 errors. 35 | $pool.AutoStart = $true 36 | 37 | # "AlwaysRunning" = application pool loads when Windows starts, stays running 38 | # even when the application/site is idle. 39 | # "OnDemand" = IIS starts it when needed. If there are no requests, it may 40 | # never be started. 41 | $pool.StartMode = "OnDemand" 42 | 43 | # What account does the application pool run as? 44 | # "ApplicationPoolIdentity" = best 45 | # "LocalSysten" = bad idea! 46 | # "NetworkService" = not so bad 47 | # "SpecificUser" = useful if the user needs special rights 48 | $pool.ProcessModel.IdentityType = "ApplicationPoolIdentity" 49 | 50 | $manager.CommitChanges() 51 | 52 | # ----------------------------------------------------------------------------- 53 | # Assert 54 | # ----------------------------------------------------------------------------- 55 | 56 | if ((Get-IISAppPool -Name "My Pool") -eq $null) { Write-Error "My Pool not found" } 57 | 58 | # ----------------------------------------------------------------------------- 59 | # Clean up 60 | # ----------------------------------------------------------------------------- 61 | 62 | . ..\_Teardown.IIS.ps1 63 | 64 | $manager = Get-IISServerManager 65 | $manager.ApplicationPools["My Pool"].Delete() 66 | $manager.CommitChanges() -------------------------------------------------------------------------------- /examples/04-CreateAppPool/README.md: -------------------------------------------------------------------------------- 1 | ### Creating application pools 2 | 3 | You need to assign each application (website or application in a virtual directory) to an *application pool*. The application pool defines the executable process in which requests to the application are handled. 4 | 5 | IIS comes with a handful of application pools already defined for common options, but I always recommend creating your own application pool for each website or application that you deploy. This provides process-level isolation between applications and lets you set different permissions around what each application can do. The examples below show many of the common application pool settings. For the IIS Administration module, there are no built-in CmdLets to create application pools, so you have to do it with the `ServerManager` object directly. 6 | -------------------------------------------------------------------------------- /examples/04-CreateAppPool/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.Web.ps1 8 | 9 | Remove-Item -Path "IIS:\AppPools\My Pool" -Recurse -Force -ErrorAction SilentlyContinue 10 | 11 | # ----------------------------------------------------------------------------- 12 | # Example 13 | # ----------------------------------------------------------------------------- 14 | 15 | Import-Module WebAdministration 16 | 17 | New-Item -Path "IIS:\AppPools" -Name "My Pool" -Type AppPool 18 | 19 | # What version of the .NET runtime to use. Valid options are "v2.0" and 20 | # "v4.0". IIS Manager often presents them as ".NET 4.5", but these still 21 | # use the .NET 4.0 runtime so should use "v4.0". For a "No Managed Code" 22 | # equivalent, pass an empty string. 23 | Set-ItemProperty -Path "IIS:\AppPools\My Pool" -name "managedRuntimeVersion" -value "v4.0" 24 | 25 | # If your ASP.NET app must run as a 32-bit process even on 64-bit machines 26 | # set this to $true. This is usually only important if your app depends 27 | # on some unmanaged (non-.NET) DLL's. 28 | Set-ItemProperty -Path "IIS:\AppPools\My Pool" -name "enable32BitAppOnWin64" -value $false 29 | 30 | # Starts the application pool automatically when a request is made. If you 31 | # set this to false, you have to manually start the application pool or 32 | # you will get 503 errors. 33 | Set-ItemProperty -Path "IIS:\AppPools\My Pool" -name "autoStart" -value $true 34 | 35 | # What account does the application pool run as? 36 | # "ApplicationPoolIdentity" = best 37 | # "LocalSysten" = bad idea! 38 | # "NetworkService" = not so bad 39 | # "SpecificUser" = useful if the user needs special rights. See other examples 40 | # below for how to do this. 41 | Set-ItemProperty -Path "IIS:\AppPools\My Pool" -name "processModel" -value @{identitytype="ApplicationPoolIdentity"} 42 | 43 | # Older applications may require "Classic" mode, but most modern ASP.NET 44 | # apps use the integrated pipeline. 45 | # 46 | # On newer versions of PowerShell, setting the managedPipelineMode is easy - 47 | # just use a string: 48 | # 49 | # Set-ItemProperty -Path "IIS:\AppPools\My Pool 3" ` 50 | # -name "managedPipelineMode" ` 51 | # -value "Integrated" 52 | # 53 | # However, the combination of PowerShell and the IIS module in Windows 54 | # Server 2008 and 2008 R2 requires you to specify the value as an integer. 55 | # 56 | # 0 = Integrated 57 | # 1 = Classic 58 | # 59 | # If you hate hard-coding magic numbers you can do this (or use the string 60 | # if 2008 support isn't an issue for you): 61 | # 62 | # Add-Type -Path "${env:SystemRoot}\System32\inetsrv\Microsoft.Web.Administration.dll" 63 | # $pipelineMode = [Microsoft.Web.Administration.ManagedPipelineMode]::Integrated 64 | # Set-ItemProperty -Path "..." -name "managedPipelineMode" -value ([int]$pipelineMode) 65 | # 66 | # If this DLL doesn't exist, you'll need to install the IIS Management 67 | # Console role service. 68 | Set-ItemProperty -Path "IIS:\AppPools\My Pool" -name "managedPipelineMode" -value 0 69 | 70 | # This setting was added in IIS 8. It's different to autoStart (which means 71 | # "start the app pool when a request is made") in that it lets you keep 72 | # an app pool running at all times even when there are no requests. 73 | # Since it was added in IIS 8 you may need to check the OS version before 74 | # trying to set it. 75 | # 76 | # "AlwaysRunning" = application pool loads when Windows starts, stays running 77 | # even when the application/site is idle. 78 | # "OnDemand" = IIS starts it when needed. If there are no requests, it may 79 | # never be started. 80 | if ([Environment]::OSVersion.Version -ge (new-object 'Version' 6,2)) { 81 | Set-ItemProperty -Path "IIS:\AppPools\My Pool" -name "startMode" -value "OnDemand" 82 | } 83 | 84 | # ----------------------------------------------------------------------------- 85 | # Assert 86 | # ----------------------------------------------------------------------------- 87 | 88 | if ((Get-WebAppPoolState -Name "My Pool") -eq $null) { Write-Error "Website1 not found" } 89 | if ((Get-ItemProperty -Path "IIS:\AppPools\My Pool" -name "managedPipelineMode") -ne "Integrated") { Write-Error "Wrong pipeline mode" } 90 | 91 | # ----------------------------------------------------------------------------- 92 | # Clean up 93 | # ----------------------------------------------------------------------------- 94 | 95 | . ..\_Teardown.Web.ps1 96 | 97 | Remove-Item -Path "IIS:\AppPools\My Pool" -Recurse -Force 98 | -------------------------------------------------------------------------------- /examples/05-AssigningAppPools/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | 11 | New-IISSite -Name "Website1" -BindingInformation "*:8022:" -PhysicalPath "C:\Sites\Website1" 12 | 13 | $manager = Get-IISServerManager 14 | $pool = $manager.ApplicationPools.Add("My Pool") 15 | $app = $manager.Sites["Website1"].Applications.Add("/MyApp", "C:\Sites\MyApp") 16 | $manager.CommitChanges() 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Example 20 | # ----------------------------------------------------------------------------- 21 | Import-Module IISAdministration 22 | 23 | $manager = Get-IISServerManager 24 | 25 | # Assign to a website 26 | $website = $manager.Sites["Website1"] 27 | $website.Applications["/"].ApplicationPoolName = "My Pool" 28 | 29 | # Assign to an application in a virtual directory 30 | $website = $manager.Sites["Website1"] 31 | $website.Applications["/MyApp"].ApplicationPoolName = "My Pool" 32 | 33 | $manager.CommitChanges() 34 | 35 | # ----------------------------------------------------------------------------- 36 | # Assert 37 | # ----------------------------------------------------------------------------- 38 | 39 | if ((Get-IISAppPool -Name "My Pool") -eq $null) { Write-Error "My Pool not found" } 40 | 41 | # ----------------------------------------------------------------------------- 42 | # Clean up 43 | # ----------------------------------------------------------------------------- 44 | 45 | . ..\_Teardown.IIS.ps1 46 | 47 | Remove-IISSite -Name "Website1" -Confirm:$false 48 | 49 | $manager = Get-IISServerManager 50 | $manager.ApplicationPools["My Pool"].Delete() 51 | $manager.CommitChanges() 52 | -------------------------------------------------------------------------------- /examples/05-AssigningAppPools/README.md: -------------------------------------------------------------------------------- 1 | ### Assigning application pools 2 | 3 | Once you've defined your application pool, you must assign applications to it. The examples below show you how to assign websites or applications in a virtual directory to your new application pool. -------------------------------------------------------------------------------- /examples/05-AssigningAppPools/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.Web.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\MyApp" -ErrorAction SilentlyContinue 11 | 12 | New-Website -Name "Website1" -Port 80 -IPAddress "*" -HostHeader "" -PhysicalPath "C:\Sites\Website1" 13 | New-Item -Type Application -Path "IIS:\Sites\Website1\MyApp" -physicalPath "C:\Sites\MyApp" 14 | New-Item -Path "IIS:\AppPools" -Name "My Pool" -Type AppPool 15 | 16 | # ----------------------------------------------------------------------------- 17 | # Example 18 | # ----------------------------------------------------------------------------- 19 | 20 | Import-Module WebAdministration 21 | 22 | # Assign the application pool to a website 23 | Set-ItemProperty -Path "IIS:\Sites\Website1" -name "applicationPool" -value "My Pool" 24 | 25 | # Assign the application pool to an application in a virtual directory 26 | Set-ItemProperty -Path "IIS:\Sites\Website1\MyApp" -name "applicationPool" -value "My Pool" 27 | 28 | # ----------------------------------------------------------------------------- 29 | # Assert 30 | # ----------------------------------------------------------------------------- 31 | 32 | if ((Test-Path IIS:\Sites\Website1\MyApp) -eq $false) { Write-Error "App not found" } 33 | if ((Get-ItemProperty -Path "IIS:\Sites\Website1\MyApp" -name "applicationPool") -ne "My Pool") { Write-Error "Wrong app pool" } 34 | 35 | # ----------------------------------------------------------------------------- 36 | # Clean up 37 | # ----------------------------------------------------------------------------- 38 | 39 | . ..\_Teardown.Web.ps1 40 | 41 | Remove-Item -Path "IIS:\Sites\Website1" -Recurse -Force 42 | Remove-Item -Path "IIS:\AppPools\My Pool" -Recurse -Force 43 | -------------------------------------------------------------------------------- /examples/06-CheckExists/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | 11 | $manager = Get-IISServerManager 12 | $manager.ApplicationPools.Add("My Pool") 13 | $manager.Sites.Add("Website1", "http", "*:8022:", "C:\Sites\Website1") 14 | $manager.Sites["Website1"].Applications.Add("/MyApp", "C:\Sites\MyApp") 15 | $manager.CommitChanges() 16 | 17 | # ----------------------------------------------------------------------------- 18 | # Example 19 | # ----------------------------------------------------------------------------- 20 | Import-Module IISAdministration 21 | 22 | $manager = Get-IISServerManager 23 | 24 | # The pattern here is to get the things you want, then check if they are null 25 | 26 | if ($manager.ApplicationPools["My Pool"] -eq $null) { 27 | # Application pool does not exist, create it... 28 | # ... 29 | } 30 | 31 | if ($manager.Sites["Website1"] -eq $null) { 32 | # Site does not exist, create it... 33 | # ... 34 | } 35 | 36 | if ($manager.Sites["Website1"].Applications["/MyApp"] -eq $null) { 37 | # App/virtual directory does not exist, create it... 38 | # ... 39 | } 40 | 41 | $manager.CommitChanges() 42 | 43 | # ----------------------------------------------------------------------------- 44 | # Assert 45 | # ----------------------------------------------------------------------------- 46 | if ($manager.ApplicationPools["My Pool"] -eq $null -or $manager.ApplicationPools["sdij18uc"] -ne $null) { Write-Error "Our logic is wrong" } 47 | if ($manager.Sites["Website1"] -eq $null -or $manager.Sites["isjdjdsoidj"] -ne $null) { Write-Error "Our logic is wrong" } 48 | if ($manager.Sites["Website1"].Applications["/MyApp"] -eq $null -or $manager.Sites["Website1"].Applications["/sdidsuidshdsh"] -ne $null) { Write-Error "Our logic is wrong" } 49 | 50 | # ----------------------------------------------------------------------------- 51 | # Clean up 52 | # ----------------------------------------------------------------------------- 53 | 54 | . ..\_Teardown.IIS.ps1 55 | 56 | Remove-IISSite -Name "Website1" -Confirm:$false 57 | 58 | $manager = Get-IISServerManager 59 | $manager.ApplicationPools["My Pool"].Delete() 60 | $manager.CommitChanges() 61 | -------------------------------------------------------------------------------- /examples/06-CheckExists/README.md: -------------------------------------------------------------------------------- 1 | ### Check whether sites, virtual directories or application pools already exist 2 | 3 | You'll re-deploy your application to IIS many times over the course of a project, so you can't assume the script is being run for the first time. The examples below show a pattern for checking whether a site, application, or application pool already exists before making a change. 4 | -------------------------------------------------------------------------------- /examples/06-CheckExists/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.Web.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\MyApp" -ErrorAction SilentlyContinue 11 | 12 | New-Item -Path "IIS:\AppPools" -Name "My Pool" -Type AppPool 13 | New-Website -Name "Website1" -Port 80 -IPAddress "*" -HostHeader "" -PhysicalPath "C:\Sites\Website1" 14 | New-Item -Type Application -Path "IIS:\Sites\Website1\MyApp" -physicalPath "C:\Sites\MyApp" 15 | 16 | # ----------------------------------------------------------------------------- 17 | # Example 18 | # ----------------------------------------------------------------------------- 19 | 20 | Import-Module WebAdministration 21 | 22 | # The pattern here is to use Test-Path with the IIS:\ drive provider 23 | 24 | if ((Test-Path "IIS:\AppPools\My Pool") -eq $False) { 25 | # Application pool does not exist, create it... 26 | # ... 27 | } 28 | 29 | if ((Test-Path "IIS:\Sites\Website1") -eq $False) { 30 | # Site does not exist, create it... 31 | # ... 32 | } 33 | 34 | if ((Test-Path "IIS:\Sites\Website1\MyApp") -eq $False) { 35 | # App/virtual directory does not exist, create it... 36 | # ... 37 | } 38 | 39 | # ----------------------------------------------------------------------------- 40 | # Assert 41 | # ----------------------------------------------------------------------------- 42 | 43 | if ((Test-Path "IIS:\AppPools\My Pool") -eq $false -or (Test-Path "IIS:\AppPools\sijsijd") -ne $false) { Write-Error "Our logic is wrong" } 44 | if ((Test-Path "IIS:\Sites\Website1") -eq $false -or (Test-Path "IIS:\Sites\sdijdsijdd") -ne $false) { Write-Error "Our logic is wrong" } 45 | if ((Test-Path "IIS:\Sites\Website1\MyApp") -eq $false -or (Test-Path "IIS:\Sites\Website1\sdddsds") -ne $false) { Write-Error "Our logic is wrong" } 46 | 47 | # ----------------------------------------------------------------------------- 48 | # Clean up 49 | # ----------------------------------------------------------------------------- 50 | 51 | . ..\_Teardown.Web.ps1 52 | 53 | Remove-Item -Path "IIS:\Sites\Website1" -Recurse -Force 54 | Remove-Item -Path "IIS:\AppPools\My Pool" -Recurse -Force 55 | -------------------------------------------------------------------------------- /examples/07-ChangePaths/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\Website1\1.0" -ErrorAction SilentlyContinue 11 | mkdir "C:\Sites\Website1\1.1" -ErrorAction SilentlyContinue 12 | 13 | $manager = Get-IISServerManager 14 | $manager.ApplicationPools.Add("My Pool") 15 | $manager.Sites.Add("Website1", "http", "*:8022:", "C:\Sites\Website1") 16 | $manager.Sites["Website1"].Applications.Add("/MyApp", "C:\Sites\MyApp") 17 | $manager.CommitChanges() 18 | 19 | # ----------------------------------------------------------------------------- 20 | # Example 21 | # ----------------------------------------------------------------------------- 22 | Import-Module IISAdministration 23 | 24 | $manager = Get-IISServerManager 25 | 26 | # Remember, in the IIS Administration view of the world, sites contain 27 | # applications, and applications contain virtual directories, and it is 28 | # virtual directories that point at a physical path on disk. 29 | 30 | # Change for a top-level website 31 | $manager.Sites["Website1"].Applications["/"].VirtualDirectories["/"].PhysicalPath = "C:\Sites\Website1\1.1" 32 | 33 | # Change for an app within a website 34 | $manager.Sites["Website1"].Applications["/MyApp"].VirtualDirectories["/"].PhysicalPath = "C:\Sites\Website1\1.1" 35 | 36 | $manager.CommitChanges() 37 | 38 | # ----------------------------------------------------------------------------- 39 | # Assert 40 | # ----------------------------------------------------------------------------- 41 | if ($manager.Sites["Website1"].Applications["/"].VirtualDirectories["/"].PhysicalPath -ne "C:\Sites\Website1\1.1") { Write-Error "Our logic is wrong" } 42 | 43 | # ----------------------------------------------------------------------------- 44 | # Clean up 45 | # ----------------------------------------------------------------------------- 46 | 47 | . ..\_Teardown.IIS.ps1 48 | 49 | Remove-IISSite -Name "Website1" -Confirm:$false 50 | 51 | $manager = Get-IISServerManager 52 | $manager.ApplicationPools["My Pool"].Delete() 53 | $manager.CommitChanges() 54 | -------------------------------------------------------------------------------- /examples/07-ChangePaths/README.md: -------------------------------------------------------------------------------- 1 | ### Change physical path of a site or application 2 | 3 | When deploying a new version of an application, my preference (and the way Octopus Deploy works) is to deploy to a fresh, new folder on disk, then update IIS to point to it. So you begin with: 4 | 5 | C:\Sites\Website1\1.0 <--- IIS points here 6 | 7 | You deploy the new version: 8 | 9 | C:\Sites\Website1\1.0 <--- IIS points here 10 | C:\Sites\Website1\1.1 11 | 12 | You can then make any necessary changes to configuration files, etc. and then update IIS to point to it: 13 | 14 | C:\Sites\Website1\1.0 15 | C:\Sites\Website1\1.1 <--- Now IIS points here 16 | 17 | Should you ever need to roll back in a hurry, you can leave the old folder on disk and point back to it: 18 | 19 | C:\Sites\Website1\1.0 <--- IIS points here (we rolled back manually) 20 | C:\Sites\Website1\1.1 21 | -------------------------------------------------------------------------------- /examples/07-ChangePaths/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.Web.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\Website1\1.0" -ErrorAction SilentlyContinue 11 | mkdir "C:\Sites\Website1\1.1" -ErrorAction SilentlyContinue 12 | mkdir "C:\Sites\MyApp" -ErrorAction SilentlyContinue 13 | 14 | New-Item -Path "IIS:\AppPools" -Name "My Pool" -Type AppPool 15 | New-Website -Name "Website1" -Port 80 -IPAddress "*" -HostHeader "" -PhysicalPath "C:\Sites\Website1" 16 | New-Item -Type Application -Path "IIS:\Sites\Website1\MyApp" -physicalPath "C:\Sites\MyApp" 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Example 20 | # ----------------------------------------------------------------------------- 21 | 22 | Import-Module WebAdministration 23 | 24 | # The pattern here is to use Test-Path with the IIS:\ drive provider 25 | 26 | Set-ItemProperty -Path "IIS:\Sites\Website1" -name "physicalPath" -value "C:\Sites\Website1\1.1" 27 | Set-ItemProperty -Path "IIS:\Sites\Website1\MyApp" -name "physicalPath" -value "C:\Sites\Website1\1.1" 28 | 29 | # ----------------------------------------------------------------------------- 30 | # Assert 31 | # ----------------------------------------------------------------------------- 32 | 33 | if ((Get-ItemProperty -Path "IIS:\Sites\Website1" -name "physicalPath") -ne "C:\Sites\Website1\1.1") { Write-Error "Our logic is wrong" } 34 | 35 | # ----------------------------------------------------------------------------- 36 | # Clean up 37 | # ----------------------------------------------------------------------------- 38 | 39 | . ..\_Teardown.Web.ps1 40 | 41 | Remove-Item -Path "IIS:\Sites\Website1" -Recurse -Force 42 | Remove-Item -Path "IIS:\AppPools\My Pool" -Recurse -Force 43 | -------------------------------------------------------------------------------- /examples/08-ChangeAuth/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\Website1\1.0" -ErrorAction SilentlyContinue 11 | mkdir "C:\Sites\Website1\1.1" -ErrorAction SilentlyContinue 12 | 13 | $manager = Get-IISServerManager 14 | $manager.Sites.Add("Website1", "http", "*:8022:", "C:\Sites\Website1") 15 | $manager.Sites["Website1"].Applications.Add("/MyApp", "C:\Sites\MyApp") 16 | $manager.CommitChanges() 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Example 20 | # ----------------------------------------------------------------------------- 21 | Import-Module IISAdministration 22 | 23 | $manager = Get-IISServerManager 24 | 25 | # ServerManager makes it easy to get the various config files that belong to 26 | # an app, or at the applicationHost level. Since this setting is locked 27 | # to applicationHost, we need to get the applicationHost configuration. 28 | $config = $manager.GetApplicationHostConfiguration() 29 | 30 | # Note that we have to specify the name of the site or application we are 31 | # editing, since we are working with individual sections within 32 | # the global applicationHost.config file. 33 | $section = $config.GetSection(` 34 | "system.webServer/security/authentication/windowsAuthentication", ` 35 | "Website1") 36 | $section.Attributes["enabled"].Value = $true 37 | 38 | # The section paths are: 39 | # 40 | # Anonymous: system.webServer/security/authentication/anonymousAuthentication 41 | # Basic: system.webServer/security/authentication/basicAuthentication 42 | # Windows: system.webServer/security/authentication/windowsAuthentication 43 | 44 | # Changing options for an application in a virtual directory is similar, 45 | # just specify the site name and app name together: 46 | $section = $config.GetSection(` 47 | "system.webServer/security/authentication/windowsAuthentication", ` 48 | "Website1/MyApp") 49 | $section.Attributes["enabled"].Value = $true 50 | 51 | $manager.CommitChanges() 52 | 53 | # ----------------------------------------------------------------------------- 54 | # Assert 55 | # ----------------------------------------------------------------------------- 56 | if ($manager.Sites["Website1"].Applications["/MyApp"].GetWebConfiguration().GetSection("system.webServer/security/authentication/windowsAuthentication").Attributes["enabled"].Value -ne "true") { Write-Error "Our logic is wrong" } 57 | 58 | # ----------------------------------------------------------------------------- 59 | # Clean up 60 | # ----------------------------------------------------------------------------- 61 | 62 | . ..\_Teardown.IIS.ps1 63 | 64 | Remove-IISSite -Name "Website1" -Confirm:$false 65 | -------------------------------------------------------------------------------- /examples/08-ChangeAuth/README.md: -------------------------------------------------------------------------------- 1 | ### Changing authentication methods 2 | 3 | IIS supports a number of authentication methods. As described above, these are "locked" to `applicationHost.config` by default - so if you want to automatically enable them. The examples below show how to enable/disable: 4 | 5 | - Anonymous authentication 6 | - Basic authentication 7 | - Digest authentication 8 | - Windows authentication 9 | 10 | IIS Manager also shows "ASP.NET impersonation" and "Forms authentication" as settings at the same level, but these are actually set in your app's `web.config` file so I've left them out of here. -------------------------------------------------------------------------------- /examples/08-ChangeAuth/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.Web.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\Website1\1.0" -ErrorAction SilentlyContinue 11 | mkdir "C:\Sites\Website1\1.1" -ErrorAction SilentlyContinue 12 | mkdir "C:\Sites\MyApp" -ErrorAction SilentlyContinue 13 | 14 | New-Website -Name "Website1" -Port 80 -IPAddress "*" -HostHeader "" -PhysicalPath "C:\Sites\Website1" 15 | New-Item -Type Application -Path "IIS:\Sites\Website1\MyApp" -physicalPath "C:\Sites\MyApp" 16 | 17 | # ----------------------------------------------------------------------------- 18 | # Example 19 | # ----------------------------------------------------------------------------- 20 | 21 | Import-Module WebAdministration 22 | 23 | # The pattern here is to use Test-Path with the IIS:\ drive provider. 24 | 25 | Set-WebConfigurationProperty ` 26 | -Filter "/system.webServer/security/authentication/windowsAuthentication" ` 27 | -Name "enabled" ` 28 | -Value $true ` 29 | -Location "Website1/MyApp" ` 30 | -PSPath IIS:\ # We are using the root (applicationHost.config) file 31 | 32 | # The section paths are: 33 | # 34 | # Anonymous: system.webServer/security/authentication/anonymousAuthentication 35 | # Basic: system.webServer/security/authentication/basicAuthentication 36 | # Windows: system.webServer/security/authentication/windowsAuthentication 37 | 38 | # ----------------------------------------------------------------------------- 39 | # Assert 40 | # ----------------------------------------------------------------------------- 41 | 42 | if ((Get-WebConfigurationProperty -PSPath "IIS:\Sites\Website1\MyApp" -Filter "/system.webServer/security/authentication/windowsAuthentication" -Name "enabled") -ne $true) { Write-Error "Our logic is wrong" } 43 | 44 | # ----------------------------------------------------------------------------- 45 | # Clean up 46 | # ----------------------------------------------------------------------------- 47 | 48 | . ..\_Teardown.Web.ps1 49 | 50 | Remove-Item -Path "IIS:\Sites\Website1" -Recurse -Force 51 | -------------------------------------------------------------------------------- /examples/09-ChangeLogging/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\Logs" -ErrorAction SilentlyContinue 11 | 12 | $manager = Get-IISServerManager 13 | $manager.Sites.Add("Website1", "http", "*:8022:", "C:\Sites\Website1") 14 | $manager.CommitChanges() 15 | 16 | # ----------------------------------------------------------------------------- 17 | # Example 18 | # ----------------------------------------------------------------------------- 19 | Import-Module IISAdministration 20 | 21 | $manager = Get-IISServerManager 22 | 23 | $site = $manager.Sites["Website1"] 24 | $logFile = $site.LogFile 25 | $logFile.LogFormat = "W3c" # Formats: W3c, Iis, Ncsa, Custom 26 | $logFile.Directory = "C:\Sites\Logs" 27 | $logFile.Enabled = $true 28 | $logFile.Period = "Daily" 29 | 30 | $manager.CommitChanges() 31 | 32 | # ----------------------------------------------------------------------------- 33 | # Assert 34 | # ----------------------------------------------------------------------------- 35 | if ($manager.Sites["Website1"].LogFile.Directory -ne "C:\Sites\Logs") { Write-Error "Our logic is wrong" } 36 | 37 | # ----------------------------------------------------------------------------- 38 | # Clean up 39 | # ----------------------------------------------------------------------------- 40 | 41 | . ..\_Teardown.IIS.ps1 42 | 43 | Remove-IISSite -Name "Website1" -Confirm:$false 44 | -------------------------------------------------------------------------------- /examples/09-ChangeLogging/README.md: -------------------------------------------------------------------------------- 1 | ### Changing logging settings 2 | 3 | HTTP request logging is provided by IIS and can be specified server-wide or on an individual site level. Applications and virtual directories can disable logging, but they can't do any logging of their own. The examples below show how to set logging at the site level. 4 | 5 | Logging settings are stored in `applicationHost.config` underneath the site: 6 | 7 | ```xml 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ``` -------------------------------------------------------------------------------- /examples/09-ChangeLogging/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.Web.ps1 8 | 9 | mkdir "C:\Sites\Website1" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\Logs" -ErrorAction SilentlyContinue 11 | 12 | New-Website -Name "Website1" -Port 80 -IPAddress "*" -HostHeader "" -PhysicalPath "C:\Sites\Website1" 13 | 14 | # ----------------------------------------------------------------------------- 15 | # Example 16 | # ----------------------------------------------------------------------------- 17 | 18 | Import-Module WebAdministration 19 | 20 | $settings = @{ ` 21 | logFormat="W3c"; ` # Formats: W3c, Iis, Ncsa, Custom 22 | enabled=$true; ` 23 | directory="C:\Sites\Logs"; ` 24 | period="Daily"; ` 25 | } 26 | 27 | Set-ItemProperty "IIS:\Sites\Website1" -name "logFile" -value $settings 28 | 29 | # ----------------------------------------------------------------------------- 30 | # Assert 31 | # ----------------------------------------------------------------------------- 32 | 33 | if ((Get-ItemProperty "IIS:\Sites\Website1" -name "logFile.directory") -ne "C:\Sites\Logs") { Write-Error "Our logic is wrong" } 34 | 35 | # ----------------------------------------------------------------------------- 36 | # Clean up 37 | # ----------------------------------------------------------------------------- 38 | 39 | . ..\_Teardown.Web.ps1 40 | 41 | Remove-Item -Path "IIS:\Sites\Website1" -Recurse -Force 42 | -------------------------------------------------------------------------------- /examples/10-SpecificUser/IIS.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.IIS.ps1 8 | 9 | # ----------------------------------------------------------------------------- 10 | # Example 11 | # ----------------------------------------------------------------------------- 12 | Import-Module IISAdministration 13 | 14 | $manager = Get-IISServerManager 15 | $pool = $manager.ApplicationPools.Add("My Pool") 16 | $pool.ProcessModel.IdentityType = "SpecificUser" 17 | $pool.ProcessModel.Username = "My User" 18 | $pool.ProcessModel.Password = "Password" 19 | 20 | $manager.CommitChanges() 21 | 22 | # ----------------------------------------------------------------------------- 23 | # Assert 24 | # ----------------------------------------------------------------------------- 25 | 26 | if ((Get-IISAppPool -Name "My Pool") -eq $null) { Write-Error "My Pool not found" } 27 | 28 | # ----------------------------------------------------------------------------- 29 | # Clean up 30 | # ----------------------------------------------------------------------------- 31 | 32 | . ..\_Teardown.IIS.ps1 33 | 34 | $manager = Get-IISServerManager 35 | $manager.ApplicationPools["My Pool"].Delete() 36 | $manager.CommitChanges() -------------------------------------------------------------------------------- /examples/10-SpecificUser/README.md: -------------------------------------------------------------------------------- 1 | ### Running application pools as a specific user 2 | 3 | You can usually get by running your application pools as the `ApplicationPoolIdentity` accounts. This creates a virtual account for each different application pool automatically, isolating them from each other. On the local machine, you can grant access to resources like the file system to each separate application pool. For remote resources (like a SQL Server on a different machine), the application pool identities act as Network Service, so you can grant access at the machine level. Learn more about [application pool identities](https://www.iis.net/learn/manage/configuring-security/application-pool-identities). 4 | 5 | For more control over what the application pool can do, you should run it under a specific, custom user account. You'll want to use [`aspnet_regiis`](https://msdn.microsoft.com/en-us/library/k6h9cz8h.aspx) to give your custom account all the permissions it needs to run as an application pool and execute ASP.NET requests. You can then set your application pool to run as that user. 6 | -------------------------------------------------------------------------------- /examples/10-SpecificUser/Web.ps1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Setup 3 | # ----------------------------------------------------------------------------- 4 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 5 | cd $here 6 | 7 | . ..\_Setup.Web.ps1 8 | 9 | Remove-Item -Path "IIS:\AppPools\My Pool" -Recurse -Force -ErrorAction SilentlyContinue 10 | 11 | # ----------------------------------------------------------------------------- 12 | # Example 13 | # ----------------------------------------------------------------------------- 14 | 15 | Import-Module WebAdministration 16 | 17 | New-Item -Path "IIS:\AppPools" -Name "My Pool" -Type AppPool 18 | 19 | $identity = @{ ` 20 | identitytype="SpecificUser"; ` 21 | username="My Username"; ` 22 | password="My Password" ` 23 | } 24 | Set-ItemProperty -Path "IIS:\AppPools\My Pool" -name "processModel" -value $identity 25 | 26 | # ----------------------------------------------------------------------------- 27 | # Assert 28 | # ----------------------------------------------------------------------------- 29 | 30 | if ((Get-WebAppPoolState -Name "My Pool") -eq $null) { Write-Error "Pool not found" } 31 | 32 | # ----------------------------------------------------------------------------- 33 | # Clean up 34 | # ----------------------------------------------------------------------------- 35 | 36 | . ..\_Teardown.Web.ps1 37 | 38 | Remove-Item -Path "IIS:\AppPools\My Pool" -Recurse -Force 39 | -------------------------------------------------------------------------------- /examples/IIS-Change-Handler-Mapping-EditFeaturePermissions.ps1: -------------------------------------------------------------------------------- 1 | #Script to edit the feature permissions for individual Site Handler Mappings in IIS 2 | 3 | Import-Module WebAdministration 4 | 5 | #Set location of your sites. 6 | $site = "IIS:\Sites" 7 | 8 | #enter the setting for the "Edit Feature Permissions" Read, Script, Execute - Execute require script. 9 | $setpolicy = "Read, Script" 10 | 11 | #Sets the Handler Mapping feature delegation to Read/Write 12 | Set-WebConfiguration //System.webServer/handlers -metadata overrideMode -value Allow -PSPath IIS:/ -verbose 13 | 14 | #Sets the AccessPolicy for @site to $setpolicy value. 15 | Set-WebConfiguration -filter "/system.webServer/handlers/@AccessPolicy" -PSPath $site -value $setpolicy -verbose 16 | 17 | #Sets the Handler Mapping feature delegation to Read Only. (Left commented out by default) 18 | #Set-WebConfiguration //System.webServer/handlers -metadata overrideMode -value Deny -PSPath IIS:/ -verbose 19 | -------------------------------------------------------------------------------- /examples/_Setup.IIS.ps1: -------------------------------------------------------------------------------- 1 | clear 2 | Write-Host "Loading modules" 3 | 4 | Import-Module IISAdministration -ErrorAction SilentlyContinue 5 | 6 | # We use this folder for testing, often as the physical directory for sites 7 | mkdir "C:\Sites" -ErrorAction SilentlyContinue 8 | mkdir "C:\Sites\MySite" -ErrorAction SilentlyContinue 9 | 10 | $ErrorActionPreference = "Continue" 11 | 12 | # In case previous invocations left one open 13 | Stop-IISCommitDelay -commit $false -WarningAction SilentlyContinue 14 | Stop-IISCommitDelay -commit $false -WarningAction SilentlyContinue -------------------------------------------------------------------------------- /examples/_Setup.Web.ps1: -------------------------------------------------------------------------------- 1 | clear 2 | Write-Host "Loading modules" 3 | 4 | # Easiest way to load the module on 2008 (which used a SnapIn) and above (modules) 5 | Add-PSSnapin WebAdministration -ErrorAction SilentlyContinue 6 | Import-Module WebAdministration -ErrorAction SilentlyContinue 7 | 8 | # We use this folder for testing, often as the physical directory for sites 9 | mkdir "C:\Sites" -ErrorAction SilentlyContinue 10 | mkdir "C:\Sites\MySite" -ErrorAction SilentlyContinue 11 | 12 | $ErrorActionPreference = "Continue" 13 | 14 | # In case previous invocations left one open 15 | Stop-WebCommitDelay -commit $false -WarningAction SilentlyContinue -ErrorAction SilentlyContinue 16 | Stop-WebCommitDelay -commit $false -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /examples/_Teardown.IIS.ps1: -------------------------------------------------------------------------------- 1 | Write-Host "Unloading modules" 2 | -------------------------------------------------------------------------------- /examples/_Teardown.Web.ps1: -------------------------------------------------------------------------------- 1 | Write-Host "Unloading modules" 2 | -------------------------------------------------------------------------------- /run.cmd: -------------------------------------------------------------------------------- 1 | pushd src\ExampleRunner\bin\Debug 2 | 3 | .\ExampleRunner.exe 4 | 5 | popd 6 | -------------------------------------------------------------------------------- /src/ExampleRunner.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleRunner", "ExampleRunner\ExampleRunner.csproj", "{9732705A-6332-4884-B9B8-A66B63C89EAF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {9732705A-6332-4884-B9B8-A66B63C89EAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {9732705A-6332-4884-B9B8-A66B63C89EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {9732705A-6332-4884-B9B8-A66B63C89EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {9732705A-6332-4884-B9B8-A66B63C89EAF}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /src/ExampleRunner.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> -------------------------------------------------------------------------------- /src/ExampleRunner/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/ExampleRunner/ExampleRunner.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9732705A-6332-4884-B9B8-A66B63C89EAF} 8 | Exe 9 | Properties 10 | ExampleRunner 11 | ExampleRunner 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | ..\packages\TeamCity.ServiceMessages.3.0.5.1\lib\net35\JetBrains.TeamCity.ServiceMessages.dll 40 | True 41 | 42 | 43 | False 44 | C:\Windows\System32\inetsrv\Microsoft.Web.Administration.dll 45 | 46 | 47 | ..\packages\Serilog.2.0.0\lib\net45\Serilog.dll 48 | True 49 | 50 | 51 | ..\packages\Serilog.Sinks.Literate.2.0.0\lib\net45\Serilog.Sinks.Literate.dll 52 | True 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 86 | -------------------------------------------------------------------------------- /src/ExampleRunner/Generation/Example.cs: -------------------------------------------------------------------------------- 1 | namespace ExampleRunner.Generation 2 | { 3 | public class Example 4 | { 5 | public string Code { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/ExampleRunner/Generation/ExampleParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using Serilog; 4 | 5 | namespace ExampleRunner.Generation 6 | { 7 | public static class ExampleParser 8 | { 9 | public static Example Parse(string scriptContents) 10 | { 11 | var match = ParserRegex.Match(scriptContents); 12 | if (!match.Success) 13 | { 14 | // Fail 15 | } 16 | 17 | var example = new Example 18 | { 19 | Code = match.Groups["example"].Value.Trim() 20 | }; 21 | return example; 22 | } 23 | 24 | static readonly Regex ParserRegex = new Regex(@"# Meta: 25 | # Whatever in the middle 26 | .*? 27 | 28 | # Example: 29 | \#\s-----+\r\n 30 | \#\s*Example\s*\r\n 31 | \#\s-----+\r\n 32 | (?.*?) 33 | (?=\#\s-----+) 34 | 35 | ", RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ExampleRunner/Generation/ExampleWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace ExampleRunner.Generation 5 | { 6 | internal class ExampleWriter : IDisposable 7 | { 8 | readonly TextWriter writer; 9 | 10 | public ExampleWriter(TextWriter writer) 11 | { 12 | this.writer = writer; 13 | } 14 | 15 | public void WriteIntroduction(string contents) 16 | { 17 | writer.WriteLine(contents); 18 | } 19 | 20 | public void WriteExample(string name, Example example) 21 | { 22 | writer.WriteLine("```powershell " + name); 23 | writer.WriteLine(example.Code); 24 | writer.WriteLine("```"); 25 | writer.WriteLine(); 26 | } 27 | 28 | public void Dispose() 29 | { 30 | writer.Dispose(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/ExampleRunner/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | using ExampleRunner.Generation; 7 | using ExampleRunner.Testing; 8 | using Microsoft.Web.Administration; 9 | using Serilog; 10 | using Serilog.Events; 11 | 12 | namespace ExampleRunner 13 | { 14 | class Program 15 | { 16 | static readonly string ExamplesDirectory = Path.GetFullPath("..\\..\\..\\..\\examples"); 17 | static readonly string OutputFile = Path.GetFullPath("..\\..\\..\\..\\out.md"); 18 | static readonly Regex FileNameFilterRegex = new Regex("^[^_].*README\\.md$", RegexOptions.IgnoreCase); 19 | 20 | static int Main(string[] args) 21 | { 22 | Initialize(); 23 | EnsureExamplesDirectory(); 24 | GenerateDocumentation(); 25 | RunAllExamples(); 26 | 27 | if (TeamCitySink.Errors != 0) 28 | { 29 | Console.WriteLine("Failed {0} errors", TeamCitySink.Errors); 30 | } 31 | else 32 | { 33 | Console.WriteLine("Success!"); 34 | } 35 | return TeamCitySink.Errors; 36 | } 37 | 38 | static void Initialize() 39 | { 40 | Log.Logger = new LoggerConfiguration() 41 | .WriteTo.LiterateConsole(outputTemplate: "[{Level:U3}] {Indent}{Message}{NewLine}{Exception}", restrictedToMinimumLevel: LogEventLevel.Debug) 42 | .WriteTo.Sink() 43 | .Enrich.FromLogContext() 44 | .CreateLogger(); 45 | } 46 | 47 | static void EnsureExamplesDirectory() 48 | { 49 | if (!Directory.Exists(ExamplesDirectory)) 50 | { 51 | Log.Error("Expected to find examples directory at {examples}", ExamplesDirectory); 52 | Environment.Exit(1); 53 | } 54 | 55 | Environment.CurrentDirectory = ExamplesDirectory; 56 | } 57 | 58 | static void GenerateDocumentation() 59 | { 60 | Log.Information("Generating docs"); 61 | using (var streamWriter = new StreamWriter(OutputFile)) 62 | using (var exampleWriter = new ExampleWriter(streamWriter)) 63 | { 64 | foreach (var example in GetExampleFiles()) 65 | { 66 | Log.Information("Parsing {file}", Path.GetFileNameWithoutExtension(example.WebExampleScriptFile)); 67 | exampleWriter.WriteIntroduction(File.ReadAllText(example.IntroductionMarkdownFile)); 68 | if (example.WebExampleScriptFile != null) 69 | exampleWriter.WriteExample("WebAdministration", ExampleParser.Parse(File.ReadAllText(example.WebExampleScriptFile))); 70 | if (example.IisExampleScriptFile != null) 71 | exampleWriter.WriteExample("IISAdministration", ExampleParser.Parse(File.ReadAllText(example.IisExampleScriptFile))); 72 | } 73 | } 74 | Log.Information("Docs written to: {output}", OutputFile); 75 | } 76 | 77 | static void RunAllExamples() 78 | { 79 | Log.Information("Running tests..."); 80 | 81 | foreach (var example in GetExampleFiles()) 82 | { 83 | if (example.WebExampleScriptFile != null) RunTest(example.WebExampleScriptFile); 84 | if (example.IisExampleScriptFile != null && IsIisModuleSupported) RunTest(example.IisExampleScriptFile); 85 | } 86 | 87 | Log.Information("Test run complete"); 88 | } 89 | 90 | public static bool IsIisModuleSupported => Directory.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), 91 | @"C:\Windows\System32\WindowsPowerShell\v1.0\Modules\IISAdministration")); 92 | 93 | static void RunTest(string file) 94 | { 95 | Log.Information("Running test: {file}", file); 96 | ScriptRunner.RunScript(file); 97 | } 98 | 99 | static IEnumerable GetExampleFiles() 100 | { 101 | return ( 102 | from file in Directory.GetFiles(Environment.CurrentDirectory, "*", SearchOption.AllDirectories) 103 | let fileName = Path.GetFileName(file) 104 | where string.Equals(fileName, "README.md", StringComparison.OrdinalIgnoreCase) 105 | let directory = Path.GetDirectoryName(file) 106 | where directory != null 107 | let webExample = Path.Combine(directory, "Web.ps1") 108 | let iisExample = Path.Combine(directory, "IIS.ps1") 109 | orderby directory 110 | select new ExampleFileSet 111 | { 112 | IntroductionMarkdownFile = file, 113 | WebExampleScriptFile = File.Exists(webExample) ? webExample : null, 114 | IisExampleScriptFile = File.Exists(iisExample) ? iisExample : null 115 | }).ToList(); 116 | } 117 | } 118 | 119 | public class ExampleFileSet 120 | { 121 | public string IntroductionMarkdownFile { get; set; } 122 | public string WebExampleScriptFile { get; set; } 123 | public string IisExampleScriptFile { get; set; } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/ExampleRunner/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ExampleRunner")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ExampleRunner")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9732705a-6332-4884-b9b8-a66b63c89eaf")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/ExampleRunner/TeamCitySink.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Serilog.Core; 4 | using Serilog.Events; 5 | 6 | namespace ExampleRunner 7 | { 8 | class TeamCitySink : ILogEventSink 9 | { 10 | public static int Errors = 0; 11 | 12 | public void Emit(LogEvent logEvent) 13 | { 14 | if (logEvent.Level >= LogEventLevel.Warning) 15 | { 16 | Interlocked.Increment(ref Errors); 17 | 18 | var formatter = new JetBrains.TeamCity.ServiceMessages.Write.ServiceMessageFormatter(); 19 | var formatted = formatter.FormatMessage("message", 20 | new 21 | { 22 | status = TranslateStatus(logEvent.Level), 23 | text = logEvent.RenderMessage() 24 | }); 25 | Console.WriteLine(formatted); 26 | } 27 | } 28 | 29 | static string TranslateStatus(LogEventLevel logEventLevel) 30 | { 31 | // The status attribute may take following values: NORMAL, WARNING, FAILURE, ERROR. The default value is NORMAL. 32 | switch (logEventLevel) 33 | { 34 | default: 35 | return "NORMAL"; 36 | case LogEventLevel.Warning: 37 | return "WARNING"; 38 | case LogEventLevel.Error: 39 | return "ERROR"; 40 | case LogEventLevel.Fatal: 41 | return "FAILURE"; 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/ExampleRunner/Testing/ScriptRunner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Threading; 5 | using JetBrains.TeamCity.ServiceMessages.Write; 6 | using JetBrains.TeamCity.ServiceMessages.Write.Special.Impl; 7 | using JetBrains.TeamCity.ServiceMessages.Write.Special.Impl.Writer; 8 | 9 | namespace ExampleRunner.Testing 10 | { 11 | public class ScriptRunner 12 | { 13 | public static void RunScript(string fileName) 14 | { 15 | var cwd = Environment.CurrentDirectory; 16 | try 17 | { 18 | var fullPath = Path.GetFullPath(fileName); 19 | var directory = Path.GetDirectoryName(fullPath) ?? Environment.CurrentDirectory; 20 | 21 | using (var scope = new TestScope(fileName)) 22 | { 23 | var exitCode = SilentProcessRunner.ExecuteCommand( 24 | "PowerShell.exe", 25 | FormatCommandArguments(fullPath), 26 | directory, 27 | scope.StdOut, 28 | scope.StdErr 29 | ); 30 | scope.Exited(exitCode); 31 | } 32 | } 33 | finally 34 | { 35 | Environment.CurrentDirectory = cwd; 36 | } 37 | } 38 | 39 | public static string FormatCommandArguments(string scriptFile) 40 | { 41 | var commandArguments = new StringBuilder(); 42 | commandArguments.Append("-NoLogo "); 43 | commandArguments.Append("-NonInteractive "); 44 | commandArguments.Append("-ExecutionPolicy Unrestricted "); 45 | var escapedBootstrapFile = scriptFile.Replace("'", "''"); 46 | commandArguments.AppendFormat("-Command \"Try {{. {{. '{0}'; if ((test-path variable:global:lastexitcode)) {{ exit $LastExitCode }}}};}} catch {{ throw }}\"", escapedBootstrapFile); 47 | return commandArguments.ToString(); 48 | } 49 | 50 | class TestScope : IDisposable 51 | { 52 | readonly TeamCityTestWriter testWriter; 53 | bool failed; 54 | 55 | public TestScope(string testName) 56 | { 57 | testWriter = new TeamCityTestWriter(new ServiceMessagesWriter(new ServiceMessageFormatter(), Console.WriteLine), testName, new NullDisposable()); 58 | testWriter.OpenTest(); 59 | } 60 | 61 | public void StdOut(string line) 62 | { 63 | testWriter.WriteStdOutput(line); 64 | } 65 | 66 | public void StdErr(string line) 67 | { 68 | Console.ForegroundColor = ConsoleColor.Red; 69 | failed = true; 70 | Interlocked.Increment(ref TeamCitySink.Errors); 71 | testWriter.WriteErrOutput(line); 72 | Console.ResetColor(); 73 | } 74 | 75 | public void Exited(int exit) 76 | { 77 | if (failed) 78 | testWriter.WriteFailed("Test encountered an error", "View logs for details"); 79 | 80 | if (exit != 0) 81 | testWriter.WriteFailed("Exited with code " + exit, ""); 82 | } 83 | 84 | public void Dispose() 85 | { 86 | testWriter.Dispose(); 87 | } 88 | } 89 | 90 | class NullDisposable : IDisposable { public void Dispose() {} } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/ExampleRunner/Testing/SilentProcessRunner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using System.Threading; 6 | 7 | namespace ExampleRunner.Testing 8 | { 9 | public static class SilentProcessRunner 10 | { 11 | // ReSharper disable once InconsistentNaming 12 | private const int CP_OEMCP = 1; 13 | private static readonly Encoding oemEncoding; 14 | 15 | static SilentProcessRunner() 16 | { 17 | try 18 | { 19 | CPINFOEX info; 20 | if (GetCPInfoEx(CP_OEMCP, 0, out info)) 21 | { 22 | oemEncoding = Encoding.GetEncoding(info.CodePage); 23 | } 24 | else 25 | { 26 | oemEncoding = Encoding.GetEncoding(850); 27 | } 28 | } 29 | catch (Exception) 30 | { 31 | Trace.WriteLine("Couldn't get default OEM encoding"); 32 | oemEncoding = Encoding.UTF8; 33 | } 34 | } 35 | 36 | public static int ExecuteCommand(string executable, string arguments, string workingDirectory, Action output, Action error) 37 | { 38 | try 39 | { 40 | using (var process = new Process()) 41 | { 42 | process.StartInfo.FileName = executable; 43 | process.StartInfo.Arguments = arguments; 44 | process.StartInfo.WorkingDirectory = workingDirectory; 45 | process.StartInfo.UseShellExecute = false; 46 | process.StartInfo.CreateNoWindow = true; 47 | process.StartInfo.RedirectStandardOutput = true; 48 | process.StartInfo.RedirectStandardError = true; 49 | process.StartInfo.StandardOutputEncoding = oemEncoding; 50 | process.StartInfo.StandardErrorEncoding = oemEncoding; 51 | 52 | using (var outputWaitHandle = new AutoResetEvent(false)) 53 | using (var errorWaitHandle = new AutoResetEvent(false)) 54 | { 55 | process.OutputDataReceived += (sender, e) => 56 | { 57 | if (e.Data == null) 58 | { 59 | outputWaitHandle.Set(); 60 | } 61 | else 62 | { 63 | output(e.Data); 64 | } 65 | }; 66 | 67 | process.ErrorDataReceived += (sender, e) => 68 | { 69 | if (e.Data == null) 70 | { 71 | errorWaitHandle.Set(); 72 | } 73 | else 74 | { 75 | error(e.Data); 76 | } 77 | }; 78 | 79 | process.Start(); 80 | 81 | process.BeginOutputReadLine(); 82 | process.BeginErrorReadLine(); 83 | 84 | process.WaitForExit(); 85 | 86 | outputWaitHandle.WaitOne(); 87 | errorWaitHandle.WaitOne(); 88 | 89 | return process.ExitCode; 90 | } 91 | } 92 | } 93 | catch (Exception ex) 94 | { 95 | throw new Exception(string.Format("Error when attempting to execute {0}: {1}", executable, ex.Message), ex); 96 | } 97 | } 98 | 99 | [DllImport("kernel32.dll", SetLastError = true)] 100 | private static extern bool GetCPInfoEx([MarshalAs(UnmanagedType.U4)] int CodePage, [MarshalAs(UnmanagedType.U4)] int dwFlags, out CPINFOEX lpCPInfoEx); 101 | 102 | private const int MAX_DEFAULTCHAR = 2; 103 | private const int MAX_LEADBYTES = 12; 104 | private const int MAX_PATH = 260; 105 | 106 | [StructLayout(LayoutKind.Sequential)] 107 | private struct CPINFOEX 108 | { 109 | [MarshalAs(UnmanagedType.U4)] 110 | public int MaxCharSize; 111 | 112 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEFAULTCHAR)] 113 | public byte[] DefaultChar; 114 | 115 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_LEADBYTES)] 116 | public byte[] LeadBytes; 117 | 118 | public char UnicodeDefaultChar; 119 | 120 | [MarshalAs(UnmanagedType.U4)] 121 | public int CodePage; 122 | 123 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] 124 | public string CodePageName; 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /src/ExampleRunner/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------