Escape chars in Terraform local exec provisioner - ansible

I want to chain Terraform and Ansible using the local-exec provisioner;
However since this requires input to Ansible from Terraform I am stuck with the following complex command:
provisioner "local-exec" {
command = 'sleep 60; ansible-playbook -i ../ansible/inventory/ ../ansible/playbooks/site.yml --extra-vars "rancher_server_rds_endpoint="${aws_db_instance.my-server-rds.endpoint}" rancher_server_elastic_ip="${aws_eip.my-server-eip.public_ip}""'
}
which keeps returning
illegal char
error;
any suggestion about escaping correctly?
If the ansible-playbook command was to run directly in the shell it would be:
ansible-playbook -i inventory playbooks/site.yml --extra-vars "my_server_rds_endpoint=my-server-db.d30ikkj222.us-west-1.rds.amazonaws.com rancher_server_elastic_ip=88.148.17.236"
(paths differ)

Terraform syntax states that:
Strings are in double-quotes.
So you need to replace single quotes with double ones, and then escape quotes inside, for example:
provisioner "local-exec" {
command = "sleep 60; ansible-playbook -i ../ansible/inventory/ ../ansible/playbooks/site.yml --extra-vars \"rancher_server_rds_endpoint='${aws_db_instance.my-server-rds.endpoint}' rancher_server_elastic_ip='${aws_eip.my-server-eip.public_ip}'\""
}

The only way I know, that will work for any special characters in variables, is to use environment, for example:
provisioner "local-exec" {
command = join(
" ", [
"sleep 60;",
"ansible-playbook -i ../ansible/inventory/",
"../ansible/playbooks/site.yml",
"--extra-vars",
"rancher_server_rds_endpoint=\"$RANCHER_SERVER_RDS_ENDPOINT\"",
"rancher_server_elastic_ip=\"$RANCHER_SERVER_ELASTIC_IP\""
]
)
environment = {
RANCHER_SERVER_RDS_ENDPOINT = aws_db_instance.my-server-rds.endpoint
RANCHER_SERVER_ELASTIC_IP = aws_eip.my-server-eip.public_ip
}
}

Related

How to pass environment variable to sh script?

I want to create an environment variable in Jenkinsfile which will consist of current workspace (using envirnoment variable WORKSPACE). Then I want to pass this new variable to sh script in next stage.
I've tried declaring variable in the following way:
environment {
artefact_path = "${env.WORKSPACE}/temp/unzipped/${artefact_name}/dev"
}
But after passing it to sh script:
sh "ansible-playbook -i inventory playbook.yml -e \"artefact_path=${env.artefact_path}\""
I get the following output:
+ ansible-playbook -i inventory playbook.yml -e 'artefact_path=C:\nowy_dir\workspace\something/temp/unzipped/something/dev'
PLAY [play] **************************************************************
TASK [task] *********************************************************
fatal: [host]: FAILED! => {"changed": false, "dest": "D:/inetpub/something", "msg": "Get-AnsibleParam: Parameter 'src' has an invalid path 'C:\nowy_dir\\workspace\\something/temp/unzipped/something/dev' specified.", "src": "C:\nowy_dir\\workspace\\something/temp/unzipped/something/dev"}
to retry, use: --limit #/etc/ansible/ansible-scripts/playbook.retry
As you can see the variable is passed with two \ instead of one. How can I prevent this from happening?
EDIT 1
I decided to change the way in which I declare the variable to:
def get_deploy_path() {
def site = get_site()
def wspace = "${env.WORKSPACE}"
def deploy_path = wspace.toString() + "\\temp\\snapshot\\" + site + "\\"
return deploy_path
}
environment {
deploy_path = get_deploy_path()
}
Now I have a problem with workspace. I'm operating on two agents (first is Windows and the second is Linux based). I need the path to be the same both in stages on Windows and on Linux. Any idea how I can make it happen?

How to call a variable of string with spaces in a terraform provisioner?

I am trying to run terraform provisioner which is calling my ansible playbook , now I am passing public key as a variable from user . When passing public key it doesnt take the entire key and just ssh-rsa , but not a complete string.
I want to pass the complete string as "ssh-rsa Aghdgdhfghjfdh"
The provisioner in terraform which I am running is :
resource "null_resource" "bastion_user_provisioner" {
provisioner "local-exec" {
command = "sleep 30 && ansible-playbook ../../../../ansible/create-user.yml --private-key ${path.module}/${var.project_name}.pem -vvv -u ubuntu -e 'username=${var.username}' -e 'user_key=${var.user_key}' -i ${var.bastion_public_ip}, -e 'root_shell=/bin/rbash' -e 'raw_password=${random_string.bastion_password.result}'"
}
}
If i run playbook alone as:
ansible-playbook -i localhost create-user.yml --user=ubuntu --private-key=kkk000.pem -e "username=kkkkk" -e 'user_key='ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+GWlljlLzW6DOEo"' -e root_shell="/bin/bash"
it works,
But I want the string to be in a terraform variable which is passed in provisioner.
I want to have key copied to a file as
ssh-rsa AWRDkj;jfdljdfldkf'sd.......
and not just
ssh-rsa
You are getting bitten by the -e key=value splitting that goes on with the command-line --extra-args interpretation [citation]. What you really want is to feed -e some JSON text, to stop it from trying to split on whitespace. That will also come in handy for sufficiently complicated random string passwords, which would otherwise produce a very bad outcome when trying to pass them on the command-line.
Thankfully, there is a jsonencode() function that will help you with that problem:
resource "null_resource" "bastion_user_provisioner" {
provisioner "local-exec" {
command = <<SH
set -e
sleep 30
ansible -vvv -i localhost, -c local -e '${jsonencode({
"username"="${var.username}",
"user_key"="${var.user_key}",
"raw_password"="${random_string.bastion_password.result}",
})}' -m debug -a var=vars all
SH
}
}

Terraform error when make's terraform apply command

When I do terraform plan -var-file=../variables.tfvars
pass all good
But then I run terraform apply -var-file=../variables.tfvars
give me this error and I don't know how to solve this because the directory's path is correct.
Error: Error applying plan:
1 error(s) occurred:
* aws_instance.mongodb_server: 1 error(s) occurred:
* Error running command 'sleep 60 && export ANSIBLE_HOST_KEY_CHECKING=False && echo "[mongodb]
54.193.20.170" > /tmp/inventory.ws && ansible-playbook -i /tmp/inventory.ws -e "mongodb_password=blahblah" -e "mongodb_user=admin" -u ec2-user -b --private-key=../BASE/files/joujou.pem ../DATABASE/files/ansible-mongodb-standalone/mongodb.yml': exit status 127. Output: /bin/sh: 2: ansible-playbook: not found
The code is like:
resource "aws_instance" "mongodb_server" {
instance_type = "${lookup(var.mongodb_instance_type_control,
var.target_env)}"
vpc_security_group_ids =
["${aws_security_group.default_internal.id}"]
ami = "${lookup(var.amazon_ami_by_location, var.aws_region)}"
key_name = "${var.key_name}"
subnet_id = "${data.aws_subnet.subnet_a.id}"
tags {
Name = "tf-mongodb-${lookup(var.environment, var.target_env)}"
}
associate_public_ip_address = true
provisioner "local-exec" {
command = "sleep 60 && export ANSIBLE_HOST_KEY_CHECKING=False && echo \"[mongodb]\n${aws_instance.mongodb_server.public_ip}\" > /tmp/inventory.ws && ansible-playbook -i /tmp/inventory.ws -e \"mongodb_password=${var.mongodb_default_password}\" -e \"mongodb_user=${var.mongodb_default_username}\" -u ec2-user -b --private-key=../BASE/files/joujou.pem ../DATABASE/files/ansible-mongodb-standalone/mongodb.yml"
}
Output: /bin/sh: 2: ansible-playbook: not found
This is your actual error. Terraform plan does not capture this error as local-exec commands are not evaluated by terraform plan.
Do you have ansible installed on the machine where you are trying to run the above terraform? And if installed, is it on the path.
Try installing ansible if its not installed already. If ansible is already installed, do a echo $PATH in your local-exec command and confirm if ansible is present in the given path.

Using parameters within a shell command in Jenkinsfile for Jenkins pipeline

I want to use defined parameters in Jenkinsfile in several shell commands, but I get an exception. In my example I want to execute a simple docker command. The parameter defines the path to docker executable.
This is my very short Jenkinsfile:
pipeline {
agent any
parameters {
string(defaultValue: '/Applications/Docker.app/Contents/Resources/bin/docker', description: '', name: 'docker')
}
stages {
stage('Test') {
steps {
sh 'sudo ${params.docker} ps -a'
}
}
}
}
And I get the following exception:
[e2e-web-tests_master-U4G4QJHPUACAEACYSISPVBCMQBR2LS5EZRVEKG47I2XHRI54NCCQ] Running shell script
/Users/Shared/Jenkins/Home/workspace/e2e-web-tests_master-U4G4QJHPUACAEACYSISPVBCMQBR2LS5EZRVEKG47I2XHRI54NCCQ#tmp/durable-e394f175/script.sh: line 2: ${params.docker}: bad substitution
When I change the Jenkinsfile without using the paramter inside the shell command it passes successfully:
pipeline {
agent any
parameters {
string(defaultValue: '/Applications/Docker.app/Contents/Resources/bin/docker', description: '', name: 'docker')
}
stages {
stage('Test') {
steps {
sh 'sudo /Applications/Docker.app/Contents/Resources/bin/docker ps -a'
}
}
}
}
So, how can I use parameters inside a shell command in Jenkinsfile? I tried string and text as parameter types.
The issue you have is that single quotes are a standard java String.
Double quotes are a templatable String, which will either return a GString if it is templated, or else a standard Java String.
So it you use double quotes:
stages {
stage('Test') {
steps {
sh "sudo ${params.docker} ps -a"
}
}
}
then params.docker will replace the ${params.docker} inside the 'sh' script in the pipeline.
If you want to put " inside the "sudo ${params.docker} ps -a" it doesn't work like bash (which is confusing) you use java style escaping, so "sudo \"${params.docker}\" ps -a"

Terraform, Looking for a simple way to use double quotation marks in commands?

I need a simple way of using regular quotations " in the provisioner "remote-exec" block of my terraform script. Only " will work for what I would like to do and just trying \" doesn't work. Whats the easiest way to have terraform interpret my command literally. For reference here is what I am trying to run:
provisioner "remote-exec" {
inline = [
"echo 'DOCKER_OPTS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock"' > /etc/default/docker",
]
}
Escaping with backslashes works fine for me:
$ cat main.tf
resource "null_resource" "test" {
provisioner "local-exec" {
command = "echo 'DOCKER_OPTS=\"-H tcp://0.0.0.0:2375\"' > ~/terraform/37869163/output"
}
}
$ terraform apply .
null_resource.test: Creating...
null_resource.test: Provisioning with 'local-exec'...
null_resource.test (local-exec): Executing: /bin/sh -c "echo 'DOCKER_OPTS="-H tcp://0.0.0.0:2375"' > ~/terraform/37869163/output"
null_resource.test: Creation complete
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
...
$ cat output
DOCKER_OPTS="-H tcp://0.0.0.0:2375"

Resources