Could not read output attribute from remote state datasource - terraform

I am new to terraform so I will attempt to explain with the best of my ability. Terraform will not read in the variable/output from the statefile and use that value in another file.
I have tried searching the internet for everything I could find to see if anyone how has had this problem and how they fixed it.
###vnet.tf
#Remote State pulling data from bastion resource group state
data "terraform_remote_state" "network" {
backend = "azurerm"
config = {
storage_account_name = "terraformstatetracking"
container_name = "bastionresourcegroups"
key = "terraform.terraformstate"
}
}
#creating virtual network and putting that network in resource group created by bastion.tf file
module "quannetwork" {
source = "Azure/network/azurerm"
resource_group_name = "data.terraform_remote_state.network.outputs.quan_netwk"
location = "centralus"
vnet_name = "quan"
address_space = "10.0.0.0/16"
subnet_prefixes = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
subnet_names = ["subnet1", "subnet2", "subnet3"]
tags = {
environment = "quan"
costcenter = "it"
}
}
terraform {
backend "azurerm" {
storage_account_name = "terraformstatetracking"
container_name = "quannetwork"
key = "terraform.terraformstate"
}
}
###resourcegroups.tf
# Create a resource group
#Bastion
resource "azurerm_resource_group" "cm" {
name = "${var.prefix}cm.RG"
location = "${var.location}"
tags = "${var.tags}"
}
#Bastion1
resource "azurerm_resource_group" "network" {
name = "${var.prefix}network.RG"
location = "${var.location}"
tags = "${var.tags}"
}
#bastion2
resource "azurerm_resource_group" "storage" {
name = "${var.prefix}storage.RG"
location = "${var.location}"
tags = "${var.tags}"
}
terraform {
backend "azurerm" {
storage_account_name = "terraformstatetracking"
container_name = "bastionresourcegroups"
key = "terraform.terraformstate"
}
}
###outputs.tf
output "quan_netwk" {
description = "Quan Network Resource Group"
value = "${azurerm_resource_group.network.id}"
}
When running the vnet.tf code it should read in the output from the outputs.tf which is stored in the azure backend storage account statefile file and use that value for the resource_group_name in the quannetwork module. Instead it creates a resource group named data.terraform_remote_state.network.outputs.quan_netwk. Any help would be greatly appreciated.

First, you need to input a string for the resource_group_name in your module quannetwork, not the resource group Id.
Second, if you want to quote something in the remote state, do not just put it in the Double quotes, the right format below:
resource_group_name = "${data.terraform_remote_state.network.outputs.quan_netwk}"

Related

Terraform Azurerm: Create blob if not exists

I got Terrafrom code that creates storage account, container and block blob. Is it possible to configure that block blob is created only if it doesn't already exist?
In case of re-running terraform I wouldn't like to replace blob if it is already there as the content might have been manually modified and i would like to keep it.
Any tips? Only alternative I could think of is running powershell/bash script during further deployment steps that would create file if needed, but I am curious if this can be done just with Terraform.
locals {
storage_account_name_teast = format("%s%s", local.main_pw_prefix_short, "teast")
}
resource "azurerm_storage_account" "teaststorage" {
name = local.storage_account_name_teast
resource_group_name = azurerm_resource_group.main.name
location = var.location
account_tier = var.account_tier
account_replication_type = var.account_replication_type
allow_nested_items_to_be_public = false
min_tls_version = "TLS1_2"
network_rules {
default_action = "Deny"
bypass = [
"AzureServices"
]
virtual_network_subnet_ids = []
ip_rules = local.ip_rules
}
tags = var.tags
}
resource "azurerm_storage_container" "teastconfig" {
name = "config"
storage_account_name = azurerm_storage_account.teaststorage.name
container_access_type = "private"
}
resource "azurerm_storage_blob" "teastfeaturetoggle" {
name = "featureToggles.json"
storage_account_name = azurerm_storage_account.teaststorage.name
storage_container_name = azurerm_storage_container.teastconfig.name
type = "Block"
source = "vars-pr-default-toggles.json"
}
After scanning through terraform plan I figured out it was forcing a blob replacement because of:
content_md5 = "9a95db04fb1ff3abcd7ff81fcfb96307" -> null # forces replacement
I added lifecycle hook to blob resource to prevent it:
resource "azurerm_storage_blob" "teastfeaturetoggle" {
name = "featureToggles.json"
storage_account_name = azurerm_storage_account.teaststorage.name
storage_container_name = azurerm_storage_container.teastconfig.name
type = "Block"
source = "vars-pr-default-toggles.json"
lifecycle {
ignore_changes = [
content_md5,
]
}
}

Accessing specific storage account id created using terraform module

I am creating an infrastructure with terraform modules. Some of the common and repeatitive infra are created using module
and other resources are created independently outside of the module. The structure of my code is described as below.
-terraform\module\storage.tf
-terraform\main.tf
-terraform\mlws.tf
This is my code for /module/storage.tf where I am createing a storage account like this
resource "azurerm_storage_account" "storage" {
name = var.storage_account_name
resource_group_name = var.rg_name
location = var.location
account_tier = "Standard"
account_replication_type = "GRS"
min_tls_version = "TLS1_2"
}
module "m1" {
source = "./modules"
storage_account_name = "m1storage"
rg_name = "rg1"
location = "USCentral"
}
module "m2" {
source = "./modules"
storage_account_name = "m2storage"
rg_name = "rg2"
location = "USCentral"
}
module "m3" {
source = "./modules"
storage_account_name = "m3storage"
rg_name = "rg3"
location = "USCentral"
}
resource "azurerm_machine_learning_workspace" "mlws" {
name = "mlws"
location = ""USCentral"
resource_group_name = "mlws-rg1"
application_insights_id = azurerm_application_insights.mlops_appins.id
key_vault_id = data.azurerm_key_vault.kv.id
storage_account_id = **<Mandatory to be filled>**
container_registry_id = azurerm_container_registry.acr.id
identity {
type = "SystemAssigned"
}
depends_on = [
module.m2
]
}
The code for storage account is under \terraform\module\storage.tf, The code for calling the module is under \terraform\main.tf, The code for machine learning workspace is under \terraform\mlws.tf.
Since my mlws.tf code is outside the module but it need to be associated with storage account id created under module m2 in above code.
I am struggling to fetch the id of "m2storage" storage account. Can you please provide solution on how can I access the id of specific storage account created through module and attach it with my code which is outside the module.
This is how it normally works. You run module m2 and it should give output something like this (should include storage_account_id):
output "storage_account_id" {
description = "M2 storage account id."
value = m2.storage_account.storage_account_id
}
Now you have the output and you want to use it you will refer to it as:
resource "azurerm_machine_learning_workspace" "mlws" {
name = "mlws"
location = ""USCentral"
resource_group_name = "mlws-rg1"
application_insights_id = azurerm_application_insights.mlops_appins.id
key_vault_id = data.azurerm_key_vault.kv.id
storage_account_id = module.m2.storage_account_id
container_registry_id = azurerm_container_registry.acr.id
identity {
type = "SystemAssigned"
}
depends_on = [
module.m2
]
}
Let me know if you need more help.

Trying to create azure storage account & use the same to store terraform state file

on the way to create aks via terraform, here i want to create azure storage account & want to use the same same account to store the terraform state file.
however getting below error
│ Error: Error loading state: Error retrieving keys for Storage Account "azurerm_resource_group.aks_rg.name": storage.AccountsClient#ListKeys: Invalid input: autorest/validation: validation failed: parameter=accountName constraint=MaxLength value="azurerm_resource_group.aks_rg.name" details: value length must be less than or equal to 24
│
#Create Resource Group
resource "azurerm_resource_group" "aks_rg" {
location = "${var.location}"
name = "${var.global-prefix}-${var.cluster-id}-${var.environment}-azwe-aks-rg"
}
#Create Storage Account & Container
resource "azurerm_storage_account" "storage_acc" {
name = "${var.cluster-id}-storage-account"
resource_group_name = azurerm_resource_group.aks_rg.name
location = azurerm_resource_group.aks_rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "storage_container" {
name = "${var.cluster-id}-storage-account-container"
storage_account_name = azurerm_storage_account.storage_acc.name
container_access_type = "private"
}
#store terraform state in remote container
terraform {
# Configure Terraform State Storage
backend "azurerm" {
resource_group_name = "azurerm_resource_group.aks_rg.name"
storage_account_name = "azurerm_storage_container.storage_acc.name"
container_name = "azurerm_storage_container.storage_container.name"
key = "terraform.tfstate"
}
}
You need to first create the storage account and container then while creating the aks cluster you need to give the below:
terraform {
# Configure Terraform State Storage
backend "azurerm" {
resource_group_name = "azurerm_resource_group.aks_rg.name"
storage_account_name = "azurerm_resource_group.aks_rg.name"
container_name = "powermeprodtfstate"
key = "terraform.tfstate"
}
}
Instead of creating the storage account and container ins the same file while storing the terraform tfstate.
Example:
Create storage account and container:
provider "azurerm" {
features {}
}
data "azurerm_resource_group" "example" {
name = "resourcegroupname"
}
resource "azurerm_storage_account" "example" {
name = "yourstorageaccountname"
resource_group_name = data.azurerm_resource_group.example.name
location = data.azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "example" {
name = "terraform"
storage_account_name = azurerm_storage_account.example.name
container_access_type = "private"
}
Then create the aks resource group and store the tfstate in container.
provider "azurerm" {
features {}
}
terraform {
# Configure Terraform State Storage
backend "azurerm" {
resource_group_name = "resourcegroup"
storage_account_name = "storageaccountnameearliercreated"
container_name = "terraform"
key = "terraform.tfstate"
}
}
resource "azurerm_resource_group" "aks_rg" {
name = "aks-rg"
location = "west us"
}
Reference:
How to store the Terraform state file in Azure Storage. » Jorge Bernhardt

Azure Databricks workspace using terraform

Trying to create Databricks workspace using terraform but unsupported arguments:
resource "azurerm_databricks_workspace" "workspace" {
name = "testdata"
resource_group_name = "cloud-terraform"
location = "east us"
sku = "premium"
virtual_network_id = azurerm_virtual_network.vnet.id
public_subnet_name = "databrickpublicsubnet"
public_subnet_cidr = "10.0.0.0/22"
private_subnet_name = "databrickprivatesubnet"
private_subnet_cidr = "10.0.0.0/22"
tags = {
Environment = "terraformtest"
}
}
Error: An argument named "virtual_network_id" is not expected here. An argument named "public_subnet_name" is not expected here. An argument named "public_subnet_cidr" is not expected here.
I haven't tried to set up databricks via Terraform, but I believe (per the docs) you need add those properties in a block:
resource "azurerm_databricks_workspace" "workspace" {
name = "testdata"
resource_group_name = "cloud-terraform"
location = "east us"
sku = "premium"
custom_parameters {
virtual_network_id = azurerm_virtual_network.vnet.id
public_subnet_name = "databrickpublicsubnet"
private_subnet_name = "databrickprivatesubnet"
}
tags = {
Environment = "terraformtest"
}
}
The two cidr entries aren't part of the TF documentation.
true. you can add terraform commands to create the subnets (assuming vnet already exists, you can use data azurerm_virtual_network then create the two new subnets, then reference the names of the two new public/private subnets.
Then you run into what seems to be a chicken/egg issue though.
You get Error: you must define a value for 'public_subnet_network_security_group_association_id' if 'public_subnet_name' is set.
Problem is, the network security group is typically auto-generated on creation of the databrick workspace (like databricksnsgrandomstring), which works when creating it in the portal, but via terraform, I have to define it to create the workspace, but it doesn't yet exist until I create the workspace. The fix is to not let it generate it's own nsg name, but name it yourself with an nsg resource block.
below is code I use (dbname means databricks name!). here I'm
adding to an existing resource group 'qa' and existing vnet as well, only showing the public subnet and nsg association, you can easily add the private ones). just copy/modify in your own tf file(s). and you'll definitely need to change the address_prefix to your own CIDR values that works within your vnet and not stomp on existing subnets within.
resource "azurerm_subnet" "public" {
name = "${var.dbname}-public-subnet"
resource_group_name = data.azurerm_resource_group.qa.name
virtual_network_name = data.azurerm_virtual_network.vnet.name
address_prefixes = ["1.2.3.4/24"]
delegation {
name = "databricks_public"
service_delegation {
name = "Microsoft.Databricks/workspaces"
}
}
}
resource "azurerm_network_security_group" "nsg" {
name = "${var.dbname}-qa-databricks-nsg"
resource_group_name = data.azurerm_resource_group.qa.name
location= data.azurerm_resource_group.qa.location
}
resource "azurerm_subnet_network_security_group_association" "nsga_public" {
network_security_group_id = azurerm_network_security_group.nsg.id
subnet_id = azurerm_subnet.public.id
}
Then in your databricks_workspace block, replace your custom parameters with
custom_parameters {
public_subnet_name = azurerm_subnet.public.name
public_subnet_network_security_group_association_id = azurerm_subnet_network_security_group_association.nsga_public.id
private_subnet_name = azurerm_subnet.private.name
private_subnet_network_security_group_association_id = azurerm_subnet_network_security_group_association.nsga_private.id
virtual_network_id = data.azurerm_virtual_network.vnet.id
}

How Do I Avoid Repeating A Variable In Terraform?

Terraform doesn't allow you to interpolate variables within the variables file otherwise you get the error:
Error: Variables not allowed
on variables.tf line 9, in variable "resource_group_name": 9:
default = "${var.prefix}-terraform-dev_rg"
Variables may not be used here.
This then means I end up duplicating the value of the prefix in my variables.tf file when I try to create the name for the resource group.
Is there a nice way around this to avoid duplicating the value of the variable?
variables.tf
variable "prefix" {
description = "The prefix used for all resources in this plan"
default = "terraform-dev"
}
variable resource_group_name {
type = "string"
default = "terraform-dev_rg"
}
variable resource_group_location {
type = "string"
default = "eastus"
}
main.tf
# Configure the Microsoft Azure Provider
provider "azurerm" {
version = "=1.28.0"
}
# Create a resource group
resource "azurerm_resource_group" "resource-group" {
name = var.resource_group_name
location = var.resource_group_location
}
#Create an application gateway with web app firewall
module "firewall" {
source = "./firewall"
resource_group_name = var.resource_group_name
resource_group_location = var.resource_group_location
}
./firewall/variables.tf
#Passed down from the root variables.tf
variable "prefix" {}
variable "resource_group_name" {}
variable "resource_group_location" {}
./firewall/main.tf
# Create a virtual network for the firewall
resource "azurerm_virtual_network" "firewall-vnet" {
name = "${var.prefix}-waf-vnet"
address_space = ["10.0.0.0/16"]
resource_group_name = var.resource_group_name
location = var.resource_group_location
}
Try to use local values,
https://www.terraform.io/docs/configuration/locals.html
variable "prefix" {
description = "The prefix used for all resources in this plan"
default = "terraform-dev"
}
variable resource_group_location {
type = "string"
default = "eastus"
}
locals {
resource_group_name = "${var.prefix}_rg"
}
resource "azurerm_resource_group" "resource-group" {
name = local.resource_group_name
location = var.resource_group_location
}
Terraform does not support variables inside a variable.
If you want to generate a value based on two or more variables then you can try Terraform locals (https://www.terraform.io/docs/configuration/locals.html).
Locals should help you here to achieve goal.
something like
variables.tf
variable "prefix" {
description = "The prefix used for all resources in this plan"
default = "terraform-dev"
}
variable resource_group_location {
type = "string"
default = "eastus"
}
main.tf
locals {
resource_group_name = "${var.prefix}_rg"
}
# Configure the Microsoft Azure Provider
provider "azurerm" {
version = "=1.28.0"
}
# Create a resource group
resource "azurerm_resource_group" "resource-group" {
name = local.resource_group_name
location = var.resource_group_location
}
Hope this helps.
Please read similar discussion here -https://stackoverflow.com/questions/58841060/terraform-variables-within-variables/58841360?noredirect=1#comment129460631_58841360

Resources