This Docker image can be used to easily provision an Azure environment to host a Pimcore solution, leveraging Docker and Container Apps.
The topography of the resulting environment will look like (assuming all resources are declared within the same Resource Group):
Follow these steps to provision an environment for the first time:
- Pull the image and run it with either
docker run
ordocker-compose
. Withcompose
, use something like the following:services: pimcore-azure-provisioning: image: ghcr.io/torqit/pimcore-azure-provisioning:latest volumes: # Necessary for running Docker commands within the container - /var/run/docker.sock:/var/run/docker.sock # Volume mount in your parameter file as needed - copy this from stub.parameters.json and # fill in your preferred values - ./azure/parameters.json:/azure/parameters.json:rw # You may also want to declare per-environment files like so - ./azure/parameters.dev.json:/azure/parameters.dev.json:rw - ./azure/parameters.prod.json:/azure/parameters.prod.json:rw environment: # These vars are required so that the scripts can properly tag and # push the necessary images to Azure. Ensure these images are built # and set the values here to match the image names (can be found by # running docker image ls). - LOCAL_PHP_FPM_IMAGE=${LOCAL_PHP_FPM_IMAGE} - LOCAL_SUPERVISORD_IMAGE=${LOCAL_SUPERVISORD_IMAGE} - LOCAL_REDIS_IMAGE=${LOCAL_REDIS_IMAGE}
- Update
parameters.json
with the appropriate values for your Azure environment. Note that the comments present instub.parameters.json
will need to be removed. Note that you will also need to remove the parameters related to custom domains and certificates (see section below) for the initial provisioning. - Enter the container shell with
docker exec -it <container-name> bash
. - Run
./login-to-tenant.sh parameters.json
and follow the browser prompts to log in. If you wish to use a Service Principal instead of your Microsoft account to perform the provisioning, instead runaz login --service-principal -u <service principal id> -p <service principal password> --tenant <your tenant>
. - If a Resource Group and Service Principal have not yet been created (e.g. if you are not an Owner in the Azure tenant), run
initialize-resource-group-and-service-principal.sh parameters.json
. Once complete, note down theappId
andpassword
that are returned from the creation of the Service Principal (the app ID is the service principal ID). The service principal can then be used in your CI/CD pipeline. Note that in order to continue, the user account you are using to run the rest of the steps should have the Owner role in the created Resource Group. - Run
./create-key-vault.sh parameters.json
to create a Key Vault in your Resource Group. Once created, navigate to the created Key Vault in the Azure Portal and use the "Access control (IAM)" blade to add yourself to the "Key Vault Secrets Officer" role (the Owner role at the Resource Group will allow you to do this; but it is not itself sufficient to actually manage secrets). Additionally, make sure the Key Vault is using a "Role-based Access Policy" in the "Access configuration" blade. Make up a secure database password and add it as a secret to this vault using either the Azure Portal or CLI (make sure thedatabasePasswordSecretName
value matches the secret name in the vault). Add any other secrets your Container App will need to this vault as well (seestub.parameters.jsonc
for details on how to reference these).- NOTE: There is an open issue to improve the Key Vault scripting (see #50)
- Run
./provision.sh parameters.json
to provision the Azure environment. - Once provisioned, follow these steps to seed the database with the Pimcore schema:
- Make up a secure password that you will use to log into the Pimcore admin panel and save it somewhere secure such as a password manager, or within the key vault you created earlier. Note that symbols such as % and # will not work with the bash command below, so a long alphanumeric password should be used.
- Ensure that your PHP-FPM image contains the SSL certificate required for communicating with the database (can be downloaded from https://dl.cacerts.digicert.com/DigiCertGlobalRootCA.crt.pem). The command below assumes the file is present at
/var/www/html/config/db/DigiCertGlobalRootCA.crt.pem
. Additionally, your Symfony database connection string (usually present inconfig/database.yaml
) must be configured to use the certificate (e.g.options: !php/const:PDO::MYSQL_ATTR_SSL_CA: '/var/www/html/config/db/DigiCertGlobalRootCA.crt.pem'
). If this is not properly set, the command below will fail with "Connections using insecure transport are prohibited". - Run
az containerapp exec --resource-group <your-resource-group> --name <your-php-fpm-container-app> --command bash
to enter the Container App's shell. - Run the following command to seed the database:
runuser -u www-data -- vendor/bin/pimcore-install \ --admin-username=admin \ --admin-password=<secure admin password> \ --mysql-host-socket=$DATABASE_HOST \ --mysql-database=$DATABASE_NAME \ --mysql-username=$DATABASE_USER \ --mysql-password=$DATABASE_PASSWORD \ --mysql-ssl-cert-path=config/db/DigiCertGlobalRootCA.crt.pem \ --skip-database-config # If you are still on Pimcore 10.x, add the --ignore-existing-config flag
Container Apps support custom domains and Azure-managed HTTPS certificates, but since they require some manual interaction with your DNS, it is best to configure them manually in your initial provisioning. Use this repository to manage these as follows:
- For the initial provisioning, leave the
phpFpmContainerAppCustomDomains
array blank, like so:"phpFpmContainerAppCustomDomains": { "value": [ ] },
- Once your environment is provisioned, go to https://portal.azure.com and navigate to your PHP-FPM Container App.
- In the left-hand menu, click "Custom Domains". Click "Add", select the "Managed Certificate" option, and follow the instructions for adding a custom domain to your DNS.
- Once complete, you should be able to access your Container App at the configured custom domain, and it should be secured with HTTPS.
- Add the custom domain and certificate to the
phpFpmContainerAppCustomDomains
parameter in yourparameters.json
file like so:This will ensure these settings are maintained whenever you deploy infrastructure updates. The certificate name can be found by going to the Container Apps Environment, clicking "Certificates", and copying the value in the "Friendly name" column."phpFpmContainerAppCustomDomains": { "value": [ { "domainName": "my-domain.example.com" "certificateName": "my-certificate" } ] }
The provisioning script will automatically configure the following backups:
- Point-in-time snapshots of the database. Retention of these snapshots is controlled by the
databaseBackupRetentionDays
parameter. - Point-in-time snapshots of the Storage Account (which contains persistent Pimcore files such as assets). Retention of these snapshots is controlled by the
storageAccountBackupRetentionDays
parameter. - Long-term backups of the database. As Azure Database for MySQL does not have built-in support for long-term backups, this image uses a custom solution using https://github.com/TorqIT/pimcore-database-backup-bundle to store backups in a Storage Account configured by the
databaseBackupsStorageAccount*
parameters. - Long-term backups of the Storage Account. The provisioning script will automatically create a Backup Vault that stores monthly backups of the containers. These backups are retained for up to one year.
Note that all backups are stored using Local Redundancy (see https://learn.microsoft.com/en-us/azure/storage/common/storage-redundancy#locally-redundant-storage for more information).
See https://github.com/TorqIT/pimcore-github-actions-workflows for examples of GitHub Actions workflows that can be used to deploy to Container Apps, in particular the container-apps-*.yml
files.
Bicep files are declarative, meaning that they declare the desired state of your resources. This means that you can deploy using the same files multiple times, and only the new changes that you've made will be applied. If you wish to change any resource names or properties, simply update them in your parameters.json
file and re-run ./provision.sh parameters.json
. Keeping the parameters.json
files committed in your source control is a good practice as it will allow you to maintain a snapshot of your environment's state.
When adding/updating/removing Container Apps secrets for the PHP-FPM container, you will need to deactivate any active revisions that are using the existing secrets (Azure will throw an error if you do not first deactivate). To deactivate the active revisions, enter this container and run ./scripts/deactivate-php-fpm-container-app-revisions.sh parameters.json
.
Once an environment has been provisioned, the scripts/
directory contains some useful scripts that can be run against the running environment (see its README).