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: # The image uses semantic versioning image: ghcr.io/torqit/pimcore-azure-provisioning:1 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 # Define a volume to hold your login information between container restarts - azure:/root/.azure volumes: azure:
- 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 has not yet been created (e.g. if you are not an Owner in the Azure tenant), ensure it is created before running any scripts. Ensure also that you have Owner permissions on 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. - Use whatever method you prefer to push your Docker images to the Container Registry. Refer to the steps in the section below for pushing via CI/CD (GitHub Actions).
- (ONLY REQUIRED IF YOU ARE NOT DEPLOYING AN INIT CONTAINER) Once provisioned and deployed, 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 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-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 \ --no-interaction # If you are still on Pimcore 10.x, add the --ignore-existing-config flag
- On first run of the script, a Service Principal will be created with permissions that will allow it to deploy to your environment via CI/CD workflows. Note down the appId and password returned by this section of the script.
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
phpContainerAppCustomDomains
array blank, like so:"phpContainerAppCustomDomains": { "value": [ ] },
- Once your environment is provisioned, go to https://portal.azure.com and navigate to your PHP 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
phpContainerAppCustomDomains
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."phpContainerAppCustomDomains": { "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. - As Azure Database for MySQL does not have built-in support for long-term backups, the scripts will create a Storage Account configured by the
databaseBackupsStorageAccount*
parameters. A custom solution is then required to write backups of the database to this Storage Account. - 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.
Once an environment has been provisioned, the helper-scripts/
directory contains some useful scripts that can be run against the running environment (see its README).