Ansible remote provisioning vagrant - vagrant

I'm using an ansible remote server to provision my production server, that works well.
Now I thought about using this ansible server to provision my vagrant VMs.
Is this possible somehow? I thought about a shell script provision for the vagrant file that logs into the ansible server via ssh and executes the playbook command towards the VM on the local machine.
I don't have too much experience with shell scripts. Has anybody tried this or can tell me a better way to do it?

As Mxx wrote the best way would be to configure Vagrant box in a way to provision it using Ansible from the local machine. Then you would just need to type vagrant up [name of the box] and this would start the machine and provision it. Below I'am attaching a simple example of provisioning Vagrant box using Ansible, you can find all details concerning Ansible provisioner here.
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# Example of vagrant box provisioned using Ansible
# Start box: "vagrant up dev"
config.vm.define "dev" do |dev|
dev.vm.box = "Centos-6.5-minimal-x86_64-20140116"
dev.vm.box_url = "https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box"
dev.vm.provider :virtualbox do |vb|
vb.customize [
"modifyvm", :id,
"--name", "example-vagrant-box",
"--memory", 1024,
"--cpus", 2,
]
end
dev.vm.network :private_network, ip: "10.0.0.1"
dev.vm.hostname = "vagrant.local"
# Provision the box using Ansible provisioner
dev.vm.provision "ansible" do |ansible|
# Path to the inventory file
ansible.inventory_path = "./inventories/local"
# Path to playbook that should be run against the machine
ansible.playbook = "someplaybook.yml"
# Provisioning verbosity level
# "v", "vv", "vvv", "vvvv". 4 x v for the most verbose debugging info
ansible.verbose = "vvvv"
# Limit provisioning to the following groups
# This defines all the servers (or server groups) which should be provisioned to
# These are defined in the inventory file.
ansible.limit = ["db_servers"]
# Determines whether Ansible ask for Ansible Vault password when provisioner encounter encrypted file
# ansible.ask_vault_pass = "true"
# Optionally Ansible Vault password can be stored in the seperate file and passed like this
ansible.vault_password_file = "./vault-password"
end
end

Related

How to run ansible task based on the Vagrant provider

I have a vagrant job to create new VMs. Depending on the provider I pass to it, this could be created locally on Virtualbox, or on a Vsphere cluster with the vagrant-vsphere plugin.
Because of this, there are times when I want to run certain tasks on Virtualbox, and certain tasks on Vsphere. I figured the easiest way to do so would be to just pass a variable from Vagrant to ansible based on the provider. Roughly, this is what I have in my Vagrantfile so far.
$ansible_provider = ''
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.define vmconf[:name] do |vagrantconf|
vagrantconf.vm.provider :virtualbox do |vb|
$ansible_provider = "virtualbox"
end
vagrantconf.vm.provider :vsphere do |vb|
$ansible_provider = "vsphere"
end
end
config.vm.provision :ansible do |ansible|
ansible.playbook = "provision.yml"
ansible.extra_vars = { ansible_ssh_user: 'test',
ansible_provider: $ansible_provider }
end
end
I added a print statement that shows the two extra_vars and ansible_ssh_user works correctly, but ansible_provider is just blank. When I remove the first line and change it to a local variable, I get the following error:
Message: undefined local variable or method 'ansible_provider'
I haven't used ruby all that much, so I figured I'm doing something wrong there. Any help with this would be greatly appreciated.
For this specific case, I think there is an easier way of achieving what you want. Ansible creates a series of facts for each host it runs against, which includes collecting virtualisation information for guests.Try adding the following task in one of your playbooks to see what I mean:
- name: Display Virtualisation Type fact
debug:
var: ansible_virtualization_type
That should mean you don't need to pass the variable in from Vagrant. If you want to see all the facts, in the directory containing your Vagrantfile, just run (you can add '-l host' to limit to one of your VM's):
ansible -m setup all
To answer your specific question, I think this will work for you:
# In this case I don't believe you need the '$' prefix for your variables
ansible_provider = ''
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.define vmconf[:name] do |vagrantconf|
vagrantconf.vm.provider :virtualbox do |vb|
ansible_provider = "virtualbox"
end
vagrantconf.vm.provider :vsphere do |vb|
ansible_provider = "vsphere"
end
end
config.vm.provision :ansible do |ansible|
ansible.playbook = "provision.yml"
ansible.extra_vars = { ansible_ssh_user: "test",
ansible_provider: "#{ansible_provider}" }
end
end

How to use aliases in Vagrant

I'm trying to create aliases that I can use in Vagrant any time I run the VM. I've found several sources on the web about it, but can't get it working. I tried making a .bash_profile in my synced folder, but that didn't work. I noticed if I run the command alias name="command" this will work, but only for the current session. Anyone know how to do this? I'm using macOS. Thanks for your help!
Here is my Vagrantfile:
# -*- mode: ruby -*-
# vi: set ft=ruby :
unless Vagrant.has_plugin?("vagrant-vbguest")
warn "\nWARNING: The vagrant-vbguest plugin should be installed or your shared folders might not mount properly!"
warn "You can do this by running the command 'vagrant plugin install vagrant-vbguest'.\n\n"
end
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "pype_vm"
config.vm.box_url = "https://.../pype_vm.json"
config.vm.network "private_network", ip: ""
config.vm.boot_timeout = 600
config.vm.provider "virtualbox" do |v|
# This forces VirtualBox to use the host's DNS resolver instead of
# VirtualBox's
v.customize ["modifyvm", :id, "", "on"]
# This enables the PAE/NX option, which resolved at least one user's
# issues with the VM hanging on boot
v.customize ["modifyvm", :id, "--pae", "on"]
# The RHEL VM was created with 2GB of memory to facilitate provisioning,
# but this is causing issues with certain workstations. This reduces
# the amount of memory allocated to the VM but should not impact development
# performance. The number is in MB and can be increased if desired.
v.memory = 1024
end
# Share an additional folder to the guest VM.
config.vm.synced_folder File.dirname(__FILE__), "/pype"
end
The details depend on the specific of the guest being run, but some notes:
Assuming the default user account is active for vagrant ssh, ensure that any dotfiles you wish to override are copied to /home/vagrant.
If overriding .bashrc, ensure that the remote shell is started with the interactive flag (if this is true, echo $- will include i).
If overriding .bash_profile, ensure that the remote shell is started as a login shell (if this is true, echo $- will include l).

Pass environment variable to provisioner in Vagrant

I am using the Ansible (local) provisioner for my Vagrant setup. Is there a recommended way to pass an environment variable to the provisioner?
For example, I want to run ENV=development vagrant up and have Vagrant pass the environment variable ENV to Ansible.
I tried using extra_vars, taken from the Vagrant documentation:
Vagrant.configure(2) do |vagrant|
# other configuration
vagrant.vm.provision :ansible_local do |ansible|
ansible.playbook = "ansible/server.yml"
ansible.extra_vars = {
env: ENV.fetch("ENV", "development")
}
end
end
However, when I run vagrant provision (without even using the env variable in Ansible), I get the following:
$ vagrant provision
==> default: Running provisioner: ansible_local...
default: Running ansible-playbook...
ERROR: Expecting property name: line 1 column 2 (char 1)
Ansible failed to complete successfully. Any error output should be
visible above. Please fix these errors and try again.
Removing ansible.extra_vars fixes this error, but then I can't accomplish what I wanted to.
You need to install the plugin for Vagrant:
vagrant plugin install vagrant-env
I hope my code snippets will help:
config.vm.define "k8s-master" do |master|
master.vm.box = IMAGE_NAME
master.env.enable # Enable vagrant-env(.env)
master.vm.network "private_network", ip: "192.168.50.10"
master.vm.hostname = "k8s-master"
master.vm.provision "ansible" do |ansible|
ansible.playbook = "./playbooks/master.yml"
ansible.compatibility_mode = "2.0"
ansible.extra_vars = {
env: development,
}
end
end
With config.env.enable added whenever you run a Vagrant command it’ll load .env into ENV which will allow your customizations.
And you can use this inside playbook as {{ variable_name }}
.
To access development environment variable, use: ENV['development'] syntax.
You can also assign it to the variable at the beginning of the file:
development = ENV['development']
and use variable instead:
ansible.extra_vars = {
env: development
}
Check the following Vagrantfile as example.
What is it that requires the environment variable be set?
I would try to use a different approach if possible. If you are expecting to act on the environment variable in ansible, you could instead set the value in an inventory group_vars file which would only be in effect for local provisioning vagrant. Other environments could use different values for the same variable by updating the appropriate inventory group_vars file.

Setting vagrant home directory during provisioning

I have a VM that I'm pulling down with vagrant and using a VERY basic vagrant file
Vagrant.configure(2) do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search.
config.vm.box = "vagrant-rhel-devel"
config.vm.network :private_network, ip: "192.168.33.101"
config.vm.synced_folder ".", "/vagrant"
config.ssh.username = "vagrant"
config.ssh.password = "vagrant"
config.vm.provider "virtualbox" do |vb|
# Display the VirtualBox GUI when booting the machine
# vb.gui = true
# Enable 3d Rendering
vb.customize ["modifyvm", :id, "--accelerate3d", "on"]
# Sets 32megs video ram, higher number here = more POWERS
vb.customize ["modifyvm", :id, "--vram", "32"]
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
vb.name = "RedHat 3D"
end
end
My issue is that the vagrant user gets a home directory of /localhome/vagrant and I'd like him to have /home/vagrant as the home directory.
Is this something I'm able to change with provisioning or is it something that is set in the VM itself? I'm rather unskilled at the provisioning step at the moment so an example would be great.
The HOME directory has been set when the user has been created so this was done before the VM was packaged as vagrant box.
From there, you should be able to change that with
usermod -m -d /path/to/new/home/dir userNameHere
so in your case it would be
usermod -m -d /home/vagrant vagrant
check if you have existing files under the /localhome directory that would need to be copied into the new one (bash preferences file)
if you do not plan to destroy/create a bunch of new VM from this box, its not needed to add as provisioning; if you do plan to use this box and create lot of new VM, then it could make sense, you would just need to add an inline shell provision

Using Vagrant to manage development and production environments?

How are people handling simple automation (with puppet) for dev / prod environments with vagrant (ideally from the same vagrantfile)?
Use case I'm trying to solve
I would love to spin up the the production machine with vagrant if it isn't created.
I would love to reload nginx or apache confs on production with vagrant if they were tweaked in the puppet files for my dev environment.
The Problem
When you call vagrant up with a provider like AWS or Digital Ocean, it becomes the active provider and you can't switch. You get this error:
An active machine was found with a different provider. Vagrant
currently allows each machine to be brought up with only a single
provider at a time. A future version will remove this limitation.
Until then, please destroy the existing machine to up with a new
provider.
It seems the answer it to destroy, but I just need to switch. I don't want to destroy.
I would love to be able to say
vagrant up prod
or
vagrant reload prod
and then a simple vagrant up would fall back to the default machine.
This syntax is similar to how multiple machines work, but I don't want to spin up a dev and production environment when I just call vagrant up (which is the default behavior).
Should I be looking at packer as part of the workflow? I watched the whole talk at puppetconf 2013 on Mitchell's talk on Multi-Provider http://puppetlabs.com/presentations/multi-provider-vagrant-aws-vmware-and-more
I'm still not seeing a solution for my problem.
UPDATE 9/27/13
In case anybody else is fighting this idea, this article cleared up a lot of questions I had.
http://pretengineer.com/post/packer-vagrant-infra
As for workaround, you should define config.vm.define (as suggested here), in order to support multiple providers.
Please find the following configuration posted by #kzap as example:
Vagrant.configure("2") do |config|
# Store the current version of Vagrant for use in conditionals when dealing
# with possible backward compatible issues.
vagrant_version = Vagrant::VERSION.sub(/^v/, '')
# Configuration options for the VirtualBox provider.
def configure_vbox_provider(config, name, ip, memory = 2048, cpus = 1)
config.vm.provider :virtualbox do |v, override|
# override box url
override.vm.box = "ubuntu/trusty64"
# configure host-only network
override.vm.hostname = "#{name}.dev"
override.vm.network :private_network, id: "vvv_primary", ip: ip
v.customize ["modifyvm", :id,
"--memory", memory,
"--cpus", cpus,
"--name", name,
"--natdnshostresolver1", "on",
"--natdnsproxy1", "on"
]
end
end
default_provider = "virtualbox"
supported_providers = %w(virtualbox rackspace aws managed)
active_provider = ENV['VAGRANT_ACTIVE_PROVIDER'] # it'd be better to get this from the CLI --provider option
supported_providers.each do |provider|
next unless (active_provider.nil? && provider == default_provider) || active_provider == provider
#
# VM per provider
#
config.vm.define :"sample-#{provider}" do | sample_web_config |
case provider
when "virtualbox"
configure_vbox_provider(sample_web_config, "examine-web", "192.168.50.1")
when "aws"
configure_aws_provider(sample_web_config)
when "managed"
configure_managed_provider(sample_web_config, "1.2.3.4")
when "rackspace"
configure_rackspace_provider(sample_web_config)
end
end
end
Or the following example posted at gist by #maxlinc:
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "dummy"
config.vm.provider :rackspace do |rs|
rs.username = ENV['RAX_USERNAME']
rs.api_key = ENV['RAX_API_KEY']
rs.rackspace_region = :ord
end
supported_providers = %w(virtualbox rackspace)
active_provider = ENV['VAGRANT_ACTIVE_PROVIDER'] # it'd be better to get this from the CLI --provider option
supported_providers.each do |provider|
next unless active_provider.nil? || active_provider == provider
config.vm.define "exact_name_#{provider}" do |box|
box.vm.provider :rackspace do |rs|
rs.flavor = '1 GB Performance'
rs.image = 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
end
end
config.vm.define "regex_#{provider}" do |box|
box.vm.provider :rackspace do |rs|
rs.flavor = /1\s+GB\s+Performance/
rs.image = /Ubuntu.*Trusty Tahr.*(PVHVM)/
end
end
config.vm.define "id_#{provider}" do |box|
box.vm.provider :rackspace do |rs|
rs.flavor = 'performance1-1'
rs.image = 'bb02b1a3-bc77-4d17-ab5b-421d89850fca'
end
end
config.vm.define "unlisted_#{provider}" do |box|
box.vm.provider :rackspace do |rs|
rs.flavor = 'performance1-1'
rs.image = '547a46bd-d913-4bf7-ac35-2f24f25f1b7a'
end
end
end
end
Not an ideal solution, but what about using git branches? My thinking is that it could be conceptually similar to using heroku, where you might have a master, staging, and production versions (since they're usually different remotes).
In this case you start off the prod branch with the small edit to the Vagrantfile to name the VM a little differently. Then you should be able to merge all changes from dev with the prod branch as they occur. So your workflow would look like:
$ git checkout prod
$ vagrant up
$ git checkout master
... make changes to puppet ...
$ git checkout prod
$ git merge master
$ vagrant reload
$ git checkout master
You could script and alias these so you end up with
$ start_production
$ reload_production
Here is a simple way of dynamically changing the 'default' machine name depending on the specified --provider from the command line, so they won't conflict between the different providers:
require 'getoptlong'
opts = GetoptLong.new(
[ '--provider', GetoptLong::OPTIONAL_ARGUMENT ],
[ '--vm-name', GetoptLong::OPTIONAL_ARGUMENT ]
)
provider=ENV['PROVIDER'] || 'virtualbox'
vm_name=ENV['VM_NAME'] || 'default'
opts.each do |opt, arg|
case opt
when '--provider'
provider=arg
when '--vm-name'
vm_name=arg
end
end
Vagrant.configure(2) do |config|
# HERE you are dynamically changing the machine name to prevent conflict.
config.vm.define "mt-#{provider}-#{vm_name}"
# Below sections are just examples, not relevant.
config.vm.provider "virtualbox" do |vm|
vm.name = "test.local"
vm.network "private_network", ip: "192.168.22.22"
vm.customize ['modifyvm', :id, '--natdnshostresolver1', 'on']
config.vm.box = "ubuntu/wily64"
end
config.vm.provider :aws do |aws, override|
aws.aws_profile = "testing"
aws.instance_type = "m3.medium"
aws.ami = "ami-7747d01e"
config.vm.box = "testing"
end
end
Example usage:
VM_NAME=dev PROVIDER=virtualbox vagrant up --provider=virtualbox
VM_NAME=uat PROVIDER=aws vagrant up --provider=aws
VM_NAME=test PROVIDER=aws vagrant up --provider=aws
VM_NAME=prod PROVIDER=aws vagrant up --provider=aws
VM_NAME=uat PROVIDER=aws vagrant destroy -f
VM_NAME=test PROVIDER=aws vagrant status
See also: Multiple provisioners in a single vagrant file?
what I came up with to work with this scenario is to manage 2 distincts .vagrant folder.
Note: most of the other answers deal with setting up multi-provider assuming you will run dev and prod on different provider, in most cases this might be true but you can definitely have cases where you have same provider for dev and prod. Lets say you're using aws and you want to use dev and prod as ec2 instance it will be the same provider.
Say you want to manage dev and prod instances, potentially using different providers (but could also very well be on the same provider) so you'll do:
set up dev instance with normal vagrant up --provider <dev_provider>.
This will create a dev VM that you can manage
back up the .vagrant folder created in your project directory and rename it like .vagrant.dev
set up prod instance with your provider of choice vagrant up --provider <prod_provider>. This now creates your prod VM
back up the newly .vagrant folder created in your project directory and rename it like .vagrant.prod
now, depending if you want to work on dev or prod, you'll rename the .vagrant.dev or .vagrant.prod directory as .vagrant and vagrant will operate the right VM.
I did not come up with a script as mainly the most of the time I work with dev and very few times I need to switch to the other provider. but I dont think it will be too hard to read the parameter from CLI and make the renaming more dynamic.

Resources