IIS Chef

IIS Chef is an infrastructure-as-code tooling utility for Windows environments, specifically targeted at deploying applications on IIS and Windows.

You describe your application needs (storage, IIS configuration, etc.) on a configuration file that is part of you artifact's/source, and Chef will deploy accordingly.

---------------------

To quickly install the powershell CMDLet in your system with the latest build place the following code in a .bat file and run it:

powershell -command "(New-Object Net.WebClient).DownloadFile('https://ci.appveyor.com/api/projects/David19767/iischef/artifacts/iischef.cmdlet.zip?branch=1.x','%CD%\chef_cmdlet.zip')"
set DESTINATION=%ProgramFiles%\WindowsPowerShell\Modules\Chef
mkdir "%DESTINATION%"
powershell -command "(new-object -com shell.application).namespace('%DESTINATION%').CopyHere((new-object -com shell.application).namespace('%CD%\chef_cmdlet.zip').Items(),16)"
del %CD%\chef_cmdlet.zip

Alternatively, a powershell script:

$WorkingDir = Convert-Path .
$ZipPath = Join-Path $WorkingDir '\chef_cmdlet.zip'
(New-Object Net.WebClient).DownloadFile('https://ci.appveyor.com/api/projects/David19767/iischef/artifacts/iischef.cmdlet.zip?branch=1.x', $ZipPath)
$DESTINATION= Join-Path $env:ProgramFiles "\WindowsPowerShell\Modules\Chef"
New-Item -ItemType directory -Force -Path $DESTINATION
(new-object -com shell.application).namespace($DESTINATION).CopyHere((new-object -com shell.application).namespace($ZipPath).Items(),16)
Remove-Item $ZipPath

To automatically install chef use the following command:

Invoke-ChefSelfInstall

You will be prompted for the install location.

------------------------------------------------------------------------------------------------------------

Windows Chef

What it is

Windows chef is a set of tools that can be run either through command line (powershell cmdlet) or as a windows service that automates the process of deploying applications based on descriptive yml files that are part of the application itself.

Think of this of a cheap self-hosted version of the "infrastructure as code" trend, but it only deals with infrastructure configuration at the application level, not with setting up the infrastructure itself.

You can use this tool to automate application deployment in production environments as well as local development to ensure that the application is deployed in exactly the same way in both places.

It is designed to deploy applications in high density multi-tenant (Shared) resources with as much isolation as possible. So you can host as many applications as you want on a single web server sharing the same IIS, SQL Server and other resources.

The tool can also be used to automate modern devops practices such as "environment-per-branch".

Is this tool helpful now that Windows Containers are a deployment trend?

Sure. This tool can automate the process of deploying your application inside the container and setting up any required configuration.

Adding deployment information to your application

The next step is to add to your application information regarding "what" services it needs to run. I.e. to run a Drupal site you will need a database backend, temporary, permanent and private disk storage, a caching backend such as couchbase, an site in iis, etc..

To describe this setup you create a folder in your application root called "chef". Inside this folder you can place several configuration files that will be applied by chef depending on the environment:

  • chef.yml: default settings
  • chef__local: setings for an environment called "local"
  • chef__local_1.x: settings only used for and environment called "local" and the "1.x" branch.

Prior to deployment, chef will choose the correct configuration file depending on the current environment and application branch.

Installing an application

To deploy an application on a system you "install" the application, that is, provide the necessary details to chef so that it can obtain the required artifacts for deployment.

Currently Chef only support deploying artifacts from Appveyor or the Filesystem (a local folder).

To install an application from a path you can use the Invoke-ChefAppDeployPath command:

Invoke-ChefAppDeployPath c:\applications\mycode myapplicationname

Once installed, Chef internally stores the information of "where to grab" the artifact from and you can directly use other commands to interact with your application.

For example, to re-deploy an application (if you made any changes to the chef.yml settings file) you can use:

Invoke-ChefAppRedeploy myapplicationname

The previous example is a shortcut to deploy applications from local paths (mostly used in development environments).

The way to properly deploy an application is to create an application definition yml file and register it.

Example for an application using a local deployer (local path):

id: 'php-test'
mount_strategy: '%MOUNTSTRATEGY%'
# Type of downloader/monitor to use,
# currently only appveyor
downloader:
# Currently only "appveyor" and "localpath" supported
 type: 'localpath'
 path: '%PATH%'
runtime_overrides:
 'deployment.custom.setting': 'my_custom_setting'
 'services.contents.mount.files.path': 'c:\\missingdir'

Example for an application using Appveyor to produce artifacts:

id: 'sabentisplus'
# Type of downloader/monitor to use,
# currently only appveyor
downloader:
# Currently only "appveyor" and "localpath" supported
 type: 'appveyor'
 project: 'zzzz'
 username: 'yyyy'
 apitoken: 'xxxx'
 branch: 'trunk'
 publish_regex_filter: ''

Depending on the type of downloader, you might need to specify additional information. I.e. for the AppVeyor downloader you must provide a branch (plus the security credentials).

To install an application from such a configuration file use:

Invoke-ChefAppDeploy c:\myapplications\myappsettings.yml

Deploying a specific version of an application and doing rollbacks

Shit happens. If a faulty application has passed your CI and QA processes and was deployed, yet you need to rollback to a previous version Chef has the tools needed to do so.

By default when calling Invoke-ChefAppRedeploy the downloader will look for the latest available succesful build (if the downloader has this capability, currently only AppVeyorDownloader).

You can specify a specific buildId:

Invoke-ChefAppRedeploy myaplicationid -Force -BuildId "1.0.233"

Once you have forced a deployment with a specific buildId, only doing specific buildId deployments with the -Force option will work. That means that automatic deployments will be locked until you release the verson constraint.

To release this constraint specify the literal "latest" as the buildId:

Invoke-ChefAppRedeploy myaplicationid -Force -BuildId "latest"

After doing so, automatic deployments to latest successful builds will be restored.

You must understand that going back to previous builds of an application can completely break it (i.e. a newer version alters a database schema and the old version is not prepared to deal with it).

Application environment settings

Once your application is deployed, it needs to know "where" and "how" to access the requested services. I.e. if you requested an SQL Server database, you need to know the credentials, host and other relevant information to make the connection.

All this information is stored in a JSON key-value pair file that can be accessed in several ways.

A file is written to the web root with the name "chef-runtime.path". This is a plain text file that points to a JSON file on the file system containing the deployment settings, such as:

{
  "environment.enable_mail_verification": "true",
  "environment.mail_send_enable": "true",
  "environment.mail_send_redirect": null,
  "iis.local.bindings.default.url": "http://xxx",
  "iis.public.bindings.default.url": "http://xxx",
  "iis.cdn.bindings.cdn.cdn.url": "http://xxx",
  "iis.cdnssl.bindings.cdn.cdnssl.url": "http://xxx",
  "iis.local.bindings.cdn.local.url": "http://xx",
  "deployment.shortId": "chf_upcplus_sfk",
  "deployment.appPath": "D:\\_Webs\\chf_upcplus_sfk\\app",
  "deployment.logPath": "E:\\ChefApplicationData\\Logs\\upcplus",
  "deployment.tempPath": "E:\\ChefApplicationData\\Temporary\\upcplus",
  "installedApp.id": "upcplus",
  "installedApp.branch": null,
  "services.disk.contents.mount.files.path": "E:\\ChefApplicationData\\Contents\\store_upcplus\\files",
  "services.disk.contents.mount.private.path": "E:\\ChefApplicationData\\Contents\\store_upcplus\\private",
  "services.disk.contents.mount.temp.path": "E:\\ChefApplicationData\\Contents\\store_upcplus\\temporary",
  "services.sqlsrv.default.username": "chf_upcplus_default",
  "services.sqlsrv.default.password": "5e603bd3-f4e6-4fac-9734-0d0fa2cb4576",
  "services.sqlsrv.default.database": "chf_upcplus_default",
  "services.sqlsrv.default.host": "192.168.3.2",
  "services.couchbase.default.uri": "couchbase://192.168.3.2",
  "services.couchbase.default.bucket-name": "couch",
  "services.couchbase.default.bucket-password": "couch"
}

You can parse this file from within your application and use the information as needed.

Another possibility is to have Chef replace these key-value pairs in any file you instruct upon deployment.

You can for example have a config.inc.template.php file in your project such as:

;;;;;;;;;;;;;;;;;;;;;
; Database Settings ;
;;;;;;;;;;;;;;;;;;;;;

[database]

driver = mysql
host = "{orpjournal.database.host}"
username = "{orpjournal.database.username}"
password = "{orpjournal.database.password}"
name = "{orpjournal.database.database}"

Then on the your chef settings file instruct the app deployer to perform the replacements and rename the file:

- type: 'app'
  configuration_replacement_files:
   'config.inc.template.php': 'config.inc.php'

This will load the contents of "/config.inc.template.php", replace any matching values from the runtime settings and save the resulting file to "/config.inc.php"

Managing applications

To see a list of deployed application in your system use:

Invoke-ChefAppFind

This will list (and also provide a powershell array) all the applications installed.

You can trigger redeployment of a single application like this:

Invoke-ChefAppRedeploy myapplication

Note that downloaders (such as Appveyor) will NOT redeploy an application unless a new artifact is available.

To force deployment use:

Invoke-ChefAppRedeploy myapplication -Force

If you don't specify an application name, the Invoke-ChefAppRedeploy command attempt to redeploy all installed applications.

Uninstalling an application

To remove an application from the system, use the following command:

Invoke-ChefAppRemove myapplicationname

Note that removing an application will DELETE any resources allocated to it, including database, disk storage and others.

Installing the windows service

The Chef core is designed to work as a windows service that will attempt to redeploy all installed applications periodically.

To install the service download an extract the binaries to a location in your computer, and run the installer:

iischef.service.exe -install

you can remove the service at any time using:

iischef.service.exe -uninstall

Service startup issues will be logged to system as an Application Event type.

Merging Chef configuration files or handling settings per environment

In the same repository you can have multiple chef configuration files, and have them combined or applied depending on the deployment environment or branch.

At the application level you can limit the scope of a chef file using regular expressions, use these options at the root of your configuration file:

scope-branch-regex: '.*'
scope-environment-regex: '.*'
scope-weight: 0
inherit: default

Internally what the Chef deployer will do is scan all chef configuration files, grab all the ones that either match the current environment or the current branch, sort them by scope weight and the merge them using an additive strategy.

In example, to add debugging support to a PHP development environment we simply need to add a few extra lines to an environment specific chef file:

# Only use this for environment with name "local"
scope-environment-regex: 'local.*'
inherit: default
deployers:
  php:
    runtime:
      - {type: 'ini', multivalue: true, 'key':zend_extension, 'value':php_xdebug.dll}
      - {type: 'ini','key':opcache.revalidate_freq , 'value': 5}
      - {type: 'ini','key':opcache.validate_timestamps , 'value': On}

The available configuration options are:

  • scope-branch-regex (Default to all): What branches this configuration is applied to.
  • scope-environment-regex (Default to all): What environments this configuration is applied to. An environment is the ID of a Chef Installation on a server set on the global chef configuration file.
  • scope-weight: Allows you to control what order are the chef settings files merged
  • inherit: Let's you break the inheritance behavior, letting a specific chef configuration file override any other less specific configuration. Currently only supports the "break" keyword. When used, any higher level configuration will be dismissed (use scope-weight to control levels).

NOTE: There is no way to "clear" parent configurations partially.

Mirroring applications

One of the advantages of fully automated deployment processes is that it makes it easy to spin up cloned environments that can easily sync contents and databases between environments.

To spin up an application instance that "inherits" from another application, use the inherit keyword and specify the parent application id:

inherit: 'upcplus-production'
id: 'upcplus-dev'
mount_strategy: 'move'
downloader:
 type: 'appveyor'
 project: 'drupal7'
 username: '---'
 apitoken: '---'
 branch: 'b-upcplus-dev'

In this case, we are creating a mirror application of "upcplus-production". To all effects, these are independent applications (you need to ensure there are no hostname or other resource collisions by using specific chef configuration files). The only "relationship" between both applications is that persistent storage (database and disk) will by "syncronized". This synchronization takes place automatically when first installing the application. Any future synchronizations must be manually triggered through a command:

Invoke-ChefAppSync upcplus-dev