When I try to use interpolation syntax like this:
vpc_id = "${aws_vpc.prod-vpc.id}"
I get the suggestion in IntelliJ that "Interpolation could be replaced by HCL2 expression", so if I change the line into this:
vpc_id = "aws_vpc.prod-vpc.id"
and issue terraform apply, I get:
C:\tf_ptojects\aws\subnet>terraform apply -auto-approve
aws_subnet.prod-subnet: Creating...
aws_vpc.prod-vpc: Creating...
aws_vpc.prod-vpc: Creation complete after 2s [id=vpc-0cfb27255522bdf15]
Error: error creating subnet: InvalidVpcID.NotFound: The vpc ID 'aws_vpc.prod-vpc.id' does not exist
status code: 400, request id: dab3fb03-424d-4bf2-ace6-bef93a94ee9c
If I re-apply interpolation syntax and run terraform apply again, then the resources get deployed but I get the warning in Terraform, saying that interpolation-only expressions are deprecated:
Warning: Interpolation-only expressions are deprecated
on main.tf line 16, in resource "aws_subnet" "prod-subnet":
16: vpc_id = "${aws_vpc.prod-vpc.id}"
So TF is discouraging the use of interpolation syntax, yet issues an error if it's not used. Is it some kind of bug or something?
C:\tf_ptojects\aws\subnet>terraform -version
Terraform v0.14.4
+ provider registry.terraform.io/hashicorp/aws v3.25.0
Entire TF code for reference:
provider "aws" {
region = "eu-central-1"
}
resource "aws_vpc" "prod-vpc" {
cidr_block = "10.100.0.0/16"
tags = {
name = "production vpc"
}
}
resource "aws_subnet" "prod-subnet" {
cidr_block = "10.100.1.0/24"
vpc_id = "aws_vpc.prod-vpc.id"
tags = {
name = "prod-subnet"
}
}
You just have to get the id without using double quotes vpc_id = aws_vpc.prod-vpc.id because you are getting vpc id from the resource.
If you use the double quotes, it will be considered as a string and no evaluation will be done, and terraform will consider "aws_vpc.prod-vpc.id" as the id.
This is the corrected code:
provider "aws" {
region = "eu-central-1"
}
resource "aws_vpc" "prod-vpc" {
cidr_block = "10.100.0.0/16"
tags = {
name = "production vpc"
}
}
resource "aws_subnet" "prod-subnet" {
cidr_block = "10.100.1.0/24"
vpc_id = aws_vpc.prod-vpc.id
tags = {
name = "prod-subnet"
}
}
I had tested the above code snippet and it is working perfectly fine.
Related
I am new to terraform and I'm trying to create a VPC with multiple subnets and adding route tables to all those subnets in a for loop manner.
VPC: 10.207.0.0/16
There's number_of_subnets which will create subnets like this: 10.207.x.0/24
This code works fine:
variable "region" {
default = "us-east-1"
}
variable "availability_zone" {
default = "us-east-1a"
}
variable "cidr_block" {
default = "207"
}
variable "number_of_subnets" {
default = 5
}
provider "aws" {
region = var.region
}
resource "aws_vpc" "test_vpc" {
cidr_block = "10.${var.cidr_block}.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "test_vpc_${var.cidr_block}"
}
}
resource "aws_subnet" "test_subnets" {
count = var.number_of_subnets
vpc_id = aws_vpc.test_vpc.id
cidr_block = "10.${var.cidr_block}.${count.index+1}.0/24" # start from x.x.1.0/24
availability_zone = var.availability_zone
map_public_ip_on_launch = false
tags = {
Name = "test_subnet_${var.cidr_block}_${count.index+1}"
}
}
Now if I try to add this code to the bottom of the same file (everything is in one file called main.tf) to get the subnets and add route table to each:
# get all subnet IDs
data "aws_subnets" "q_subnets" {
filter {
name = "vpc-id"
values = [aws_vpc.test_vpc.id]
}
}
# add route table to all subnets
resource "aws_route_table_association" "rt_assoc_subnet" {
depends_on = [aws_subnet.test_subnets]
for_each = toset(data.aws_subnets.q_subnets.ids)
subnet_id = each.value
route_table_id = aws_route_table.test_rt.id
}
and run terraform apply it will give this error:
invalid for_each argument...
The "for_each" value depends on resource attribute that cannot be deteremined until apply,...
which doesn't make scense. First create vpc, then subnet, then get all subnets...
I also tried depends_on and didn't help.
How would I write this to make it work?
Any help is appreciated.
UPDATE1:
I tried to use aws_subnet.test_subnets.*.id instead of data and it still gives depencendy error:
variable "region" {
default = "us-east-1"
}
variable "availability_zone" {
default = "us-east-1a"
}
variable "cidr_block" {
default = "207"
}
variable "number_of_subnets" {
default = 5
}
provider "aws" {
region = var.region
}
resource "aws_vpc" "test_vpc" {
cidr_block = "10.${var.cidr_block}.0.0/16"
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "test_vpc_${var.cidr_block}"
}
}
resource "aws_route_table" "test_rt" {
vpc_id = aws_vpc.test_vpc.id
route = []
tags = {
Name = "test_rt_${var.cidr_block}"
}
}
resource "aws_subnet" "test_subnets" {
count = var.number_of_subnets
vpc_id = aws_vpc.test_vpc.id
cidr_block = "10.${var.cidr_block}.${count.index+1}.0/24" # start from x.x.1.0/24
availability_zone = var.availability_zone
map_public_ip_on_launch = false
tags = {
Name = "test_subnet_${var.cidr_block}_${count.index+1}"
}
}
output "subnets" {
value = aws_subnet.test_subnets.*.id
}
# add route table to all subnets
resource "aws_route_table_association" "rt_assoc_subnet" {
depends_on = [aws_subnet.test_subnets]
for_each = toset(aws_subnet.test_subnets.*.id)
subnet_id = each.value
route_table_id = aws_route_table.test_rt.id
}
is there another way to pass the subnets to aws_route_table_association without getting dependency error?
Since you are using count, it is very hard to make count work with for_each. It would be better to continue using count for route table association as well. If you decide to go down that route, the only change you need is:
resource "aws_route_table_association" "rt_assoc_subnet" {
count = var.number_of_subnets
subnet_id = aws_subnet.test_subnets.*.id[count.index]
route_table_id = aws_route_table.test_rt.id
}
This will work as intended. However, if you must use for_each I would suggest defining a variable that could be used with it in all the resources you are now using count. If you really want to use for_each with the current code, then you can use the -target option [1]:
terraform apply -target=aws_vpc.test_vpc -target=aws_route_table.test_rt -target=aws_subnet.test_subnets
When running this command, this will be shown in the command output:
│ Warning: Resource targeting is in effect
│
│ You are creating a plan with the -target option, which means that the result of this plan may not represent all of the changes requested by the current
│ configuration.
│
│ The -target option is not for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform
│ specifically suggests to use it as part of an error message.
After the targeted resources are created, you could re-run terraform apply and it should create the route table associations.
[1] https://www.terraform.io/cli/commands/plan#target-address
I am new to terraform and want to change the subnet on a network and I am getting a weird error.
google got nothing. here's what I am entering (after changing the main.tf and running plan)
terraform apply -replace="azurerm_subnet.subnet1"
Terraform will perform the following actions:
# module.network.azurerm_subnet.subnet[0] will be updated in-place
~ resource "azurerm_subnet" "subnet" {
~ address_prefixes = [
- "10.0.2.0/24",
+ "10.0.4.0/24",
]
id =
"/subscriptions/xxxxxxxxxxxxxxxxxxxxxxxxx/resourceGroups/lab-
resources/providers/Microsoft.Network/virtualNetworks/acctvnet/subnets/subnet1"
name = "subnet1"
# (7 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
I enter yes and I get this error:
Error: updating Subnet: (Name "subnet1" / Virtual Network Name "acctvnet" / Resource Group "lab-resources"): network.SubnetsClient#CreateOrUpdate: Failure sending request: StatusCode=400 -- Original Error: Code="InUseSubnetCannotBeUpdated" Message="Subnet subnet1 is in use and cannot be updated." Details=[]
│
│ with module.network.azurerm_subnet.subnet[0],
│ on .terraform/modules/network/main.tf line 15, in resource "azurerm_subnet" "subnet":
│ 15: resource "azurerm_subnet" "subnet" {
│
The VM is off and I do not see what else can be using it.
I also tried using the terraform taint "azurerm_subnet.subnet1"
Any ideas? Is what I am doing not possible?
Here is my main.tf
terraform {
required_version = ">=0.12"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~>2.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "lab_autodeploy" {
name = "lab-resources"
location = "East US 2"
}
module "Windowsservers" {
source = "Azure/compute/azurerm"
resource_group_name = azurerm_resource_group.lab_autodeploy.name
is_windows_image = true
vm_hostname = "new_ddc" // line can be removed if only one VM module per resource group
size = "Standard_F2"
admin_password = "$omePassw0rd"
vm_os_simple = "WindowsServer"
public_ip_dns = ["srv"] // change to a unique name per datacenter region
vnet_subnet_id = module.network.vnet_subnets[0]
depends_on = [azurerm_resource_group.lab_autodeploy]
}
module "network" {
source = "Azure/network/azurerm"
resource_group_name = azurerm_resource_group.lab_autodeploy.name
subnet_prefixes = ["10.4.0.0/24"]
subnet_names = ["subnet1"]
depends_on = [azurerm_resource_group.lab_autodeploy]
}
output "windows_vm_public_name" {
value = module.windowsservers.public_ip_dns_name
}
This isn't an issue specific to Terraform - in Azure you cannot change a subnet that has things attached to it. The fact that the VM is powered off makes no difference.
To get around this without destroying the VM, you could move the NIC to a different subnet (create a temporary subnet if necessary), perform the address space change and then move the NIC back.
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.
I have a Terraform configuration with various AWS resources in one file, including a VPC, three private, three public subnets, and an EB environment.
My Terraform version is 0.12.0, and the AWS provider version is ~> 2.12
The VPC looks like this:
resource "aws_vpc" "terraform-vpc" {
cidr_block = "${var.cidr_block}"
assign_generated_ipv6_cidr_block = true
}
My six subnets look like this:
resource "aws_subnet" "private-a" {
vpc_id = "${aws_vpc.terraform-vpc.id}"
availability_zone = "eu-west-2a"
cidr_block = "10.0.1.0/24"
}
resource "aws_subnet" "public-a" {
vpc_id = "${aws_vpc.terraform-vpc.id}"
availability_zone = "eu-west-2a"
cidr_block = "10.0.2.0/24"
}
resource "aws_subnet" "private-b" {
vpc_id = "${aws_vpc.terraform-vpc.id}"
availability_zone = "eu-west-2b"
cidr_block = "10.0.3.0/24"
}
resource "aws_subnet" "public-b" {
vpc_id = "${aws_vpc.terraform-vpc.id}"
availability_zone = "eu-west-2b"
cidr_block = "10.0.4.0/24"
}
resource "aws_subnet" "private-c" {
vpc_id = "${aws_vpc.terraform-vpc.id}"
availability_zone = "eu-west-2c"
cidr_block = "10.0.5.0/24"
}
resource "aws_subnet" "public-c" {
vpc_id = "${aws_vpc.terraform-vpc.id}"
availability_zone = "eu-west-2c"
cidr_block = "10.0.6.0/24"
}
Then, in my aws_elastic_beanstalk_environment resource I have:
...
setting {
namespace = "aws:ec2:vpc"
name = "Subnets"
value = "#{aws_subnet.private-a.id},#{aws_subnet.private-b.id},#{aws_subnet.private-c.id}"
}
setting {
namespace = "aws:ec2:vpc"
name = "ELBSubnets"
value = "#{aws_subnet.public-a.id},#{aws_subnet.public-b.id},#{aws_subnet.public-c.id}"
}
...
depends_on = [
aws_security_group.default, # created earlier in the same file
aws_subnet.public-a,
aws_subnet.public-b,
aws_subnet.public-c,
aws_subnet.private-a,
aws_subnet.private-b,
aws_subnet.private-c
]
The problem is I always get an error that one of the subnets does not exist, for example:
Error: ConfigurationValidationException: Configuration validation exception:
Invalid option value: '["#{aws_subnet.private-c.id}","#{aws_subnet.private-b.id}","#{aws_subnet.private-a.id}"]'
(Namespace: 'aws:ec2:vpc', OptionName: 'Subnets'): The subnet '#{aws_subnet.private-c.id}' does not exist.
It is always related to a different subnet, sometimes one of the private ones used for the Subnets option, sometimes one of the public ones used in the ELBSubnets option.
What baffles is me is that I have explicitly defined them as dependencies, although I think it should work even without the explicit dependency. And in the terraform apply logs all of the subnets are always created before the eb environment:
aws_subnet.private-c: Creation complete after 1s [id=subnet-some-id]
aws_subnet.public-a: Creation complete after 1s [id=subnet-some-id]
aws_subnet.public-c: Creation complete after 1s [id=subnet-some-id]
aws_subnet.public-b: Creation complete after 1s [id=subnet-some-id]
aws_subnet.private-a: Creation complete after 6s [id=subnet-some-id]
aws_subnet.private-b: Creation complete after 6s [id=subnet-some-id]
...
aws_elastic_beanstalk_environment.default: Creating...
Upon checking in AWS, everything the subnets are created as expected and as reported by apply. What could be the reason for this error?
As ydaetskcor mentioned in the interpolation of the subnets and used # instead of $, so #{aws_subnet.public-a.id},#{aws_subnet.public-b.id} should be ${aws_subnet.public-a.id},${aws_subnet.public-b.id}.
I am a beginner to Terraform.
I am trying to execute following code from Terraform Getting started guide.
provider "aws" {
access_key = "${var.access_key}"
secret_key = "${var.secret_key}"
region = "${var.region}"
}
resource "aws_instance" "example" {
ami = "${lookup(var.amis, var.region)}"
instance_type = "t2.micro"
tags {
Name = "newprovisionerstest"
}
provisioner "local-exec" {
command = "echo ${aws_instance.example.public_ip} > ip_address.txt"
}
}
output "ip" {
value = "${aws_eip.ip.public_ip}"
}
When I run
terraform apply
or
terraform refresh
It gives following error:
Error: output 'ip': unknown resource 'aws_eip.ip' referenced in variable aws_eip.ip.public_ip
Why is it so? Is it because "aws_eip" resource is not declared anywhere?
Like you said it yourself, there is no aws_eip resource called ip.
If you use the
aws_instance.example.public_ip
it should work totally fine