I want to identify the public IP of the terraform execution environment and add it to aws security group inbound to prevent access from other environments.
Currently, I
The most elegant solution I could come up with is to use the "external data source" provider. https://www.terraform.io/docs/providers/external/data_source.html
This was written for these kind of purposes where people were combining local-exec, null-resource and vars to inject something locally.
Anyway, I'm sure you can do this without writing a small script. The thing is the "external data source" expects to read JSON. So in my example I just built a JSON string in a program and then call that program. I'm sure this could be done in a one liner using echo or jq...
Here is my main.tf file:
data "external" "example" {
program = ["sh", "test.sh" ]
}
output "commandout" {
value = "${data.external.example.result}"
}
Here is my shell script (test.sh):
#!/bin/bash
echo {\"ip\":\""`hostname -I`"\"}
Technically once you have this situation you can use:
${data.external.example.result}
As your var input.
Here is my working example with terraform output.
data.external.example: Refreshing state...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
commandout = {
ip = 10.0.2.15
}
Note that hostname -I
is ok if you only have one NIC :) Otherwise use an alternative command or cut the output for your desired results.
There's an easier way to do that without any scripts. The trick is having a website such as icanhazip.com
which retrieve your IP, so set it in your terraform file as data
:
data "http" "myip" {
url = "http://ipv4.icanhazip.com"
}
And whenever you want to place your IP just use data.http.myip.body
, example:
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = ["${chomp(data.http.myip.body)}/32"]
}
Note I used terraform chomp()
method to remove any trailing space or new line which comes with body.
You can use your ipv6 with http://ipv6.icanhazip.com. Take care by just using http://icanhazip.com because it can retrieve ipv4 or ipv6
In Terraform 0.12 and greater you can use native jsondecode and jsonencode to get better reliability and long term reproducibility rather than relying on string manipulation. For reference, this implementation was tested on Terraform 0.12.16.
An example implementation for grabbing your public ip address using the newer Terraform methods currently available is below. Output block is added to provide visual verification that it is working as intended.
data "http" "my_public_ip" {
url = "https://ifconfig.co/json"
request_headers = {
Accept = "application/json"
}
}
locals {
ifconfig_co_json = jsondecode(data.http.my_public_ip.body)
}
output "my_ip_addr" {
value = local.ifconfig_co_json.ip
}
If you want to utilize this in a network ingress rule, you can do the following (icmp utilized for testability in the example, without unnecessarily opening up ports in case this is blindly copy and pasted) :
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["${local.ifconfig_co_json.ip}/32"]
}
Please note that the service referenced is rate limited to ~1 request per min, so you might end up wanting to host this yourself if your needs exceed this; or if you need more guaranteed availability. The echoip service is containerized so can be hosted nearly anywhere.
https://github.com/mpolden/echoip