When hosting HTTP WCF services as a self-hosted Windows Services the server needs to have the HTTP Namespace reserved. The reservation allows for the domain account which runs the service to setup a listener on a particular port, for a particular address.
There are some tools already available which can help in this process:
- HTTP Namespace Manager – A nice GUI interface, which is easy to understand and setup. It also works on Server Core Servers.
 - httpcfg – Windows Server 2003
 - netsh – Windows Server 2008+
 
But, there are no PowerShell wrappers for these commands. So, here’s a wrapper that provides:
- Add-HttpNamespace
 - Get-HttpNamespace
 - Get-HttpNamespaces
 - Test-HttpNamespaceExists
 
There’s no remove because I haven’t needed it yet. A namespace is usually associated with a particular port, and I haven’t been involved in a situation where a port needed to be reused.
<#
.SYNOPSIS
    Parses the output from netsh to turn them in PSObjects.
#>
Function Get-HttpNamespaces {
[CmdletBinding()]
[OutputType([PSObject[]])]
Param()
    # the $propsReady variable causes alot of errors to occur, but the results are accurate.
    # so this helps hide the errors
    $originalErrorAction = $ErrorActionPreference
    $ErrorActionPreference = 'SilentlyContinue'
    try {
        # pull the data from netsh
        $urlaclOutput = . netsh http show urlacl
        # parse the data into PSObjects
        $httpNamespaces = New-Object System.Collections.Generic.List[PSObject]
        $props = @{}
        $userProps = @{}
        $userRdy = $false
        for($i = 0; $i -lt $urlaclOutput.Count; $i++) {
            $line = $urlaclOutput[$i].Trim()
            $split = $line.Split(":", [StringSplitOptions]::RemoveEmptyEntries)
            $first = ""
            if($split.Count -gt 0) { $first = $split[0] }
        
            # line parsing
            switch($first.Trim()) {
                "Reserved URL" {
                    $props.ReservedUrl = $line.Substring(25).Trim()
                    $users = New-Object System.Collections.Generic.List[PSObject]
                }
                "User" {
                    if($userRdy) {
                        $user = New-Object PSObject -Property $userProps
                        $users.Add($user)
                        $userProps = @{}
                        $userRdy = $false
                    }
                    $userProps.User = $split[1].Trim()
                }
                "Listen" { $userProps.Listen = $split[1].Trim() }
                "Delegate" {
                    $userProps.Delegate = $split[1].Trim()
                    $userRdy = $true
                }
                "SDDL" {
                    $userProps.SDDL = $line.Substring(5).Trim()
                    $userRdy = $true
                }
                "" {
                    if($userRdy) {
                        # user
                        $user = New-Object PSObject -Property $userProps
                        $users.Add($user)
                        $userProps = @{}
                        # url
                        $props.Users = $users.ToArray()
                        $cnObj = New-Object PSObject -Property $props
                        $httpNamespaces.Add($cnObj)
                        $props = @{}
                        # reset flag
                        $userRdy = $false
                    }
                }
            }
        }
    } finally {
        $ErrorActionPreference = $originalErrorAction # revert the error action
    }
    return $httpNamespaces.ToArray()
}
<#
.SYNOPSIS
    Retrieves the namespace information for a given namespace. It will also search for namespaces
    which match but the host names have been replaced with + or * symbols.
#>
Function Get-HttpNamespace {
[CmdletBinding()]
[OutputType([PSObject])]
Param (
    [Parameter(Mandatory = $true)]
    [string] $HttpNamespace
)
    $httpNamespaces = Get-HttpNamespaces
    # get * and + versions of the url ready
    $starNamespace = $HttpNamespace
    $plusNamespace = $HttpNamespace
    $namespaceRegex = [regex] "http.*://(.*):.*/.*"
    if($HttpNamespace -match $namespaceRegex) {
        $hostname = $Matches[1]
        $starNamespace = $HttpNamespace.Replace($hostname, "*")
        $plusNamespace = $HttpNamespace.Replace($hostname, "+")
    }
    # sometimes the http namespaces get /'s added to the end
    $namespace = $httpNamespaces |? {
                            $_.ReservedUrl -eq $HttpNamespace `
                    -or     $_.ReservedUrl -eq ($HttpNamespace + '/') `
                    -or     $_.ReservedUrl -eq $starNamespace `
                    -or     $_.ReservedUrl -eq ($starNamespace + '/') `
                    -or     $_.ReservedUrl -eq $plusNamespace `
                    -or     $_.ReservedUrl -eq ($plusNamespace + '/')
                }
    return $namespace
}
<#
.SYNOPSIS
    Checks if a namespace already exists. It will also search if the namespace has had its host name
    replaced with + or * symbols.
#>
Function Test-HttpNamespaceExists {
[CmdletBinding()]
[OutputType([bool])]
Param (
    [Parameter(Mandatory = $true)]
    [string] $HttpNamespace
)
    $namespace = Get-HttpNamespace $HttpNamespace
    return $namespace -ne $null
}
<#
.SYNOPSIS
    Adds a new Http Namespace. This will automatically swap out the host name for a + symbol. The
    + symbol allows the Http Namespace to bind on all NIC addresses.
#>
Function Add-HttpNamespace {
[CmdletBinding()]
[OutputType([PSObject])]
Param (
    [Parameter(Mandatory = $true)]
    [string] $HttpNamespace,
    [Parameter(Mandatory = $true)]
    [string] $DomainAccount
)
    $create = $true
    if(Test-HttpNamespaceExists $HttpNamespace) {
        # it already exists, so maybe not create it
        $create = $false
        $namespace = Get-HttpNamespace $HttpNamespace
        # but, if the given DomainAccount doesn't exist then create it
        $user = $namespace.users |? { $_.user -eq $DomainAccount }
        if($user) {
            Write-Warning "NET $env:COMPUTERNAME - Http Namespace '$HttpNamespace' already contains a rule for '$DomainAccount'. Skipping creation."
            return
        } else {
            $create = $true
        }
    }
    if($create) {
        # the standard pattern to use is http://+:port/servicename.
        #   eg. http://contoso01:15110/EmployeeService would become http://+:15110/EmployeeService
        $plusNamespace = $HttpNamespace
        $namespaceRegex = [regex] "http.*://(.*):.*/.*"
        if($HttpNamespace -match $namespaceRegex) {
            $hostname = $Matches[1]
            $plusNamespace = $HttpNamespace.Replace($hostname, "+")
        } else {
            throw "NET $env:COMPUTERNAME - Http Namespace '$HttpNamespace' could not be parsed into plus format before being added. Plus format " + `
                "looks like http://+:port/servicename. For example, http://contoso01:15110/EmployeeService would be formatted into " + `
                "http://+:15110/EmployeeService."
        }
        # ensure the full domain account name is used
        $fullDomainAccount = Get-FullDomainAccount $DomainAccount
        # create the permission
        Write-Warning "NET $env:COMPUTERNAME - Adding Http Namespace '$Httpnamespace' for account '$fullDomainAccount'"
        $results = . netsh http add urlacl url=$plusNamespace user=$fullDomainAccount listen=yes delegate=yes
        Write-Host "NET $env:COMPUTERNAME - Added Http Namespace '$Httpnamespace' for account '$fullDomainAccount'"
    }
    $namespace = Get-HttpNamespace $HttpNamespace
    return $namespace
}
