Feature Flags in Terraform

Feature flagging any code can be useful to developers but many don’t know how to or even that you can do it in Terraform.

Some benefits of Feature Flagging your code

  • You can enable different features for different environments
    • For example, if you want to enable something in a lower environment to test it but are not ready to deploy it to production
    • This is also beneficial to save money, you might want to only have a large deployment in your production environment but you don’t want to in a lower environment. So, you enable a feature in production and disable it in your lower environments.
  • Enable/Disable features quickly
    • If you have something wrong in your environment and you want to quickly disable a resource that is causing issues, you can just change the Terraform variable to false and re-run your pipeline
    • Otherwise, you would have to delete or comment out that section of your code

How to Feature Flag in Terraform

First, you will want to create a variable that is named in a way that anyone can come in and know what they are enabling/disabling:

variable "enable_postgres" {
    description = "Variable to enable or disable postgres deployment"
    default     = false
}

Now you can use this variable with a conditional expression in the resources related to the feature (postgres in this example). How you do this is with count which can be added to any Terraform resource.

resource "azurerm_postgresql_flexible_server" "example" {
  name                   = "example-psqlflexibleserver"
  resource_group_name    = azurerm_resource_group.example.name
  location               = azurerm_resource_group.example.location
  version                = "12"
  administrator_login    = "psqladmin"
  administrator_password = "<password>"
  storage_mb             = 32768
  sku_name               = "GP_Standard_D4s_v3"
  count                  = var.enable_postgres ? 1 : 0
}

resource "azurerm_postgresql_flexible_server_database" "example" {
  name      = "example-db"
  server_id = azurerm_postgresql_flexible_server.example[0].id
  collation = "en_US.utf8"
  charset   = "utf8"
  count     = var.enable_postgres ? 1 : 0
}

How this works is the argument count is being set by your variable, the conditional expression is checking your variable and if it is true then it is setting count to 1 and will deploy the resource. If the variable is set to false it is setting the count to 0 and will not deploy the resource.

You can use a single feature flag with multiple resources as usually you need more than one resource for a deployment. This will control if all of them get deployed on the next terraform apply at the same time. In my example with postgres you can see both the azurerm_postgresql_flexible_server and azurerm_postgresql_flexible_server_database both have enable_postgres on them so if the variable is set to true they will both be deployed.

Additional Considerations

When you add a count argument to any resource in Terraform, it changes how you call that resource in the rest of your Terraform code. If you notice in my example above, in the azurerm_postgresql_flexible_server_database block I had to call the server with a [0]

Example calling without count:

resource "azurerm_postgresql_flexible_server_database" "example" {
  name      = "example-db"
  server_id = azurerm_postgresql_flexible_server.example.id
  collation = "en_US.utf8"
  charset   = "utf8"
}

Example calling with count:

resource "azurerm_postgresql_flexible_server_database" "example" {
  name      = "example-db"
  server_id = azurerm_postgresql_flexible_server.example[0].id
  collation = "en_US.utf8"
  charset   = "utf8"
}

This is because count is a meta argument and tells Terraform how many of that resource to create, so when it is set to 0 it creates 0 of that resource. But let’s say you tell Terraform to create 5, you would have to call the correct one you need and that’s where the index comes into play. When you use count to feature flag, you still have to call the index even if there is only one of that resource.

Why it is [0] in this example? This is because Terraform uses zero-based counting, which means it starts at 0 so the first object will always be index 0.

What about Modules?

You can also feature flag an entire module instead of each individual resource underneath a module. Then everything in the module either will or will not be built depending on how your feature flag variable is set.

module "postgresql" {
  source = "Azure/postgresql/azurerm"

  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_resource_group.example.location

  server_name                  = "example-server"
  sku_name                     = "GP_Gen5_2"
  storage_mb                   = 5120
  backup_retention_days        = 7
  geo_redundant_backup_enabled = false
  administrator_login          = "login"
  administrator_password       = "password"
  server_version               = "9.5"
  ssl_enforcement_enabled      = true
  db_names                     = ["my_db1", "my_db2"]
  db_charset                   = "UTF8"
  db_collation                 = "English_United States.1252"

  firewall_rule_prefix = "firewall-"
  firewall_rules = [
    { name = "test1", start_ip = "10.0.0.5", end_ip = "10.0.0.8" },
    { start_ip = "127.0.0.0", end_ip = "127.0.1.0" },
  ]

  vnet_rule_name_prefix = "postgresql-vnet-rule-"
  vnet_rules = [
    { name = "subnet1", subnet_id = "<subnet_id>" }
  ]

  tags = {
    Environment = "Production",
    CostCenter  = "Contoso IT",
  }

  postgresql_configurations = {
    backslash_quote = "on",
  }

  depends_on = [azurerm_resource_group.example]
  count = var.enable_postgres ? 1 : 0
}

About the Author

Object Partners profile.
Leave a Reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]