state management in terraform - azure

I'm building terraform scripts to orcastrate Azure deployment. I used Azure blob storage to store a tfstate file. This file is shared with several pipelines IAC pipelines.
If for instance I create an Azure Resource Group with terraform, when that is done, I try to create a new custom role, terraform plan will mark the Resource Group for destruction.
This is the script for the role creation:
terraform {
backend "azurerm" {
storage_account_name = "saiac"
container_name = "tfstate"
key = "dev.terraform.tfstate"
resource_group_name = "rg-devops"
}
}
data "azurerm_subscription" "primary" {
}
resource "azurerm_role_definition" "roles" {
count = length(var.roles)
name = "${var.role_prefix}${var.roles[count.index]["suffix_name"]}${var.role_suffix}"
scope = "${data.azurerm_subscription.primary.id}"
permissions {
actions = split(",", var.roles[count.index]["actions"])
not_actions = split(",", var.roles[count.index]["not_actions"])
}
assignable_scopes = ["${data.azurerm_subscription.primary.id}"]
}
and this is script for resource group creation:
terraform {
backend "azurerm" {
storage_account_name = "saiac"
container_name = "tfstate"
key = "dev.terraform.tfstate"
resource_group_name = "rg-devops"
}
}
resource "azurerm_resource_group" "rg" {
count = "${length(var.rg_purposes)}"
name = "${var.rg_prefix}-${var.rg_postfix}-${var.rg_purposes[count.index]}"
location = "${var.rg_location}"
tags = "${var.rg_tags}"
}
If I remove the backend block, everything works as expected, does that mean I need the backend block?

Terraform use the .tfstate file to check and compare your code and existing cloud infra structure, it is like backbone of terraform.
If your code and existing infra is differe, terraform will destroy it and apply code changes.
To overcome this, terraform provides the import facility, you can import the existing resource and terraform will update it's .tfstate file.
This .tfstate file must be specify into your backend.tf file,best practices is to store your .tfstate file on cloude storage not in local directory.
When you run the terraform init command, it will check for the .tfstate file.
below is the sample file for backend.tf file (aws s3 is used):
backend "s3" {
bucket = "backends.terraform.file"
key = "my-terraform.tfstate_key"
region = "my-region-1"
encrypt = "false"
acl = "bucket-owner-full-control"
}
}

A terraform backend is not required for terraform. If you do not use it however no one else will be able to pull your code and run your terraform. The state will ONLY be stored in your .terraform directory. This means if you lose your local files your in trouble. It is recommended to use a backend that also supports state locking which azurerm does. With a backend in place the state will get pulled on terraform init after pulling the repo.

Related

Get the .tf file from azure remote backend in terraform

I am trying the terraform operations using azure as remote backend. So far, I have been able to store the state remotely in azure. I am now unable to retrieve the state from remote, as I do not store the .tf files or .tfstate files locally. How to fetch the same from remote? This is the code I have:
terraform init -backend-config=config.backend.tfbackend
terraform state pull # fails due to no configuration file existing locally
This is my config.backend.tfbackend
backend "azurerm" {
resource_group_name = "rg098"
storage_account_name = "tstate6565"
container_name = "tstate"
key = "test5176"
}

How to create Azure Databricks Notebook via Terraform?

So I am completely new to the terraform and I found that by using this in terraform main.tf I can create Azure Databricks infrastructure:
resource "azurerm_databricks_workspace" "bdcc" {
depends_on = [
azurerm_resource_group.bdcc
]
name = "dbw-${var.ENV}-${var.LOCATION}"
resource_group_name = azurerm_resource_group.bdcc.name
location = azurerm_resource_group.bdcc.location
sku = "standard"
tags = {
region = var.BDCC_REGION
env = var.ENV
}
}
And I also found here
That by using this I can even create particular notebook in this Azure DataBricks infrastructure:
resource "databricks_notebook" "notebook" {
content_base64 = base64encode(<<-EOT
# created from ${abspath(path.module)}
display(spark.range(10))
EOT
)
path = "/Shared/Demo"
language = "PYTHON"
}
But since I am new to this, I am not sure in what order I should put those pieces of code together.
It would be nice if someone could point me to the full example of how to create notebook via terraform on Azure Databricks.
Thank you beforehand!
In general you can put these objects in any order - it's a job of the Terraform to detect dependencies between the objects and create/update them in the correct order. For example, you don't need to have depends_on in the azurerm_databricks_workspace resource, because Terraform will find that it needs resource group before workspace could be created, so workspace creation will follow the creation of the resource group. And Terraform is trying to make the changes in the parallel if it's possible.
But because of this, it's becoming slightly more complex when you have workspace resource together with workspace objects, like, notebooks, clusters, etc. As there is no explicit dependency, Terraform will try create notebook in parallel with creation of workspace, and it will fail because workspace doesn't exist - usually you will get a message about authentication error.
The solution for that would be to have explicit dependency between notebook & workspace, plus you need to configure authentication of Databricks provider to point to newly created workspace (there are differences between user & service principal authentication - you can find more information in the docs). At the end your code would look like this:
resource "azurerm_databricks_workspace" "bdcc" {
name = "dbw-${var.ENV}-${var.LOCATION}"
resource_group_name = azurerm_resource_group.bdcc.name
location = azurerm_resource_group.bdcc.location
sku = "standard"
tags = {
region = var.BDCC_REGION
env = var.ENV
}
}
provider "databricks" {
host = azurerm_databricks_workspace.bdcc.workspace_url
}
resource "databricks_notebook" "notebook" {
depends_on = [azurerm_databricks_workspace.bdcc]
...
}
Unfortunately, there is no way to put depends_on on the provider level, so you will need to put it into every Databricks resource that is created together with workspace. Usually the best practice is to have a separate module for workspace creation & separate module for objects inside Databricks workspace.
P.S. I would recommend to read some book or documentation on Terraform. For example, Terraform: Up & Running is very good intro

How can I reuse the terraform output values in another deployment

I have a terraform code that deploys Azure resources and outputs a bunch of values I need to use for further configuration.
I have initialized terraform backend and the state is saved to an Azure Storage account, I see the tfstate file with all the correct values.
FYI I have added this configuration but still no luck, also I am running the terraform init command in the second location so the backend is initialized with the same state:
backend "azurerm" {
storage_account_name = "${var.STATE_STORAGE_ACCOUNT_NAME}"
container_name = "${var.STATE_CONTAINER_NAME}"
key = "${var.STATE_STORAGE_ACCOUNT_KEY}"
}
What I want to be able to do is pull this state in some way so I can do terraform output -raw some_output in a different location than where I deployed the resources.
I can't seem to find a way to do this. How could this be achieved? Thanks
It really depends on your use case. You can take two different approaches:
Import resource with "data" source
Data sources allow Terraform use information defined outside of Terraform, defined by another separate Terraform configuration, or modified by functions.
Terraform docs
For AWS it would be something like this:
// create ssm parameter in Terraform code A
resource "aws_ssm_parameter" "secret" {
name = "/secret"
type = "String"
value = "SecretValue"
}
// Import this resource in Terraform code B
data "aws_ssm_parameter" "imported_secret" {
name = "/secret"
}
// So later you can reference it
locals any {
secretValue = data.aws_ssm_parameter.secret.value
}
Create modules
Modules are containers for multiple resources that are used together. A module consists of a collection of .tf and/or .tf.json files kept together in a directory. Modules are the main way to package and reuse resource configurations with Terraform.
Terraform docs
It is basic example of Terraform modules. We created module vpc where source of this module is in ../../modules/vpc directory and we referenced this module by module.vpc in rds module.
module "vpc" {
source = "../../modules/vpc"
env = var.env
azs = var.azs
cidr = var.cidr
db_subnets = var.db_subnets
private_subnets = var.private_subnets
public_subnets = var.public_subnets
}
module "rds" {
source = "../../modules/rds"
db_subnets_cidr_blocks = module.vpc.db_subnets_cidr_block
private_subnets_cidr_blocks = module.vpc.private_subnets_cidr_block
public_subnets_cidr_blocks = module.vpc.public_subnets_cidr_block
vpc_id = module.vpc.vpc_id
env = var.env
db_subnets_ids = module.vpc.db_subnets
}
I didn't find a straight forward way to do this, so the solution was, since the state file was being saved to Azure Blob Storage:
Run an Azure CLI command to get the blob locally:
az storage blob download --container-name tstate --file $tf_state_file_name --name $tf_state_file_name --account-key $tf_state_key --account-name $tf_state_storage_account_name
Where the local file name is: $tf_state_file_name
Read the desired values using JQ:
jq '.outputs.storage_account_name.value' ./$tf_state_file_name -r
You can read the values raw thanks to the -r paramater. This is the same as doing:
terraform output -raw storage_account_name

data source terraform_remote_state with workspaces

I'm running terraform v0.14.8 with a non-default terraform workspace.
I have an Atlantis server that handles my plans and applies.
When I clone my repo locally and run my plans I get errors about my datasource. I don't quite understand why as I don't get these errors on my Atlantis server which I believe performs the same operations. The Atlantis server also uses tf v0.14.8.
My terraform:
data "terraform_remote_state" "route53" {
backend = "s3"
config = {
key = "web/terraform.tfstate"
region = "us-west-2"
bucket = "prod-terraform"
role_arn = "arn:aws:iam::xxxxxxxxxx:role/atlantis"
}
Before I run my local plan, i switch the workspace
terraform workspace select web
# in addition I also tried
export TF_WORKSPACE=web
My plan
teraform plan
...
Error: Unable to find remote state
on provider.tf line 46, in data "terraform_remote_state" "route53":
46: data "terraform_remote_state" "route53" {
No stored state was found for the given workspace in the given backend.
I could easily edit my "key" with env: and things will work, but I'm trying to figure out how to do this without making that adjustment, seeing that my Atlantis server just works.
data "terraform_remote_state" "route53" {
backend = "s3"
config = {
key = "env:/web/web/terraform.tfstate"
region = "us-west-2"
bucket = "prod-terraform"
role_arn = "arn:aws:iam::xxxxxxxxxx:role/atlantis"
}
Your question seems to imply some confusion over which of these backends the web workspace is selected for. Running terraform workspace select web selects the web workspace from the backend of the current configuration (the directory where you are running Terraform), but I suspect your intent is to select the web backend from the backend you've configured in data "terraform_remote_state" instead.
If so, you can do that by setting the workspace argument in the data source configuration:
data "terraform_remote_state" "route53" {
backend = "s3"
workspace = "web"
config = {
key = "web/terraform.tfstate"
region = "us-west-2"
bucket = "prod-terraform"
role_arn = "arn:aws:iam::xxxxxxxxxx:role/atlantis"
}
}

Terraform remote state issue for Azureblob in 0.12.x

I am using Azure provider and storing the terraform state in Azure blob storage. Using the below code snippet for this.
data "terraform_remote_state" "xxxxxx" {
backend = "azurerm"
config = {
container_name = "terraform-state"
resource_group_name = "${var.remote_state_resource_group}"
storage_account_name = "${var.remote_state_storage_account}"
access_key = "${var.remote_state_credentials}"
key = "${var.cluster_name}-k8s-worker"
}
defaults = {}
}
If i run the above code with latest version of terraform version 0.12.x, it is failing with below error. But running the same code with 0.11.x, it is working as expected.
Error message:
Error: Unable to find remote state
on example2.tf line 20, in data "terraform_remote_state" "xxxxxx":
20: data "terraform_remote_state" "xxxxxx" {
No stored state was found for the given workspace in the given backend.
Any one faced similar issue in terraform 0.12.x with Azure blob storage.
I think the possible reasons would be here:
use the wrong storage account
use the wrong container name
use the wrong key
All the above reasons will cause the error you got. And the remote state works fine in terraform version 0.12.x.
I have encountered this issue when I have one terraform configuration that stores state in azurerm and then I want to use that state in another terraform configuration as a remote azurerm data source.
Specifically the issue appears when the first configuration uses terraform workspaces. The azurerm backend silently appends a suffix of the form env:${terraform.workspace} at the end of the blob key. You must explicitly correct for this in the data source.
If the backend of the first configuration looks like this:
terraform {
backend "azurerm" {
resource_group_name = "rg-myapp"
storage_account_name = "myappterraform"
container_name = "tfstate"
key = "myapp.tfstate"
}
}
The data source of the second configuration must look like this:
data "terraform_remote_state" "myapp" {
backend = "azurerm"
config = {
resource_group_name = "rg-myapp"
storage_account_name = "myappterraform"
container_name = "tfstate"
key = "myapp.tfstateenv:${terraform.workspace}"
}
}

Resources