Unable to read from terraform.tfstate while using modules - amazon-web-services

I am using Terraform v0.12.6. I am using modules to create a VPC,Subnets and EC2 instances.
root.tf
vpc.tf
pub_subnet.tf
web_server.tf
vpc.tf and pub_subnet.tf are working fine and displaying the required output. However, I am unable to use the subnet_id from the module pub_subnet.tf as input to my web_server.tf.
The reason being that it is a list and I am getting Inappropriate value for attribute "subnet_id": string required.
Looks like I have to read the terraform.tfstate file.
Here is my present code -
root.tf
provider "aws" {
region = "us-east-1"
}
data "terraform_remote_state" "public_subnet" {
backend = "local"
config = {
path = "terraform.tfstate"
}
}
module "my_vpc" {
source = "../modules/vpc_flowlogs"
vpc_cidr = "10.0.0.0/16"
# vpc_id = "${module.my_vpc.vpc_id}"
}
module "vpc_igw" {
source = "../modules/vpc_igw"
vpc_id = "${module.my_vpc.vpc_id}"
}
module "public_subnets" {
source="../modules/pub_subnets"
vpc_id = "${module.my_vpc.vpc_id}"
}
module "web_servers" {
source = "../modules/webservers"
vpc_id = "${module.my_vpc.vpc_id}"
subnet_id =
"${data.terraform_remote_state.public_subnet.outputs.subnet_id[0]}"
}
web_servers.tf
resource "aws_instance" "web-srvs" {
count="${var.instance_count == "0" ? "1" : var.instance_count}"
ami = "ami-035b3c7efe6d061d5"
instance_type = "t2.nano"
key_name="xxx-dev"
subnet_id = "${var.subnet_id}"
vpc_security_group_ids = ["${aws_security_group.pub_sg.id}"]
associate_public_ip_address=true
}
I am trying to use of the two subnet_ids created.
I have tried different ways but now running out of ideas.
Just as an FYI, my tfstate file is located in the same directory as root.tf
Appreciate any help. OR is this a bug ?

You're requesting a remote state for no reason. Remote state is for referencing output from other configs. You have modules so you should just change it to reference the module resource, but you are going to have to output the values in the module so you can reference it elsewhere.
subnet_id =
"${data.terraform_remote_state.public_subnet.outputs.subnet_id[0]}"
}
Should be
subnet_id =
"${module.public_subnets.subnet.id}"
}
In your subnet module, create an output resource.
output "subnet" {
value = "${aws_subnet.some_subnet.id}"
}

Related

no matching EC2 Security Group found

I create modules for creating AWS vpc, security group and EC2. I make EC2 module depend on vpc and the security group module.
but I get that error:
no matching EC2 Security Group found
on .terraform/modules/main_ec2/modules/EC2Module/main.tf line 26, in data "aws_security_group" "security_group_id":
26: data "aws_security_group" "security_group_id" {
EC2 module
data "aws_subnet" "subnet_id" {
vpc_id = var.vpc_id
count = length(var.subnet_name)
depends_on = [var.subnet_id_depends_on]
filter {
name = "tag:Name"
values = ["VPC0${var.vpc_number}-${element(var.subnet_name, count.index)}"]
}
}
data "aws_security_group" "security_group_id" {
count = length(var.Server_Group_Name)
depends_on = [var.security_group_depends_on]
filter {
name = "tag:Name"
values = ["${local.name}-SG-${var.Server_Group_Name[count.index]}-${format("%02d", count.index + 1)}"]
}
}
resource "aws_instance" "create_instance" {
ami = "${element(data.aws_ami_ids.ami_id.*.id, count.index)}"
instance_type = var.EC2_Type[count.index]
subnet_id = "${element(data.aws_subnet.subnet_id.*.id, count.index)}"
associate_public_ip_address = "true"
vpc_security_group_ids = [ data.aws_security_group.security_group_id[count.index].id ]
key_name = "${local.name}-KP-${var.Server_Name[count.index]}"
depends_on = [var.EC2_depends_on]
}
sg module
resource "aws_security_group" "SGS" {
count = length(var.Server_Group_Name)
name = "${local.name}-SG-${var.Server_Group_Name[count.index]}-${format("%02d", count.index + 1)}"
description = "${local.name}-SG-${var.Server_Group_Name[count.index]}-${format("%02d", count.index + 1)}"
vpc_id = var.vpc_id
tags = {
name = "tag:Name"
values = "${local.name}-SG-${var.Server_Group_Name[count.index]}-${format("%02d", count.index + 1)}"
}
varibles.tf
variable "subnet_id_depends_on" {
type = list(string)
default = ["module.vpc_main"]
}
variable "security_group_depends_on" {
type = list(string)
default = ["module.sg"]
}
variable "EC2_depends_on" {
type = list(string)
default = [ "module.vpc_main", "module.sg", "module.key_pair" ]
}
for example, I call modules like that:
module "main_ec2" {
source = "/home/reham/Data/projectes/terraform//modules/EC2Module"
subnet_id_depends_on = var.subnet_id_depends_on
security_group_depends_on = var.security_group_depends_on
ami_name = var.ami_name
EC2_depends_on = var.EC2_depends_on
}
Outputs defined in the sg module:
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.vpc.id
}
output "primary_public_subnets_id" {
description = "public subnet ID"
value = aws_subnet.primary_public_subnets.*.id
}
output "secondary_public_subnets_id" {
description = "public subnet ID"
value = aws_subnet.secondary_public_subnets.*.id
}
output "primary_private_subnets_id" {
description = "public subnet ID"
value = aws_subnet.primary_private_subnets.*.id
}
output "security-groups" {
value = aws_security_group.SGS[*].id
}
what must I do to solve this problem?
There is more than one issue with the code and it is a result of probably not understanding how the module outputs work [1]. The second part of the issue is that there are many explicit dependencies defined with depends_on which are not required to be there since implicit dependencies are good enough [2]. Furthermore, as per comments, data sources in the EC2 module are not required as information about VPC, subnets and security groups can be provided with the outputs. Additionally, the EC2 instance resource is using count, but there is no count meta-argument there, so it will not work even if all the inputs are fixed. So here are my suggestions:
Redefine variables in the EC2 module (no need for depdends_on) and remove data sources (except possibly the one for the AMI)
Decide which variable you are going to use for the count meta-argument in the EC2 instance resource
I am going to give you an example with one subnet and security groups and then you can build from there. The EC2 module fixes:
resource "aws_instance" "create_instance" {
ami = data.aws_ami_ids.ami_id[0].id
instance_type = var.EC2_Type
subnet_id = var.subnet_id
associate_public_ip_address = "true"
vpc_security_group_ids = var.security_group_ids
key_name = "${local.name}-KP-${var.Server_Name[0]}"
}
In EC2 module, the variables would have to be changed to:
variable "subnet_id" {}
variable "security_group_ids" {}
Then, in the root module:
module "main_ec2" {
source = "/home/reham/Data/projectes/terraform//modules/EC2Module"
subnet_id = module.sg.primary_public_subnets_id[0]
security_group_ids = module.sg.security-groups
ami_name = var.ami_name
}
Referencing the module outputs in the EC2 module will make sure that the SG module is created first (that is an implicit dependency). At best this should give you a result for one EC2 instance as it seems there are many resources that rely on using the count meta-argument and that code is not in the question.
[1] https://www.terraform.io/language/values/outputs#accessing-child-module-outputs
[2] https://www.terraform.io/language/resources/behavior#resource-dependencies

Terraform change file layout

Hello i have 3 files in my terraform directory
vpc.tf aerospike-ec2.tf vars.tf
and here is contents of vpc.tf file
resource "aws_vpc" "wizzair-dev-qa-vpc" {
cidr_block = var.wizzair-dev-qa-vpc_cidr
tags = {
Environment = "dev-qa"
Name = "wizzair-aws-vpc"
Project = "Network"
}
}
data "aws_availability_zones" "available" {}
resource "aws_subnet" "private_subnets" {
vpc_id = aws_vpc.wizzair-dev-qa-vpc.id
cidr_block = var.subnet_cidr
availability_zone = "eu-north-1a"
tags = {
Environment = "dev-qa"
Project = "Network"
Name = "wizzair-aws-subnet-private"
}
}
here is my aerospike.tf file
resource "aws_network_interface" "private" {
subnet_id = aws_subnet.private_subnets.id
private_ips = ["10.249.10.4"]
security_groups = [aws_security_group.aerospike_traffic.id, aws_security_group.general.id]
tags = {
Environment = "dev"
Project = "wizzair"
Name = "aerospike-interface"
}
}
resource "aws_instance" "dev-wizzair-aerospike" {
ami = "ami-077b12cf33hb9a995"
availability_zone = "eu-north-1a"
instance_type = "t3.large"
key_name = "${var.generated_key_name}"
network_interface {
device_index=0
network_interface_id = aws_network_interface.private.id
}
tags = {
Environment = "dev"
Project = "wizzair"
Name = "aerospike-instance-dev"
}
}
resource "aws_ebs_volume" "dev-wizzair-aerospike-ebs" {
availability_zone = "eu-north-1a"
size = 10
tags = {
Environment = "dev"
Project = "wizzair"
Name = "aerospike-volume"
}
}
resource "aws_volume_attachment" "dev-wizzair-aerospike-ebs-att" {
device_name = "/dev/sdh"
volume_id = aws_ebs_volume.dev-wizzair-aerospike-ebs.id
instance_id = aws_instance.dev-wizzair-aerospike.id
}
and vars.tf
variable "wizzair-dev-qa-vpc-cidr" {
default = "10.249.10.0/24"
}
and if the files are in the same directory, then everything works, but if I create the aerospike directory and transfer the aerospike.tf file there, then go to the aerospike directory and enter terraform plan there, then an error occurs
mkdir aerospike && mv aerospike.tf aerospike && cd aerospike && terraform plan
terraform plan
╷
│ Error: Reference to undeclared resource
│
│ on main.tf line 2, in resource "aws_network_interface" "private":
│ 2: subnet_id = aws_subnet.private_subnets.id
│
│ A managed resource "aws_subnet" "private_subnets" has not been declared in the root module.
i've heard about state outputs, but how to deal with it in my case?
You can't just randomly move files to sub-folders. You have to construct TF modules for that, which you then have to appropriately call and use in your parent script.

Override a module's local.tf variable in Terraform

I want to override the value of root_volume_type to gp2 in https://github.com/terraform-aws-modules/terraform-aws-eks/blob/master/local.tf
This is the only file I created called main.tf in my terraform code. I want to override this in the code and not set it via the command line while running terraform apply
module "eks_example_basic" {
source = "terraform-aws-modules/eks/aws//examples/basic"
version = "14.0.0"
region = "us-east-1"
}
The error is correct because you are sourcing an example, which does not support such variables as workers_group_defaults. You can't overwrite it, unless you fork the example and modify it yourself.
workers_group_defaults is supported in the core module, for instance:
data "aws_vpc" "default" {
default = true
}
data "aws_subnet_ids" "default" {
vpc_id = data.aws_vpc.default.id
}
module "eks_example" {
source = "terraform-aws-modules/eks/aws"
version = "14.0.0"
cluster_name = "SomeEKSCluster"
cluster_version = "1.18"
subnets = data.aws_subnet_ids.default.ids
vpc_id = data.aws_vpc.default.id
workers_group_defaults = { root_volume_type = "gp2" }
}

Terraform using output from module

I just started with Terraform infrastructure. Trying to create a vpc module that will contain code for vpc, subnets, internet gateway, rout table. Also creating a separate tf file for rds , which will refer to the vpc module and utilize the private subnets declared in vpc module.
Created a vpc module that has vpc.tf with following
provider "aws" {
region = var.region
}
terraform {
backend "s3" {}
}
resource "aws_vpc" "production-vpc" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
tags = {
Name = "Dev-VPC"
}
}
// Private Subnets
resource "aws_subnet" "private-subnet-1" {
cidr_block = var.private_subnet_1_cidr
vpc_id = aws_vpc.production-vpc.id
availability_zone = "us-east-1a"
tags = {
Name = "Private-Subnet-1"
}
}
resource "aws_subnet" "private-subnet-2" {
cidr_block = var.private_subnet_2_cidr
vpc_id = aws_vpc.production-vpc.id
availability_zone = "us-east-1b"
tags = {
Name = "Private-Subnet-2"
}
}
The output.tf has following
output "private-subnet1-id" {
description = "Private Subnet1 Id"
value = aws_subnet.private-subnet-1.*.id
}
output "private-subnet2-id" {
description = "Private Subnet2 Id"
value = aws_subnet.private-subnet-2.*.id
}
The file is saved in folder \module\vpc folder
Created rds.tf as follows in folder \rds
provider "aws" {
region = var.region
}
terraform {
backend "s3" {}
}
module "vpc" {
source = "../module/vpc"
}
resource "aws_db_subnet_group" "subnetgrp" {
name = "dbsubnetgrp"
subnet_ids = [module.vpc.private-subnet1-id.id, module.vpc.private-subnet2-id.id]
}
When I run terraform plan , I get following error
Error: Unsupported attribute
on rds.tf line 16, in resource "aws_db_subnet_group" "subnetgrp":
16: subnet_ids = [module.vpc.private-subnet1-id.id, module.vpc.private-subnet2-id.id]
|----------------
| module.vpc.private-subnet1-id is tuple with 1 element
This value does not have any attributes.
Error: Unsupported attribute
on rds.tf line 16, in resource "aws_db_subnet_group" "subnetgrp":
16: subnet_ids = [module.vpc.private-subnet1-id.id, module.vpc.private-subnet2-id.id]
|----------------
| module.vpc.private-subnet2-id is tuple with 1 element
This value does not have any attributes.
You don't need the splat expression in the output.tf. Try the following,
output "private-subnet1-id" {
description = "Private Subnet1 Id"
value = aws_subnet.private-subnet-1.id
}
output "private-subnet2-id" {
description = "Private Subnet2 Id"
value = aws_subnet.private-subnet-2.id
}

Terraform: data source aws_instance doesn't work

I'm trying to work with aws_instance data source. I created a simple configuration which should create an ec2 instance and should return ip as output
variable "default_port" {
type = string
default = 8080
}
provider "aws" {
region = "us-west-2"
shared_credentials_file = "/Users/kharandziuk/.aws/creds"
profile = "prototyper"
}
resource "aws_instance" "example" {
ami = "ami-0994c095691a46fb5"
instance_type = "t2.small"
tags = {
name = "example"
}
}
data "aws_instances" "test" {
instance_tags = {
name = "example"
}
instance_state_names = ["pending", "running", "shutting-down", "terminated", "stopping", "stopped"]
}
output "ip" {
value = data.aws_instances.test.public_ips
}
but for some reasons I can't configure data source properly. The result is:
> terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
data.aws_instances.test: Refreshing state...
Error: Your query returned no results. Please change your search criteria and try again.
on main.tf line 21, in data "aws_instances" "test":
21: data "aws_instances" "test" {
how can I fix it?
You should use depends_on option into data.aws_instances.test.
like :
data "aws_instances" "test" {
instance_tags = {
name = "example"
}
instance_state_names = ["pending", "running", "shutting-down", "terminated", "stopping", "stopped"]
depends_on = [
"aws_instance.example"
]
}
It means that build data.aws_instances.test after make resource.aws_instance.example.
Sometimes, We need to use this option. Because of dependencies of aws resources.
See :
Here's a document about depends_on option.
You don't need a data source here. You can get the public IP address of the instance back from the resource itself, simplifying everything.
This should do the exact same thing:
resource "aws_instance" "example" {
ami = "ami-0994c095691a46fb5"
instance_type = "t2.small"
tags = {
name = "example"
}
}
output "ip" {
value = aws_instance.example.public_ip
}