This series of posts is about how to use Azure DevOps and Pipelines to set up a basic C# web application, app service infrastructure, and CI/CD pipelines – with everything saved as code in our source code repository.

Last time, I finished up with a coded multi-stage Pipeline, which created three resource groups using a parameterised YAML file. The diagram below is a high level description of the overall system architecture I’ve implemented so far.

resource-group-architecture-diagram

I inserted placeholder to the pipeline for tasks like creating an Azure app service – the diagram below shows all of these stages (including parallel ones) deployed using Azure pipelines.

stages

This time I’ll show how to create a simple Azure App Service using code, and integrate it into my deployment pipeline – by the end of this post I’ll have implemented the infrastructure shown below.

app picture

Create an App Service using Infrastructure as Code

I’m going to use ARM (Azure Resource Manager) templates to create my Azure App Service, which is where I’ll host my website. Using an ARM template allows me to specify all of my my host infrastructure details in code, which makes creating my infrastructure much easier, and more repeatable and stable.

I’ve chosen to use Visual Studio 2019 to create my ARM template – I know a lot of people prefer VSCode, but this is just the way that I’m used to creating templates.

Before starting with Visual Studio 2019, I need to make sure I’ve got the correct packs installed for Azure development – I’ve included a screenshot of the “Azure development” pack I needed to install in my “Visual Studio Updater” (highlighted below in red).

vs azure

Next, I just create a new project in Visual Studio 2019. I’ve selected the “Azure Resource Group” template type, as shown below, and then I click “Next”.

arg picture

The next screen allows me to configure the name of the project (I’ve just called my project SimpleAzureAppService), and where it’s saved. I’ve chosen to save it in the same folder as I’ve used in the previous parts of this series.

This screen offers me an option to select the .NET Framework version, but I don’t really understand the point of this as the ARM templates are JSON files.

arg configure

Next, and probably most importantly, Visual Studio 2019 gives me the option to select what kind of ARM template I want to use. There are a bunch of useful canned templates available, and to create an Azure App Service, I’ve selected the “Web app” template, as shown below.

select azure template

After I click OK on that screen, the ARM project is created. All the magic happens in the WebSite.json file, which is where all the hosting infrastructure is specified. I haven’t altered any parameters – I’ll do that in my Azure Pipeline directly.

I’m not going to talk about the innards of the WebSite.json file here – that’s a whole other series of posts.

solution explorer

At this point I pushed the code up to my public GitHub repository.

Now add a task to the Azure Pipeline

In my Azure DevOps project (which I’ve created in the previous posts, part #2 and part #3), I want to add a new stage in my pipeline which will use this ARM project to deploy my Azure App Service.

I can do this by adding an “Azure Resource Group” task. In the screen where I can edit my pipeline definition file (azure-pipelines.yml), I searched for “Azure Resource Group” in the Tasks window on the right hand side (as highlighted below).

task

Selecting this task will open a window on the right side of the screen like the one below, where you can enter parameters for the task.

arm task

I entered the values for this task that I’ve specified below:

  • Azure Subscription: “Visual Studio Professional with MSDN
    • This might be different for you – and I’m going to replace this with the $(subscription) parameter anyway as I’ve specified it at the top of my azure-pipelines.yml file.
  • Action: Create or update resource group
  • Resource Group: integration-rg
    • This is the resource group created in the previous stage.
  • Region: UK South
  • Template Location: URL of the file
  • Template link: https://raw.githubusercontent.com/jeremylindsayni/EverythingAsCode/master/SimpleAzureAppService/SimpleAzureAppService/WebSite.json
    • Obviously this is specific to my public GitHub repo, it will be different for you.
  • Override template parameters: -hostingPlanName integration-webfarm
    • This is a parameter which is left empty in the ARM file (WebSite.json) that’s generated by Visual Studio 2019. I don’t specify this in the WebSite.parameters.json file because I want to use different parameters in the different environments (i.e. integration, testing, demonstration)
  • Deployment mode: Incremental

When you add this to your YAML, it might complain about indentation or position – for completeness, here’s the YAML stage I’ve used:

stage: deploy_app_service_to_integration
  displayName: 'Deploy the app service to the integration environment'
  dependsOn: build_integration
  jobs:
  - job: deploy_app_service_to_integration
    pool:
      vmImage: 'Ubuntu 16.04'
    steps:
    - task: AzureResourceGroupDeployment@2
      inputs:
        azureSubscription: '$(subscription)'
        action: 'Create Or Update Resource Group'
        resourceGroupName: 'integration-rg'
        location: 'UK South'
        templateLocation: 'URL of the file'
        csmFileLink: 'https://raw.githubusercontent.com/jeremylindsayni/EverythingAsCode/master/SimpleAzureAppService/SimpleAzureAppService/WebSite.json'
        overrideParameters: '-hostingPlanName integration-webfarm'
        deploymentMode: 'Incremental'

Now we can run the pipeline until it finishes, and when I look in my Azure portal, I can see that an App Service plan has been created with an App Service in my “integration-rg” resource group.

webfarm

This is great – using only code, I’m able to specify infrastructure to which I’ll be able to deploy my website. You can even see the default website running in this app service (which will show you a site like the image below) if you browse to the App Service site. For me, I just browse to a site which is the name of my App Service with”.azurewebsites.net” added to the end.

appservice website

But there’s also an Application Insights instance, which strangely has been created in the “East US” region. What’s happened here?

Fortunately, because everything is in code, I’m able to look at what the WebSite.json file has specified and work it out by doing a simple text search. Inside the JSON node that specifies the Application Insights part, the location “East US” is actually hard-coded inside the default template generated by Visual Studio 2019!

{
  "apiVersion""2014-04-01",
  "name""[variables('webSiteName')]",
  "type""Microsoft.Insights/components",
  "location""East US",
  "dependsOn": [
    "[resourceId('Microsoft.Web/sites/', variables('webSiteName'))]"
  ],
  "tags": {
    "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('webSiteName'))]""Resource",
    "displayName""AppInsightsComponent"
  },
  "properties": {
    "applicationId""[variables('webSiteName')]"
  }
}

I’m going to guess that when the template was written, Application Insights was not available in all regions and therefore the authors hardwired a region that they knew would work.

Anyway, I know that Application Insights is available in “UK South”, so I can modify the WebSite.json code to remove the “East US” hardwired reference, and change it to:

"location""[resourceGroup().location]"

And now if I re-run the pipeline, everything is in the UK South region.

webfarm

You’ll also notice that the website is called “webSite” and then seemingly random numbers and letters – this is because it’s coded that way in the “WebSite.json” variables section, as shown below.

"variables": {
  "webSiteName""[concat('webSite', uniqueString(resourceGroup().id))]"
},

Again because this is all in code, I can alter this – say I want my websites to be prefixed with “everythingAsCode-” instead of “webSite”, I can just change the variable to the code below:

"variables": {
  "webSiteName""[concat('everythingAsCode-', uniqueString(resourceGroup().id))]"
}

And after re-running the pipeline, my website has a new name.

newwebfarm

Wrapping up

This post has shown how I can use an ARM template to specify hosting infrastructure for my website. So far, everything in the high-level architecture diagram below has been specified in code.

app picture

I’ve mentioned the advantages of this approach in the previous posts, but I’ll say it again – it blows my mind that I can arrive at a project, pull the source code, and then I can just click a button to build my own copy of the entire infrastructure and deployment pipeline using Azure DevOps. Next time I’ll look at how to deploy the default MVC website written in C# using this approach.

4 thoughts on “Everything as Code with Azure DevOps Pipelines: C#, ARM, and YAML: Part #4, Deploying an ARM template to create an Azure App Service with code

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s