Post-deployment configuration for Azure Web Apps

There are many tutorials on the web that describe more or less automated ways of deploying packaged software to Azure Web Apps. For this particular one I’ve decided to use the popular Content Management System WordPress.

What many such applications have in common is that there is an initial installation procedure, consisting of copying files to a web host and configuring database access, followed by a web-based graphical installer that sets up all of the application configuration.

In the case of WordPress, this is known as the “famous five minute installation process”.

The famous five-minute WordPress installation process

The famous five-minute WordPress installation process

I’ve often wondered whether it would be possible to combine provisioning the resources in Azure, deploying the web site assets, and running through the post-deployment configuration in a single step. It’s quite common for such applications to provide a command-line interface for managing the system, and WordPress is no exception. The WordPress CLI is known as WP-CLI. WP-CLI isn’t included in the main WordPress distribution and must be downloaded separately. WP-CLI can do most actions that can be done through the WordPress admin UI, as well as a few that can’t.

Before going any further, it’s worth pointing out that this is probably not the best way to install and configure WordPress for your personal website1 . WordPress isn’t really the point of this discussion, I’m just using it as an example of a packaged web application with a graphical installer.

The script

Initial installation

The main script is a shell script that uses the Azure CLI to create and configure resources. You’ll need to connect with az login or equivalent before running it. There are a couple of other dependencies that may not be present in all Linux distributions, notably expect, but you should be able to install these with your package manager. I’ve only tested this on Fedora Silverblue, but there’s no reason why it shouldn’t work in bash on OSX, WSL, or even a build agent, which is probably where you want to be doing things like this.

The script creates all the required resources, namely an App Service Plan, a Web App, and a MariaDB server and database. All the config information required by WordPress is stored in AppSettings in the Web App. In “real life”, you’d want to use Azure Key Vault references for many of these app settings, as under the current arrangement the secret values will all be echoed to the console, but I’ve omitted the key vault here in the interest of brevity.

All the settings are read from environment variables, if you have a file named .env in the same folder as the script then its contents will be sourced. I have included a .env.example file with all the variables the script requires.

The WordPress code is downloaded in a zip file and deployed with az webapp deployment source config-zip, which deploys directly to the site. There is an app setting project that specifies which subfolder of the zip file we want to extract.

Post-deployment configuration

Rather than sshing directly to the Web App instance, the script attempts to use az webapp create-remote-connection to connect to the running app. This isn’t particularly reliable, but I’ve attempted to work around the worst symptoms in the script. These workarounds involve a combination of waiting for things to start, killing processes by name to clear up old connections, and other classic techniques. It does appear there are fixes in this area of Azure CLI in development. My workarounds still aren’t 100% reliable, the ssh tunnel sometimes fails, but will generally work the second time the script is run. Any suggestions for improving this would be welcome.

Having created the tunnel, we need to ssh with a password, which is set in the web app image. Searching StackOverflow for techniques to automate password-based ssh will uncover a large number of suggestions that this is a bad idea, some more polite than others.

In our case, since we have no alternative, we use expect to automate the sending of the ssh command and responding to the Password: prompt. We also specify a couple of extra ssh options with -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" to suppress error messages about unknown hosts.

Once ssh is working, we upload a wp-config.php file which is pretty similar to the boilerplate one, but with changes to enable SSL between the browser and the site, as well as between the site and the database2. The configuration values which would be hard-coded in this file in a “classic” WordPress installation are also moved out to environment variables - created by App Settings in the web app - and read dynamically.

Since the MariaDB server is only accessible from Azure IPs, we ssh again to use the mysql client provided inside the web app to log in and configure the WordPress database and user.

Finally we download the WP-CLI .phar and place it in the path. By convention the downloaded file is renamed to wp. The wp client can then be used to configure our new installation. By far the most difficult part of this script was getting the quoting and escaping right for strings that are parsed by bash, by expect, by bash again, then by WP-CLI (i.e. php), and then by the WordPress API. This is the reason the expect interactions are broken out into three separate blocks.


At the end of the script, the site should look like this:

The famous zero-minute WordPress installation

The famous zero-minute WordPress installation

I’ve created a multi-file gist with the main script, along with the wp-config.php and .env.example files. You can clone the whole repo with

git clone some_folder

  1. If you’ve stumbled on this page whilst searching for instructions on how to set up WordPress for your personal website, you could do worse than looking at They do all kinds of things, including WordPress hosting. If you sign up through this link you get some kind of discount, and they give me a small credit on my existing bill. This site isn’t hosted there, but I have others that are, and all my domains are registered with them. ↩︎

  2. Although WP-CLI can create this config file, it seems that it can’t create the file until it has connected to the database, but it can’t connect to the database until it has read the SSL options from the file. ↩︎