{"id":7445,"date":"2018-06-09T16:04:14","date_gmt":"2018-06-09T14:04:14","guid":{"rendered":"https:\/\/blog.redbaronofazure.com\/?p=7445"},"modified":"2018-06-13T19:37:27","modified_gmt":"2018-06-13T17:37:27","slug":"2-paas-appservices-webapps-and-dbs","status":"publish","type":"post","link":"https:\/\/blog.redbaronofazure.com\/?p=7445","title":{"rendered":"2 &#8211; PaaS, AppServices WebApps and DBs"},"content":{"rendered":"<p>The second post in a series of 8 from my classroom hands on tech training. It will be about automatically deploying a PaaS WebApp and a database to Azure. It will be done completly with a script and I&#8217;ll provide a Powershell version and two versions for CLI &#8211; one for Windows and one for Mac. The WebApp is a super-tiny PHP+MySQL app I found on a tutorial site that I&#8217;ve slightly modified to avoid having to install the MySQL client on your client to complete the setup. The reason that there are two different versions of the CLI script is simply because I make use of environment variables and bash and the DOS command prompt have different syntax for that.<\/p>\n<h1>The Task<\/h1>\n<p>The task that the scripts carry out is in short:<\/p>\n<ol>\n<li>Deploy the WebApp\n<ol>\n<li>Create an AppService plan in Azure<\/li>\n<li>Create a WebApp in the AppService plan<\/li>\n<li>Deploy an application from Github to the WebApp<\/li>\n<\/ol>\n<\/li>\n<li>Create a MySQL database (PaaS)<\/li>\n<li>Update WebApp config settings with MySQL connection info<\/li>\n<\/ol>\n<p>I deliberatly choose a PHP\/MySQL webapp to highlight the fact that Azure isn&#8217;t just about Windows and .Net. I got bitten a bit by this choice since performing step 2 in Powershell was a bit of a mouthful, since the azure Powershell cmdlets at the time of writing was a little bit behind the CLI. Otherwise it&#8217;s straight forward.<\/p>\n<h2>Step 1: Deploy the WebApp in CLI<\/h2>\n<p>In Azure CLI for Mac, the steps to create the WebApp looks like the below<\/p>\n<pre class=\"theme:github font-size:14 lang:sh decode:true\">gitrepo=https:\/\/github.com\/cljung\/php-mysql-sample.git\r\n\r\nwebappname=$USER$RANDOM\r\nrgname=\"$webappname-rg\" \r\nwebappplan=\"$webappname-plan\"\r\n\r\naz group create --location westeurope --name \"$rgname\"\r\naz appservice plan create --name \"$webappplan\" --resource-group \"$rgname\" --sku FREE\r\naz webapp create --name \"$webappname\" --resource-group \"$rgname\" --plan \"$webappplan\"\r\naz webapp deployment source config --name \"$webappname\" --resource-group \"$rgname\" \r\n        --repo-url \"$gitrepo\" --branch master --manual-integration\r\n<\/pre>\n<p>Every Azure resource exists in a Resource Group, so the first thing we need to create is a resource group. I derive the name of the resource group from the logged in username together with random digits and a &#8220;-rg&#8221; prefix. This makes it unique, repeatable and easily identifiable.<\/p>\n<p>The SKU of the AppService plan is set to FREE. There are only a limited number of free plans within a subscription, so if you get an error here, change it to S0 which is the cheapest you can get.<\/p>\n<p>The &#8220;az webapp deployment&#8221; is the magic that tells the Azure WebApp to pull the code for the webapp from a Github repo. Once this command has run, you will have a webapp deployed (but no db). If you checkin a code change to the github repo, Azure WebApp will detect that and automatically pull the latest stuff from the source control. You don&#8217;t need to rerun the command.<\/p>\n<h2>Step 2: Create the MySQL database in CLI<\/h2>\n<p>Azure offers a managed MySQL database solution and to provision it you need to create a server (just a name &#8211; not a VM), create the database and set some firewall rules (optional).<\/p>\n<p>The server name needs to be in lower case, so the first part is using the current logged in user and making sure that userid actually is in lower case. I also grab the public ip address I&#8217;m currently working from by making a web request to https:\/\/ipinfo.io\/ip so I can use that value and limit the MySQL firewall to grant me network access but block the rest of the world. The other firewall setting that is made is to let the Azure WebApp access the MySQL database. That is the 0.0.0.0 nonsense.<\/p>\n<pre class=\"theme:github font-size:14 lang:sh decode:true\"># transform userid to lowercase since some Azure resource names don't like uppercase\r\nuserid=$(echo \"$USER\" | awk '{print tolower(S0)}')\r\n\r\nSERVERNAME=\"$userid-mysqlsrv01\"\r\nDBAUID=dba01\r\nDBAPWD=\"MySqlDb$RANDOM\"\r\nDBNAME=msgdb\r\n_PIP=$(curl ipinfo.io\/ip)   \r\n\r\naz mysql server create -l westeurope -g \"$rgname\" -n \"$SERVERNAME\" \r\n          -u \"$DBAUID\" -p \"$DBAPWD\" --sku-name B_Gen4_2 \r\n          --ssl-enforcement Disabled --storage-size 51200 --version \"5.7\"\r\naz mysql db create -g \"$rgname\" -s \"$SERVERNAME\" -n \"$DBNAME\"\r\n\r\n# allow Azure to access it from the inside\r\naz mysql server firewall-rule create -g \"$rgname\" -s \"$SERVERNAME\" \r\n        -n AllowAllWindowsAzureIps --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0\r\n\r\n# allow login from the PIP currently being used\r\naz mysql server firewall-rule create -g \"$rgname\" -s \"$SERVERNAME\" \r\n        -n allowall --start-ip-address $_PIP --end-ip-address $_PIP\r\n\r\nDBSRV=$(az mysql server show -g $rgname -n $SERVERNAME --query \"fullyQualifiedDomainName\" -o tsv)\r\necho \"FQDN of MySQL server $DBSRV\"<\/pre>\n<p>The last az command is to retrieve the FQDN of the MySQL database which we need in the next step to update the WebApp config<\/p>\n<p>&nbsp;<\/p>\n<h2>Step 3: Update the WebApp config in CLI<\/h2>\n<p>Azure AppServices has something called Application Settings, which is a key\/value store on the Azure WebApp resource level that it applies in different formats. For a .Net based webapp, it overrides the Web.Config files AppSettings, for instance. It also sets the key\/values as environment variables which Java, PHP and node.js based apps can pick up. We are going to set four AppSettings to the Azure WebApp<\/p>\n<pre class=\"theme:github font-size:14 lang:sh decode:true \">az webapp config appsettings set -g $rgname -n $webappname --settings \"dbHost=$DBSRV\"\r\naz webapp config appsettings set -g $rgname -n $webappname --settings \"dbName=$DBNAME\"\r\naz webapp config appsettings set -g $rgname -n $webappname --settings \"dbUser=$DBAUID@$SERVERNAME\"\r\naz webapp config appsettings set -g $rgname -n $webappname --settings \"dbPwd=$DBAPWD\"\r\n<\/pre>\n<p>The names dbHost, dbName, etc, is something application dependant and has nothing to do with Azure.<\/p>\n<h2>Test drive the WebApp<\/h2>\n<p>When the script is finished, you can access the website via http:\/\/&lt;username-random&gt;.azurewebsites.net\/. The MySQL table that the app is using isn&#8217;t created, so the first thing you need to do is to go to page\u00a0http:\/\/&lt;username-random&gt;.azurewebsites.net\/createdb.php to create it, then you can write messages.<\/p>\n<p><a href=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2018\/06\/aztt-appservice-webapp-index-php.png\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-7452\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2018\/06\/aztt-appservice-webapp-index-php.png\" alt=\"\" width=\"1277\" height=\"321\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2018\/06\/aztt-appservice-webapp-index-php.png 1277w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2018\/06\/aztt-appservice-webapp-index-php-300x75.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2018\/06\/aztt-appservice-webapp-index-php-768x193.png 768w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2018\/06\/aztt-appservice-webapp-index-php-1024x257.png 1024w\" sizes=\"(max-width: 1277px) 100vw, 1277px\" \/><\/a><\/p>\n<p>I&#8217;ve added the FQDN server name of the MySQL database as a header so you can see that it&#8217;s actually picked that up.<\/p>\n<h2>Step 1: Deploy the WebApp in Powershell<\/h2>\n<p>The Powershell version of deploying the webappisn&#8217;t much different to begin with. Of course, Powershell looks a little bit different with it&#8217;s Verb-dash-what style of naming convention plus it is longer commands to type. It is pretty close still with four commands to carry out the task.<\/p>\n<pre class=\"theme:powershell font-size:14 lang:ps decode:true\">$gitrepo=\"https:\/\/github.com\/cljung\/php-mysql-sample.git\"\r\n$location=\"West Europe\"\r\n$userid=$env:USERNAME.tolower()\r\n$webappname=\"$userid$(Get-Random)\"\r\n$rgname=\"$webappname-rg\"\r\n$webappplan=\"$webappname-plan\"\r\n\r\nNew-AzureRmResourceGroup -Name \"$rgname\" -Location \"$location\"\r\n\r\nNew-AzureRmAppServicePlan -Name $webappplan -Location $location \r\n                         -ResourceGroupName $rgname -Tier Free\r\nNew-AzureRmWebApp -Name $webappname -Location $location -AppServicePlan $webappplan \r\n                         -ResourceGroupName $rgname\r\n\r\n$PropertiesObject = @{repoUrl=\"$gitrepo\"; branch=\"master\"; isManualIntegration=\"true\";}\r\nSet-AzureRmResource -ResourceGroupName $rgname -ResourceName \"$webappname\/web\" \r\n                         -ResourceType \"Microsoft.Web\/sites\/sourcecontrols\" \r\n                         -PropertyObject $PropertiesObject -ApiVersion \"2015-08-01\" -Force\r\n<\/pre>\n<p>The Set-AzureRmResource is the generic Swiss Army knife of Azure powershell commands. You basically can manipulate any resource you like by passing the appropriate set of properties to it and specify what ResourceType inside the resource we want to apply it to.<\/p>\n<h2>Step 2: Create the MySQL database in Powershell<\/h2>\n<p>At the time of writing, the Microsoft documentation for MySQL database says that there is no Powershell support for creating or managing MySQL. That is both true and false. There are no commands called New-AzureRmMySQLServer, etc, but that doesn&#8217;t mean that we can provision a MySQL database from powershell. After all, there must be a REST API available, because otherwise the CLI wouldn&#8217;t work, right?<\/p>\n<p>The answer in a situation like this is to revert to using the Net-AzureRmResource and do it a bit more low level way. Step 2 in powershell therefor looks like this<\/p>\n<pre class=\"theme:terminal font-size:14 lang:ps decode:true\">$SERVERNAME=\"$userid-mysqlsrv01\"\r\n$DBAUID=\"dba01\"\r\n$DBAPWD=\"MySqlDb$(Get-Random)\"\r\n$DBNAME=\"msgdb\"\r\n# get our current public ip addr so we can set the NSG below to only allow access from that ip addr\r\n$resp = Invoke-RestMethod \"http:\/\/ipinfo.io\"\r\n$_PIP=$resp.ip\r\n\r\n# register the ARM provider - one time operation\r\nRegister-AzureRmResourceProvider -ProviderNamespace \"Microsoft.DBforMySql\"\r\n$apiVersion=\"2017-12-01\"\r\n                        \r\n# create server\r\n$json2='{\"administratorLogin\":\"'+$DBAUID+'\",\"administratorLoginPassword\":\"'+$DBAPWD+'\",\r\n          \"storageProfile\":{\"storageMB\":51200,\"backupRetentionDays\":7,\r\n          \"geoRedundantBackup\":\"Disabled\"},\"version\":\"5.7\",\"sslEnforcement\":\"Disabled\",\r\n          \"replicationRole\":\"None\",\"primaryServerId\":\"\",\"replicaCapacity\":5}'\r\n$prop2=($json2 | ConvertFrom-json)\r\n$resSrv = New-AzureRmResource -Force -ResourceType \"Microsoft.DBforMySQL\/servers\" \r\n                       -ResourceGroupName \"$rgname\" -ApiVersion $apiVersion -Location $location `\r\n    -ResourceName \"$SERVERNAME\" -SkuObject @{name='B_Gen4_2'} -PropertyObject @prop2\r\n\r\n# create db\r\n$propDB = ('{\"charset\":\"latin1\",\"collation\":\"latin1_swedish_ci\"}' | ConvertFrom-json)        \r\nNew-AzureRmResource -Force -ResourceType \"Microsoft.DBforMySQL\/servers\/databases\" \r\n    -ResourceGroupName \"$rgname\" -ApiVersion $apiVersion -Location $location `\r\n    -ResourceName \"$SERVERNAME\/$DBNAME\" -PropertyObject @propDB\r\n        \r\n# set MySQL firweall to allow your current public ip address\r\n$propFW = ('{\"startIpAddress\":\"'+$_PIP+'\",\"endIpAddress\":\"'+$_PIP+'\"}' | ConvertFrom-json)        \r\nNew-AzureRmResource -Force -ResourceType \"Microsoft.DBforMySQL\/servers\/firewallRules\" \r\n    -ResourceGroupName \"$rgname\" -ApiVersion $apiVersion -Location $location `\r\n    -ResourceName \"$SERVERNAME\/AllowPip\" -PropertyObject @propFW\r\n\r\n# set MySQL firweall to allow internal Azure traffic\r\n$propFW = ('{\"startIpAddress\":\"0.0.0.0\",\"endIpAddress\":\"0.0.0.0\"}' | ConvertFrom-json)        \r\nNew-AzureRmResource -Force -ResourceType \"Microsoft.DBforMySQL\/servers\/firewallRules\" \r\n    -ResourceGroupName \"$rgname\" -ApiVersion $apiVersion -Location $location `\r\n    -ResourceName \"$SERVERNAME\/AllowAllWindowsAzureIps\" -PropertyObject @propFW\r\n<\/pre>\n<p>Yikes, you may say, and that isn&#8217;t completely wrong. The point here is not showing off but to prove to you that you still can succeed even there is no specific commands because powershell is very powerfull. How the h@ did I get all these JSON structure might be another thing you say. Well, that wasn&#8217;t so hard as it looks. I simply created the MySQL database with CLI, used Get-AzureRmResource to query out the resources and used the $resource | ConvertTo-Json\u00a0 command to see how it looked in JSON.<\/p>\n<h2>Step 3: Update the WebApp config in Powershell<\/h2>\n<p>The final step is luckily not that complex, since all we need to do is to create a Dictionary object in powershell with the application settings and call the Set-AzureRmWebApp command<\/p>\n<pre class=\"theme:powershell font-size:14 lang:ps decode:true\">$newAppSettings = @{\"dbHost\"=\"$($resSrv.Properties.fullyQualifiedDomainName)\";\r\n                    \"dbName\"=\"$DBNAME\";\"dbUser\"=\"$DBAUID@$SERVERNAME\";\"dbPwd\"=\"$DBAPWD\"}\r\nSet-AzureRmWebApp -ResourceGroupName \"$rgName\" -Name \"$webappname\" -AppSettings $newAppSettings\r\n<\/pre>\n<h1>Summary<\/h1>\n<p>This post is all about showing you that it is very easy to provision a classic webapp with it&#8217;s complementary database, even if it is a PHP and MySQL type of app. Running the script will have your app ready in minutes. Since its deployment model is bound to Github, that means that what ever changes you check in will be pushed to Azure.<\/p>\n<h1>References<\/h1>\n<p>You&#8217;ll find three scripts in my github repo\u00a0<a href=\"https:\/\/github.com\/cljung\/aztechdays\">https:\/\/github.com\/cljung\/aztechdays<\/a> for this post<\/p>\n<ul>\n<li>appservices-mysql-deploy-git.ps1\u00a0 &#8211; the Powershell version<\/li>\n<li>appservices-mysql-deploy-git-win.cmd &#8211; the CLI version for Windows Command Prompt<\/li>\n<li>appservices-mysql-deploy-git-mac.sh &#8211; the CLI version for Mac\/Linux<\/li>\n<\/ul>\n<p>Note that the CLI\/Windows version is written to be executed in the DOS command prompt. If you rather prefer to run it in the Powershell command prompt, you need to change the use of environment variables, ie from %USERNAME% to $env:USERNAME, etc<\/p>\n<p>Other useful Microsoft documentation<\/p>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/app-service\/scripts\/app-service-cli-deploy-github\">https:\/\/docs.microsoft.com\/en-us\/azure\/app-service\/scripts\/app-service-cli-deploy-github<\/a><\/p>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/mysql\/quickstart-create-mysql-server-database-using-azure-cli\">https:\/\/docs.microsoft.com\/en-us\/azure\/mysql\/quickstart-create-mysql-server-database-using-azure-cli<\/a><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The second post in a series of 8 from my classroom hands on tech training. It will be about automatically deploying a PaaS WebApp and a database to Azure. It will be done completly with a script and I&#8217;ll provide a Powershell version and two versions for CLI &#8211; one for Windows and one for [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[231,424,426,101,141],"tags":[427,425],"_links":{"self":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts\/7445"}],"collection":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=7445"}],"version-history":[{"count":10,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts\/7445\/revisions"}],"predecessor-version":[{"id":7501,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts\/7445\/revisions\/7501"}],"wp:attachment":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=7445"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=7445"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=7445"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}