I have created couple of Ec2 instances in multiple subnets and assigned 4 secondary Ips to default ENI(device 0)- now the output of the aws_network_interface of private_ips attribute is giving all IP's including the host-private ip and other secondary Ip's randomly.
Now my problem is I need to exclude the "host private ip" and one ip which i assign to "windows cluster" from the output and provide the rest to create aws network load balancer. Is there an easy way to sort the output and exclude the required ip's from the List output? Here i am creating NLB with target by IP- so i need to provide the set of secondary IPs from each ec2 instance to create target group.
code:
resource "aws_instance" "web_servers" {
count="2"
ami = "ami-0a9ca0496f746e6e0" # us-west-2
instance_type = "${var.instance_type}" # ="t2.medium"
#associate_public_ip_address = "true"
#user_data = ""
network_interface {
network_interface_id = "${element(aws_network_interface.foo.*.id,count.index}"
device_index = 0
}
}
resource "aws_network_interface" "foo" {
count="${var.instance["count"]}"
subnet_id = "{aws_subnet.web_subnet.*.id[count.index]}"
private_ips_count=4
security_groups = ["${aws_security_group.web.id}"]
}
output "priviate-ip-list"
{
value="${list(aws_network_interface.foo.*.private_ips)}"
}
the output has whole list of private_ips including host private ip.
From the list I need to exclude 2 ips(one is host private ip and second one I will use for windows cluster") say here in the code I gave private_ips_count=4 (the output will have 5 ips for each instance).
so if I exclude 2 ips, I have remaining 3 from each instance.
outputs:
private-ip-list=[
[
[10.170.20.110,10.170.21.120,10.170.22.177,10.170.18.111,10.170.21.100],
[10.170.150.10,10.170.152.44,10.170.151.11,10.170.150.11,10.170.155.10]
]
]
here private ip of 1st ec2 instance is 10.170.21.120
private ip of ec2 2nd instance is 10.170.151.11
so from the output i need to exclude 10.170.21.120 and additional 1 more IP
from second set exclude 10.170.151.11 and additional 1 more ip.
from the remaining set
like from each insance one IP i need to assign to NLB target_id input to create a NLB. Here i am creating NLB with target by IP.
resource "aws_lb_target_group_attachment" "test" {
target_group_arn = "${aws_lb_target_group.test.arn}"
#target_id = "ipaddress of 1 set [10.170.20.110,10.170.150.10]"
}
I am looking for ideas on how to save the Ip's and exclude the required IPs, and pass to parameter target_id with remaining ips
Related
I'm trying to figure out how to create different number of EC2 instances between two different Terraform workspaces. My approach is to have all Terraform code in one Github branch. I would like to have one aws_instance block that creates a different number of instances that are also different sized EC2 instances. I plan on using a a TFVARS file for separate environments in this case to specify what size instances are to be used. Any advice on how to best approach this scenario would be helpful. I am using Terraform version 0.12.26
You can simply do similar like this: (3 instances for staging and 1 for other workspaces)
resource "aws_instance" "cluster_nodes" {
count = terraform.workspace == "staging" ? 3 : 1
ami = var.cluster_aws_ami
instance_type = var.cluster_aws_instance_type
# subnet_id = aws_subnet.cluster_subnet[var.azs[count.index]].id
subnet_id = var.public_subnet_ids[count.index]
vpc_security_group_ids = [aws_security_group.cluster_sg.id]
key_name = aws_key_pair.cluster_ssh_key.key_name
iam_instance_profile = "${aws_iam_instance_profile.cluster_ec2_instance_profile.name}"
associate_public_ip_address = true
tags = {
Name = "Cluster ${terraform.workspace} node-${count.index}"
}
}
I am trying to use count to create a number of instances in terraform. The way our IPA works is that it attaches the last octet of the private IP address right before the '.fdqn' of the name tag. I would like to have the IPA-name tag created to match the name IPA gives the instance. I have tried a few variations of the following.
resource "aws_instance" "test-instance" {
count = 3
ami = lookup(var.ami,var.aws_region)
instance_type = var.instance_type
key_name = "Default_AWS_Key"
tags = {
name = "test-.fdqn"
IPA-Name = format("test-%s.fdqn", split(".", aws_instance.test-instance[index.count].private_ip))
}
}
I'm relatively new to terraform and I'm trying to iterate over all aws_instances to apply a null_resource. Can you use multiple splats to access all instances, regardless of their names?
The EC2 instances are broken down by three types:
aws_instance.web.* (3 instances)
aws_instance.app.* (3 instances)
aws_instance.db.* (2 instances)
Here's my attempt to apply a null_resource to all eight aws_instances:
resource "null_resource" "install_security_package" {
#count = "${length(aws_instance)}" #terraform error: resource count can't reference variable: aws_instance
#count = "${length(aws_instance.*)}" #terraform error: resource variables must be three parts: TYPE.NAME.ATTR
count = "${length(aws_instance.*.*)}" #terraform error: unknown resource 'aws_instance.*'
connection {
type = "ssh"
host = "${element(aws_instance.*.private_ip, count.index)}"
user = "${lookup(var.user, var.platform)}"
private_key = "${file("${var.private_key_path}")}"
timeout = "2m"
}
provisioner "remote-exec" {
inline = [
"sudo rpm -Uvh http://www.example.com/security/repo/security_baseline.rpm",
]
}
}
It is not currently possible to match all resources of a given type. The "splat" syntax, as you've seen, only allows selecting all of the instances created from a particular resource block.
The closest you can get to this with Terraform today is to concatenate together the different resources:
concat(aws_instance.web.*.private_ip, aws_instance.app.*.private_ip, aws_instance.db.*.private_ip)
In the current version of Terraform as of this answer it is necessary to use some of the workarounds shared in github issue #4084 in order to avoid duplicating that complex expression in multiple places. A forthcoming feature called Local Values will make this simpler in the near future, allowing the list to be given an name to be re-used in multiple places:
# Won't work until Terraform PR#15449 is merged and released
locals {
aws_instance_addrs = "${concat(aws_instance.web.*.private_ip, aws_instance.app.*.private_ip, aws_instance.db.*.private_ip)}"
}
resource "null_resource" "install_security_package" {
count = "${length(local.aws_instance_addrs)}"
connection {
type = "ssh"
host = "${local.aws_instance_addrs[count.index]}"
user = "${lookup(var.user, var.platform)}"
private_key = "${file("${var.private_key_path}")}"
timeout = "2m"
}
provisioner "remote-exec" {
inline = [
"sudo rpm -Uvh http://www.example.com/security/repo/security_baseline.rpm",
]
}
}
I am trying to setup a remote deployment with Capistrano on the Amazon Cloud.
The idea : I SSH to a random machine of the autoscaling group and I want to deploy to all the other machines from there. In order to do that I need to get the names of the other instances so I can define the Capistrano servers I want to deploy to
I have installed the Ruby sdk but I cannot figure out the best way to retrieve the instances names (taking advantage that I am on the VPN).
I have actually two possibilities : either find the instances by tags (I have tagged them with "production") or by the ID of the autoscaling group.
I don't want to use other "big guns" like Chef, etc.
After reading too much documentation
Two strategies : retrieve the dns names by autoscaling group OR by tags
By Tags
ec2 = Aws::EC2::Client.new
instances_tagged = ec2.describe_instances(
dry_run: false,
filters: [
{
name: 'tag:environment',
values: ['production'],
},
{
name: 'tag:stack',
values: ['rails'],
}
],
)
dns_tagged = instances_tagged.reservations[0].instances.map(&:private_dns_name)
By Autoscaling group
as = Aws::AutoScaling::Client.new
instances_of_as = as.describe_auto_scaling_groups(
auto_scaling_group_names: ['Autoscaling-Group-Name'],
max_records: 1,
).auto_scaling_groups[0].instances
if instances_of_as.empty?
autoscaling_dns = []
else
instances_ids = instances_of_as.map(&:instance_id)
autoscaling_dns = instance_ids.map do |instance_id|
ec2.instances[instance_id].private_dns_name
end
end
Let's say I'm using Terraform to provision two machines inside AWS:
An EC2 Machine running NodeJS
An RDS instance
How does the NodeJS code obtain the address of the RDS instance?
You've got a couple of options here. The simplest one is to create a CNAME record in Route53 for the database and then always point to that CNAME in your application.
A basic example would look something like this:
resource "aws_db_instance" "mydb" {
allocated_storage = 10
engine = "mysql"
engine_version = "5.6.17"
instance_class = "db.t2.micro"
name = "mydb"
username = "foo"
password = "bar"
db_subnet_group_name = "my_database_subnet_group"
parameter_group_name = "default.mysql5.6"
}
resource "aws_route53_record" "database" {
zone_id = "${aws_route53_zone.primary.zone_id}"
name = "database.example.com"
type = "CNAME"
ttl = "300"
records = ["${aws_db_instance.default.endpoint}"]
}
Alternative options include taking the endpoint output from the aws_db_instance and passing that into a user data script when creating the instance or passing it to Consul and using Consul Template to control the config that your application uses.
You may try Sparrowform - a lightweight provision tool for Terraform based instances, it's capable to make an inventory of Terraform resources and provision related hosts, passing all the necessary data:
$ terrafrom apply # bootstrap infrastructure
$ cat sparrowfile # this scenario
# fetches DB address from terraform cache
# and populate configuration file
# at server with node js code:
#!/usr/bin/env perl6
use Sparrowform;
$ sparrowfrom --ssh_private_key=~/.ssh/aws.pem --ssh_user=ec2 # run provision tool
my $rdb-adress;
for tf-resources() -> $r {
my $r-id = $r[0]; # resource id
if ( $r-id 'aws_db_instance.mydb') {
my $r-data = $r[1];
$rdb-address = $r-data<address>;
last;
}
}
# For instance, we can
# Install configuration file
# Next chunk of code will be applied to
# The server with node-js code:
template-create '/path/to/config/app.conf', %(
source => ( slurp 'app.conf.tmpl' ),
variables => %(
rdb-address => $rdb-address
),
);
# sparrowform --ssh_private_key=~/.ssh/aws.pem --ssh_user=ec2 # run provisioning
PS. disclosure - I am the tool author