Terrafrom | variable | input to bash script - terraform

I have veraibles in varibles.tf files in terraf from
locals {
deployment-type = lower(var.env)
I am making an instance that will join ecs cluster. I need to inject cluster name in the Template file from TF vars
resource "aws_instance" "ec2-instance" {......
user_data = "${data.template_file.user_data.rendered}"
data "template_file" "user_data" {
template = "${file("${path.module}/user_data.tpl")}"
}
Here is tpl file. I need to inject cluster name in this file from TF vars
# Update all packages
sudo yum update -y
sudo yum install -y ecs-init
sudo service docker start
sudo start ecs
echo "${var.env}"
#Adding cluster name in ecs config
#echo "${var.source_dest_check}"= >> /etc/ecs/ecs.config
cat /etc/ecs/ecs.config | grep "ECS_CLUSTER"```

Just off the top, I think you are not supposed to use ${var.env} in the template, but rather ${env}.
There are multiple parts to how this works. I'll have to assume you are missing one of these parts. I can provide a simple example.
From my own userdata definitions, some greatly-simplified samples:
variable "product" {
default = ""
}
data "template_file" "userdata" {
template = file("${path.module}/userdata.sh")
vars = {
product = var.product
}
}
From my terraform.tfvars file:
product = "radar"
Somewhere in the file userdata.sh, which is the template:
"product": "${product}",
The template is of course more complex than this. I'd suggest starting with a simple file and checking that it came out looking like you want, then build on that. Once you see the vars flowing through the chain as you want, it should be simple to build out a more complex bootstrap.

Related

Get most recent/newest image from ECR using terraform data source

I have an ECR repository named workflow and in this repository, there is 5 image pushed using GitHub action.
Now I have a terraform workflow that will just use the image from ECR and using this ECR image builds the ECS container definition.
so now I want to fetch the latest image with the tag whatever it would be...
I tried the below thing
data "aws_ecr_repository" "example" {
name = "workflow"
}
and then
"image": "${data.aws_ecr_repository.example.repository_url}"
but here I only get the Url for the repo without a tag
so how can I pass here the latest or newest image with the tag?
As terraform is not capable for this thing and you want to use still terraform in you are workplace then you can use terraform as an external data source
resource "aws_ecs_task_definition" "snipe-main" {
container_definitions = <<TASK_DEFINITION
[
{
"image":"${data.aws_ecr_repository.example.repository_url}:${data.external.current_image.result["image_tag"]}"
}
]
TASK_DEFINITION
}
data "external" "current_image" {
program = ["bash", "./ecs-task.sh"]
}
output "get_new_tag" {
value = data.external.current_image.result["image_tag"]
}
cat ECS-task.sh
#!/bin/bash
set -e
imageTag=$(aws ecr describe-images --repository-name <<here your repo name>> --query 'sort_by(imageDetails,& imagePushedAt)[-1].imageTags[0]')
imageTag=`sed -e 's/^"//' -e 's/"$//' <<<"$imageTag"`
jq -n --arg imageTag "$imageTag" '{"image_tag":$imageTag}'
exit 0
I was looking for the same, look if this documentation suites you
https://registry.terraform.io/providers/hashicorp/aws/2.34.0/docs/data-sources/ecr_image
it includes a way to obtain the image:
data "aws_ecr_image" "service_image" {
repository_name = "my/service"
image_tag = "latest"
}
the problem of that is that "image_uri" isnt in the resource.
There is an open issue in Github about it:
https://github.com/hashicorp/terraform-provider-aws/pull/24526
Meanwhile you can use this format for the url:
"${var.aws_account_id}.dkr.ecr.${var.region}.amazonaws.com/${var.project_name}:${var.latest-Tag}"

terraform provisioning locally cloudflared tunnel

I tried to use terraform without any Cloud instance - only for local install cloudflared tunnel using construction:
resource "null_resource" "tunell_install" {
triggers = {
always_run = timestamp()
}
provisioner "local-exec" {
command = "/home/uzer/script/tunnel.sh"
}
}
instead something like:
provider "google" {
project = var.gcp_project_id
}
but after running
$ terraform apply -auto-approve
successfully created /etc/cloudflared/cert.json with content:
{
"AccountTag" : "${account}",
"TunnelID" : "${tunnel_id}",
"TunnelName" : "${tunnel_name}",
"TunnelSecret" : "${secret}"
}
but as I undestood here must be values instead variables? It's seems that metadata_startup_script from instance.tf only applied to Google instance. How it's possible to change it for using terraform with install CF tunnel locally and running tunnel? Maybe also need to use templatefile but in other .tf file? The curent code block metadata_startup_script:
// This is where we configure the server (aka instance). Variables like web_zone take a terraform variable and provide it to the server so that it can use them as a local variable
metadata_startup_script = templatefile("./server.tpl",
{
web_zone = var.cloudflare_zone,
account = var.cloudflare_account_id,
tunnel_id = cloudflare_argo_tunnel.auto_tunnel.id,
tunnel_name = cloudflare_argo_tunnel.auto_tunnel.name,
secret = random_id.tunnel_secret.b64_std
})
Content of server.tpl file:
# Script to install Cloudflare Tunnel
# cloudflared configuration
cd
# The package for this OS is retrieved
wget https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-amd64.deb
sudo dpkg -i cloudflared-stable-linux-amd64.deb
# A local user directory is first created before we can install the tunnel as a system service
mkdir ~/.cloudflared
touch ~/.cloudflared/cert.json
touch ~/.cloudflared/config.yml
# Another herefile is used to dynamically populate the JSON credentials file
cat > ~/.cloudflared/cert.json << "EOF"
{
"AccountTag" : "${account}",
"TunnelID" : "${tunnel_id}",
"TunnelName" : "${tunnel_name}",
"TunnelSecret" : "${secret}"
}
EOF
# Same concept with the Ingress Rules the tunnel will use
cat > ~/.cloudflared/config.yml << "EOF"
tunnel: ${tunnel_id}
credentials-file: /etc/cloudflared/cert.json
logfile: /var/log/cloudflared.log
loglevel: info
ingress:
- hostname: ssh.${web_zone}
service: ssh://localhost:22
- hostname: "*"
service: hello-world
EOF
# Now we install the tunnel as a systemd service
sudo cloudflared service install
# The credentials file does not get copied over so we'll do that manually
sudo cp -via ~/.cloudflared/cert.json /etc/cloudflared/
# Now we can start the tunnel
sudo service cloudflared start
In argo.tf exist this code:
data "template_file" "init" {
template = file("server.tpl")
vars = {
web_zone = var.cloudflare_zone,
account = var.cloudflare_account_id,
tunnel_id = cloudflare_argo_tunnel.auto_tunnel.id,
tunnel_name = cloudflare_argo_tunnel.auto_tunnel.name,
secret = random_id.tunnel_secret.b64_std
}
}
If you are asking about how to create the file locally and populate the values, here is an example:
resource "local_file" "cloudflare_tunnel_script" {
content = templatefile("${path.module}/server.tpl",
{
web_zone = "webzone"
account = "account"
tunnel_id = "id"
tunnel_name = "name"
secret = "secret"
}
)
filename = "${path.module}/server.sh"
}
For this to work, you would have to assign the real values for all the template variables listed above. From what I see, there are already examples of how to use variables for those values. In other words, instead of hardcoding the values for template variables you could use standard variables:
resource "local_file" "cloudflare_tunnel_script" {
content = templatefile("${path.module}/server.tpl",
{
web_zone = var.cloudflare_zone
account = var.cloudflare_account_id
tunnel_id = cloudflare_argo_tunnel.auto_tunnel.id
tunnel_name = cloudflare_argo_tunnel.auto_tunnel.name
secret = random_id.tunnel_secret.b64_std
}
)
filename = "${path.module}/server.sh"
}
This code will populate all the values and create a server.sh script in the same directory you are running the Terraform code from.
You could complement this code with the null_resource you wanted:
resource "null_resource" "tunnel_install" {
depends_on = [
local_file.cloudflare_tunnel_script,
]
triggers = {
always_run = timestamp()
}
provisioner "local-exec" {
command = "${path.module}/server.sh"
}
}

terragrunt not accepting vars files

I am trying to use a 2-repo IaC with the so called back-end being in the form of terragrunt modules and the front-end (or live) with the instantiation of such modules that are being filled in with variables.
The image below depicts the structure of those 2 repos (terragrunt being the back-end and terraform-live the live one as the name implies).
In my terragrunt/aws-vpc/variables.tf, there is the following declaration:
variable "remote_state_bucket" {
description = "The bucket containing the terraform remote state"
}
However, when trying to perform a terragrunt apply in the live directory, i get the following:
var.remote_state_bucket
The bucket containing the terraform remote state
Enter a value:
Here is my terraform-live/environments/staging/terragrunt.hcl
remote_state {
backend = "s3"
config = {
bucket = "my-bucket-staging"
key = "terraform/state/var.env_name/${path_relative_to_include()}"
region = "eu-west-1"
}
}
# Configure root level variables that all resources can inherit
terraform {
extra_arguments "extra_args" {
commands = "${get_terraform_commands_that_need_vars()}"
optional_var_files = [
"${get_terragrunt_dir()}/${find_in_parent_folders("config.tfvars", "ignore")}",
"${get_terragrunt_dir()}/${find_in_parent_folders("secrets.auto.tfvars", "ignore")}",
]
}
}
What is more, the variable seems to be declared in one of the files that terragrunt is instructed to read variables from:
➢ cat terraform-live/environments/staging/config.tfvars
remote_state_bucket = "pkaramol-staging"
Why is terragrunt (or terraform ?) unable to read the specific variable?
➢ terragrunt --version
terragrunt version v0.19.29
➢ terraform --version
Terraform v0.12.4
Because config.tfvars is not in a parent folder :)
find_in_parent_folders looks in parent folders, but not in the current folder. And your config.tfvars is in the same folder as your terragrunt.hcl.
Try using something like:
optional_var_files = [
"${get_terragrunt_dir()}/config.tfvars",
"${get_terragrunt_dir()}/secrets.auto.tfvars",
]

Terraform - Using Local Command Result as an Variable for tf file

Is there a way to use local-exec to generate an output for a variable inside of Terraform .tf file?
data-external feature of Terraform has helped me
cat owner.sh
jq -n --arg username $(git config user.name) '{"username": $username}'
The config part which must be added on instance_create.tf files;
data "external" "owner_tag_generator" {
program = ["bash", "/full/path/of/owner.sh"]
}
output "owner" {
value = "${data.external.owner_tag_generator.result}"
}
tags {
...
CreatorName = "${data.external.owner_tag_generator.result.username}"
...
}

How to give a .tf file as input in Terraform Apply command?

I'm a beginner in Terraform.
I have a directory which contains 2 .tf files.
Now I want to run Terraform Apply on a selected .tf file & neglect the other one.
Can I do that? If yes, how? If no, why & what is the best practice?
You can't selectively apply one file and then the other. Two ways of (maybe) achieving what you're going for:
Use the -target flag to target resource(s) in one file and then the other.
Put each file (or more broadly, group of resources, which might be multiple files) in separate "modules" (folders). You can then apply them separately.
You can use the terraform -target flag. Or
You can have multiple terraform modules in a separate directory. And then you can terraform apply there.
As an example, assume you have 3 .tf files separately. But you need to run more than just one of them at the same time. If you also, need to run them more often it's better to have a terraform module.
terraform
|--frontend
| └──main.tf
|--backend-1
| └──main.tf
|--backend-2
| └──main.tf
|--modules-1
| └──module.tf
Inside the module.tf you can define which files you need to apply.
module "frontend" {
source = "terraform/frontend"
}
module "backend-1" {
source = "terraform/backend-1"
}
Then issue terraform apply staying at the module directory. And it will automatically import instances inside those paths and apply it.
Putting each terraform config file into separate directory did the job correctly.
So here, is my structure
├── aws
│   └── aws_terraform.tf
├── trash
│ └── main.tf
All you have to do:
enter each folder
terraform init && terraform plan && terraform apply
enter 'yes' to confirm terraform apply
PS: '-target' key didn't help me out.
Either use a --target option to specify module to run by using below command
terraform apply -target=module.<module_name>
Or another workaround is rename other terraform files with *.tf.disable extension to skip it by loading via Terraform. Currently it's Code loading *.tf files
If you cant have terraform files in different folders like the other answers stated. You can try using my script GitHub repo for script
Which is a script that runs throughout a specific terraform file and outputs adds "-target=" to all modules names.
This is a filler for the data block, as others have already explained resource block
You can also target data block if you're performing a read operation.
Lets say you have two files - create.tf and read.tf
Assuming create.tf is already applied:
resource "hashicups_order" "edu" {
items {
coffee {
id = 3
}
quantity = 3
}
items {
coffee {
id = 2
}
quantity = 1
}
}
output "edu_order" {
value = hashicups_order.edu
}
And you only want to apply read.tf:
data "hashicups_ingredients" "first_coffee" {
coffee_id = hashicups_order.edu.items[0].coffee[0].id
}
output "first_coffee_ingredients" {
value = data.hashicups_ingredients.first_coffee
}
You can create a plan file targeting the read only data block:
terraform plan -target=data.hashicups_ingredients.first_coffee
And similarly, apply the read operation using Terraform:
terraform apply -target=data.hashicups_ingredients.first_coffee -auto-approve
No, unfortunately, Terraform doesn't have the feature to apply a selected .tf file. Terraform applies all .tf files in the same directory.
But you can apply the selected code with comment out and uncomment. For example, you have 2 .tf files "1st.tf" and "2nd.tf" in the same directory to create the resources for GCP(Google Cloud Platform):
Then, "1st.tf" has this code below:
provider "google" {
credentials = file("myCredentials.json")
project = "myproject-113738"
region = "asia-northeast1"
}
resource "google_project_service" "project" {
service = "iam.googleapis.com"
disable_dependent_services = true
}
And "2nd.tf" has this code below:
resource "google_service_account" "service_account_1" {
display_name = "Service Account 1"
account_id = "service-account-1"
}
resource "google_service_account" "service_account_2" {
display_name = "Service Account 2"
account_id = "service-account-2"
}
Now, first, you want to only apply the code in "1st.tf" so you need to comment out the code in "2nd.tf":
1st.tf:
provider "google" {
credentials = file("myCredentials.json")
project = "myproject-113738"
region = "asia-northeast1"
}
resource "google_project_service" "project" {
service = "iam.googleapis.com"
disable_dependent_services = true
}
2nd.tf (Comment Out):
# resource "google_service_account" "service_account_1" {
# display_name = "Service Account 1"
# account_id = "service-account-1"
# }
# resource "google_service_account" "service_account_2" {
# display_name = "Service Account 2"
# account_id = "service-account-2"
# }
Then, you apply:
terraform apply -auto-approve
Next, additionally, you want to apply the code in "2nd.tf" so you need to uncomment the code in "2nd.tf":
1st.tf:
provider "google" {
credentials = file("myCredentials.json")
project = "myproject-113738"
region = "asia-northeast1"
}
resource "google_project_service" "project" {
service = "iam.googleapis.com"
disable_dependent_services = true
}
2nd.tf (Uncomment):
resource "google_service_account" "service_account_1" {
display_name = "Service Account 1"
account_id = "service-account-1"
}
resource "google_service_account" "service_account_2" {
display_name = "Service Account 2"
account_id = "service-account-2"
}
Then, you apply:
terraform apply -auto-approve
This way, you can apply the selected code with comment out and uncomment.
terraform apply -target nginx-docker.tf

Resources