DEV Community

Cover image for Terraform - Sensitive Output
Marcel.L
Marcel.L

Posted on • Updated on

Terraform - Sensitive Output

Overview

This tutorial uses examples from the following GitHub project: Azure Terraform Deployments.

When creating terraform configurations, especially when using CI/CD tooling such as Azure DevOps or GitHub it is very easy to overlook what exactly is being output as part of a Terraform configuration plan, especially if the configuration contains sensitive data. This could lead to sensitive data and settings to be leaked.

In todays tutorial we will look at examples on how we can protect and hide sensitive data in terraform output using masking.

Sensitive Variable Type

In the following demo configuration we will use Terraform to create the following resources in Azure:

  • Resource Group
  • App Service Plan
  • App Insights
  • App Service

NOTE: All the code samples used in this tutorial are updated to use the the latest version of the AzureRM provider 3.0.

Let's take a closer look at the App service configuration:



## appservices.tf ##
resource "azurerm_linux_web_app" "APPSVC" {
  name                = var.appsvc_name
  location            = azurerm_resource_group.RG.location
  resource_group_name = azurerm_resource_group.RG.name
  service_plan_id     = azurerm_service_plan.ASP.id
  https_only          = true

  identity {
    type = "SystemAssigned"
  }

  site_config {
    container_registry_use_managed_identity = true
    ftps_state                              = "FtpsOnly"
    application_stack {
      docker_image     = "${var.acr_name}.azurecr.io/${var.appsvc_name}"
      docker_image_tag = "latest"
    }
    vnet_route_all_enabled = var.vnet_route_all_enabled
  }

  app_settings = var.appsvc_settings
}


Enter fullscreen mode Exit fullscreen mode

Notice the setting called app_settings = var.appsvc_settings. The variable for this setting is defined as a map:



## variables.tf ##
variable "appsvc_settings" {
  type        = map(any)
  description = "Specifies the app service settings to be created."
  default     = null
}


Enter fullscreen mode Exit fullscreen mode

If we pass the following variable into the terraform config:



appsvc_settings = {
  APPINSIGHTS_INSTRUMENTATIONKEY = "!!sensitive_Key!!"
  sensitive_key1                 = "P@ssw0rd01"
  sensitive_key2                 = "P@ssw0rd02"
}


Enter fullscreen mode Exit fullscreen mode

Notice that when the terraform plan is being run, terraform will actually output the variable into the terraform plan and log to the CI/CD tooling as output:

image.png

This can be an issue because the data will be leaked to anyone who has access to the CI/CD logs/output. Especially dangerous if the repository or project is public.

So what we can do to mask the setting from output is to mark the variable as sensitive. We can do that by adding sensitive = true to the variable:



## variables.tf ##
variable "appsvc_settings" {
  type        = map(any)
  description = "Specifies the app service settings to be created."
  default     = null
  sensitive   = true
}


Enter fullscreen mode Exit fullscreen mode

Notice now that when the terraform plan is being run, terraform will mask the output of the variable:

image.png

Sensitive Output Type

Similarly to variables, outputs can also be marked as sensitive. For example say we want to create a sensitive output we can mark the output as sensitive = true as shown the the below example:



## appservices.tf ##
resource "azurerm_application_insights" "INSIGHTS" {
  name                = var.app_insights_name
  location            = azurerm_resource_group.RG.location
  resource_group_name = azurerm_resource_group.RG.name
  application_type    = "web"
  workspace_id        = var.workspace_id != null ? var.workspace_id : null
}

output "insights_key" {
    value = azurerm_application_insights.INSIGHTS.instrumentation_key
    sensitive = true
}


Enter fullscreen mode Exit fullscreen mode

Sensitive Function

Another way to mark output as sensitive is by using the sensitive() function. in the demo configuration let's change the way we send app_settings to the app service configuration by creating a dynamic locals config instead of a variable:



## local.tf ##
locals {
  #Appsvc Settings
  app_settings = {
    default_settings = {
      APPINSIGHTS_INSTRUMENTATIONKEY = "${azurerm_application_insights.INSIGHTS.instrumentation_key}"
      DOKCER_REGISTRY_SERVER_URL     = "${var.acr_name}.azurecr.io"
    },
    linux_app_settings = {
      APPINSIGHTS_INSTRUMENTATIONKEY = "${azurerm_application_insights.INSIGHTS.instrumentation_key}"
      DOKCER_REGISTRY_SERVER_URL     = "${var.acr_name}.azurecr.io"
      WEBSITE_PULL_IMAGE_OVER_VNET   = "true"
      LINUX_SENSITIVE_VALUE          = "!!sensitive_value!!"
    }
  }

}


Enter fullscreen mode Exit fullscreen mode

As you can see the locals configuration has two configurations, one called default_settings and another called linux_app_settings, we can send the relevant config by using the terraform lookup() function as shown below app_settings = lookup(local.app_settings, "linux_app_settings", null):



## appservices.tf ##
resource "azurerm_linux_web_app" "APPSVC" {
  name                = var.appsvc_name
  location            = azurerm_resource_group.RG.location
  resource_group_name = azurerm_resource_group.RG.name
  service_plan_id     = azurerm_service_plan.ASP.id
  https_only          = true

  identity {
    type = "SystemAssigned"
  }

  site_config {
    container_registry_use_managed_identity = true
    ftps_state                              = "FtpsOnly"
    application_stack {
      docker_image     = "${var.acr_name}.azurecr.io/${var.appsvc_name}"
      docker_image_tag = "latest"
    }
    vnet_route_all_enabled = var.vnet_route_all_enabled
  }

  app_settings = lookup(local.app_settings, "linux_app_settings", null)
}


Enter fullscreen mode Exit fullscreen mode

Since our app insights instrumentation key output is already marked as a sensitive output, it is all good and well for that value to be hidden from the output:

image.png

But what about if we want the entire app_settings config block to be hidden?

This is where the sensitive() function comes in, as you can see by just wrapping the relevant locals variables in the sensitive() function will instruct terraform to hide the entire block from output:



## local.tf ##
locals {
  #Appsvc Settings
  app_settings = {
    default_settings = sensitive({
      APPINSIGHTS_INSTRUMENTATIONKEY = "${azurerm_application_insights.INSIGHTS.instrumentation_key}"
      DOKCER_REGISTRY_SERVER_URL     = "${var.acr_name}.azurecr.io"
    }),
    linux_app_settings = sensitive({
      APPINSIGHTS_INSTRUMENTATIONKEY = "${azurerm_application_insights.INSIGHTS.instrumentation_key}"
      DOKCER_REGISTRY_SERVER_URL     = "${var.acr_name}.azurecr.io"
      WEBSITE_PULL_IMAGE_OVER_VNET   = "true"
      LINUX_SENSITIVE_VALUE          = "!!sensitive_value!!"
    })
  }

}


Enter fullscreen mode Exit fullscreen mode

image.png

image.png

I hope you have enjoyed this post and have learned something new. You can also find the code samples used in this blog post on my GitHub page. ❤️

Author

Like, share, follow me on: 🐙 GitHub | 🐧 X/Twitter | 👾 LinkedIn

Top comments (1)

Collapse
 
untada profile image
Unknownntada

But store sensetive data in TFVARS file is the best practice?