Ok, so it's another non-Flex-related post, but I've been doing a lot of .NET work lately, so I'm going to sneak a bit of it in here.
I finally solved the issue I was having setting HTTPS port bindings in InstallShield 2011.
First off, you can't do this via the InstallShield UI for IIS 7. There's an option "SecureBindings" under "Other IIS Properties" in the InstallShield website UI, but this only works for IIS 6. If you want pure, unadulterated IIS 7 support, you need a custom action (CA) that manually sets the port using InstallScript or VBScript.
Caveat emptor!! I am not a windows installer developer. It took me a lot of trial and error to get this going.
If you have a suggestion of a more correct or nicer way of doing things, please leave me a comment.
In the meantime, in the hope that it will save someone else from having to do the same thing, here is what I eventually came up with:
Create an InstallScript CA, called SetSecurePortBindings
This CA will need to be "Deferred" in order to run after the IIS installation is done.
In deferred mode, you won't have access to any non-public installer properties, so if you're setting your ports from properties (e.g. [MYSITE_HTTPS_PORT]) that you set from a config or user dialog input, you'll need to make sure that you store any props you need in your deferred CA in the CustomActionData property for that CA.
I scheduled the custom action After InstallServices but any time after IIS install should be fine. My CA calls the SetSecurePortBindings function.
SetSecurePortBindings then makes a call to ensure that the current IIS version is not IIS 6 (we only support IIS6 and 7). If it is, the function will return, as the values will be set via the SecureBindings entry. If not, the function proceeds to read the site names and port numbers from the CustomDataAction. As I have a number of sites to handle, I have passed a delimited list of properties and am using the string tokeniser function to parse them out into a list. Then, for each SiteName;PortBinding pair, I call the SetSecurePortBinding(hMSI, siteName, httpsPort) function.
export prototype SetSecurePortBindings(HWND);
prototype SetSecurePortBinding(HWND, STRING, STRING);
prototype int IISRTGetIISVersion();
/**
* Sets secure http port bindings using AppCmd.exe. Required for IIS7.
* This script should be called from a Deferred Custom Action (After InstallServices in my case) and therefore
* will need to extract any installer properties from the CustomActionData property.
* The CustomActionData property is populated via a property-set CA (After InstallInitialize),
* which sets the values of the properties that need to be passed along into a property by the
* same name as the utilizing CA.
*/
function SetSecurePortBindings(hMSI)
STRING customActionData, name, httpsPort;
LIST listProps;
NUMBER nvSize, nResult, nTokenCount, nCount;
int nIISVersion;
begin
SprintfMsiLog("SetSecurePortBindings: %s", "Entered SetSecurePortBindings");
if MAINTENANCE != TRUE
then
nvSize = 256;
SprintfMsiLog("SetSecurePortBindings: %s", "Non-maintenance operation, proceeding with CA");
// If IIS, return, this will be handled by the SecureBindings IIS6 Metabase compatibility
// module
nIISVersion = IISRTGetIISVersion() ;
SprintfMsiLog("SetSecurePortBindings: Detected IIS version '%i'", nIISVersion);
if (nIISVersion == 6) then
SprintfMsiLog("SetSecurePortBindings: IIS6 detected. Returning, as, this will be handled by the SecureBindings IIS6 metabase entry", "");
return ERROR_SUCCESS;
endif;
// CustomActionData stores the sites and https ports to be configured, in a ; delimited list
// e.g. Sitename1;*:443:;Sitename2;*:444:
MsiGetProperty (hMSI, "CustomActionData", customActionData, nvSize);
// Split the properties on the semi-colon delimiter
listProps = ListCreate (STRINGLIST);
if (StrGetTokens (listProps, customActionData, ";") > 0) then
SprintfMsiLog("SetSecurePortBindings: Failed to get tokens from CustomActionData content %s", customActionData);
else
nTokenCount = ListCount(listProps);
SprintfMsiLog("SetSecurePortBindings: Found %i tokens in CustomActionData content %s", nTokenCount, customActionData);
// Iterate through each block of two (name;port) and call the SetSecurePortBinding method
nCount = 0;
while (nCount < nTokenCount)
if (nCount == 0) then
ListGetFirstString(listProps,name);
else
ListGetNextString(listProps,name);
endif;
ListGetNextString(listProps,httpsPort);
SetSecurePortBinding(hMSI, name, httpsPort);
nCount = nCount + 2;
endwhile;
endif;
else
SprintfMsiLog("SetSecurePortBindings: %s", "Maintenance operation. Skipping...");
endif;
end;
prototype SetSecurePortBinding(HWND, STRING, STRING);
prototype int IISRTGetIISVersion();
/**
* Sets secure http port bindings using AppCmd.exe. Required for IIS7.
* This script should be called from a Deferred Custom Action (After InstallServices in my case) and therefore
* will need to extract any installer properties from the CustomActionData property.
* The CustomActionData property is populated via a property-set CA (After InstallInitialize),
* which sets the values of the properties that need to be passed along into a property by the
* same name as the utilizing CA.
*/
function SetSecurePortBindings(hMSI)
STRING customActionData, name, httpsPort;
LIST listProps;
NUMBER nvSize, nResult, nTokenCount, nCount;
int nIISVersion;
begin
SprintfMsiLog("SetSecurePortBindings: %s", "Entered SetSecurePortBindings");
if MAINTENANCE != TRUE
then
nvSize = 256;
SprintfMsiLog("SetSecurePortBindings: %s", "Non-maintenance operation, proceeding with CA");
// If IIS, return, this will be handled by the SecureBindings IIS6 Metabase compatibility
// module
nIISVersion = IISRTGetIISVersion() ;
SprintfMsiLog("SetSecurePortBindings: Detected IIS version '%i'", nIISVersion);
if (nIISVersion == 6) then
SprintfMsiLog("SetSecurePortBindings: IIS6 detected. Returning, as, this will be handled by the SecureBindings IIS6 metabase entry", "");
return ERROR_SUCCESS;
endif;
// CustomActionData stores the sites and https ports to be configured, in a ; delimited list
// e.g. Sitename1;*:443:;Sitename2;*:444:
MsiGetProperty (hMSI, "CustomActionData", customActionData, nvSize);
// Split the properties on the semi-colon delimiter
listProps = ListCreate (STRINGLIST);
if (StrGetTokens (listProps, customActionData, ";") > 0) then
SprintfMsiLog("SetSecurePortBindings: Failed to get tokens from CustomActionData content %s", customActionData);
else
nTokenCount = ListCount(listProps);
SprintfMsiLog("SetSecurePortBindings: Found %i tokens in CustomActionData content %s", nTokenCount, customActionData);
// Iterate through each block of two (name;port) and call the SetSecurePortBinding method
nCount = 0;
while (nCount < nTokenCount)
if (nCount == 0) then
ListGetFirstString(listProps,name);
else
ListGetNextString(listProps,name);
endif;
ListGetNextString(listProps,httpsPort);
SetSecurePortBinding(hMSI, name, httpsPort);
nCount = nCount + 2;
endwhile;
endif;
else
SprintfMsiLog("SetSecurePortBindings: %s", "Maintenance operation. Skipping...");
endif;
end;
/**
* Returns IIS version detected via registry key
*/
function int IISRTGetIISVersion()
NUMBER nResult, nVersion, nType, nSize;
STRING szName, szValue;
begin
SprintfMsiLog("SetSecurePortBindings: %s", "Attempting to find IIS major version");
RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE );
szName = "MajorVersion";
nSize = -1;
nType = REGDB_NUMBER;
nResult = RegDBGetKeyValueEx("SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters", szName, nType, szValue, nSize);
if (ISERR_SUCCESS != nResult) then
return nResult;
endif;
nResult = StrToNum(nVersion, szValue);
if (ISERR_SUCCESS != nResult) then
return nResult;
endif;
RegDBSetDefaultRoot(HKEY_CURRENT_USER );
return nVersion;
end;
* Returns IIS version detected via registry key
*/
function int IISRTGetIISVersion()
NUMBER nResult, nVersion, nType, nSize;
STRING szName, szValue;
begin
SprintfMsiLog("SetSecurePortBindings: %s", "Attempting to find IIS major version");
RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE );
szName = "MajorVersion";
nSize = -1;
nType = REGDB_NUMBER;
nResult = RegDBGetKeyValueEx("SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters", szName, nType, szValue, nSize);
if (ISERR_SUCCESS != nResult) then
return nResult;
endif;
nResult = StrToNum(nVersion, szValue);
if (ISERR_SUCCESS != nResult) then
return nResult;
endif;
RegDBSetDefaultRoot(HKEY_CURRENT_USER );
return nVersion;
end;
/**
* Calls AppCmd.exe to set https port bindings
*/
function SetSecurePortBinding(hMSI, svSiteName, svHttpsBinding)
STRING strMsg, svCall, svCmd, svProg, svParms[500], svOut;
NUMBER nResult, nLineNum, n;
begin
SprintfMsiLog("SetSecurePortBindings: Attempting to set port bindings for site '%s' to %s", svSiteName, svHttpsBinding);
// IIS 7 setup
// Check for the correct application pool first
svCmd = "cmd";
svProg = " /C " + WINDIR ^ "system32" ^ "inetsrv" ^ "appcmd.exe";
svCall = " set site /site.name: "+svSiteName+" /+bindings.[protocol='https',bindingInformation='"+svHttpsBinding+"']";
svParms = svProg + svCall;
SprintfMsiLog("SetSecurePortBindings: Executing 'cmd %s'", svParms);
nResult = LaunchAppAndWait(svCmd, svParms, WAIT | LAAW_OPTION_HIDDEN);
if ( nResult == -1 ) then
SprintfMsiLog("SetSecurePortBindings: Failed to set set binding. Return value %i", nResult);
return nResult;
endif;
SprintfMsiLog("SetSecurePortBindings: %s", "Binding successfully set");
return ERROR_SUCCESS;
end;
* Calls AppCmd.exe to set https port bindings
*/
function SetSecurePortBinding(hMSI, svSiteName, svHttpsBinding)
STRING strMsg, svCall, svCmd, svProg, svParms[500], svOut;
NUMBER nResult, nLineNum, n;
begin
SprintfMsiLog("SetSecurePortBindings: Attempting to set port bindings for site '%s' to %s", svSiteName, svHttpsBinding);
// IIS 7 setup
// Check for the correct application pool first
svCmd = "cmd";
svProg = " /C " + WINDIR ^ "system32" ^ "inetsrv" ^ "appcmd.exe";
svCall = " set site /site.name: "+svSiteName+" /+bindings.[protocol='https',bindingInformation='"+svHttpsBinding+"']";
svParms = svProg + svCall;
SprintfMsiLog("SetSecurePortBindings: Executing 'cmd %s'", svParms);
nResult = LaunchAppAndWait(svCmd, svParms, WAIT | LAAW_OPTION_HIDDEN);
if ( nResult == -1 ) then
SprintfMsiLog("SetSecurePortBindings: Failed to set set binding. Return value %i", nResult);
return nResult;
endif;
SprintfMsiLog("SetSecurePortBindings: %s", "Binding successfully set");
return ERROR_SUCCESS;
end;
Hey growlybear,
ReplyDeleteHow do i set the CustomActionData Property? I'm having trouble finding out how to do this from within installshield
For those looking for an answer to the above the following link will help...
ReplyDeletehttp://blogs.flexerasoftware.com/installtalk/2011/06/it-wants-me-to-do-what-some-notes-on-customactiondata-in-installshield.html
Hey growlybear,
ReplyDeleteDid you know if its possible to add multiple bindings to an IIS website via Installshield 2018 UI (or at runtime)