Learning how to execute a Bash script from Terraform
For those learning AWS/AWS CLI, Terraform is a tool for building infrastructure with various technologies including AWS, Azure, GCP, and vSphere.
Here is a very simple document on how to use Terraform to build an AWS EC2 Linux instance and then execute a bash script from Terraform against the newly created instance. The bash script will be executed from Terraform as a provisioner.
Executing a bash script from Terraform can be used to configure the newly created server or for any other purpose.
The example below will show how to execute a script named setup-lnxcfg-user; this bash script will prepare the newly created Linux instance for Ansible playbook execution from an Ansible controller server. The script creates a new login id named lnxcfg, sets up ssh-keys, and sudo access on the new instance. This instance will be an Ansible client of an Ansible controller server; using Ansible is beyond the scope of this document, however, learning how to execute a bash script from Terraform will be very useful.
The setup-lnxcfg-user bash script is as follows:
setup-lnxcfg-user:
#!/bin/bash
# setup-lnxcfg-user
# create lnxcfg user for Ansible automation
# and configuration management.# create lnxcfg user
getentUser=$(/usr/bin/getent passwd lnxcfg)
if [ -z "$getentUser" ]
then
echo "User lnxcfg does not exist. Will Add..."
/usr/sbin/groupadd -g 2002 lnxcfg
/usr/sbin/useradd -u 2002 -g 2002 -c "Ansible Automation Account" -s /bin/bash -m -d /home/lnxcfg lnxcfgecho "lnxcfg:<PUT IN YOUR OWN lnxcfg PASSWORD FROM ANSIBLE SERVER>" | /usr/sbin/chpasswdmkdir -p /home/lnxcfg/.sshfi# setup ssh authorization keys for Ansible access
echo "setting up ssh authorization keys..."cat << 'EOF' >> /home/lnxcfg/.ssh/authorized_keys
<PUT IN YOUR OWN lnxcfg .ssh/id_rsa.pub SSH RSA KEY FROM ANSIBLE SERVER>
EOFchown -R lnxcfg:lnxcfg /home/lnxcfg/.ssh
chmod 700 /home/lnxcfg/.ssh# setup sudo access for Ansible
if [ ! -s /etc/sudoers.d/lnxcfg ]
then
echo "User lnxcfg sudoers does not exist. Will Add..."
cat << 'EOF' > /etc/sudoers.d/lnxcfg
User_Alias ANSIBLE_AUTOMATION = lnxcfg
Defaults:ANSIBLE_AUTOMATION !requiretty
ANSIBLE_AUTOMATION ALL=(ALL) NOPASSWD: ALL
EOF
chmod 400 /etc/sudoers.d/lnxcfg
fi# end of script
The terraform configuration and variable files are below.
Within the main.tf Terraform file, the nested connection block will connect to the newly created Linux instance through the ssh protocol using the AWS login user of ec2-user and the AWS pem key associated with the Linux instance. The nested provisioner “file" block will copy the setup-lnxcfg-user bash script to the /tmp directory of the new instance. The nested provisioner “remote-exec” block will add execute permission on the /tmp/setup-lnxcfg-user bash script and then execute the script with sudo permissions from the AWS login user, ec2-user. The bash script’s primary purpose is to set up the new server as an Ansible client of an Ansible controller server. After the Terraform configuration is complete an Ansible controller server will be able to use the lnxcfg user to login, sudo up, and execute Ansible playbooks against the new server.
main.tf:
# create a Linux instance in AWS
# execute bash script to set up Ansible client userprovider "aws" {
version = "~> 2.0"
access_key = var.access_key
secret_key = var.secret_key
region = var.region
}# create an instance
resource "aws_instance" "linux_instance" {
ami = lookup(var.amis, var.region)
subnet_id = var.subnet
security_groups = var.securityGroups
key_name = var.keyName
instance_type = var.instanceType
# Let's create and attach an ebs volume
# when we create the instance
ebs_block_device {
device_name = "/dev/xvdb"
volume_type = "gp2"
volume_size = 8
} # Name the instance
tags = {
Name = var.instanceName
} # Name the volumes; will name all volumes included in the
# ami and the ebs block device from above with this instance.
volume_tags = {
Name = var.instanceName
} # Copy in the bash script we want to execute.
# The source is the location of the bash script
# on the local linux box you are executing terraform
# from. The destination is on the new AWS instance.
provisioner "file" {
source = "~/setup-lnxcfg-user"
destination = "/tmp/setup-lnxcfg-user"
} # Change permissions on bash script and execute from ec2-user.
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/setup-lnxcfg-user",
"sudo /tmp/setup-lnxcfg-user",
]
}
# Login to the ec2-user with the aws key.
connection {
type = "ssh"
user = "ec2-user"
password = ""
private_key = file(var.keyPath)
host = self.public_ip
}} # end resource
variables.tf:
# variables.tfvariable "access_key" {
default = "<PUT IN YOUR AWS ACCESS KEY>"
}variable "secret_key" {
default = "<PUT IN YOUR AWS SECRET KEY>"
}variable "region" {
default = "us-east-1"
}variable "availabilityZone" {
default = "us-east-1a"
}variable "instanceType" {
default = "t2.micro"
}variable "keyName" {
default = "<PUT IN NAME OF YOUR AWS PEM KEY>"
}variable "keyPath" {
default = "~/<PUT NAME AND PATH OF THE AWS PEM KEY>.pem"
}variable "subnet" {
default = "subnet-<PUT IN YOUR VPC SUBNET>"
}variable "securityGroups" {
type = list
default = [ "sg-<PUT IN YOUR VPC SECURITY GROUP>" ]
}variable "instanceName" {
default = "<PUT IN YOUR INSTANCE NAME>"
}
# ami-0b898040803850657 is the free Amazon Linux 2 AMI
# for the us-east-1 region. Amazon Linux 2
# is a downstream version of Red Hat Enterprise Linux /
# Fedora / CentOS. It is analogous to RHEL 7.variable "amis" {
default = {
"us-east-1" = "ami-0b898040803850657"
}
}# end of variables.tf
Once these files are created you can use the terraform commands to syntax check and prepare the deployment and then deploy the AWS instance.
Within the directory where these files are located issue the command:
terraform init
The init argument will initialize the environment.
Then issue:
terraform plan -out main.plan
The plan argument will syntax check the files and prepare the deployment.
Deploy the instance:
terraform apply main.plan
To view data about the instance execute:
terraform show
To destroy the instance execute:
terraform destroy
Executing a bash script against a newly created Linux server from within Terraform can be very useful.