Skip to main content

How to Create a New Website in Internet Information Services (IIS) from Batch Script

Provisioning new websites in IIS manually through the graphical IIS Manager works fine for one-off setups, but it quickly becomes unsustainable when you need to deploy dozens or hundreds of sites across multiple servers. Automating website creation via Batch Script ensures consistency, eliminates human error, and integrates cleanly into deployment pipelines.

In this guide, we will explore how to use the appcmd.exe command-line tool to create fully configured IIS websites from a Batch Script, including setting bindings, physical paths, application pools, and host headers.

Prerequisites

  • IIS must be installed: The appcmd.exe utility is only available when the IIS role is enabled.
  • Administrator privileges: Creating websites requires elevation.
  • appcmd.exe path: The tool is located at %SystemRoot%\System32\inetsrv\appcmd.exe and is not in the default system PATH.

Method 1: Creating a Basic HTTP Website

The simplest website requires just three things: a name, a physical path for the files, and an HTTP binding (IP, port, hostname).

@echo off
setlocal enabledelayedexpansion

:: -------------------------------------------------------
:: Verify Admin Privileges
:: -------------------------------------------------------
net session >nul 2>&1
if !errorlevel! neq 0 (
echo.
echo [ERROR] Administrator privileges required.
echo Right-click this script and select "Run as administrator".
echo.
pause
exit /b 1
)

echo.
echo [INFO] Admin privileges confirmed.

:: -------------------------------------------------------
:: Configuration
:: -------------------------------------------------------
set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"
set "site_name=MyNewSite"
set "site_path=C:\inetpub\sites\mynewsite"
set "binding=http/*:8080:"

:: -------------------------------------------------------
:: Verify appcmd exists
:: -------------------------------------------------------
if not exist "%appcmd%" (
echo.
echo [ERROR] appcmd.exe not found at: %appcmd%
echo IIS may not be installed or Management Tools are missing.
echo.
pause
exit /b 1
)

echo [INFO] appcmd.exe found.

:: -------------------------------------------------------
:: Check if the site already exists
:: -------------------------------------------------------
"%appcmd%" list site /name:"%site_name%" >nul 2>&1
if !errorlevel! equ 0 (
echo.
echo [INFO] Website "%site_name%" already exists. No changes made.
echo.
pause
exit /b 0
)

:: -------------------------------------------------------
:: 1. Create the physical directory
:: -------------------------------------------------------
if not exist "%site_path%" (
mkdir "%site_path%"
if !errorlevel! neq 0 (
echo [ERROR] Failed to create directory: %site_path%
pause
exit /b 1
)
echo [OK] Created directory: %site_path%
) else (
echo [OK] Directory already exists: %site_path%
)

:: -------------------------------------------------------
:: 2. Create a default page
:: -------------------------------------------------------
> "%site_path%\index.html" echo ^<html^>^<body^>^<h1^>%site_name% is working!^</h1^>^</body^>^</html^>
echo [OK] Created default index.html

:: -------------------------------------------------------
:: 3. Create the website in IIS
:: -------------------------------------------------------
echo [INFO] Creating website "%site_name%" ...
echo.

"%appcmd%" add site /name:"%site_name%" /physicalPath:"%site_path%" /bindings:%binding%

if !errorlevel! equ 0 (
echo.
echo [SUCCESS] Website "%site_name%" created on port 8080.
echo Visit: http://localhost:8080
) else (
echo.
echo [ERROR] Failed to create website.
echo The binding may conflict with an existing site.
echo Try: %appcmd% list site
)

echo.
pause
exit /b 0

Understanding the Binding Format

The binding string follows the format: protocol/IPAddress:Port:HostHeader

Binding ExampleMeaning
http/*:80:HTTP, all IPs, port 80, no host header
http/*:8080:HTTP, all IPs, port 8080, no host header
http/*:80:www.example.comHTTP, all IPs, port 80, host header www.example.com
https/*:443:HTTPS, all IPs, port 443 (requires SSL cert)

The asterisk * means "listen on all available IP addresses."

Method 2: Creating a Website with a Specific Host Header

In production, multiple websites share port 80 by using different host headers (domain names). IIS routes incoming requests to the correct site based on the Host header sent by the browser.

@echo off
setlocal enabledelayedexpansion

set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"

:: -------------------------------------------------------
:: Site Configuration
:: -------------------------------------------------------
set "site_name=CompanyBlog"
set "site_path=C:\inetpub\sites\blog"
set "hostname=blog.company.com"
set "binding=http/*:80:%hostname%"

:: -------------------------------------------------------
:: Admin Check
:: -------------------------------------------------------
net session >nul 2>&1
if !errorlevel! neq 0 (
echo.
echo [ERROR] Admin required.
echo Right-click and select "Run as administrator".
echo.
pause
exit /b 1
)

echo [OK] Admin privileges confirmed.

:: -------------------------------------------------------
:: Verify appcmd exists
:: -------------------------------------------------------
if not exist "%appcmd%" (
echo.
echo [ERROR] appcmd.exe not found at: %appcmd%
echo IIS may not be installed.
echo.
pause
exit /b 1
)

echo [OK] appcmd.exe found.

:: -------------------------------------------------------
:: Check for duplicate site name
:: -------------------------------------------------------
"%appcmd%" list site /name:"%site_name%" >nul 2>&1
if !errorlevel! equ 0 (
echo.
echo [INFO] Website "%site_name%" already exists. No changes made.
echo.
pause
exit /b 0
)

:: -------------------------------------------------------
:: Check for binding conflict (port 80 + same hostname)
:: -------------------------------------------------------
"%appcmd%" list site /bindings:"%binding%" >nul 2>&1
if !errorlevel! equ 0 (
echo.
echo [ERROR] Another site is already using binding: %binding%
echo Change the hostname or port to avoid conflict.
echo.
pause
exit /b 1
)

:: -------------------------------------------------------
:: Create directory
:: -------------------------------------------------------
if not exist "%site_path%" (
mkdir "%site_path%"
if !errorlevel! neq 0 (
echo [ERROR] Failed to create directory: %site_path%
pause
exit /b 1
)
echo [OK] Created directory: %site_path%
) else (
echo [OK] Directory already exists: %site_path%
)

:: -------------------------------------------------------
:: Create default page
:: -------------------------------------------------------
> "%site_path%\index.html" echo ^<html^>^<body^>^<h1^>%site_name% is live!^</h1^>^</body^>^</html^>
echo [OK] Created default index.html

:: -------------------------------------------------------
:: Create the site with host header binding
:: -------------------------------------------------------
echo [INFO] Creating website "%site_name%"...
echo Binding: %binding%
echo.

"%appcmd%" add site /name:"%site_name%" /physicalPath:"%site_path%" /bindings:%binding%

if !errorlevel! equ 0 (
echo.
echo [SUCCESS] Site "%site_name%" created successfully.
echo Accessible at: http://%hostname%
echo.
echo [REMINDER] Make sure DNS or hosts file resolves:
echo %hostname% -^> this server's IP
) else (
echo.
echo [ERROR] Failed to create website.
echo Run this to check existing bindings:
echo %appcmd% list site
)

echo.
pause
exit /b 0
info

For the host header to work, the domain blog.company.com must have a DNS record (A or CNAME) pointing to this server's IP address. For local testing, you can add an entry to %SystemRoot%\System32\drivers\etc\hosts:

127.0.0.1 blog.company.com

Method 3: Creating a Website with a Dedicated Application Pool

By default, appcmd add site assigns the new site to the DefaultAppPool. In production, each site should have its own application pool for process isolation, independent recycling, and separate identity credentials.

@echo off
setlocal

set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"

set "site_name=ClientPortal"
set "pool_name=ClientPortalPool"
set "site_path=C:\inetpub\sites\clientportal"
set "hostname=portal.client.com"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

if not exist "%appcmd%" (
echo [ERROR] appcmd.exe not found. IIS may not be installed.
pause
exit /b 1
)

:: 1. Create the directory
if not exist "%site_path%" mkdir "%site_path%"

:: 2. Create a dedicated Application Pool (skip if it already exists)
"%appcmd%" list apppool /name:"%pool_name%" >nul 2>&1
if %errorlevel% equ 0 (
echo [INFO] App Pool "%pool_name%" already exists. Reusing it.
) else (
"%appcmd%" add apppool /name:"%pool_name%" /managedRuntimeVersion:"v4.0" /managedPipelineMode:"Integrated"
if %errorlevel% equ 0 (
echo [OK] App Pool "%pool_name%" created.
) else (
echo [ERROR] Failed to create App Pool "%pool_name%".
pause
exit /b 1
)
)

:: 3. Create the website (skip if it already exists)
"%appcmd%" list site /name:"%site_name%" >nul 2>&1
if %errorlevel% equ 0 (
echo [INFO] Website "%site_name%" already exists.
) else (
"%appcmd%" add site /name:"%site_name%" /physicalPath:"%site_path%" /bindings:http/*:80:%hostname%
if %errorlevel% equ 0 (
echo [OK] Website "%site_name%" created.
) else (
echo [ERROR] Failed to create website "%site_name%".
pause
exit /b 1
)
)

:: 4. Assign the site to the dedicated pool
"%appcmd%" set site /site.name:"%site_name%" /[path='/'].applicationPool:"%pool_name%"
echo [OK] Site "%site_name%" assigned to pool "%pool_name%".

:: 5. Verify
echo.
echo Site Configuration:
"%appcmd%" list site /name:"%site_name%"
"%appcmd%" list apppool /name:"%pool_name%"

pause

Managed Runtime Versions

ValueFramework
v2.0.NET Framework 2.0/3.0/3.5
v4.0.NET Framework 4.x
""No managed code (static files, PHP)

For sites serving only static HTML/CSS/JS or PHP, set the managed runtime to empty:

"%appcmd%" add apppool /name:"%pool_name%" /managedRuntimeVersion:"" /managedPipelineMode:"Integrated"

Method 4: Bulk Site Creation from a Configuration File

For provisioning multiple sites at once, read the site definitions from a text file.

Create a file called sites.txt:

SiteA|C:\inetpub\sites\sitea|www.sitea.com
SiteB|C:\inetpub\sites\siteb|www.siteb.com
SiteC|C:\inetpub\sites\sitec|www.sitec.com

Then process it with a Batch Script:

@echo off
setlocal enabledelayedexpansion

set "appcmd=%SystemRoot%\System32\inetsrv\appcmd.exe"
set "config_file=%~dp0sites.txt"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

if not exist "%appcmd%" (
echo [ERROR] appcmd.exe not found. IIS may not be installed.
pause
exit /b 1
)

if not exist "%config_file%" (
echo [ERROR] Configuration file not found: %config_file%
pause
exit /b 1
)

echo Provisioning sites from %config_file%...
echo.

set "success_count=0"
set "skip_count=0"
set "fail_count=0"

for /f "usebackq tokens=1,2,3 delims=|" %%A in ("%config_file%") do (
set "name=%%A"
set "path=%%B"
set "host=%%C"

:: Validate that all three fields are present
if not defined name (
echo [SKIP] Empty line or missing site name.
set /a "skip_count+=1"
) else if not defined path (
echo [SKIP] !name!: Missing physical path.
set /a "skip_count+=1"
) else if not defined host (
echo [SKIP] !name!: Missing hostname.
set /a "skip_count+=1"
) else (
:: Check if site already exists
"%appcmd%" list site /name:"!name!" >nul 2>&1
if !errorlevel! equ 0 (
echo [SKIP] !name!: Already exists.
set /a "skip_count+=1"
) else (
echo Creating: !name! at !host!...

if not exist "!path!" mkdir "!path!"

"%appcmd%" add apppool /name:"!name!Pool" /managedRuntimeVersion:"v4.0" >nul 2>&1
"%appcmd%" add site /name:"!name!" /physicalPath:"!path!" /bindings:http/*:80:!host! >nul 2>&1

if !errorlevel! equ 0 (
"%appcmd%" set site /site.name:"!name!" /[path='/'].applicationPool:"!name!Pool" >nul 2>&1
echo [OK] !name! provisioned.
set /a "success_count+=1"
) else (
echo [FAIL] !name!: Could not create site. Binding may conflict.
set /a "fail_count+=1"
)
)
)
)

echo.
echo =============================================
echo Results: !success_count! created, !skip_count! skipped, !fail_count! failed.
echo =============================================
echo.
echo All registered sites:
"%appcmd%" list site

pause
tip

The configuration file format uses | as a delimiter because site names, paths, and hostnames can all contain spaces (though hostnames with spaces are invalid, paths commonly have them). If your configuration values might contain the | character, switch to a different delimiter or use a more structured format like CSV with quoted fields.

Common Mistakes

The Wrong Way: Using a Port Already in Use

:: WRONG - Port 80 may already be used by "Default Web Site"
"%appcmd%" add site /name:"MySite" /physicalPath:"C:\web" /bindings:http/*:80:
danger

If another site already binds to *:80: (with no host header), the new site cannot start because two sites cannot share the exact same binding. Either add a host header to differentiate, use a different port, or remove the conflicting binding from the existing site. Use "%appcmd%" list site to check existing bindings before creating new ones.

The Wrong Way: Forgetting the Physical Path

:: WRONG - The directory does not exist yet
"%appcmd%" add site /name:"MySite" /physicalPath:"C:\web\newsite" /bindings:http/*:8080:

While IIS will create the site entry even if the directory does not exist, browsing to the site will return a 500.19 error because IIS cannot find the content root. Always create the directory first with mkdir.

The Wrong Way: Creating Duplicate Sites

:: WRONG - Does not check whether the site already exists
"%appcmd%" add site /name:"MySite" /physicalPath:"C:\web" /bindings:http/*:8080:
:: Running this script a second time produces an error
warning

If a site with the same name already exists, appcmd add site fails with an error. If a site with a different name but the same binding already exists, the new site is created but cannot start. Always check for existing sites by name (appcmd list site /name:"...") and verify bindings are not already in use before creating.

Best Practices

  1. One app pool per site: Never share application pools between production websites. An unhandled exception in one site should not crash another.
  2. Use host headers: Avoid binding multiple sites to unique ports. Host headers are the standard way to serve multiple domains from a single server on port 80/443.
  3. Create the directory first: Ensure the physical path exists before creating the IIS site to avoid 500.19 errors.
  4. Check for existing sites: Always verify that a site with the same name or binding does not already exist before attempting to create one. This makes scripts idempotent and safe to re-run.
  5. Verify after creation: Always run appcmd list site after provisioning to confirm the site was created with the correct bindings and pool assignment.
  6. Verify appcmd.exe exists: Check for the tool before running any commands to provide a clear error message on systems where IIS is not installed.

Conclusion

Creating new websites in IIS from a Batch Script is powered by the appcmd.exe command-line tool. By specifying the site name, physical path, and binding string, administrators can provision fully functional websites in seconds. Pairing each site with a dedicated application pool ensures process isolation, while bulk provisioning from a configuration file scales the approach to handle dozens of sites across server farms with consistent, repeatable results.