Building a Software Survey using Blazor - Part 10 - Bicep
Tags: blazor, csharp, azure, bicep
Published: 2020-12-07 20:49:10

I've decided to write a small survey site using Blazor.  Part of this is an excuse to learn Blazor.

As I learn with Blazor I will blog about it as a series of articles.

This series of articles is not intended to be a training course for Blazor - rather my thought process as I go through learning to use the product.

Earlier articles in this series:

* [Part 1 - State](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-1-state)
* [Part 2 - Better State](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-2-better-state)
* [Part 3 - Components](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-3-components)
* [Part 4 - Render Mode](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-4-render-mode)
* [Part 5 - Client IP](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-5-client-ip)
* [Part 6 - Azure Cosmos DB](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-6-azure-cosmos-db)
* [Part 7 - Azure SignalR Service](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-7-azure-signalr-service)
* [Part 8 - bUnit](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-8-bunit)
* [Part 9 - End to End Tests](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-9-end-to-end-tests)

---

I host my Software Survey site on Azure - not that much of as surprise given the use of both Azure Cosmos DB and Azure SignalR Service.

And I want to document my setup as Infrastructure as Code (IaC) so that it would repeatable and reliable.

Normally I would use the Azure ARM template format.

But given I was already being experimental using Blazor ... why not try something new for the infrastructure as well.

Enter Bicep.

## Bicep
Bicep is an open-source project from the Microsoft Azure team - who describe it as:

> "Bicep is a Domain Specific Language (DSL) for deploying Azure resources declaratively. It aims to drastically simplify the authoring experience with a cleaner syntax and better support for modularity and code re-use. Bicep is a transparent abstraction over ARM and ARM templates, which means anything that can be done in an ARM Template can be done in bicep" [Bicep Github](https://github.com/Azure/bicep)

They also caveat it with:

> "Note: Bicep is currently an experimental language and we expect to ship breaking changes in future releases. It is not yet recommended for production usage."

And yet for something very experimental, it really does seem to work very well.

## Abstractions over cloud templating languages
I've found ARM templates, like AWS CloudFormation templates, to be very verbose - and can be rather cumbersome to work with.

For AWS work, I've really enjoyed using their [Cloud Development Kit](https://github.com/aws/aws-cdk) as a much better option.  The AWS CDK allows you to "develop" your infrastructure using a variety of development languages (although TypeScript is probably the best choice).

For example (taken from [AWS Documentation](https://docs.aws.amazon.com/cdk/latest/guide/hello_world.html)):

```
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';

export class HelloCdkStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new s3.Bucket(this, 'MyFirstBucket', {
      versioned: true
    });
  }
}
```

This defines a new CloudFormation stack with an S3 bucket in it.

AWS CDK then will compile this into a standard CloudFormation template.

And at a high level, Azure Bicep operates in much the same way.

It allows you to define your infrastructure in a domain specific language (rather than a general purpose programming language like AWS CDK) and then compiles to an ARM template.

## My infrastructure
Here is the Bicep file for a full infrastructure ([source](https://github.com/Red-Folder/software-survey/blob/master/Infrastructure/SoftwareSurveyInfrastructure.bicep))

```
param environment string = 'e2e'

var nameprefix = 'rfc-${environment}-software-survey'

resource signalr 'Microsoft.SignalRService/SignalR@2020-07-01-preview' = {
    name: '${nameprefix}-signalr'
    location: resourceGroup().location
    kind: 'SignalR'
    sku: {
        name: 'Free_F1'
    }
}

resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2020-06-01-preview' = {
    name: '${nameprefix}-db'
    location: resourceGroup().location
    kind: 'GlobalDocumentDB'
    properties: {
        enableFreeTier: true
        databaseAccountOfferType: 'Standard'
        consistencyPolicy: {
            defaultConsistencyLevel: 'Session'
        }
    }
}

resource applicationInsights 'microsoft.insights/components@2018-05-01-preview' = {
    name: '${nameprefix}-application-insights'
    location: resourceGroup().location
    kind: 'web'
    properties: {
        ApplicationType: 'web'
        publicNetworkAccessForIngestion: 'Enabled'
        publicNetworkAccessForQuery: 'Enabled'
    }
}

resource farm 'Microsoft.Web/serverfarms@2018-11-01' = {
    name: '${nameprefix}-app-plan'
    location: resourceGroup().location
    kind: 'linux'
    sku: {
        name: 'F1'
        capacity: 1
    }
}

resource website 'Microsoft.Web/sites@2018-11-01' = {
    name: '${nameprefix}-web-app'
    location: resourceGroup().location
    kind: 'app'
    properties: {
        serverFarmId: farm.id
        siteConfig: {
            appSettings: [
                {
                    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
                    value: reference(applicationInsights.id).InstrumentationKey
                }
                {
                    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
                    value: reference(applicationInsights.id).ConnectionString
                }
                {
                    name: 'ApplicationInsightsAgent_EXTENSION_VERSION'
                    value: '~2'
                }
                {
                    name: 'Azure__SignalR__ConnectionString'
                    value: listKeys(signalr.id, '2020-07-01-preview').primaryConnectionString
                }
                {
                    name: 'Persistance:CosmosDbEndpoint'
                    value: cosmos.properties.documentEndpoint
                }
                {
                    name: 'Persistance:CosmosDbPrimaryKey'
                    value: listKeys(cosmos.id, '2020-06-01-preview').primaryMasterKey
                }
                {
                    name: 'XDT_MicrosoftApplicationInsights_Mode'
                    value: 'default'
                }
            ]
        }
    }
}

output appServiceName string = website.name
output appServiceUrl string = 'http://${website.name}.azurewebsites.net/'
output cosmosEndpoint string = cosmos.properties.documentEndpoint
output cosmosPrimaryKey string = listKeys(cosmos.id, '2020-06-01-preview').primaryMasterKey
```

In it I:

* Create a SignalR Service - free tier
* Create a Cosmos DB - free tier
* Create an Application Insights
* Create an App Service farm - free tier
* Create an App Service - passing in all the app settings needed by the Survey app

These 92 lines then get compiled into 129 line ARM template - which can be seen [here](https://github.com/Red-Folder/software-survey/blob/master/Infrastructure/SoftwareSurveyInfrastructure.json)

## Benefits
Mainly, I think that Bicep is considerably more readable than native ARM template.

Bicep is also handling an amount of default values for you.

It is however early days for the tool.  As of writing, they have only just moved to Bicep 0.2 - and I've yet to try (there could potentially be breaking changes).

I currently wouldn't recommend for a client's production use - unless they had an appetite for the bleeding edge.

For my purpose however, given it still generated a valid ARM template, I'm more than happy to use it.

## A note on Terraform
If you have much experience with IaC, you maybe reading this and asking why I didn't go with Terraform.

To be blunt, I've never really found the opportunity to use Terraform.  While I've studied it, my clients are generally working with native template formats.

I would see no problems using Terraform to create the infrastructure.

## Outputs
You may have noted that I output a number of values at the end of my script:

```
output appServiceName string = website.name
output appServiceUrl string = 'http://${website.name}.azurewebsites.net/'
output cosmosEndpoint string = cosmos.properties.documentEndpoint
output cosmosPrimaryKey string = listKeys(cosmos.id, '2020-06-01-preview').primaryMasterKey
```

Those outputs are passed to my End to End tests (introduced in the [last article](https://red-folder.com/blog/building-a-software-survey-using-blazor-part-9-end-to-end-tests)).

Thank you for reading.  See you in the next article.