Scenario / Questions

I’m trying to control bindings in an IIS app using powershell. I’d like to create a site with both a http and https binding using a script.

This is what I have thus far:

Param(
    [Parameter(Mandatory=$True,Position=1)]
    [string]$hostname,
    [Parameter(Mandatory=$True,Position=2)]
    [string]$installPath,
    [Parameter(Mandatory=$False,Position=3)]
    [string]$ip
)

Import-Module WebAdministration

$appPoolName =  $hostname + 'Pool'

$port = 80
$hostRecord = $hostname+'.example.com'

$bindings = @{protocol="http";bindingInformation=$ip + ":"+ $port + ":" + $hostRecord}

New-Item IIS:\AppPools\$appPoolName
Set-ItemProperty IIS:\AppPools\$appPoolName managedRuntimeVersion v4.0

New-Item IIS:\Sites\$hostname -Bindings $bindings -PhysicalPath $installPath
Set-ItemProperty IIS:\Sites\$hostname -Name applicationPool -Value $appPoolName

How do I add bindings to my $bindings variable / use some other mechanism to achieve my goal?

Find below all possible solutions or suggestions for the above questions..

Suggestion: 1

You can use New-WebBinding: http://technet.microsoft.com/en-us/library/ee790567.aspx

e.g.

IIS:\>New-WebBinding -Name "Default Web Site" -IPAddress "*" -Port 80 -HostHeader TestSite

Suggestion: 2

I went through the process of trying to add an https binding to a site and it can be pretty painful. There are a lot of ways to accomplish each step and each one has pitfalls. I am leaving behind the final solution hoping that someone will find it useful.

This solution assumes that you have IIS installed and a web site defined. Call the site sample.contoso.com for the purposes of this post. Assume that you have a certificate in a sample.contoso.com.pfx file that you want to use as well.

The first step is to import the certificate from the file.

$certPwd = ConvertTo-SecureString -String "password" -Force -AsPlainText
$webServerCert = Import-PfxCertificate -FilePath c:\some\folder\sample.contoso.com.pfx -CertStoreLocation Cert:\LocalMachine\My -Password $certPwd

It would be nice if that was sufficient. And in some cases it may be. However, for me, this left the certificate without proper access to the private key. This caused a powershell error “A specified logon session does not exist. It may already have been terminated” when I went to add the certificate to the binding (see that step later). So, the next step is to fix up the ACL for the private key.

$privateKeyFilename = $webServerCert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
$privateKeyFullPath = "c:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\"+$privateKeyFilename
$aclRule = "SYSTEM", "Full", "Allow"
$aclEntry = New-Object System.Security.AccessControl.FileSystemAccessRule $aclRule
$privateKeyAcl = (Get-Item $privateKeyFullPath).GetAccessControl("Access")
$privateKeyAcl.AddAccessRule($aclEntry)
Set-Acl $privateKeyFullPath $privateKeyAcl

This will allow local system to have full access to the private key if that is not inherited from the containing folder.

If you want to get a certificate that is already installed, you need the hash for it and can retrieve it with Get-Item like so:

$webServerCert = get-item Cert:\LocalMachine\My\XFX2DX02779XFD1F6F4X8435A5X26ED2X8DEFX95

The next step is to create the binding.

New-WebBinding -Name sample.contoso.com -IPAddress * -Port 443 -Protocol "https"

It is important to note that “https” is case sensitive. If you use “HTTPS” instead, you get a really different binding result.

This binding does not have a certificate attached to it yet, so the last step is to attach the certificate. If the certificate is properly trusted and the security is correct, this step should be successful. It can be finicky if there is any issue with the certificate though.

$bind = Get-WebBinding -Name $webSiteDNSName -Protocol https
$bind.AddSslCertificate($webServerCert.GetCertHashString(), "my")

If this fails with a message about a logon session does not exist, then the certificate may have some issue. Review the event viewer for more details. During my efforts, I found event 5061 in the security log. When it failed, it showed that OpenKey failed with 80090016 (The Keyset Does not Exist). And the failure was because SYSTEM did not have access to the private key.

That was sufficient for me to create the https binding. The http binding was a byproduct of using the New-WebSite cmdlet. If it does not come for free, I didn’t find creating the port 80 binding with the New-WebBinding cmdlet to be a challenge.

Suggestion: 3

I think what you’re after is the following:

$bindings = @(
   @{protocol="http";bindingInformation=$ip + ":"+ $port + ":" + $hostRecord},
   @{protocol="https";bindingInformation=$ip + ":"+ $port + ":" + $hostRecord}
)

Basically you need to pass an array of bindings in. More helpful information here – (http://blogs.iis.net/jeonghwan/iis-powershell-user-guide-comparing-representative-iis-ui-tasks)

(Edit: Fixed typo in array syntax – extraneous comma)