Project 6: IIS Website Provisioner

Build a PowerShell provisioning script that creates or updates an IIS site, app pool, bindings, and permissions in an idempotent way.

Quick Reference

Attribute Value
Difficulty Intermediate (Level 3-4)
Time Estimate 8-12 hours
Main Programming Language Windows PowerShell 5.1 (Alternatives: PowerShell 7)
Alternative Programming Languages None practical for IIS without modules
Coolness Level Level 3: Infrastructure automation
Business Potential Level 4: Internal provisioning tool
Prerequisites Windows admin basics, PowerShell scripting
Key Topics IIS provider, idempotency, ACLs, provisioning workflows

1. Learning Objectives

By completing this project, you will:

  1. Use the WebAdministration module to manage IIS programmatically.
  2. Create idempotent provisioning logic for app pools and sites.
  3. Configure bindings (HTTP/HTTPS) reliably.
  4. Apply correct file permissions for app pool identities.
  5. Produce a clear audit log of provisioning actions.

2. All Theory Needed (Per-Concept Breakdown)

2.1 IIS Configuration and the WebAdministration Provider

Fundamentals

IIS exposes configuration through a PowerShell provider (WebAdministration) that lets you manage sites, app pools, and bindings as if they were files in a drive (IIS:\). Cmdlets like Get-Website, New-Website, Get-WebAppPool, and New-WebAppPool allow you to create and update IIS resources programmatically. Understanding the IIS configuration model and how the provider maps it into PowerShell is critical for writing a reliable provisioning script.

Deep Dive into the Concept

The WebAdministration module is the canonical interface for IIS automation. It exposes configuration via a provider rooted at IIS:\, where sites, application pools, and bindings appear as nodes. This model maps to the underlying applicationHost.config file, which stores IIS configuration. When you call New-Website, PowerShell writes to this config and instructs the IIS service to reload. Similarly, New-WebAppPool creates app pool definitions with identity and runtime settings.

Bindings are a central part of IIS. A binding defines a protocol (HTTP/HTTPS), IP address, port, and host header. The combination of these parameters must be unique for each site. Provisioning scripts often fail when bindings already exist or are misconfigured. Therefore, you must design a binding strategy: either enforce a fixed binding or update existing bindings. The Get-WebBinding and New-WebBinding cmdlets allow you to inspect and set bindings explicitly. If HTTPS is required, you must also bind a certificate from the machine’s certificate store. That adds complexity, but even a basic tool should be structured to allow certificate binding in the future.

App pools isolate web applications. Each app pool has a process model, identity, and runtime version. For modern ASP.NET Core apps, managedRuntimeVersion is typically empty, while legacy .NET apps use v4.0. Your provisioning script should allow users to specify the runtime version and app pool identity (e.g., ApplicationPoolIdentity vs custom service account). The choice affects file permissions and security. If you use ApplicationPoolIdentity, IIS creates a virtual account named IIS AppPool\<AppPoolName>, which must be granted file access.

The provider model makes it easy to read and write configuration, but it’s not transactional. If your script creates a pool but fails to create the site, you can end up with orphaned resources. That is why idempotency and cleanup logic matter. It is also why the script should log each action, so operators can undo or verify changes.

How this Fits on Projects

This project builds on file system and idempotency concepts from Projects 2 and 4. It introduces a real system configuration provider.

Definitions & Key Terms

  • WebAdministration module -> PowerShell module for IIS management.
  • IIS Provider -> IIS:\ drive for IIS configuration.
  • App Pool -> Isolated worker process group.
  • Binding -> Protocol/IP/port/host mapping for a site.
  • applicationHost.config -> IIS configuration file.

Mental Model Diagram (ASCII)

IIS:\
  Sites
    Default Web Site
    DemoSite
  AppPools
    DefaultAppPool
    DemoSiteAppPool

How It Works (Step-by-Step)

  1. Import WebAdministration module.
  2. Check existing sites and app pools.
  3. Create or update app pool.
  4. Create or update site with bindings.
  5. Configure permissions and start site.

Minimal Concrete Example

Import-Module WebAdministration
New-WebAppPool -Name 'DemoSite'
New-Website -Name 'DemoSite' -Port 8080 -PhysicalPath 'C:\Sites\DemoSite'

Common Misconceptions

  • “IIS config changes are instant everywhere.” -> IIS reloads config but may need restart for some settings.
  • “Bindings are optional.” -> A site without a binding is unreachable.
  • “App pool identity doesn’t matter.” -> It controls file access and security.

Check-Your-Understanding Questions

  1. What does the WebAdministration provider expose?
  2. Why are bindings critical?
  3. Where is IIS configuration stored?

Check-Your-Understanding Answers

  1. Sites, app pools, and configuration nodes under IIS:\.
  2. Bindings determine how HTTP requests map to a site.
  3. In applicationHost.config.

Real-World Applications

  • Automated provisioning of web apps in staging environments.
  • Consistent IIS setup across multiple servers.

Where You’ll Apply It

  • In this project: see Section 3.2 Functional Requirements and Section 4.1 High-Level Design.
  • Also used in: Project 9: DSC Web Server.

References

  • Microsoft Learn: WebAdministration module
  • Microsoft Learn: New-Website

Key Insights

IIS is just configuration; the provider makes it scriptable and repeatable.

Summary

The WebAdministration provider maps IIS configuration into PowerShell drives, enabling automation of sites, app pools, and bindings.

Homework/Exercises to Practice the Concept

  1. List all IIS sites and their bindings.
  2. Create a new app pool and inspect its properties.
  3. Remove a test site after creating it.

Solutions to the Homework/Exercises

  1. Get-Website | Select-Object Name, Bindings.
  2. Get-WebAppPoolState -Name DemoSite.
  3. Remove-Website -Name DemoSite.

2.2 Idempotent Provisioning and State Checks

Fundamentals

Provisioning scripts must be safe to run multiple times. Idempotency means re-running the script leaves the system in the same desired state. For IIS, that means checking for existing app pools and sites before creating them, updating bindings only when needed, and skipping work when configuration already matches the desired state. This is the same mindset as DSC but applied manually.

Deep Dive into the Concept

Idempotent provisioning starts with state inspection. Before creating resources, you query current state with Get-Website and Get-WebAppPool. You then compare the current configuration to the desired configuration. If they match, you skip. If they differ, you update. This prevents duplication and ensures consistency. In IIS, the most common idempotency issues are duplicate bindings and mismatched physical paths.

Consider bindings: if a site already exists with a binding on port 8080 but you want port 8081, you must remove or update the binding. A naive script that just calls New-WebBinding will fail with a conflict error. An idempotent script checks existing bindings, removes incorrect ones, and adds missing ones. This is a deliberate update process, not just a creation step.

Another idempotency issue is app pool configuration drift. If an app pool exists but uses the wrong identity or runtime, your script should correct it. Use Set-ItemProperty on the IIS:\AppPools\<name> path to update fields like processModel.identityType or managedRuntimeVersion.

Idempotency also implies consistent file system structure. Your script should ensure the site directory exists and contains expected content (e.g., a placeholder index.html). If content is missing, create it. If you require permissions, set them explicitly even if they “might already be correct.” This ensures convergence.

Finally, idempotency must be measurable. Include a -WhatIf mode and verbose output that shows when actions are skipped because the state already matches. This builds operator trust and makes your script usable in automation pipelines.

How this Fits on Projects

Idempotency is the core of provisioning in this project, and it sets the stage for Project 9’s DSC configuration.

Definitions & Key Terms

  • Idempotent -> Running repeatedly yields the same state.
  • Desired state -> The target configuration you want.
  • Drift -> A difference between current and desired state.
  • Convergence -> Process of reaching desired state.
  • WhatIf -> Preview changes without applying them.

Mental Model Diagram (ASCII)

Current State -> Compare -> Apply Changes -> Desired State
      |             |             |
      |             |             +-- Create/Update
      |             +-- Detect drift
      +-- Read from IIS + FileSystem

How It Works (Step-by-Step)

  1. Query current IIS configuration.
  2. Compute desired configuration.
  3. Compare and detect differences.
  4. Apply only required changes.
  5. Report actions taken or skipped.

Minimal Concrete Example

$site = Get-Website -Name $Name -ErrorAction SilentlyContinue
if (-not $site) { New-Website -Name $Name -Port $Port -PhysicalPath $Path } else { Set-ItemProperty \"IIS:\\Sites\\$Name\" -Name physicalPath -Value $Path }

Common Misconceptions

  • “Provisioning scripts run once.” -> Real ops runs scripts repeatedly.
  • “If it exists, skip it.” -> Existing config might be wrong.
  • “WhatIf is optional.” -> It is critical for safe changes.

Check-Your-Understanding Questions

  1. What is configuration drift?
  2. Why should a script update existing resources?
  3. How do you safely preview changes?

Check-Your-Understanding Answers

  1. Differences between current and desired state.
  2. To converge on the correct configuration.
  3. Use -WhatIf and verbose logging.

Real-World Applications

  • Infrastructure provisioning pipelines.
  • Repeatable server builds in staging/production.

Where You’ll Apply It

  • In this project: see Section 3.2 Functional Requirements and Section 5.10 Implementation Phases.
  • Also used in: Project 9: DSC Web Server.

References

  • Microsoft Learn: about_WhatIf
  • Microsoft Learn: Set-ItemProperty

Key Insights

Provisioning is about convergence, not one-time creation.

Summary

Idempotency ensures your IIS provisioning script can be rerun safely and still reach the desired state.

Homework/Exercises to Practice the Concept

  1. Write a script that checks if a site exists before creating it.
  2. Add a -WhatIf switch to your provisioning logic.
  3. Detect and report binding drift.

Solutions to the Homework/Exercises

  1. Use Get-Website -ErrorAction SilentlyContinue.
  2. Wrap changes in if (-not $WhatIf).
  3. Compare Get-WebBinding results to desired binding.

2.3 File Permissions and App Pool Identities

Fundamentals

IIS app pools run under identities that need file access to the site folder. The default ApplicationPoolIdentity uses a virtual account named IIS AppPool\<AppPoolName>. If the site folder lacks read permissions for this identity, the site fails to load. PowerShell can manage ACLs with Get-Acl, Set-Acl, and icacls. Correctly applying permissions is essential for a working site.

Deep Dive into the Concept

Windows file permissions are based on Access Control Lists (ACLs). Each ACL contains Access Control Entries (ACEs) that grant or deny rights to security principals (users, groups, service accounts). For IIS, the worker process must have at least read (and often write) access to the site’s physical path. When you create a new site folder, it inherits permissions from its parent. This may or may not include the app pool identity. Therefore, a provisioning script should explicitly grant permissions to the app pool identity to avoid surprises.

The most common identity types are:

  • ApplicationPoolIdentity: virtual account tied to app pool name.
  • NetworkService: built-in account with network access.
  • Custom service account: domain account with explicit permissions.

For the default identity, you grant access to IIS AppPool\<AppPoolName>. You can do this with icacls or with .NET ACL APIs. icacls is simpler and well understood: icacls C:\Sites\DemoSite /grant "IIS AppPool\DemoSite:(OI)(CI)RX". (OI)(CI) ensures inheritance to files and subfolders. RX grants read/execute. If your app needs to write logs, you might grant M (modify) to a log folder only, not the whole site.

Using Get-Acl and Set-Acl gives you more control but requires dealing with FileSystemAccessRule objects. This is more verbose but integrates better with PowerShell objects. For a provisioning script, using icacls in a controlled way is often acceptable. Regardless, you should record permissions applied in the audit log so operators know exactly what changed.

Permissions are a security boundary. Grant the minimum necessary rights. If you grant FullControl to everyone, you create vulnerabilities. Therefore, your script should have a minimal default and only allow elevated permissions via explicit parameters.

How this Fits on Projects

Permissions are required for the IIS provisioner to function and are a concrete application of safe automation practices from Project 2.

Definitions & Key Terms

  • ACL -> Access Control List.
  • ACE -> Access Control Entry.
  • ApplicationPoolIdentity -> Virtual account for app pool.
  • Inheritance -> Permissions that flow to child items.
  • Least privilege -> Grant only necessary rights.

Mental Model Diagram (ASCII)

Site Folder ACL
  |
  +-- BUILTIN\Administrators: FullControl
  +-- IIS AppPool\DemoSite: Read/Execute

How It Works (Step-by-Step)

  1. Create site folder if missing.
  2. Determine app pool identity.
  3. Apply ACL rules to grant access.
  4. Verify permissions with icacls.
  5. Log permissions applied.

Minimal Concrete Example

icacls $Path /grant "IIS AppPool\$AppPool:(OI)(CI)RX"

Common Misconceptions

  • “App pools always have access.” -> Not unless granted.
  • “Granting FullControl is safer.” -> It is more dangerous.
  • “ACLs are optional.” -> Without them, the site fails.

Check-Your-Understanding Questions

  1. What identity does ApplicationPoolIdentity map to?
  2. Why use (OI)(CI) in ACLs?
  3. When should you grant write permissions?

Check-Your-Understanding Answers

  1. IIS AppPool\<AppPoolName>.
  2. It ensures permissions inherit to files and subfolders.
  3. Only when the app needs to write (e.g., logs, uploads).

Real-World Applications

  • Automated web app provisioning in enterprise environments.
  • Secure deployment pipelines for IIS.

Where You’ll Apply It

  • In this project: see Section 3.2 Functional Requirements and Section 3.6 Edge Cases.
  • Also used in: Project 3: AD User Provisioning for security concepts.

References

  • Microsoft Learn: icacls
  • Microsoft Learn: Get-Acl / Set-Acl

Key Insights

Permissions are the most common hidden cause of “site not loading.” Set them explicitly.

Summary

Explicitly grant least-privilege permissions to the app pool identity so IIS can read (and optionally write) site files.

Homework/Exercises to Practice the Concept

  1. Grant read access to a test folder for a local account.
  2. Verify permissions using icacls output.
  3. Create a log folder with write permissions only.

Solutions to the Homework/Exercises

  1. icacls C:\Test /grant "Users:(OI)(CI)RX".
  2. icacls C:\Test and inspect entries.
  3. icacls C:\Sites\DemoSite\logs /grant "IIS AppPool\DemoSite:(OI)(CI)M".

3. Project Specification

3.1 What You Will Build

A script named New-IISSite.ps1 that:

  • Creates or updates an IIS site and app pool.
  • Configures HTTP/HTTPS bindings.
  • Ensures site folder exists and has correct permissions.
  • Outputs a provisioning summary.

3.2 Functional Requirements

  1. Input: -Name, -Path, -Port, optional -HostHeader, -AppPool.
  2. App pool: create if missing and configure runtime/identity.
  3. Site: create or update to match desired bindings.
  4. Permissions: grant app pool identity access to site path.
  5. Idempotency: re-running script results in same configuration.
  6. Exit codes: 0 success, 2 partial failures, 3 invalid input.

3.3 Non-Functional Requirements

  • Reliability: no duplicate bindings.
  • Usability: clear output of changes vs skipped actions.
  • Security: least-privilege file permissions.

3.4 Example Usage / Output

PS> .\New-IISSite.ps1 -Name "DemoSite" -Path C:\Sites\DemoSite -Port 8080
[SUCCESS] App pool DemoSite created
[SUCCESS] Site DemoSite created and started
[SUCCESS] Binding added: http://*:8080

3.5 Data Formats / Schemas / Protocols

Audit log schema:

{
  Time: datetime,
  Action: string,
  Target: string,
  Result: 'Created'|'Updated'|'Skipped'|'Failed',
  Message: string
}

3.6 Edge Cases

  • Site exists with different binding -> update binding.
  • App pool exists with wrong runtime -> update settings.
  • Path missing -> create folder.
  • Permission denied -> log and fail.

3.7 Real World Outcome

3.7.1 How to Run (Copy/Paste)

powershell .\New-IISSite.ps1 -Name "DemoSite" -Path C:\Sites\DemoSite -Port 8080

3.7.2 Golden Path Demo (Deterministic)

  • Use a fixed demo site name and port in a VM snapshot.
  • Output is stable for documentation.

3.7.3 CLI Terminal Transcript (Success)

$ powershell .\New-IISSite.ps1 -Name "DemoSite" -Path C:\Sites\DemoSite -Port 8080
[SUCCESS] App pool DemoSite created
[SUCCESS] Site DemoSite created and started
[SUCCESS] Binding added: http://*:8080
ExitCode: 0

3.7.4 CLI Terminal Transcript (Failure)

$ powershell .\New-IISSite.ps1 -Name "DemoSite" -Path C:\Sites\DemoSite -Port 8080
ERROR: Access denied while setting permissions
ExitCode: 2

4. Solution Architecture

4.1 High-Level Design

[Inputs] -> [Validate] -> [AppPool] -> [Site] -> [Bindings] -> [Permissions]
                 |                                                |
                 +-- Audit log                                   +-- Summary

4.2 Key Components

| Component | Responsibility | Key Decisions | |———–|—————-|—————| | AppPool Manager | Ensure pool exists | Update settings if drift | | Site Manager | Ensure site exists | Idempotent create/update | | Binding Manager | Ensure bindings | Remove incorrect bindings | | Permission Manager | Set ACLs | Least-privilege defaults |

4.3 Data Structures (No Full Code)

[PSCustomObject]@{
  Action = 'CreateSite'
  Target = $Name
  Result = 'Created'
  Message = 'Site created and started'
}

4.4 Algorithm Overview

Key Algorithm: IIS Provisioning

  1. Validate inputs and paths.
  2. Ensure app pool exists and configured.
  3. Ensure site exists and path is correct.
  4. Configure bindings to match desired state.
  5. Apply file permissions.
  6. Report results.

Complexity Analysis

  • Time: O(1) per resource (constant operations).
  • Space: O(1) for logs per action.

5. Implementation Guide

5.1 Development Environment Setup

# Install IIS and management tools
Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServerRole

5.2 Project Structure

project-root/
+-- New-IISSite.ps1
+-- logs/
+-- tests/

5.3 The Core Question You’re Answering

“How do I provision IIS sites repeatedly without breaking existing state?”

5.4 Concepts You Must Understand First

  1. IIS provider and WebAdministration cmdlets.
  2. Idempotent provisioning logic.
  3. File permissions and app pool identities.

5.5 Questions to Guide Your Design

  1. How will you handle existing sites with different bindings?
  2. What identity will the app pool run under?
  3. Which folders need write permissions?

5.6 Thinking Exercise

Sketch the steps to go from a blank server to a running IIS site.

5.7 The Interview Questions They’ll Ask

  1. What is idempotency and why does it matter in provisioning?
  2. How do you configure IIS bindings with PowerShell?
  3. How do app pool identities affect file permissions?

5.8 Hints in Layers

Hint 1: Start with app pool creation only. Hint 2: Add site creation with a single HTTP binding. Hint 3: Add permission handling last.

5.9 Books That Will Help

| Topic | Book | Chapter | |——|——|———| | IIS automation | PowerShell for Sysadmins | IIS chapters | | Security/ACLs | PowerShell in Action | Security chapters |

5.10 Implementation Phases

Phase 1: App Pool + Site (3-4 hours)

  • Create app pool and site. Checkpoint: site appears in IIS Manager.

Phase 2: Binding Updates (2-3 hours)

  • Add/update bindings idempotently. Checkpoint: bindings match desired port/host header.

Phase 3: Permissions + Audit (2-3 hours)

  • Apply ACLs and write logs. Checkpoint: site loads without 403 errors.

5.11 Key Implementation Decisions

| Decision | Options | Recommendation | Rationale | |———|———|—————-|———–| | Identity | AppPoolIdentity vs custom | AppPoolIdentity | Simpler and secure default | | Binding update | Add only vs replace | Replace incorrect bindings | Ensures convergence | | ACL tool | icacls vs Set-Acl | icacls | Simpler and explicit |


6. Testing Strategy

6.1 Test Categories

| Category | Purpose | Examples | |———-|———|———-| | Unit | Binding parser | desired vs actual binding | | Integration | IIS provisioning | create site in VM | | Edge Case | Permission failure | simulate denied ACL |

6.2 Critical Test Cases

  1. Script is idempotent (run twice, no changes).
  2. Binding update replaces incorrect binding.
  3. Missing path is created and permissions applied.

6.3 Test Data

SiteName=DemoSite
Port=8080
Path=C:\Sites\DemoSite

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

| Pitfall | Symptom | Solution | |———|———|———-| | Incorrect binding | Site not reachable | Update bindings explicitly | | Missing ACLs | 403 Forbidden | Grant app pool access | | Wrong runtime | App fails | Set app pool runtime version |

7.2 Debugging Strategies

  • Use Get-Website and Get-WebBinding to inspect state.
  • Check IIS logs and Windows Event Viewer.

7.3 Performance Traps

  • Repeatedly restarting IIS after each action; batch changes instead.

8. Extensions & Challenges

8.1 Beginner Extensions

  • Add -WhatIf mode.
  • Add logging to a CSV file.

8.2 Intermediate Extensions

  • Add HTTPS binding with certificate thumbprint.
  • Add support for custom app pool identity.

8.3 Advanced Extensions

  • Integrate with DSC for full declarative provisioning.
  • Add rollback script for failed steps.

9. Real-World Connections

9.1 Industry Applications

  • Automated web app deployments.
  • Repeatable staging/prod environment provisioning.
  • IISAdministration tooling patterns.

9.3 Interview Relevance

  • Discuss idempotency, permissions, and IIS configuration.

10. Resources

10.1 Essential Reading

  • PowerShell for Sysadmins – IIS automation.
  • PowerShell in Action – security and ACLs.

10.2 Video Resources

  • “Automating IIS with PowerShell” – Microsoft Learn.

10.3 Tools & Documentation

  • WebAdministration module documentation.

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain how IIS config maps to PowerShell provider.
  • I can define idempotent provisioning.
  • I can apply least-privilege file permissions.

11.2 Implementation

  • Script creates or updates site and app pool.
  • Bindings match desired state.
  • Permissions allow IIS to read content.

11.3 Growth

  • I can extend to HTTPS and custom identities.
  • I can explain this project in an interview.

12. Submission / Completion Criteria

Minimum Viable Completion

  • Script provisions app pool and site with HTTP binding.
  • Permissions allow site to load.

Full Completion

  • Idempotent updates and audit logs.
  • Binding updates and error handling.

Excellence (Going Above & Beyond)

  • HTTPS + certificate binding.
  • DSC integration.

13. Deep-Dive Addendum: IIS Provisioning at Scale

13.1 App Pool Identity and Security

App pools define the security boundary for IIS sites. Decide early which identity to use: built-in ApplicationPoolIdentity is safest for most sites, but some apps require a custom service account. Your provisioner should allow both. If using a custom account, validate that the password is set and the account has Log on as a service rights. Document the required folder permissions explicitly and apply them idempotently. Security misconfiguration is the most common cause of broken IIS deployments.

13.2 Binding Strategy and Certificate Handling

Bindings define how users reach your site. Your tool should support HTTP and HTTPS bindings and handle host headers. For HTTPS, you need a certificate thumbprint and correct store location. The script should validate the certificate exists and is not expired. If you are provisioning many sites, implement a binding policy so port collisions are avoided (for example, allocate from a defined port range or require host headers). Bindings are where most provisioning scripts fail, so treat them as first-class configuration.

13.3 Idempotency Beyond Existence

Idempotency is not just “site exists.” You must check the entire desired state: app pool settings, bindings, physical path, and permissions. If a site exists but settings differ, decide whether to update or stop with a drift report. A safe pattern is to support -Enforce (update in place) and default to “report only”. This keeps you from accidentally disrupting production sites.

13.4 Logging and Rollback

Provisioning should be transactional. Before changes, capture a snapshot of the current IIS configuration for that site and app pool. If provisioning fails, attempt to rollback or at least output the snapshot so a human can restore it. Log all actions with timestamps and include a summary at the end. This makes the tool usable in real environments where change control matters.

13.5 Operational Readiness Checklist

Verify that IIS is installed, the WebAdministration module is available, and required services are running. Confirm that the target folder exists or can be created. Validate that the chosen port is free and that the site name is unique. A script that checks these prerequisites and fails fast saves hours of troubleshooting.