Terraform is a popular infrastructure-as-code software tool built by HashiCorp. You use it to provision all kinds of infrastructure and services, including New Relic dashboards and alerts.
Terragrunt is a thin wrapper that provides extra tools for keeping your configurations DRY, working with multiple Terraform modules, and managing remote state.
In this guide, you will use Terragrunt to generate your Terraform configurations, create files with Terragrunt, and reuse the Terragrunt configuration to manage multiple environments.
Before you begin
To use this guide, you should have some basic knowledge of both New Relic and Terraform. If you haven't deployed a New Relic open source agent yet, install New Relic Infrastructure agent for your host. Also, install the Terraform CLI.
Start by initializing a working directory:
You will also need to install Terragrunt to follow this guide:
$brew install terragrunt
To follow the examples in this guide, the accompanying example code is available on GitHub
$git clone https://github.com/jsbnr/nr-terraform-intro-example.git
Create a new configuration
Terragrunt is a wrapper that provides extra tooling for your Terraform configurations. In the next steps, you will create a new configuration using Terragrunt, Terraform, and New Relic.
Start by initializing a working directory:
$mkdir terragrunt-config && cd terragrunt-config
In the new directory, create a terragrunt.hcl
file, create a ./src
directory, and create a ./environments
directory:
$touch terragrunt.hcl$mkdir src$mkdir environments
Inside of your environments
folder, create a folder named dev
.
In the ./src
directory, create a main.tf
and provider.tf
files:
$mkdir environments/dev$touch src/main.tf$touch src/provider.tf
In the provider.tf file add the appropriate Terraform and New Relic provider configuration information:
terraform { required_version = "~> 0.14.3" required_providers { newrelic = { source = "newrelic/newrelic" version = "~> 2.14.0" } }}
In the main.tf add a New Relic alert policy named DemoPolicy:
resource "newrelic_alert_policy" "DemoPolicy" { name = "My Demo Policy"}
In the ./environments/dev
directory create a new file named terragrunt.hcl
$cd ./environments/dev/$touch terragrunt.hcl
Within the dev
folder update the terragrunt.hcl
file by adding the following include
statement. The include
statement tells Terragrunt to use any .hcl
confgiuration files it finds in parent folders.
include { path= find_in_parent_folders()}
Next, add a Terraform block to provide Terragrunt with a Terraform source reference.
terraform { source = "../../src"}
Now you need to configure the provider with your New Relic account details. You will need your New Relic user API key and account ID.
provider "newrelic" { account_id = 12345 # Your New Relic account ID api_key = "NRAK-***" # Your New Relic user key region = "US" # US or EU (defaults to US)}
Tip
If you have a single account: The account ID is displayed on the API keys page.
If your organization has multiple accounts: From one.newrelic.com, select Entity explorer and use the account selector dropdown near the top of the page.
Tip
Documentation about the New Relic provider is provided on the Terraform doc site.
Update the provider.tf file to define the New Relic account variables that will help keep the Terraform configuration dynamic.
variable "newrelic_personal_apikey" {}variable "newrelic_account_id" {}variable "newrelic_region" {}provider "newrelic" { account_id = var.newrelic_account_id api_key = var.newrelic_personal_apikey region = var.newrelic_region}
In your terminal, change directories to the environments/dev
folder and run the command terragrunt init
:
$terragrunt init
Terraform init sets up a bit of context, sets up environment variables, and then runs terraform init
. You will see the following output:
# Example output------------------------------------------------------------------------Terraform has created a lock file .terraform.lock.hcl to record the providerselections it made above. Include this file in your version control repositoryso that Terraform can guarantee to make the same selections by default whenyou run "terraform init" in the future.Terraform has been successfully initialized!You may now begin working with Terraform. Try running "terraform plan" to seeany changes that are required for your infrastructure. All Terraform commandsshould now work.If you ever set or change modules or backend configuration for Terraform,rerun this command to reinitialize your working directory. If you forget, othercommands will detect it and remind you to do so if necessary.[terragrunt] [/Users/jsius/Sites/deco/terragrunt-config/environments/dev] 2021/02/02 13:30:31 Copying lock file [output] from /Users/jsius/Sites/deco/terragrunt-config/environments/dev/.terragrunt-cache/e-PoBgWhdv3v8QGOtDQxS_WeYu4/69zjIFUfApJiUt8gFmi-6-dcPe8/.terraform.lock.hcl to /Users/jsius/Sites/deco/terragrunt-config/environments/dev
Run terragrunt plan
:
$terragrunt plan
Terragrunt starts to run but pauses to ask for the inputs to the New Relic account variables added to the provider.tf file.
# Example output------------------------------------------------------------------------2/02 13:32:08 Running command: terraform planvar.newrelic_account_id Enter a value:Interrupt received.
Exit the terragrunt plan
process with CTRL+C . In the environments/dev/terragrunt.hcl
file, add an inputs
block to provide the values for your New Relic account variables:
inputs = { newrelic_personal_apikey = "NRAK-***" # Your New Relic account ID newrelic_account_id = "12345" # Your New Relic account ID newrelic_region = "US" # US or EU (defaults to US)}
The updated terragrunt.hcl only has the include
, terraform
, and inputs
block
include { path = find_in_parent_folders()}terraform { source = "../../src"}inputs = { newrelic_personal_apikey = "NRAK-***" # Your New Relic account ID newrelic_account_id = "12345" # Your New Relic account ID newrelic_region = "US" # US or EU (defaults to US)}
Now, when you run terragrunt plan
, Terragrunt will provide the input block's values, no longer prompting the command line for values.
# Example output------------------------------------------------------------------------An execution plan has been generated and is shown below.Resource actions are indicated with the following symbols: + createTerraform will perform the following actions: # newrelic_alert_policy.DemoPolicy will be created + resource "newrelic_alert_policy" "DemoPolicy" { + account_id = (known after apply) + id = (known after apply) + incident_preference = "PER_POLICY" + name = "My Demo Policy" }Plan: 1 to add, 0 to change, 0 to destroy.------------------------------------------------------------------------Note: You didn't specify an "-out" parameter to save this plan, so Terraformcan't guarantee that exactly these actions will be performed if"terraform apply" is subsequently run.
Run terragrunt apply
. After Terraform finishes process, check your New Relic account for the new Demo Policy.
Adding to your configuration
Now that you have a basic New Relic configuration created, add configurations built in the Getting Started with Terraform and Terraform modules guides in this step.
Tip
If you haven't done the previous developer guides, you can copy the configurations for this section from the Terragrunt Intro Github Repo.
Update the main.tf to include the additional terraform configurations. Update the email address in the alert channel to your preferred email address:
resource "newrelic_alert_policy" "DemoPolicy" { name = "My Demo Policy"}resource "newrelic_alert_channel" "DemoChannel" { name = "My Demo Channel" type = "email" config { recipients = "your@email_address.com" include_json_attachment = "1" }}resource "newrelic_alert_policy_channel" "ChannelSubs" { policy_id = newrelic_alert_policy.DemoPolicy.id channel_ids = [ newrelic_alert_channel.DemoChannel.id ]}module "HostConditions" { source = "git::https://github.com/jsbnr/demo-terraform.git" policyId = newrelic_alert_policy.DemoPolicy.id cpu_critical = 88 cpu_warning = 78 diskPercent = 68}
You have added a New Relic alert channel, subscribed the demo policy to the alert channel, and added a module hosted on Github.
To clone the module from GitHub, run terragrunt init
and then run terragrunt apply
. After Terraform is done processing, You will see your Demo Policy now has two conditions in your New Relic account and one alert channel attached.
Using your Environment as a Terragrunt Variable
Using Terragrunt, you can add the name of the environment you are running to the name of the data you are creating, making your resource more identifiable in New Relic.
In the root terragrunt.hcl file, create a new input for env_name
inputs = { env_name = "develop"}
in the main.tf file add a new variable for 'env_name'
variable "env_name" {}
Using string interpolation, add the new env_name
variable to the alert policy and alert channel resource blocks.
resource "newrelic_alert_policy" "DemoPolicy" { name = "${var.env_name}: My Demo Policy"}resource "newrelic_alert_channel" "DemoChannel" { name = "${env_name}: My Demo Channel" type = "email" config { recipients = "your@email_address.com" include_json_attachment = "1" }}
Run terragrunt plan
to see the environment variable added to the configuration name.
# Example output------------------------------------------------------------------------# newrelic_alert_policy.DemoPolicy will be updated in-place~ resource "newrelic_alert_policy" "DemoPolicy" { id = "1216533" ~ name = "My Demo Policy" -> "develop: My Demo Policy" # (2 unchanged attributes hidden) }# newrelic_alert_policy_channel.ChannelSubs must be replaced-/+ resource "newrelic_alert_policy_channel" "ChannelSubs" { ~ channel_ids = [ - 4737437, ] -> (known after apply) # forces replacement ~ id = "1216533:4737437" -> (known after apply) # (1 unchanged attribute hidden) }
Currently, the environment variable name is hardcoded into the root terragrunt.hcl file. You want to avoid having your variables hardcoded, using a terragrunt built-in function will pass the value.
In the root terragrunt.hcl file, update the input to use the path_relative_to_include()
built-in function and pass the value has the env_name
variable:
inputs = { env_name = path_relative_to_include()}
Run terragrunt plan
. Looking at the output, the env_name
variable will have the entire ./environments/dev/
path.
# Example output------------------------------------------------------------------------# newrelic_alert_policy.DemoPolicy will be updated in-place~ resource "newrelic_alert_policy" "DemoPolicy" { id = "1216533" ~ name = "My Demo Policy" -> "environments/dev: My Demo Policy" # (2 unchanged attributes hidden) }# newrelic_alert_policy_channel.ChannelSubs must be replaced-/+ resource "newrelic_alert_policy_channel" "ChannelSubs" { ~ channel_ids = [ - 4737437, ] -> (known after apply) # forces replacement ~ id = "1216533:4737437" -> (known after apply) # (1 unchanged attribute hidden) }Plan: 2 to add, 1 to change, 2 to destroy.
Next, update the terragrunt.hcl to only include 'dev' as the env_name
variable. Adding a locals
block creates a local variable, using the replace built-in function will remove the unwanted parts of the relative path, and update the inputs
block to use the local variable.
locals { env_name = replace(path_relative_to_include(), "environments/", "")}inputs = { env_name = local.env_name}
Run terragrunt plan
. Looking at the output the env_name
variable will now have 'dev:' added. Run terragrunt apply
to update your configurations.
# Example output------------------------------------------------------------------------ # newrelic_alert_policy.DemoPolicy will be updated in-place ~ resource "newrelic_alert_policy" "DemoPolicy" { id = "1216533" ~ name = "My Demo Policy" -> "dev: My Demo Policy" # (2 unchanged attributes hidden) } # newrelic_alert_policy_channel.ChannelSubs must be replaced-/+ resource "newrelic_alert_policy_channel" "ChannelSubs" { ~ channel_ids = [ - 4737437, ] -> (known after apply) # forces replacement ~ id = "1216533:4737437" -> (known after apply) # (1 unchanged attribute hidden) }Plan: 2 to add, 1 to change, 2 to destroy.
Storing the State Remotely
At the moment, the state file is local. The next steps will update your Terraform configurations to remove the local state file and use a file stored remotely in Amazon S3.
Tip
Since Terragrunt allows you to configure multiple environments, various state files will need to be store in separate S3 buckets, not to override each other.
Terragrunt allows you to generate files on the fly. Adding the remote_state
block into the root level terragrunt.hcl file, you will generate a new file named backend.tf.
remote_state { backend = "s3" generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" } config = { bucket = "my-terraform-state" key = "${path_relative_to_include()}/terraform.tfstate" region = "us-east-1" encrypt = true }}
After adding the remote_state
block, you will need to give the configuration for the S3 bucket that will store the Terraform state.
Tip
To give access to the S3 bucket in your Amazon account, create an IAM user. Give the IAM user access to the S3 bucket storing the terraform state.
Now, update the remote_state
block to provide your S3 bucket and update the key to use the local environment variable created previously.
remote_state { backend = "s3" generate = { path = "backend.tf" if_exists = "overwrite_terragrunt" } config = { bucket = "YOUR_S3_BUCKET_NAME" # Amazon S3 bucket required key = "envs/${local.env_name}/terraform.tfstate" region = "us-east-1" encrypt = true profile = "YOUR_PROFILE_NAME" # Profile name required }}
Next, run terragrunt plan
.
If you look in the Amazon S3 bucket, you will see a folder named envs. Within the envs folder is a directory called devs containing terraform.tfstate file.
Tip
To see the files created by Terragrunt, look inside of the ./environments/dev
directory. There is a hidden directory named terragrunt-cache. Within the terragrunt-cache directory, you will see the backend.tf file that Terragrunt generated.
Creating a New Environment
Now that you have a working configuration for your dev environment, you will create a new environment reusing the dev configurations in the next steps.
In the environments directory, create a new folder names nonprod. In the nonprod folder, create a new terragrunt.hcl file:
$mkdir nonprod && cd nonprod$touch terragrunt.hcl
In the nonprod terragrunt.hcl
file copy over the configuration from the environment terragrunt.hcl:
include { path= find_in_parent_folders()}terraform { source = "../../src"}inputs = { newrelic_personal_apikey = "NRAK-***" # Your New Relic account ID newrelic_account_id = "12345" # Your New Relic account ID newrelic_region = "US" # US or EU (defaults to US)}
Tip
If you are using a different account for your nonprod environment, you can update the inputs with a new New Relic account ID and API key.
Inside of your nonprod directory, run terragrunt init
then run terragrunt apply
.
In the output, you will see Terraform has created a new set of resources. The new resources should have the nonprod name prefixed.
Adding variables in the configurations allows your configurations to be more dynamic. In the next steps, you will add variables to your configurations.
In the main.tf add new variables for the Host Conditions modules cpu_critical
, cpu_warning
, and diskPercentage
values:
variable "cpu_critical" {default = 89}variable "cpu_warningl" {default = 79}variable "diskPercentage" {default = 69}
In the main.tf update the HostConditions
module values to use the cpu_critical
, cpu_warning
, and diskPercentage
variables:
module "HostConditions" { source = "git::https://github.com/jsbnr/demo-terraform.git" policyId = newrelic_alert_policy.DemoPolicy.id cpu_critical = var.cpu_critical cpu_warning = var.cpu_warninig diskPercent = var.dskPercentage}
Run terragrunt plan
. In the output, the HostConditions
values have changed to use the variable defaults.
Next, you want to pass the values into your environment configurations by adding the inputs block variables. In the nonprod/terragrunt.hcl
file, add the cpu_critical
, cpu_warning
, and diskPercentage
variables:
inputs = { newrelic_personal_apikey = "NRAK-***" # Your New Relic account ID newrelic_account_id = "12345" # Your New Relic account ID newrelic_region = "US" # US or EU (defaults to US) cpu_critical = 50 cpu_warninig = 40 diskPercentage = 98}
Run terragrunt apply
. In your New Relic account, you will see a new policy with configurations changed based on the environment.
Conclusion
Congratulations! You've used Terragrunt to generate your New Relic configurations and manage multiple environments. Review the Terragrunt Intro Example, New Relic Terraform provider documentation, and Terragrunt quick start to learn how you can take your configuration to the next level.