Error: "network_interface": conflicts with vpc_security_group_ids - amazon-web-services

I'm trying to create an aws instance with an aws_network_interface as below:
resource "aws_network_interface" "lustre-mds01" {
subnet_id = "${var.subnet_id}"
private_ips = ["10.1.0.10"]
}
resource "aws_instance" "lustre-mds01" {
ami = "${var.ec2_ami}"
instance_type = "t2.nano"
key_name = "${var.key_name}"
vpc_security_group_ids = [ "${var.vpc_security_group_id}" ]
root_block_device {
volume_type = "gp2"
volume_size = 128
}
network_interface {
network_interface_id = "${aws_network_interface.lustre-mds01.id}"
device_index = 0
}
}
However, this results in:
Error: "network_interface": conflicts with vpc_security_group_ids
It appears there is an issue for this, but the ticket was closed due to inactivity. I'm a terraform noob, so I'm not sure if this looks like a bug or is just user error.
My environment:
$ terraform -v
Terraform v0.12.2
+ provider.aws v2.15.0
+ provider.external v1.1.2
+ provider.local v1.2.2
+ provider.null v2.1.2

The aws_network_interface resource allows you to set the security group for the interface (security groups are scoped by the ENI so this makes sense) so if you define the network_interface block then you're overriding the default ENI and so can't specify security groups at the instance level.
So in your case you probably want something like:
resource "aws_network_interface" "lustre-mds01" {
subnet_id = "${var.subnet_id}"
private_ips = ["10.1.0.10"]
security_groups = ["${var.vpc_security_group_id}"]
}
resource "aws_instance" "lustre-mds01" {
ami = "${var.ec2_ami}"
instance_type = "t2.nano"
key_name = "${var.key_name}"
root_block_device {
volume_type = "gp2"
volume_size = 128
}
network_interface {
network_interface_id = "${aws_network_interface.lustre-mds01.id}"
device_index = 0
}
}
However, I would question why you are replacing the default ENI here when it's much simpler to just set the private IP address of the instance directly in the aws_instance resource instead:
resource "aws_instance" "lustre-mds01" {
ami = "${var.ec2_ami}"
instance_type = "t2.nano"
key_name = "${var.key_name}"
subnet_id = "${var.subnet_id}"
private_ip = "10.1.0.10"
vpc_security_group_ids = ["${var.vpc_security_group_id}"]
root_block_device {
volume_type = "gp2"
volume_size = 128
}
}
You would also probably benefit from using data sources to select your security group and AMI instead of passing in opaque IDs for these. This allows them to be more self documenting.

Related

ec2 instances created by a launch template in auto-scaling group are not being registered with the target group

These are my alb configurations in case they are necessary. Skip down to see the meat of the problem.
resource "aws_autoscaling_attachment" "asg_attachment" {
autoscaling_group_name = aws_autoscaling_group.web.id
lb_target_group_arn = aws_lb_target_group.main.arn
}
resource "aws_lb" "main" {
name = "test-${var.env}-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.elb.id]
subnets = [aws_subnet.main1.id, aws_subnet.main2.id]
tags = {
Name = "${var.project_name}-${var.env}-alb"
Project = var.project_name
Environment = var.env
ManagedBy = "terraform"
}
}
resource "aws_lb_target_group" "main" {
name = "${var.project_name}-${var.env}-alb-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
deregistration_delay = 30
health_check {
interval = 10
matcher = "200-299"
path = "/"
}
}
resource "aws_lb_listener" "main" {
load_balancer_arn = aws_lb.main.arn
protocol = "HTTPS"
port = "443"
ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01"
certificate_arn = aws_acm_certificate.main.arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.main.arn
}
}
============
I have an auto-scaling group created through terraform:
resource "aws_autoscaling_group" "web" {
vpc_zone_identifier = [aws_subnet.main1.id]
launch_template {
id = aws_launch_template.web.id
version = "$Latest"
}
min_size = 1
max_size = 10
lifecycle {
create_before_destroy = true
}
}
the launch template looks like this:
resource "aws_launch_template" "web" {
name_prefix = "${var.project_name}-${var.env}-autoscale-web-"
image_id = var.web_ami
instance_type = "t3.small"
key_name = var.key_name
vpc_security_group_ids = [aws_security_group.webserver.id]
}
if i use a launch configuration instead of a launch template, it works:
resource "aws_launch_configuration" "web" {
image_id = var.web_ami
instance_type = "t3.small"
key_name = var.key_name
security_groups = [aws_security_group.webserver.id]
root_block_device {
volume_size = 8 # GB
volume_type = "gp3"
}
}
when using the launch configuration, the one line in the autoscaling_group is added:
launch_configuration = aws_launch_configuration.web.name
and the launch_template section is removed.
aws_launch_configuration is deprecated, so I'd like to use the launch_template.
Everything is working fine; the instance spins up and I can connect to it and it passes the health check. The problem is that the EC2 instance doesn't automatically register with the target group. When I manually register it with the target group, then everything works fine.
How can I get the EC2 instances that spin up with a launch template to automatically get added to the target group?
It turns out aws_autoscaling_attachment is also deprecated, and I needed to add:
target_group_arns = [aws_lb_target_group.main.arn]
to my aws_autoscaling_group.

How to optionally configure root_block_device in EC2 instance using terraform

I am creating AWS EC2 instance using terraform. The instance will be created using the provided ami id. In terraform the root_block_device block can be used to configure root volume.
I want to make this configuration optional based on the variable. So if root_block_override variable is true then use provided values; else use the root volume configuration from the image.
The terraform below throws error An argument named "count" is not expected here.
How to configure the root optionally?
resource "aws_instance" "ec2" {
ami = var.ami_id
instance_type = var.instance_type
iam_instance_profile = aws_iam_instance_profile.ec2.name
key_name = var.key_name
vpc_security_group_ids = var.security_group_ids
subnet_id = var.subnet_id
user_data_base64 = base64encode(templatefile(var.user_data_file_path, var.user_data_variables))
root_block_device {
count = var.root_block_override ? 1 : 0
delete_on_termination = var.root_block_delete_on_termination
encrypted = true
iops = var.root_block_volume_type == "gp2" ? null : var.root_block_iops
kms_key_id = var.root_block_kms_key_id
throughput = var.root_block_throughput
volume_size = var.root_block_volume_size
volume_type = var.root_block_volume_type
}
}
You can use a combination of for_each [1] meta-argument and dynamic [2] block:
dynamic "root_block_device" {
for_each = var.root_block_override ? [1] : []
content {
delete_on_termination = var.root_block_delete_on_termination
encrypted = true
iops = var.root_block_volume_type == "gp2" ? null : var.root_block_iops
kms_key_id = var.root_block_kms_key_id
throughput = var.root_block_throughput
volume_size = var.root_block_volume_size
volume_type = var.root_block_volume_type
}
}
[1] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each
[2] https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks

Assign fixed private IP for ec2 instance in terraform

I have came up with below:
resource "aws_network_interface" "eni_carl" {
subnet_id = module.vpc.public_subnets[3]
private_ip = "10.0.4.1/32"
security_groups = [module.vpc.ssh_sg]
}
resource "aws_instance" "carl" {
ami = var.ami
instance_type = "t2.micro"
key_name = var.key_name
network_interface {
network_interface_id = aws_network_interface.eni_carl.id
device_index = 0
}
}
But aws will randomly assign private ip for my instance. How can I achieve generating the same and fixed private ip address for my instance?
Terraform v1.3.4
on linux_amd64
provider registry.terraform.io/hashicorp/aws v4.39.0
From aws_instance | Resources | hashicorp/aws | Terraform Registry:
resource "aws_network_interface" "foo" {
subnet_id = aws_subnet.my_subnet.id
private_ips = ["172.16.10.100"]
tags = {
Name = "primary_network_interface"
}
}
Therefore, the parameter appears to be: private_ips = ["172.16.10.100"]
It takes an IP address instead of a CIDR block.

Terraform one EC2 instance with two subnets

I need to create one EC2 and associate 2 subnets to it.
variables.tf
variable "aws_subnet_id_this" {
description = "Subnet ID"
default = ["subnet-09df122a4faee8882", "subnet-2fcc756f02ddb4b62"]
}
main.tf
resource "aws_instance" "test" {
ami = var.ami_id
instance_type = var.ec2_instance_type
subnet_id = var.aws_subnet_id_this
key_name = var.pki_name
vpc_security_group_ids = [aws_security_group.Allow_SSH_in.id]
}
Error:
Error: Incorrect attribute value type
on main_count_data.tf line 57, in resource "aws_instance" "test":
57: subnet_id = var.aws_subnet_id_this
|----------------
| var.aws_subnet_id_eks is tuple with 2 elements
Inappropriate value for attribute "subnet_id": string required.
So I tried this:
main.tf
resource "aws_instance" "prueba" {
ami = var.ami_id
instance_type = var.ec2_instance_type
#subnet_id = var.aws_subnet_id_this
count = 2
subnet_id = "${element(var.aws_subnet_id_this, count.index)}"
key_name = var.pki_name
vpc_security_group_ids = [aws_security_group.Allow_SSH_in.id]
}
}
but this last portion of code tries to create a new EC2 instance with the second subnet and this is not what I expect tough.
To sum up: I need 1 EC2 containing 2 subnets defined in the variables.tf file.
How can I do this?
Below is an example of how you can create one instance with two NICs in different subnets. The NICs must be in same AZ. So instance can have two NICs in different subnets as long as they are in same AZ:
variable "aws_subnet_id_this" {
description = "Subnet ID"
default = ["subnet-09df122a4faee8882", "subnet-2fcc756f02ddb4b62"]
}
resource "aws_network_interface" "nic1" {
subnet_id = var.aws_subnet_id_this[0]
}
resource "aws_network_interface" "nic2" {
subnet_id = var.aws_subnet_id_this[1]
}
resource "aws_instance" "prueba" {
ami = var.ami_id
instance_type = var.ec2_instance_type
key_name = var.pki_name
network_interface {
device_index = 0
network_interface_id = aws_network_interface.nic1.id
}
network_interface {
device_index = 1
network_interface_id = aws_network_interface.nic2.id
}
}

How to correctly use Count and pick multiple az subnets in Terraform

I am trying to implement a module where i am trying to spin a number of instance in already created subnets (by terraform) , but i am not sure how to actually use count in modules and also how to pick values from s3 bucket datasource to spin instance in multi-az , here is what my resource in module dir looks like
resource "aws_instance" "ec2-instances" {
count = "${var.count_num }"
ami = "${data.aws_ami.ubuntu.id}"
instance_type = "${var.machine_type}"
key_name = "${var.key_name}"
#vpc_security_group_ids = ["${aws_security_group.jumpbox-sec-group.id}"]
vpc_security_group_ids = ["${var.sec-group}"]
disable_api_termination = "${var.is_production ? true : false}"
subnet_id = "${element(var.es_stg_subnets, count.index)}" <--- This won't work , i need to use data-source as s3
tags {
#Name = "${var.master_name}-${count.index+1}"
Name = "${var.instance-tag}-${count.index+1}"
Type = "${var.instance-type-tag}"
}
root_block_device {
volume_size = "${var.instance-vol-size}"
volume_type = "gp2"
}
}
And here is the actual module :
module "grafana-stg" {
source = "../../modules/services/gen-ec2"
#ami_id = "${data.aws_ami.ubuntu.id}"
instance_type = "${var.grafana_machine_type}"
key_name = "jumpbox"
vpc_security_group_ids = ["${aws_security_group.grafana-sec-group.id}"]
#subnets = "${data.terraform_remote_state.s3_bucket_state.subnet-public-prod-1a}"
subnet_id = ??????????????????
disable_api_termination = "${var.is_production ? true : false}"
}
I would look at retrieving your subnets utilising a data source.
Utilising Data Sources
Terraform has the concept of data sources. You can pull information from AWS that you require for resources. In your gen-ec2.tf file -
// In order to get subnets, you need the VPC they belong to.
// Note you can filter on a variety of different tags.
data "aws_vpc" "selected" {
tags {
Name = "NameOfVPC"
}
}
// This will then retrieve all subnet ids based on filter
data "aws_subnet_ids" "private" {
vpc_id = "${data.aws_vpc.selected.id}"
tags {
Tier = "private*"
}
}
resource "aws_instance" "ec2-instances" {
count = "${length(data.aws_subnet_ids.private.ids)}"
ami = "${data.aws_ami.ubuntu.id}"
instance_type = "${var.machine_type}"
key_name = "${var.key_name}"
vpc_security_group_ids = ["${var.sec-group}"]
disable_api_termination = "${var.is_production ? true : false}"
subnet_id = "${element(data.aws_subnet_ids.private.*.ids, count.index)}"
tags {
Name = "${var.instance-tag}-${count.index+1}"
Type = "${var.instance-type-tag}"
}
root_block_device {
volume_size = "${var.instance-vol-size}"
volume_type = "gp2"
}
}
Your module now looks like so -
module "grafana-stg" {
source = "../../modules/services/gen-ec2"
#ami_id = "${data.aws_ami.ubuntu.id}"
instance_type = "${var.grafana_machine_type}"
key_name = "jumpbox"
vpc_security_group_ids = ["${aws_security_group.grafana-sec-group.id}"]
disable_api_termination = "${var.is_production ? true : false}"
}
For me as I am using Terraform v0.12.5, the bellow snippet worked fine
data "aws_subnet_ids" "public_subnet_list" {
vpc_id = "${var.vpc_id}"
tags = {
Tier = "Public"
}
}
resource "aws_instance" "example" {
count = 2
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = tolist(data.aws_subnet_ids.public_subnet_list.ids)[count.index]
}