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.
All articles in this series:
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 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
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.
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 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):
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.
Here is the Bicep file for a full infrastructure (source)
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:
These 92 lines then get compiled into 129 line ARM template - which can be seen here
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.
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.
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).
Thank you for reading. See you in the next article.