Using Terraform v0.13.5
I have a module which has some outputs derived from some sub modules within it, e.g.:
module "egressvnet" {
source = "../modules/vnet/egress"
}
output "subnet" {
value = module.egressvnet.subnet
}
terraform output confirms that what I expect to be outputted is.
Within another terraform setup I would like to reference the outputs from the above.
So I have this in my terraform config:
data "terraform_remote_state" "network" {
backend = "azurerm"
config = {
resource_group_name = "xxx"
storage_account_name = "xxx"
container_name = "terraform"
key = "network.tfstate"
}
}
module "web" {
source = "../modules/web"
subnet_id = terraform_remote_state.network.outputs.subnet
}
Is what I'm trying to do possible?
But when I do a plan I get this error:
Error: Reference to undeclared resource
on base.tf line 111, in module "web":
111: subnet_id = terraform_remote_state.network.outputs.subnet
A managed resource "terraform_remote_state" "network" has not been declared in
the root module.
Since your terraform_remote_state is a data source, you should refer to it using data.:
subnet_id = data.terraform_remote_state.network.outputs.subnet
Related
I have a module which has a variable defined using for_each, and its output is as below:
output "nic_ids" {
value = [for x in azurerm_network_interface.nic : x.id]
}
nic_ids = [
"/subscriptions/*****/resourceGroups/test-rg/providers/Microsoft.Network/networkInterfaces/test-nic-1",
"/subscriptions/*****/resourceGroups/test-rg/providers/Microsoft.Network/networkInterfaces/test-nic-2",
]
My aim is to pass above NIC ids to the VM module and have 1:1 mapping between NIC id and VM (test-nic-1 should only be attached to vm-1, test-nic-2 to vm-2 etc.)
module "vm" {
source = "*****/vm/azurerm"
version = "0.1.0"
vms = var.vms
nic_ids = module.nic[each.value.id].nic_ids
}
I am getting below error:
Error: each.value cannot be used in this context
on main.tf line 58, in module "vm":
58: nic_ids = module.nic[each.value.id].nic_ids
A reference to "each.value" has been used in a context in which it
unavailable, such as when the configuration no longer contains the value in
its "for_each" expression. Remove this reference to each.value in your
configuration to work around this error.
I used this similar question as reference.
Can you please suggest?
You could pass the above NIC id list to the VM modules with the count.
module "vm" {
source = "./modules/vms"
vm_names = ["aaa","bbb"]
nic_ids = module.nic.network_interface_nics
}
module "nic" {
source = "./modules/nic"
nic_names = ["nic1","nic2"]
}
the main.tf in the VM module:
resource "azurerm_virtual_machine" "vm-windows" {
count = length(var.vm_names)
name = var.vm_names[count.index]
resource_group_name = data.azurerm_resource_group.vm.name
location = var.location
vm_size = var.vm_size
network_interface_ids = [ var.nic_ids[count.index] ]
...
}
The output.tf in the NIC module:
output "network_interface_nics" {
description = "ids of the vm nics provisoned."
value = [for x in azurerm_network_interface.nic : x.id]
}
When i'm trying to initialize terraform, i'm getting following error only with vnet module,
But terraform initilization working with azure_resource_group, azure_virtual_machine modules
Terraform 0.13 and earlier allowed provider version constraints inside the
provider configuration block, but that is now deprecated and will be removed
in a future version of Terraform. To silence this warning, move the provider
version constraint into the required_providers block.
Error: Failed to query available provider packages
Could not retrieve the list of available versions for provider
hashicorp/azure: provider registry registry.terraform.io does not have a
provider named registry.terraform.io/hashicorp/azure
If you have just upgraded directly from Terraform v0.12 to Terraform v0.14
then please upgrade to Terraform v0.13 first and follow the upgrade guide for
that release, which might help you address this problem.
Did you intend to use terraform-providers/azure? If so, you must specify that
source address in each module which requires that provider. To see which
modules are currently depending on hashicorp/azure,
enter image description here
enter image description here
main.tf
resource "azurerm_virtual_network" "vnet" {
for_each = { for n in var.networks : n.name => n }
name = each.value.name
address_space = each.value.address_space
location = each.value.location
resource_group_name = each.value.rg_name
dynamic "subnet" {
for_each = each.value.subnets
content {
name = subnet.value.name
address_prefix = subnet.value.address_prefixes
}
}
}
variables.tf
variable networks {
type = list(object({
name = string
address_space = list(string)
rg_name = string
location = string
subnets = list(object({
name = string
address_prefixes = string
}))
}))
}
module (main.tf)
module "azurevnet"{
source = "./vnet"
networks = var.networks
}
provider.tf
provider "azurerm" {
version = "=2.37.0"
}
As I know, the registry terraform-providers/azure is a deprecated provider. This model will not add new things anymore and Azure already change into ARM model. So I recommend you use the terraform-providers/azurerm model only and it supports more Azure features.
Update:
And use the azurerm model, the directory structure would look like this:
main.tf
module "azurevnet" {
source = "./vnet"
networks = var.networks
}
providers.tf
provider "azurerm" {
features {}
version = "=2.37.0"
}
vnet/main.tf
variable "networks" {}
resource "azurerm_virtual_network" "vnet" {
for_each = { for n in var.networks : n.name => n }
name = each.value.name
address_space = each.value.address_space
location = each.value.location
resource_group_name = each.value.rg_name
dynamic "subnet" {
for_each = each.value.subnets
content {
name = subnet.value.name
address_prefix = subnet.value.address_prefixes
}
}
}
I only give the code for VNet, but other resources will be in the same format. And you can also do not use the providers.tf file and put the content into the main.tf file.
UPDATED
I am trying to provision multiple SQL databases in Azure using Terraform.
My child module has the following code that provisions a SQL database:
providers.tf
// default provider
provider "azurerm" {
alias = "main"
features {}
}
// The provider that can access the storage account to store diagnostics
provider "azurerm" {
alias = "storage_account"
features {}
}
sql_db.tf
resource "azurerm_mssql_database" "default" {
name = var.name
base_name = var.base_name
...
tags = var.tags
provider = azurerm.main
}
data.tf
data "azurerm_storage_account" "storage" {
name = var.storage_account_name
resource_group_name = var.storage_account_rg
provider = azurerm.storage_account
}
I am calling this module in my main.tf file as follows where I want to provision multiple SQL databases using a for_each:
module "sql_db" {
for_each = var.sql_db
source = "...../sql_db.git"
base_name = each.value.base_name
name = each.value.name
providers = {
azurerm.main = azurerm.main
azurerm.storage_account = azurerm.storage_account
}
}
provider "azurerm" {
features {}
version = "=2.20.0"
}
// default provider
provider "azurerm" {
alias = "main"
features {}
}
provider "azurerm" {
alias = "storage_account"
features {}
}
When I run plan, I get the following error:
Error: Module does not support for_each
on main.tf line 35, in module "sql_db":
35: for_each = var.sql_db
Module "sql_db" cannot be used with for_each because it contains a nested
provider configuration for "azurerm.main", at
.terraform\modules\sql_db\providers.tf:2,10-19.
This module can be made compatible with for_each by changing it to receive all
of its provider configurations from the calling module, by using the
"providers" argument in the calling module block.
Error: Module does not support for_each
on main.tf line 35, in module "sql_db":
35: for_each = var.sql_db
Module "sql_db" cannot be used with for_each because it contains a nested
provider configuration for "azurerm.storage_account", at
.terraform\modules\sql_db\providers.tf:8,10-19.
This module can be made compatible with for_each by changing it to receive all
of its provider configurations from the calling module, by using the
"providers" argument in the calling module block.
The simple answer is, it's not supported. From the Terraform documentation:
A module containing its own provider configurations is not compatible with the for_each, count, and depends_on arguments that were introduced in Terraform v0.13.
HashiCorp has been absolutely adamant that providers can never be declared dynamically, which is why they allow neither a for_each/count within a provider block, nor a for_each/count on a module that contains a provider block.
I have terraform directory structure as below:
terraform/
main.tf modules outputs.tf provider.tf variables.tf
./modules:
compute network resourcegroup
./modules/compute:
main.tf outputs.tf variables.tf
./modules/network:
main.tf outputs.tf variables.tf
./modules/resourcegroup:
main.tf outputs.tf variables.tf
resourcegroup module config files as below:
Purpose: In this module, I am referencing an existing resource group which I would like to utilized to create a Virtual machine and its associated objects.
main.tf
data "azurerm_resource_group" "tf-rg-external" {
name = var.rg_name
}
variables.tf
variable "rg_name" {
type = string
}
network module
Purpose: I would like to use resource group from resourcegroup module to be referenced in this module. That way, I define at one place and use it in root and other modules example, compute, app service, aks etc
main.tf
# Reference existing Virtual Network
data "azurerm_virtual_network" "tf-vn" {
name = var.vnet_name
resource_group_name = module.resource_groups.external_rg_name
}
# Reference existing subnet
data "azurerm_subnet" "tf-sn" {
name = var.subnet_name
virtual_network_name = data.azurerm_virtual_network.tf-vn.name
resource_group_name = module.resource_groups.external_rg_name
}
variables.tf
# Declare env variable
variable "vnet_name" {
type = string
}
variable "subnet_name" {
type = string
}
compute module.
Purpose: To define all attributes for compute(VM). The idea is, root module will use this module to spin up different VM roles.
main.tf
module "vm_iis" {
source = "Azure/compute/azurerm"
location = data.resourcegroup.tf-rg-external.location
vnet_subnet_id = data.network.tf-sn.id
admin_password = var.admin_password
data_sa_type = var.data_sa_type
delete_os_disk_on_termination = var.delete_os_disk_on_termination
nb_instances = var.nb_instances
nb_public_ip = var.nb_public_ip
public_ip_address_allocation = var.public_ip_address_allocation
resource_group_name = data.resourcegroup.tf-rg-external.name
.
.
.
}
variables.tf
variable "admin_password" {
type = string
}
variable "admin_username" {
type = string
}
variable "boot_diagnostics" {
type = bool
}
variable "boot_diagnostics_sa_type" {
type = string
}...
terraform root module.
Purpose: This should utilize modules defined to create a variety of VMs of different sizes and host names
main.tf:
module "sql_vm" {
source = "./modules/compute/"
#location = data.resourcegroup.tf-rg-external.location
#vnet_subnet_id = data.network.tf-sn.id
public_ip_address_allocation = var.public_ip_address_allocation
#resource_group_name = data.resourcegroup.tf-rg-external.name
storage_account_type = var.storage_account_type
vm_hostname = var.vm_hostname
}
variables.tf: Declares all variables in main.tf file.
Note: I have intentionally hard coded the variables in root module main/variable file. This is just get the communication between the modules right. Correct approach to understand and use modules.
However, when I run terraform plan in the root module. I get the error below:
Error: Reference to undeclared resource
on modules/compute/main.tf line 3, in module "vm_iis":
3: location = data.resourcegroup.tf-rg-external.location
A data resource "resourcegroup" "tf-rg-external" has not been declared in
sql_vm.
Error: Reference to undeclared resource
on modules/compute/main.tf line 4, in module "vm_iis":
4: vnet_subnet_id = data.network.tf-sn.id
A data resource "network" "tf-sn" has not been declared in sql_vm.
Error: Reference to undeclared resource
on modules/compute/main.tf line 22, in module "vm_iis":
22: resource_group_name = data.resourcegroup.tf-rg-external.name
A data resource "resourcegroup" "tf-rg-external" has not been declared in
sql_vm.
What is the issue and how to resolve it?
Also, possible to create different (roles) vms by some loop? example sql-vm, iis-vm, testvm, abcvm? What is going to change is their hostnames and vm sizes.
==========
Post answer changes
==========
I updated the values for subnet, resource group and location in
compute/main.tf and terraform/main.tf as like below:
location = module.resourcegroup.tf-rg-external-location
vnet_subnet_id = module.network.subnet-id
resource_group_name = module.resourcegroup.tf-rg-external-name
My outputs.tf file in resourcegroup and network modules look like below:
outputs.tf of network module
output "subnet-id" {
value = "data.network.tf-sn.id"
}
outputs.tf of resourcegroup module
output "tf-rg-external-location" {
value = data.resourcegroup.tf-rg-external.location
}
output "tf-rg-external-name" {
value = data.resourcegroup.tf-rg-external.name
}
I'm unfortunately still getting errors like below
Error: Unsupported argument
on main.tf line 3, in module "sql_vm":
3: location = module.resourcegroup.tf-rg-external-location
An argument named "location" is not expected here.
Error: Unsupported argument
on main.tf line 4, in module "sql_vm":
4: vnet_subnet_id = module.network.subnet-id
An argument named "vnet_subnet_id" is not expected here.
Error: Unsupported argument
on main.tf line 5, in module "sql_vm":
5: resource_group_name = module.resourcegroup.tf-rg-external-name
An argument named "resource_group_name" is not expected here.
So, it appears that we should not be referencing them in the root module?
Also, where their variables should be defined as in root modules variables.tf file as I believe you can override values for a variable of modules in the root module?
Forgive me if I am appearing as stupid. I'm trying to understand how it works in real life.
After last commit and public repo, error's are as below
Error: Reference to undeclared module
on main.tf line 3, in module "sql_vm":
3: location = module.resourcegroup.tf-rg-external-location
No module call named "resourcegroup" is declared in the root module.
Error: Reference to undeclared module
on main.tf line 4, in module "sql_vm":
4: vnet_subnet_id = module.network.subnet-id
No module call named "network" is declared in the root module.
Error: Reference to undeclared module
on main.tf line 5, in module "sql_vm":
5: resource_group_name = module.resourcegroup.tf-rg-external-name
No module call named "resourcegroup" is declared in the root module.
Error: Reference to undeclared module
on modules/compute/main.tf line 3, in module "vm_iis":
3: location = module.resourcegroup.tf-rg-external-location
No module call named "resourcegroup" is declared in sql_vm.
Error: Reference to undeclared module
on modules/compute/main.tf line 4, in module "vm_iis":
4: vnet_subnet_id = module.network.subnet-id
No module call named "network" is declared in sql_vm.
Error: Reference to undeclared module
on modules/compute/main.tf line 5, in module "vm_iis":
5: resource_group_name = module.resourcegroup.tf-rg-external-name
No module call named "resourcegroup" is declared in sql_vm.
Error: Reference to undeclared module
on modules/network/main.tf line 5, in data "azurerm_virtual_network" "tf-vn":
5: resource_group_name = module.resource_groups.external_rg_name
No module call named "resource_groups" is declared in test2.
Error: Reference to undeclared resource
on modules/resourcegroup/outputs.tf line 2, in output "tf-rg-external-location":
2: value = data.resourcegroup.tf-rg-external.location
A data resource "resourcegroup" "tf-rg-external" has not been declared in
test1.
Error: Reference to undeclared resource
on modules/resourcegroup/outputs.tf line 5, in output "tf-rg-external-name":
5: value = data.resourcegroup.tf-rg-external.name
A data resource "resourcegroup" "tf-rg-external" has not been declared in
test1.
Go through the repo you are working on (https://github.com/ameyaagashe/help_me_cross/tree/d7485d2a3db339723e9c791e592b2f1dbc1f0788) . It makes sense for me now.
The problem is, you mix the idea on how to use public modules with your own created modules.
In fact, you needn't set any modules to reference other public terraform registry modules.
Move all codes in sub-modules (module/compute, module/network, module/resourcegroup) to top folder (<repo_root>/main.tf).
such as (codes are not validated, just for reference)
data "azurerm_resource_group" "tf-rg-external" {
name = var.rg_name
}
data "azurerm_virtual_network" "tf-vn" {
name = var.vnet_name
resource_group_name = var.rg_name
}
# Reference existing subnet
data "azurerm_subnet" "tf-sn" {
name = var.subnet_name
virtual_network_name = data.azurerm_virtual_network.tf-vn.name
resource_group_name = var.rg_name
}
module "sql_vm" {
source = "Azure/compute/azurerm"
location = data.azurerm_resource_group.tf-rg-external.location
vnet_subnet_id = data.azurerm_virtual_network.tf-vn.subnets
resource_group_name = data.azurerm_resource_group.tf-rg-external.name
admin_password = var.admin_password
admin_username = var.admin_username
boot_diagnostics = var.boot_diagnostics
boot_diagnostics_sa_type = var.boot_diagnostics_sa_type
data_disk = var.data_disk
data_disk_size_gb = var.data_disk_size_gb
data_sa_type = var.data_sa_type
delete_os_disk_on_termination = var.delete_os_disk_on_termination
enable_accelerated_networking = var.enable_accelerated_networking
# flag is_windows_image is required only when you use a custom image to spin up a VM
# is_windows_image
# flag vm_os_id is required only when you are using custom image
# you need to provide id of your custom image
# vm_os_id
nb_instances = var.nb_instances
nb_public_ip = var.nb_public_ip
public_ip_address_allocation = var.public_ip_address_allocation
storage_account_type = var.storage_account_type
vm_hostname = var.vm_hostname
vm_os_offer = var.vm_os_offer
vm_os_publisher = var.vm_os_publisher
# vm_os_simple is to be used is you do not wish to specify offer, publisher and sku
# vm_os_simple = UbuntuServer, WindowsServer, RHEL, openSUSE-Leap, CentOS, Debian, CoreOS and SLES
vm_os_sku = var.vm_os_sku
vm_os_version = var.vm_os_version
vm_size = var.vm_size
}
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}"