How to create EC2 instance with two private IPs using Terraform - amazon-web-services

I am trying to create 3 EC2 instances with two private IPs attached to eth0 and eth1 using terraform.
Can you suggest the correct Terraform resource I need to use to create and attach secondary private IP address' to each of the EC2 machines?
I know by default it creates eth0 and attaches a private IP address, I am looking to create eth1 as part of instance creation and attach a private IP from a different subnet.
resource "aws_instance" "test" {
count = "${var.instance_count["test"]}"
ami = "${var.ami}"
instance_type = "${var.instance_type}"
key_name = "${var.key_name}"
vpc_security_group_ids = ["${aws_security_group.kafka_sg.id}"]
associate_public_ip_address = "${var.associate_public_ip_address}"
ebs_optimized = "${var.ebs_optimized}"
disable_api_termination = "${var.disable_api_termination}"
subnet_id = "${var.subnet_id}"
user_data = "${base64encode(file("${path.module}/mount.sh"))}"
tags = {
Name = "test-${var.instance_prefix}-${format("%02d", count.index+1)}"
}
root_block_device {
volume_type = "${var.root_volume_type}"
volume_size = "${var.root_volume_size}"
}
ebs_block_device{
device_name = "/dev/sdb"
volume_size = 10
volume_type = "gp2"
}
}

Add an Elastic Network Interface to the server by creating the ENI via aws_network_interface, and then attaching it via aws_network_interface_attachment

Related

Get Private IP from ASG

I need to output private IP from Auto Scaling Group of EC2 instance that configuration by Terraform. How can i get private IP from ASG?
# Launch config for ASG
resource "aws_launch_configuration" "asg_conf" {
name = "ASG-Conf-${var.env}"
image_id = data.aws_ami.ubuntu.id # Get image from data
instance_type = var.instance_type # use t2.micro ad default
security_groups = [var.sg_app.id, var.sg_alb.id] # SG for APP
key_name = var.ssh_key # SSH key for connection to EC2
user_data = file("./modules/ec2/shell/apache.sh") # install apache
lifecycle {
create_before_destroy = true
}
}
# Auto Scaling Group
resource "aws_autoscaling_group" "asg" {
count = length(var.private_subnet_id) # count numbers of private subnets
name = "ASG-${var.env}-${count.index + 1}"
vpc_zone_identifier = [var.private_subnet_id[count.index]]
launch_configuration = aws_launch_configuration.asg_conf.name
target_group_arns = [var.alb_target.arn]
min_size = 1 # Min size of creating EC2
max_size = 1 # Max size of creating EC2
health_check_grace_period = 120
health_check_type = "ELB"
force_delete = true
lifecycle {
create_before_destroy = true
}
tag {
key = "Name"
value = "Webserver-ec2-${count.index + 1}"
propagate_at_launch = true
}
}
output {
# How can i get this???
value = aws_autoscaling_group.asg.private_ip
}
My infrastructure create 1 EC2 instance in 2 private AZ by ASG and i need output IP from this ASG
You need custom data source to get the IPs. But this really does not make much sense as instances in ASG can be changed by AWS at any time, thus making your IPs obsolete.

How do you launch an instance with EIP straight away?

Having trouble with attaching eip to ec2 instance. I can only do it be assigning public ip then after instance is built then attaching eip. I want to add the EIP before the instance builds.
resource "aws_instance" "third-WG" {
provider = aws.third
ami = data.aws_ami.ubuntu3.id
instance_type = "t2.micro"
key_name = aws_key_pair.third_WGkey.key_name
associate_public_ip_address = true
private_ip = "10.3.0.10"
subnet_id = aws_subnet.third-WG-Subnet.id
vpc_security_group_ids = [aws_security_group.third-SG.id]
depends_on = [aws_instance.WG_DL_SRV]
}
resource "aws_eip" "third_WG_EIP" {
provider = aws.third
vpc = true
instance = aws_instance.third-WG.id
depends_on = [aws_internet_gateway.third-IGW]
provisioner "local-exec" {
command = "echo ${aws_eip.third_WG_EIP.public_ip} > ~/Desktop/whole-
wash/var/3rd_PUB_IP.txt"
}
tags = {
Name = "third_WG_EIP"
}
}
Instead of attaching the Elastic IP to the instance, which makes the instance a dependency of the Elastic IP forcing it to be built first, you can instead create a network interface, and attach the Elastic IP to that. You then also make that interface used by the instance, and so everything is then brought up together.
resource "aws_network_interface" "nic" {
private_ips = ["10.3.0.10"]
subnet_id = aws_subnet.third-WG-Subnet.id
#Todo: populate any other needed NIC parameters
}
resource "aws_eip" "third_WG_EIP" {
provider = aws.third
vpc = true
network_interface = aws_network_interface.nic
depends_on = [aws_internet_gateway.third-IGW]
provisioner "local-exec" {
command = "echo ${aws_eip.third_WG_EIP.public_ip} > ~/Desktop/whole-
wash/var/3rd_PUB_IP.txt"
}
tags = {
Name = "third_WG_EIP"
}
}
resource "aws_instance" "third-WG" {
provider = aws.third
ami = data.aws_ami.ubuntu3.id
instance_type = "t2.micro"
key_name = aws_key_pair.third_WGkey.key_name
associate_public_ip_address = true
vpc_security_group_ids = [aws_security_group.third-SG.id]
depends_on = [aws_instance.WG_DL_SRV]
network_interface {
network_interface_id = aws_network_interface.nic.id
device_index = 0
}
}
If you find it's still in slightly the wrong order, you can also then add the EIP to the depends_on parameter of the instance, since the EIP is no longer dependent on the instance itself.
Note that it's been a while since I've done precisely this, you may find you need to move a few of the other networky type parameters (security groups etc) out of the instance and into the network interface block.

terraform aws ec2 instance ip address assignment

I have a typical problem with terraform and aws. I have to deploy 26 instances via terraform but they all should have ip address in an increment order.
for example
instance 1: 0.0.0.1
instance 2: 0.0.0.2
instance 3: 0.0.0.3
Is it possible somehow to achieve in terraform?
Below, you can find an example of how to do it. It just creates for instances with ip range from 172.31.64.100 to 172.31.64.104 (you can't use first few numbers as they are reserved by AWS).
You will have to adjust subnet id and initial IP range which I used in my example to your subnets. You also must ensure that these IP addresses are not used. AWS could already use them for its load balances in your VPC, existing instances or other services. If any IP address in this range is already taken, it will fail.
locals {
ip_range = [for val in range(100, 104): "172.31.64.${val}"]
}
resource "aws_network_interface" "foo" {
for_each = toset(local.ip_range)
subnet_id = "subnet-b64b8988"
private_ips = [each.key]
tags = {
Name = "primary_network_interface"
}
}
resource "aws_instance" "web" {
for_each = toset(local.ip_range)
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
network_interface {
network_interface_id = aws_network_interface.foo[each.key].id
device_index = 0
}
tags = {
Name = "HelloWorld"
}
}

Terraform AWS : Couldn't reuse previously created root_block_device with AWS EC2 instance launched with aws_launch_configuration

I've deployed an ELK stack to AWS ECS with terraform. All was running nicely for a few weeks, but 2 days ago I had to restart the instance.
Sadly, the new instance did not rely on the existing volume to mount the root block device. So all my elasticsearch data are no longer available to my Kibana instance.
Datas are still here, on previous volume, currently not used.
So I tried many things to get this volume attached at "dev/xvda" but without for exemple:
Use ebs_block_device instead of root_block_device using
Swap "dev/xvda" when instance is already running
I am using an aws_autoscaling_group with an aws_launch_configuration.
resource "aws_launch_configuration" "XXX" {
name = "XXX"
image_id = data.aws_ami.latest_ecs.id
instance_type = var.INSTANCE_TYPE
security_groups = [var.SECURITY_GROUP_ID]
associate_public_ip_address = true
iam_instance_profile = "XXXXXX"
spot_price = "0.04"
lifecycle {
create_before_destroy = true
}
user_data = templatefile("${path.module}/ecs_agent_conf_options.tmpl",
{
cluster_name = aws_ecs_cluster.XXX.name
}
)
//The volume i want to reuse was created with this configuration. I though it would
//be enough to reuse the same volume. It doesn't.
root_block_device {
delete_on_termination = false
volume_size = 50
volume_type = "gp2"
}
}
resource "aws_autoscaling_group" "YYY" {
name = "YYY"
min_size = var.MIN_INSTANCES
max_size = var.MAX_INSTANCES
desired_capacity = var.DESIRED_CAPACITY
health_check_type = "EC2"
availability_zones = ["eu-west-3b"]
launch_configuration = aws_launch_configuration.XXX.name
vpc_zone_identifier = [
var.SUBNET_1_ID,
var.SUBNET_2_ID]
}
Do I miss something obvious about this?
Sadly, you cannot attach a volume as a root volume to an instance.
What you have to do is create a custom AMI based on your volume. This involves creating a snapshot of the volume followed by construction of the AMI:
Creating a Linux AMI from a snapshot
In terraform, there is aws_ami specially for that purpose.
The following terraform script exemplifies the process in three steps:
Creation of a snapshot of a given volume
Creation of an AMI from the snapshot
Creation of an instance from the AMI
provider "aws" {
# your data
}
resource "aws_ebs_snapshot" "snapshot" {
volume_id = "vol-0ff4363a40eb3357c" # <-- your EBS volume ID
}
resource "aws_ami" "my" {
name = "my-custom-ami"
virtualization_type = "hvm"
root_device_name = "/dev/xvda"
ebs_block_device {
device_name = "/dev/xvda"
snapshot_id = aws_ebs_snapshot.snapshot.id
volume_type = "gp2"
}
}
resource "aws_instance" "web" {
ami = aws_ami.my.id
instance_type = "t2.micro"
# key_name = "<your-key-name>"
tags = {
Name = "InstanceFromCustomAMI"
}
}

generate user_data including "IP Address" while creating ec2 instance using terraform

I am trying to spin 2 ec2 instances using terraform. Something like this
resource "aws_instance" "example" {
count = "${var.number_of_instances}"
ami = "${var.ami_name}"
associate_public_ip_address = "${var.associate_public_ip_address}"
instance_type = "${var.instance_type}"
key_name = "${var.keyname}"
subnet_id = "${element(var.subnet_ids, count.index)}"
user_data = "${element(data.template_file.example.*.rendered, count.index)}"
vpc_security_group_ids = ["${aws_security_group.example.id}","${var.extra_security_group_id}"]
root_block_device {
volume_size = "${var.root_volume_size}"
volume_type = "${var.root_volume_type}"
iops = "${var.root_volume_iops}"
}
tags {
Name = "${var.prefix}${var.name}${format("%02d", count.index + 1)}"
}
}
In template_file all I am trying to do is to generate a config file with IP Address of both the instances using user_data but this fails saying Cycle Error.
Is there any way to get the file to generate with IP Address while the ec2 instances are coming up
Make use of the AWS Instance Metadata endpoint in your userdata script to get each instance's IP address and put it into a config file. Here is a Powershell example of a userdata script:
<powershell>
$HostIp = (Invoke-RestMethod -URI 'http://169.254.169.254/latest/meta-data/local-ipv4' -UseBasicParsing)
Add-Content "C:\installer\config.txt" "HostIp:$HostIp"
</powershell>
You can also get the instance's public-ipv4 in this manner if that's desired instead.
By combining the various information, you can use user_data argument of the resource "aws_launch_template" block, to call a shell, itself may call in return a typical special metadata endpoint. For the private IP, it would be:
curl http://169.254.169.254/latest/meta-data/local-ipv4