%3CLINGO-SUB%20id%3D%22lingo-sub-1598097%22%20slang%3D%22en-US%22%3EMulti-container%20WordPress%20on%20Azure%20deployed%20with%20Azure%20DevOps%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1598097%22%20slang%3D%22en-US%22%3E%3CP%3EA%20fully%20automated%20DevOps%20deployment%20of%20CMS%20WordPress%20on%20Azure%20App%20Services%20in%20a%20Linux%20Multi-Container.%20Setup%20with%20services%20like%3A%20App%20Service%2C%20Azure%20MySQL%2C%20Redis%20Cache%20(in%20a%20container)%2C%20Azure%20CDN%2C%20Azure%20Storage%20and%20Key%20Vault%20(to%20store%20the%20mysqluser%20password).%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EThe%20Github%20repository%3A%26nbsp%3B%3CA%20href%3D%22https%3A%2F%2Fgithub.com%2Fpascalvanderheiden%2Fmulti-container-wordpress-azure%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fgithub.com%2Fpascalvanderheiden%2Fmulti-container-wordpress-azure%3C%2FA%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EThe%20multi-container%20setup%20is%20used%20to%20host%20WordPress%20and%20Redis%20Cache%20in%20one%20App%20Service.%20This%20DevOps%20project%20is%20inspired%20on%20this%20tutorial%3A%3CSPAN%3E%26nbsp%3B%3C%2FSPAN%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fapp-service%2Fcontainers%2Ftutorial-multi-container-app%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fapp-service%2Fcontainers%2Ftutorial-multi-container-app%3C%2FA%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EThe%20Docker%20Compose%20YML's%20are%20based%20on%20these%20samples%3A%3CSPAN%3E%26nbsp%3B%3C%2FSPAN%3E%3CA%20href%3D%22https%3A%2F%2Fgithub.com%2FAzure-Samples%2Fmulticontainerwordpress%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fgithub.com%2FAzure-Samples%2Fmulticontainerwordpress%3C%2FA%3E%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId--1293399477%22%20id%3D%22toc-hId--1293399476%22%3E-ERR%3AREF-NOT-FOUND-Step%20by%20Step%20installation%3C%2FH2%3E%0A%3CH3%20id%3D%22toc-hId--602838003%22%20id%3D%22toc-hId--602838002%22%3E-ERR%3AREF-NOT-FOUND-Step%201%3A%20In%20the%20Azure%20Portal%20create%20a%20Service%20Principal%3C%2FH3%3E%0A%3CP%3EIn%20the%20Azure%20Cloud%20Shell%20(%3CA%20href%3D%22https%3A%2F%2Fshell.azure.com%2F%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fshell.azure.com%3C%2FA%3E%3A(%3C%2Fimg%3E%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3Eaz%20ad%20sp%20create-for-rbac%20--name%20%5Byour-service-principal-name%5D%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3ECopy%20the%20JSON%20Output!%20We'll%20be%20needing%20this%20information%20to%20create%20the%20service%20connection%20in%20Azure%20DevOps.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-1884674830%22%20id%3D%22toc-hId-1884674831%22%3E-ERR%3AREF-NOT-FOUND-Step%202%3A%20Generate%20your%20Azure%20DevOps%20Project%20for%20Continuous%20Integration%20%26amp%3B%20Deployment%20with%20the%20Azure%20DevOps%20Generator%3C%2FH3%3E%0A%3CUL%3E%0A%3CLI%3EIn%20the%20devops%20folder%20of%20this%20repo%20the%20Azure%20DevOps%20template%20is%20included.%20Download%20it.%3C%2FLI%3E%0A%3CLI%3ELogin%20with%20your%20account%20and%20open%20the%20DevOps%20Generator%3A%3CSPAN%3E%26nbsp%3B%3C%2FSPAN%3E%3CA%20href%3D%22https%3A%2F%2Fazuredevopsdemogenerator.azurewebsites.net%2Fenvironment%2Fcreateproject%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fazuredevopsdemogenerator.azurewebsites.net%2Fenvironment%2Fcreateproject%3C%2FA%3E%3C%2FLI%3E%0A%3CLI%3EChoose%20a%20custom%20template%20and%20point%20to%20the%20zip-file%20in%20the%20devops%20folder.%20This%20repo%20will%20be%20imported%20into%20Azure%20DevOps%20and%20Pipelines%20are%20created%20for%20you.%3C%2FLI%3E%0A%3CLI%3EThe%20project%20is%20split-up%20into%202%20pieces%3B%20shared%20resources%20%26amp%3B%20web%20app%20resources.%20Enabling%20you%20to%20extend%20your%20project%20with%20more%20web%20apps%20and%20re-using%20the%20shared%20resources%20for%20cost%20efficiency.%3C%2FLI%3E%0A%3CLI%3EYou%20can%20find%20the%20documentation%20on%20the%20Azure%20DevOps%20Generator%20here%3A%3CSPAN%3E%26nbsp%3B%3C%2FSPAN%3E-ERR%3AREF-NOT-FOUND-%3CA%20href%3D%22https%3A%2F%2Fvstsdemodata.visualstudio.com%2FAzureDevOpsDemoGenerator%2F_wiki%2Fwikis%2FAzureDevOpsGenerator.wiki%2F58%2FBuild-your-own-template%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Ehttps%3A%2F%2Fvstsdemodata.visualstudio.com%2FAzureDevOpsDemoGenerator%2F_wiki%2Fwikis%2FAzureDevOpsGenerator.wiki%2F58%2FBuild-your-own-template%3C%2FA%3E%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-77220367%22%20id%3D%22toc-hId-77220368%22%3E-ERR%3AREF-NOT-FOUND-Step%203%3A%20In%20Azure%20DevOps%2C%20create%20a%20service%20connection%3C%2FH3%3E%0A%3CUL%3E%0A%3CLI%3ELogin%20with%20your%20account%20Azure%20DevOps.%20Go%20to%20the%20Project%20Settings%20of%20the%20DevOps%20Project%20you've%20created%20in%20step%202.%3C%2FLI%3E%0A%3CLI%3EGo%20to%20Service%20Connections*.%3C%2FLI%3E%0A%3CLI%3ECreate%20a%20service%20connection%2C%20choose%20Azure%20Resource%20Manager%2C%20next.%3C%2FLI%3E%0A%3CLI%3ESelect%20Service%20Principal%20Authentication.%20Choose%20the%20correct%20Subscription%20first%2C%20then%20click%20the%20link%20at%20the%20bottom%3A%20%22use%20the%20full%20version%20of%20the%20service%20connection%20dialog.%22.%3C%2FLI%3E%0A%3CLI%3EEnter%20a%20name%20for%20your%20Service%20Principal%20and%20copy%20the%20appId%20from%20step%201%20in%20%22Service%20principal%20client%20ID%22%20and%20the%20password%20from%20step%201%20in%20%22Service%20principal%20key%22.%20And%20Verify%20the%20connection.%3C%2FLI%3E%0A%3CLI%3ETick%20%22Allow%20all%20pipelines%20to%20use%20this%20connection.%22.%20OK.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId--1730234096%22%20id%3D%22toc-hId--1730234095%22%3E%3CA%20id%3D%22user-content-step-4-in-azure-devops-update-the-variables-group%22%20class%3D%22anchor%22%20href%3D%22https%3A%2F%2Fgithub.com%2Fpascalvanderheiden%2Fmulti-container-wordpress-azure%23step-4-in-azure-devops-update-the-variables-group%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%20aria-hidden%3D%22true%22%3E%3C%2FA%3EStep%204%3A%20In%20Azure%20DevOps%2C%20update%20the%20Variables%20Group%3C%2FH3%3E%0A%3CUL%3E%0A%3CLI%3EGo%20to%20Pipelines%2C%20Library.%20Click%20on%20the%20Variable%20group%20%22Shared%20Resources%22.%3C%2FLI%3E%0A%3CLI%3ETick%20%22Allow%20access%20to%20all%20pipelines.%3C%2FLI%3E%0A%3CLI%3EUpdate%20the%20variables%20to%20match%20your%20naming%20conventions%20needs.%20You%20can%20leave%20the%20Storage%20Account%20and%20CDN%20empty%20if%20you%20don't%20want%20to%20use%20these%20services.%20Keep%20in%20mind%20to%20pick%20unique%20naming%20for%20exposed%20services.%3C%2FLI%3E%0A%3CLI%3EThe%20variable%20%22KVMYSQLPWD%22%20is%20NOT%20the%20MySQL%20password%2C%20but%20the%20naming%20tag%20in%20Key%20Vault%20for%20the%20MySQL%20password.%20Leave%20that%20as%20it%20is%3A%20%22mysqladminpwd%22.%3C%2FLI%3E%0A%3CLI%3EThe%20variable%20%22MYSQLUSER%22%20cannot%20be%20longer%20than%2016%20characters.%3C%2FLI%3E%0A%3CLI%3EDon't%20forget%20to%20save.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-757278737%22%20id%3D%22toc-hId-757278738%22%3E%3CA%20id%3D%22user-content-step-5-in-azure-devops-update-the-build-pipeline-and-run-it%22%20class%3D%22anchor%22%20href%3D%22https%3A%2F%2Fgithub.com%2Fpascalvanderheiden%2Fmulti-container-wordpress-azure%23step-5-in-azure-devops-update-the-build-pipeline-and-run-it%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%20aria-hidden%3D%22true%22%3E%3C%2FA%3EStep%205%3A%20In%20Azure%20DevOps%2C%20update%20the%20Build%20pipeline%20and%20Run%20it%3C%2FH3%3E%0A%3CUL%3E%0A%3CLI%3EGo%20to%20Pipelines%2C%20Pipelines.%3C%2FLI%3E%0A%3CLI%3ESelect%20%22Build%20Multi-container%20WordPress%20on%20Azure-CI%22%2C%20Edit.%3C%2FLI%3E%0A%3CLI%3EIn%20Tasks%2C%20select%20the%20Tasks%20which%20have%20the%20explaination%20mark%20%22Some%20settings%20need%20attention%22%2C%20and%20update%20Azure%20Subscription%20to%20your%20Service%20Principal%20Connection.%3C%2FLI%3E%0A%3CLI%3EIn%20Variables%2C%20update%20the%20variables%20to%20match%20your%20naming%20conventions%20needs.%20In%20my%20case%20I%20give%20the%20App%20Service%20Plan%20and%20the%20Resource%20Group%20of%20the%20Web%20App%20a%20more%20%22generic%22%20name.%20I%20want%20to%20re-use%20the%20App%20Service%20Plan%20for%20more%20websites%2C%20therefor%20all%20websites%20need%20to%20be%20deployed%20in%20the%20same%20Resource%20Group%20as%20the%20App%20Service%20plan.%20Keep%20in%20mind%20to%20pick%20unique%20naming%20for%20exposed%20services.%3C%2FLI%3E%0A%3CLI%3ESave%20%26amp%3B%20queue.%3C%2FLI%3E%0A%3CLI%3EClick%20the%20Agent%20Job%20to%20check%20the%20progress.%20Check%20if%20everything%20is%20create%20correctly%2C%20because%20of%20the%20unique%20naming%20for%20some%20services.%20And%20because%20it's%20fun%20%3A)%3C%2Fimg%3E%3C%2FLI%3E%0A%3CLI%3EKeep%20in%20mind%20that%20the%20CLI%20scripts%20will%20check%20if%20the%20resource%20is%20already%20created%2C%20before%20creating.%20You%20can%20deploy%20using%20ARM%20Templates%20as%20well.%20I%20choose%20a%20bash%20script%2C%20because%20you%20are%20free%20to%20use%20whatever%20you%20prefer%2C%20right%3F!%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId--1050175726%22%20id%3D%22toc-hId--1050175725%22%3E-ERR%3AREF-NOT-FOUND-Step%206%3A%20In%20Azure%20DevOps%2C%20add%20the%20Key%20Vault%20secret%20to%20the%20variables.%3C%2FH3%3E%0A%3CUL%3E%0A%3CLI%3EGo%20to%20Pipelines%2C%20Library.%20Add%20Variable%20group.%20Give%20it%20a%20name%2C%20something%20like%20%22Key%20Vault%20Secrets%22.%3C%2FLI%3E%0A%3CLI%3ETick%20%22Allow%20access%20to%20all%20pipelines.%3C%2FLI%3E%0A%3CLI%3ETick%20%22Link%20secrets%20from%20an%20Azure%20key%20vault%20as%20variables%22.%3C%2FLI%3E%0A%3CLI%3EUpdate%20the%20Azure%20Subscription%20to%20your%20Service%20Principal%20Connection.%3C%2FLI%3E%0A%3CLI%3ESelect%20the%20Key%20vault%20name.%20If%20your%20build%20pipeline%20ran%20succesfully%2C%20you%20can%20select%20your%20Key%20vault.%20Add%20variables%2C%20and%20it%20will%20popup%20with%20the%20secret%20we've%20created%20earlier%20%22mysqladminpwd%22.%20Select%20it%2C%20OK.%20And%20Save.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-1437337107%22%20id%3D%22toc-hId-1437337108%22%3E-ERR%3AREF-NOT-FOUND-Step%207%3A%20In%20Azure%20DevOps%2C%20update%20the%20Release%20pipeline%20and%20Run%20it.%3C%2FH3%3E%0A%3CUL%3E%0A%3CLI%3EGo%20to%20Pipelines%2C%20Releases.%20Note.%20Because%20I've%20enabled%20continuous%20deployment%20in%20my%20template%2C%20there%20is%20a%20failed%20release%20there%20already.%20You%20can%20ignore%20that%2C%20because%20we%20are%20going%20to%20fix%20the%20release%20in%20the%20step.%3C%2FLI%3E%0A%3CLI%3ESelect%20%22Release%20Multi-container%20WordPress%20on%20Azure-CD%22%2C%20Edit.%3C%2FLI%3E%0A%3CLI%3EIn%20Tasks%2C%20select%20the%20Tasks%20which%20have%20the%20explaination%20mark%20%22Some%20settings%20need%20attention%22%2C%20and%20update%20Azure%20Subscription%20to%20your%20Service%20Principal%20Connection.%3C%2FLI%3E%0A%3CLI%3EIn%20Variables%2C%20update%20the%20variables%20to%20match%20the%20naming%20you%20used%20in%20the%20Build%20pipeline.%20The%20WPDBHOST%20you%20can%20leave%20empty%2C%20because%20it%20will%20be%20updated%20in%20the%20pipeline.%3C%2FLI%3E%0A%3CLI%3EIn%20Variables%20groups%2C%20link%20the%20%22Key%20Vault%20Secrets%22%20variable%20group%2C%20by%20clicking%20the%20Link%20button.%3C%2FLI%3E%0A%3CLI%3EThe%20TARGET_YML%20will%20need%20to%20point%20to%20the%20yaml%20configuration%20files%20in%20repo.%20This%20will%20determine%20how%20the%20App%20Service%20is%20configured.%20In%20the%20DevOps%20project%20the%20TARGET_YML%20is%20default%20set%20to%20%22docker-compose-mc-wordpress-storage.yml%22%2C%20which%20is%20the%20most%20complete%20config.%20For%20inspiration%20and%20learning%20I've%20included%204%20configs%3A%3CUL%3E%0A%3CLI%3Ecompose-wordpress.yml%20(sample%20multi-container%20setup%20with%20redis%2C%20using%20local%20(not%20persistent)%20storage)%3C%2FLI%3E%0A%3CLI%3Edocker-compose-wordpress.yml%20(sample%20multi-container%20setup%20with%20MySQL%2C%20using%20local%20(not%20persistent)%20storage)%3C%2FLI%3E%0A%3CLI%3Edocker-compose-mc-wordpress-storage.yml%20(multi-container%20setup%20with%20redis%2C%20using%20Azure%20Storage%20for%20wp-content%20folder)%3C%2FLI%3E%0A%3CLI%3Edocker-compose-mc-wordpress.yml%20(multi-container%20setup%20with%20redis%2C%20using%20Azure%20App%20Service%20as%20persistent%20storage)%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3C%2FLI%3E%0A%3CLI%3EThe%20first%202%20yaml's%20are%20more%20inspirational%2C%20the%20last%202%20I%20would%20use%20for%20my%20deployment%2C%20because%20persistent%20storage%20is%20a%20must!%20Keep%20in%20mind%20that%20it%20would%20be%20illogical%20to%20use%20the%20last%20yaml%20file%2C%20and%20configure%20Azure%20Storage.%20Just%20leave%20the%20variable%20empty%20to%20skip%20this%20installation.%3C%2FLI%3E%0A%3CLI%3ESave%20%26amp%3B%20Create%20Release.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId-1426834003%22%20id%3D%22toc-hId-1426834004%22%3E%3CA%20id%3D%22user-content-step-8-go-to-your-websites%22%20class%3D%22anchor%22%20href%3D%22https%3A%2F%2Fgithub.com%2Fpascalvanderheiden%2Fmulti-container-wordpress-azure%23step-8-go-to-your-websites%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%20aria-hidden%3D%22true%22%3E%3C%2FA%3EStep%208%3A%20Go%20to%20your%20websites%3C%2FH2%3E%0A%3CP%3EYou%20need%20to%20run%20the%20website%20url%20one%20time%2C%20to%20trigger%20the%20compose%20script%20to%20download%20the%20WordPress%20image%20to%20the%20persistent%20storage%20location.%20This%20will%20take%20a%202-3%20minutes%20to%20download.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId--380620460%22%20id%3D%22toc-hId--380620459%22%3E%3CA%20id%3D%22user-content-redis-object-cache-plugin-in-wordpress%22%20class%3D%22anchor%22%20href%3D%22https%3A%2F%2Fgithub.com%2Fpascalvanderheiden%2Fmulti-container-wordpress-azure%23redis-object-cache-plugin-in-wordpress%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%20aria-hidden%3D%22true%22%3E%3C%2FA%3ERedis%20Object%20Cache%20Plugin%20in%20WordPress%3C%2FH2%3E%0A%3CUL%3E%0A%3CLI%3EIn%20WordPress%2C%20the%20plugin%20%22Redis%20Object%20Cache%22%20is%20already%20pre-installed%20on%20the%20this%20image.%3C%2FLI%3E%0A%3CLI%3EGo%20to%20Plugins%20and%20Enable%20the%20plugin.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId--514305184%22%20id%3D%22toc-hId--514305183%22%3E%3CA%20id%3D%22user-content-cdn-plugin-in-wordpress%22%20class%3D%22anchor%22%20href%3D%22https%3A%2F%2Fgithub.com%2Fpascalvanderheiden%2Fmulti-container-wordpress-azure%23cdn-plugin-in-wordpress%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%22%20aria-hidden%3D%22true%22%3E%3C%2FA%3ECDN%20Plugin%20in%20WordPress%3C%2FH2%3E%0A%3CUL%3E%0A%3CLI%3EIn%20Wordpress%2C%20install%20the%20plugin%20%22CDN%20Enabler%22.%20Or%2C%20when%20you%20have%20the%20wp-content%20folder%20mounted%20in%20Azure%20Storage%2C%20decompress%20the%20plugin%20from%20the%20wordpress-plugins%20folder%20in%20this%20repo%20and%20copy%20it%20into%20the%20%22Plugins%22%20folder%20using%20Azure%20Storage%20Explorer.%3C%2FLI%3E%0A%3CLI%3EGo%20to%20Plugins%20and%20Enable%20the%20plugin.%20Change%20the%20settings%20of%20the%20plugin%20and%20point%20it%20to%20the%20CDN%20Endpoint%20you've%20created%20earlier.%3C%2FLI%3E%0A%3CLI%3EGo%20to%20Azure%2C%20to%20your%20CDN%20Endpoint%2C%20and%20trigger%20a%20purge%20all%20to%20refresh.%20I've%20included%20a%20bash%20script%20in%20the%20deploy%20folder%20to%20trigger%20the%20purge%20in%20bash.%3C%2FLI%3E%0A%3C%2FUL%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-1598097%22%20slang%3D%22en-US%22%3E%3CP%3E%3CSPAN%3EA%20fully%20automated%20DevOps%20deployment%20of%20CMS%20WordPress%20on%20Azure%20App%20Services%20in%20a%20Linux%20Multi-Container.%20Setup%20with%20services%20like%3A%20App%20Service%2C%20Azure%20MySQL%2C%20Redis%20Cache%20(in%20a%20container)%2C%20Azure%20CDN%2C%20Azure%20Storage%20and%20Key%20Vault%20(to%20store%20the%20mysqluser%20password).%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%3CSPAN%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-center%22%20image-alt%3D%22wordpress-bg-medblue-520x273.png%22%20style%3D%22width%3A%20520px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F213249i2DD2C4DF9041EE5B%2Fimage-size%2Flarge%3Fv%3D1.0%26amp%3Bpx%3D999%22%20title%3D%22wordpress-bg-medblue-520x273.png%22%20alt%3D%22wordpress-bg-medblue-520x273.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-TEASER%3E%3CLINGO-LABS%20id%3D%22lingo-labs-1598097%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3EApp%20Services%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EApps%20%26amp%3B%20DevOps%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3EContainers%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3Ewordpress%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E
Microsoft

A fully automated DevOps deployment of CMS WordPress on Azure App Services in a Linux Multi-Container. Setup with services like: App Service, Azure MySQL, Redis Cache (in a container), Azure CDN, Azure Storage and Key Vault (to store the mysqluser password).

 

The Github repository: https://github.com/pascalvanderheiden/multi-container-wordpress-azure

 

The multi-container setup is used to host WordPress and Redis Cache in one App Service. This DevOps project is inspired on this tutorial: https://docs.microsoft.com/en-us/azure/app-service/containers/tutorial-multi-container-app

 

The Docker Compose YML's are based on these samples: https://github.com/Azure-Samples/multicontainerwordpress

Step by Step installation

Step 1: In the Azure Portal create a Service Principal

In the Azure Cloud Shell (https://shell.azure.com:(

  • az ad sp create-for-rbac --name [your-service-principal-name]

Copy the JSON Output! We'll be needing this information to create the service connection in Azure DevOps.

 

Step 2: Generate your Azure DevOps Project for Continuous Integration & Deployment with the Azure DevOps Generator

 

Step 3: In Azure DevOps, create a service connection

  • Login with your account Azure DevOps. Go to the Project Settings of the DevOps Project you've created in step 2.
  • Go to Service Connections*.
  • Create a service connection, choose Azure Resource Manager, next.
  • Select Service Principal Authentication. Choose the correct Subscription first, then click the link at the bottom: "use the full version of the service connection dialog.".
  • Enter a name for your Service Principal and copy the appId from step 1 in "Service principal client ID" and the password from step 1 in "Service principal key". And Verify the connection.
  • Tick "Allow all pipelines to use this connection.". OK.

 

Step 4: In Azure DevOps, update the Variables Group

  • Go to Pipelines, Library. Click on the Variable group "Shared Resources".
  • Tick "Allow access to all pipelines.
  • Update the variables to match your naming conventions needs. You can leave the Storage Account and CDN empty if you don't want to use these services. Keep in mind to pick unique naming for exposed services.
  • The variable "KVMYSQLPWD" is NOT the MySQL password, but the naming tag in Key Vault for the MySQL password. Leave that as it is: "mysqladminpwd".
  • The variable "MYSQLUSER" cannot be longer than 16 characters.
  • Don't forget to save.

 

Step 5: In Azure DevOps, update the Build pipeline and Run it

  • Go to Pipelines, Pipelines.
  • Select "Build Multi-container WordPress on Azure-CI", Edit.
  • In Tasks, select the Tasks which have the explaination mark "Some settings need attention", and update Azure Subscription to your Service Principal Connection.
  • In Variables, update the variables to match your naming conventions needs. In my case I give the App Service Plan and the Resource Group of the Web App a more "generic" name. I want to re-use the App Service Plan for more websites, therefor all websites need to be deployed in the same Resource Group as the App Service plan. Keep in mind to pick unique naming for exposed services.
  • Save & queue.
  • Click the Agent Job to check the progress. Check if everything is create correctly, because of the unique naming for some services. And because it's fun :)
  • Keep in mind that the CLI scripts will check if the resource is already created, before creating. You can deploy using ARM Templates as well. I choose a bash script, because you are free to use whatever you prefer, right?!

 

Step 6: In Azure DevOps, add the Key Vault secret to the variables.

  • Go to Pipelines, Library. Add Variable group. Give it a name, something like "Key Vault Secrets".
  • Tick "Allow access to all pipelines.
  • Tick "Link secrets from an Azure key vault as variables".
  • Update the Azure Subscription to your Service Principal Connection.
  • Select the Key vault name. If your build pipeline ran succesfully, you can select your Key vault. Add variables, and it will popup with the secret we've created earlier "mysqladminpwd". Select it, OK. And Save.

 

Step 7: In Azure DevOps, update the Release pipeline and Run it.

  • Go to Pipelines, Releases. Note. Because I've enabled continuous deployment in my template, there is a failed release there already. You can ignore that, because we are going to fix the release in the step.
  • Select "Release Multi-container WordPress on Azure-CD", Edit.
  • In Tasks, select the Tasks which have the explaination mark "Some settings need attention", and update Azure Subscription to your Service Principal Connection.
  • In Variables, update the variables to match the naming you used in the Build pipeline. The WPDBHOST you can leave empty, because it will be updated in the pipeline.
  • In Variables groups, link the "Key Vault Secrets" variable group, by clicking the Link button.
  • The TARGET_YML will need to point to the yaml configuration files in repo. This will determine how the App Service is configured. In the DevOps project the TARGET_YML is default set to "docker-compose-mc-wordpress-storage.yml", which is the most complete config. For inspiration and learning I've included 4 configs:
    • compose-wordpress.yml (sample multi-container setup with redis, using local (not persistent) storage)
    • docker-compose-wordpress.yml (sample multi-container setup with MySQL, using local (not persistent) storage)
    • docker-compose-mc-wordpress-storage.yml (multi-container setup with redis, using Azure Storage for wp-content folder)
    • docker-compose-mc-wordpress.yml (multi-container setup with redis, using Azure App Service as persistent storage)
  • The first 2 yaml's are more inspirational, the last 2 I would use for my deployment, because persistent storage is a must! Keep in mind that it would be illogical to use the last yaml file, and configure Azure Storage. Just leave the variable empty to skip this installation.
  • Save & Create Release.

 

Step 8: Go to your websites

You need to run the website url one time, to trigger the compose script to download the WordPress image to the persistent storage location. This will take a 2-3 minutes to download.

 

Redis Object Cache Plugin in WordPress

  • In WordPress, the plugin "Redis Object Cache" is already pre-installed on the this image.
  • Go to Plugins and Enable the plugin.

 

CDN Plugin in WordPress

  • In Wordpress, install the plugin "CDN Enabler". Or, when you have the wp-content folder mounted in Azure Storage, decompress the plugin from the wordpress-plugins folder in this repo and copy it into the "Plugins" folder using Azure Storage Explorer.
  • Go to Plugins and Enable the plugin. Change the settings of the plugin and point it to the CDN Endpoint you've created earlier.
  • Go to Azure, to your CDN Endpoint, and trigger a purge all to refresh. I've included a bash script in the deploy folder to trigger the purge in bash.