Tuesday, August 22, 2017

Building an Azure SQL Server with Terraform

I want to spend some more time learning Terraform, and I am hoping to build out a script to deploy the infrastructure for a pet project of mine, SleepySecurityNews.  I'd link the site here, but I'm almost certain it isn't functional right now as I have not maintained it in quite some time.

The first thing that my app requires is a SQL server database.  From an Azure perspective, this translates into two resources, an Azure SQL Server and an Azure SQL Database.  This is also how it is represented in Terraform.  The two resources are:
 So first, a couple of notes that I learned while doing this:


Terraform has the concept of variables.  Like ARM templates,  you can specify defaults in the original definition.  You can also pass in variables via a file, an environment variable, or on the command line at "apply" time.  It seems pretty cool.

Variables are stored in state, which by default is a local file on disk.  When you setup an Azure SQL Server, you do have to specify an admin password in the provider.  You will need to refer to the documentation on sensitive data to understand the implications.  This is something I need to look into further and will try to address in a separate blog post.

Concat doesn't work on strings, use format for that

Like ARM templates, there are several functions that you can call within the template.  Of course I instinctively used concat to merge two strings together.  Nope!  Apparently this feature was removed and the recommended advice is to use the format command to put the strings together.  Makes sense!

Timing Error

When I deployed the script, I got a weird timing error.  Terraform deployed the database server, and then when it tried to deploy the database itself, I got the following error:

Essentially, it was telling me that the db server does not exist (when it was trying to deploy the database itself).  Re-running the script again without any changes resolved the issue.  I could see this as a problem going forward, and will keep an eye when building out chains with dependent resources.

In any event, here is the full code that I used to deploy:

provider "azurerm" {
    subscription_id = ""
    client_id = ""
    client_secret = ""
    tenant_id = ""

variable "ssn_environment" {
    type = "string"
    default = "dev"
    description = "environment short"

variable "ssn_db_name" {
    type = "string"
    default = "ssn"
    description = "The name of the sql server db to use"

variable "ssn_db_server_name" {
    type = "string"
    default = "ssndbusw"
    description = "The name of the sql server to use"

variable "ssn_resource_group_name" {
    type = "string"
    default = "ssn_terraform"
    description = "The name of the resource group to deploy all resources"

variable "ssn_db_edition" {
    type = "string"
    default = "Basic"
    description = "The sql server edition"

variable "ssn_location" {
    type = "string"
    default = "West US"
    description = "The location to deploy resources to"

variable "ssn_db_server_login" {
    type = "string"
    default = "ssndbadmin"
    description = "The db server login name to use"

variable "ssn_db_password" {
    type = "string"
    default = ""
    description = "The password to use"

resource "azurerm_sql_server" "ssn_dbserver" {
    name = "${format("%s%s",var.ssn_environment,var.ssn_db_server_name)}"
    resource_group_name = "${var.ssn_resource_group_name}"
    location = "${var.ssn_location}"
    version = "12.0"
    administrator_login = "${var.ssn_db_server_login}"
    administrator_login_password = "${var.ssn_db_password}"

resource "azurerm_sql_database" "ssn_db" {
    name = "${var.ssn_db_name}"
    resource_group_name = "${var.ssn_resource_group_name}"
    server_name = "${format("%s%s",var.ssn_environment,var.ssn_db_server_name)}"
    edition = "${var.ssn_db_edition}"
    location = "${var.ssn_location}"

When running the apply, I specified a var on the command line.  Something like the following would work:

terraform.exe apply -var "ssn_db_password=<enter password here>"

In conclusion, we ran through a more complex Terraform setup that provisioned an Azure SQL server and database.  During this process, I learned more about variables, a bit about interpolation functions within the Terraform language, and ran into a timing issue.  I hope you are enjoying this journey in learning Terraform!