How to run ansible script from terraform - ansible

Hi below is my requirement.
Using terraform script i create a linux vm and post that using ansible play book i install some softwares. so i have this scripts separately with me and it is working fine.
What i want to do is that as soon as the terraform script creates the vm i want to invoke ansible script from the terraform script and install the softwares from ansible script.
I tried the below code but it did not work
provisioner "remote-exec" {
inline = ["sudo dnf -y install python"]
connection {
type = "ssh"
user = "fedora"
private_key = "${file(var.ssh_key_private)}"
}
}
provisioner "local-exec" {
command = "ansible-playbook -u fedora -i '${self.public_ip},' --private-key ${var.ssh_key_private} provision.yml"
}
So here i am not sure how the ansible get installed in the vm currently i am doing it manually and how thisansible script will get invoked from terraform
Error: Unknown root level key: provisioner

provisioner can only be used within a resource, e.g.:
resource "aws_instance" "web" {
# ...
provisioner "remote-exec" {
inline = ["sudo dnf -y install python"]
connection {
type = "ssh"
user = "fedora"
private_key = "${file(var.ssh_key_private)}"
}
}
provisioner "local-exec" {
command = "ansible-playbook -u fedora -i '${self.public_ip},' --private-key ${var.ssh_key_private} provision.yml"
}
}

Related

Post deployment bash script in Bicep file does not execute

I want to deploy an Ubuntu VM on Azure and automatically execute a few lines of Bash code right after the VM is deployed. The Bash code is supposed to install PowerShell on the VM. To do this, I use this Bicep file. Below you can see an extract of that Bicep file where I specify what Bash code I want to be executed post deployment.
resource deploymentscript 'Microsoft.Compute/virtualMachines/runCommands#2022-08-01' = {
parent: virtualMachine
name: 'postDeploymentPSInstall'
location: location
properties: {
source: {
script: '''sudo apt-get update &&\
sudo apt-get install -y wget apt-transport-https software-properties-common &&\
wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb" &&\
sudo dpkg -i packages-microsoft-prod.deb &&\
sudo apt-get update &&\
sudo apt-get install -y powershell &&\
pwsh'''
}
}
}
I searched for solutions on the web but only found conflicting explanations. I made the code above with the help of this tutorial. The only difference I see is that I'm using Bash and not PowerShell like the blog post author. Thanks for your help.
To deploy an Ubuntu VM on Azure and automatically execute a few lines of Bash code right after the VM is deployed:
I tried to create a Linux VM and used run command to install PowerShell inside the VM while deployment and was able to achieve the desired results by running below bicep file.
#description('Name of the Network Security Group')
param networkSecurityGroupName string = 'SecGroupNet'
var publicIPAddressName = '${vmName}PublicIP'
var networkInterfaceName = '${vmName}NetInt'
var osDiskType = 'Standard_LRS'
var subnetAddressPrefix = '10.1.0.0/24'
var addressPrefix = '10.1.0.0/16'
var linuxConfiguration = {
disablePasswordAuthentication: true
ssh: {
publicKeys: [
{
path: '/home/${adminUsername}/.ssh/authorized_keys'
keyData: adminPassword
}
]
}
}
resource nic 'Microsoft.Network/networkInterfaces#2021-05-01' = {
name: networkInterfaceName
location: location
properties: {
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
subnet: {
id: subnet.id
}
privateIPAllocationMethod: 'Dynamic'
publicIPAddress: {
id: publicIP.id
}
}
}
]
networkSecurityGroup: {
id: nsg.id
}
}
}
resource nsg 'Microsoft.Network/networkSecurityGroups#2021-05-01' = {
name: networkSecurityGroupName
location: location
properties: {
securityRules: [
{
name: 'SSH'
properties: {
priority: 1000
protocol: 'Tcp'
access: 'Allow'
direction: 'Inbound'
sourceAddressPrefix: '*'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '22'
}
}
]
}
}
resource vnet 'Microsoft.Network/virtualNetworks#2021-05-01' = {
name: virtualNetworkName
location: location
properties: {
addressSpace: {
addressPrefixes: [
addressPrefix
]
}
}
}
resource subnet 'Microsoft.Network/virtualNetworks/subnets#2021-05-01' = {
parent: vnet
name: subnetName
properties: {
addressPrefix: subnetAddressPrefix
privateEndpointNetworkPolicies: 'Enabled'
privateLinkServiceNetworkPolicies: 'Enabled'
}
}
resource publicIP 'Microsoft.Network/publicIPAddresses#2021-05-01' = {
name: publicIPAddressName
location: location
sku: {
name: 'Basic'
}
properties: {
publicIPAllocationMethod: 'Dynamic'
publicIPAddressVersion: 'IPv4'
dnsSettings: {
domainNameLabel: dnsLabelPrefix
}
idleTimeoutInMinutes: 4
}
}
resource vm 'Microsoft.Compute/virtualMachines#2021-11-01' = {
name: vmName
location: location
properties: {
hardwareProfile: {
vmSize: vmSize
}
storageProfile: {
osDisk: {
createOption: 'FromImage'
managedDisk: {
storageAccountType: osDiskType
}
}
imageReference: {
publisher: 'Canonical'
offer: 'UbuntuServer'
sku: ubuntuOSVersion
version: 'latest'
}
}
networkProfile: {
networkInterfaces: [
{
id: nic.id
}
]
}
osProfile: {
computerName: vmName
adminUsername: adminUsername
adminPassword: adminPassword
linuxConfiguration: ((authenticationType == 'password') ? null : linuxConfiguration)
}
}
}
resource deploymentscript 'Microsoft.Compute/virtualMachines/runCommands#2022-03-01' = {
parent: vm
name: 'linuxscript'
location: location
properties: {
source: {
script: '''# Update the list of packages
sudo apt-get update;
#Install pre-requisite packages.
sudo apt-get install -y wget apt-transport-https software-properties-common;
#Download the Microsoft repository GPG keys
wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb";
#Register the Microsoft repository GPG keys
sudo dpkg -i packages-microsoft-prod.deb;
#Update the list of packages after we added packages.microsoft.com
sudo apt-get update;
#Install PowerShell
sudo apt-get install -y powershell;
#Start PowerShell
pwsh'''
}
}
}
output adminUsername string = adminUsername
output hostname string = publicIP.properties.dnsSettings.fqdn
output sshCommand string = 'ssh $ {adminUsername}#${publicIP.properties.dnsSettings.fqdn}'
Deployed Successfully:
From Azure Portal:
After the deployment, When I ssh’d into my VM and ran Pwsh to check if PowerShell was installed
Installed successfully:
Refer MSDoc, run command template-MSDoc
Your problem is misunderstanding what the && does.
The shell will attempt to run semi-simultaneously all parts, some possibly clobbering others or not having necessary preconditions in place before starting!
Replace all instances of "&&\" with ";\" and your script should work, meaning the commands will run sequentially, waiting for the previous line to complete before attempting the subsequent lines.

Terraform template_cloudinit_config multiple part execution order is wrong

I am using the terraform to build my ec2-instances as part of instance bootstrap, added cloud-init config to run multiple userdata scripts. but the content_type = "text/x-shellscript" always executed first. I verified the cat /var/log/cloud-init-output.log file. it shows the shell script is invoked first. How do I config the shell script to run at last?
data "template_cloudinit_config" "myapp_cloudinit_config" {
gzip = false
base64_encode = false
# Main cloud-config configuration file.
part {
content_type = "text/cloud-config"
content = "${data.template_file.base_bootstrap_file.rendered}"
merge_type = "list(append)+dict(recurse_array)+str()"
}
part {
content_type = "text/cloud-config"
content = "${module.template_file_appsec_init.appsec_user_data_rendered}"
merge_type = "list(append)+dict(recurse_array)+str()"
}
part {
content_type = "text/x-shellscript"
content = "${module.template_file_beat_init.beat_user_data_rendered}"
}
}
Shell script looks like below
module " template_file_beat_init" {
source = "url" #the source url contains the zip file which includes the below shell script
}
#!/bin/sh
deploy_the_app() {
//invoke ansible playbook execution
}
deploy_the_app
Cloud provider: AWS
OS : RHEL 8.3
cloud-init --version: /usr/bin/cloud-init 19.4
Terraform v0.11.8

How can I run a shell script on multiple VMWare vm's created by terraform module?

I am using this module to spin up multiple vm's on my vmware cluster, https://registry.terraform.io/modules/Terraform-VMWare-Modules/vm/vsphere/1.6.0, and I want to run a shell script on all of the vms after using a null resource. With what i currently have, it complains that the host was not given a string, which makes sense. Here is my null resource:
# main.tf
module "jenkins-linuxvm-centos7" {
source = "Terraform-VMWare-Modules/vm/vsphere"
...
}
resource "null_resource" "vm" {
triggers = {
vm_ips = join(",", module.jenkins-linuxvm-centos7.Linux-ip)
}
# export TF_VAR_root_password=<pass>
connection {
type = "ssh"
host = module.jenkins-linuxvm-centos7.Linux-ip
user = "root"
password = var.vm_root_password
port = "22"
agent = false
}
provisioner "file" {
source = "resize_disk.sh"
destination = "/tmp/resize_disk.sh"
}
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/resize_disk.sh",
"/tmp/resize_disk.sh"
]
}
}
Do I need to use a dynamic block somehow? Or how can I modify host = module.jenkins-linuxvm-centos7.Linux-ip to include all the hosts I want to run it on?
You have to run it in a For_Each loop... Below is an example code where i am looping against the sql_var map variable. you will have to do it against the output of IPs --> module.jenkins-linuxvm-centos7.Linux-ip... you will be able to reference the IP of each machine as something like each.value i guess. I dont know how your output looks like, so guessing. If you are new to loops, here is one nice tuto.
https://blog.boltops.com/2020/10/04/terraform-hcl-loops-with-count-and-for-each
resource "null_resource" "instance" {
for_each = var.sql_var
provisioner "local-exec" {
command = "echo ${each.key} >> hello.txt"
}
}

Terraform Remote Exec Host IP from EC2 resource built

Looooong time lurker and first time poster here o/
I am currently trying to build an AWS EC2 instance with an EBS block device attached, which then needs MongoDB installed.
So I have gone the route of building the EC2 instance and attaching the EBS volume, but the remote-exec I need to run on the instance needs a host IP to connect to, to run the MongoDB install commands.
It just keeps timing out on the SSH, no matter what I try. Now I am probably just missing a step or going about this the wrong way, but hopefully you can help.
Any help would be GREATLY appreciated. :D
Below is the code sample I have slapped together:
provider "aws" {
region = "eu-west-1"
access_key = "xxxxxxx"
secret_key = "xxxxxxxx"
}
resource "tls_private_key" "mongo" {
algorithm = "RSA"
rsa_bits = 4096
}
resource "aws_key_pair" "generated_key" {
key_name = "MongoKey"
public_key = "${tls_private_key.mongo.public_key_openssh}"
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*"]
}
}
resource "aws_instance" "web" {
ami = "${data.aws_ami.ubuntu.id}"
instance_type = "t2.micro"
key_name = "MongoKey"
monitoring = true
associate_public_ip_address = true
root_block_device {
volume_size = 40
}
ebs_block_device {
volume_size = 100
device_name = "xvda"
}
tags = {
Name = "MongoDB"
}
provisioner "remote-exec" {
connection {
type = "ssh"
user = "ubuntu"
host = "MongoDB"
}
inline = [
"sudo apt-get install gnupg",
"wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add -",
"echo deb [ arch=amd64,arm64,s390x ] http://repo.mongodb.com/apt/ubuntu xenial/mongodb-enterprise/4.2 multiverse | sudo tee /etc/apt/sources.list.d/mongodb-enterprise.list",
"sudo apt-get update",
"sudo apt-get install -y mongodb-enterprise",
"sudo service mongod start",
"sudo service mongod status"
]
}
Can you try changing your connection section as below
connection {
type = "ssh"
user = "ubuntu"
host = "${aws_instance.web.private_ip}"
private_key = "${tls_private_key.mongo.private_key_pem}"
}
If you continue to face connection issues/difficulties with your remote-exec approach, I would recommend considering the user-data parameter as a replacement.
the user-data script will run during the initialization of your instance so you do not have to open any ssh sessions to provision your resource.
You can accomplish this by updating your aws_instance resource to something like this:
resource "aws_instance" "web" {
ami = "${data.aws_ami.ubuntu.id}"
instance_type = "t2.micro"
key_name = "MongoKey"
monitoring = true
associate_public_ip_address = true
root_block_device {
volume_size = 40
}
ebs_block_device {
volume_size = 100
device_name = "xvda"
}
tags = {
Name = "MongoDB"
}
user_data = << EOF
#! /bin/bash
sudo apt-get install gnupg,
wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add -,
echo deb [ arch=amd64,arm64,s390x ] http://repo.mongodb.com/apt/ubuntu xenial/mongodb-enterprise/4.2 multiverse | sudo tee /etc/apt/sources.list.d/mongodb-enterprise.list,
sudo apt-get update,
sudo apt-get install -y mongodb-enterprise,
sudo service mongod start,
sudo service mongod status
EOF
}
Hope this helps.
more userdata examples

Ansible variable from packer script

I have one variable in ansible script like
- host:{{host}}
I want to send {{host}} variable value from packer script. I want to send {{host}} value using packer build or using packer variable. Is there anyway do it?
Using an ansible provisioner in packer allows you to use both ansible_env_vars and extra_arguments.
See doco: https://www.packer.io/plugins/provisioners/ansible/ansible#configuration-reference
So we generally used extra_arguments to pass in ansible variables over the command line
{
"type": "ansible",
"playbook_file": "./my_playbook}",
"extra_arguments": "-vvv --extra-vars 'host={{user `host`}}"
}
Below a simple example:
...
variable "gitlab_version" {
type = string
default = "15.1.6"
}
...
build {
name = local.build_name
provisioner "ansible" {
...
playbook_file = "./ansible/playbook.yml"
extra_arguments = ["--extra-vars", "gitlab_version=${var.gitlab_version}"]
...
}
}
It works as it's a simple interpolation

Resources