I am using an ansible playbook to deploy a php web application in aws. The deployment is repeated many times, with a different environment, for each customer. The different scenarios range from single server deployments to full deployments using elastic load balancers, and use varying code repositories based on product type.

To allow a human to deploy this app repetitively and adjust the deployment type and environment, I am running ansible-playbook with --extra-vars "@deployment_vars_file.yml" and copying and filling this vars file on a per-deployment basis, instead of putting the variables in group_vars/all.yml, so that I can keep a copy of the variable space I set for each deployment, and avoid overwriting (or .template suffixing) group_vars/all.yml and multiple roles' vars/main.yml files, for each deployment. I rarely rely on variable precedence, since the role and play variables inherit (are constructed out of) my deployment vars file variables.

My roles are already run conditionally at the play level, and I have a handle on how to run tasks/blocks/roles/plays conditionally, depending on the environment type that is defined in the --extra-vars file.

I need to define many other variables, dynamically (during the playbook run), conditionally (based on the values of variables in my --extra-vars file), with as little work as possible (i.e., in one place), at the beginning of the playbook. My roles' vars/main.yml files, as well as vars: play keywords, will inherit these other variables, (in addition to directly inheriting some of the --extra-vars variables). The values of these other variables need to persist at the playbook level, not just within each play. I don't want to have to do this in multiple locations with the same lines of code.

How can I do this, and what is the most simple way?

  • I'm going to follow this thread. About 4 years ago I had the same problem with a big application spanning >30 "items to deploy", some terraform, some containers, some hard package installs and a lot of custom configuration/templates for each individual tenant and also split up in several OTAP environments. Headache time. I eventually settled using github.com/larsks/ansible-toolbox. That allows us to do ansible-role style deploys instead of full playbook style. That approach has been working really good, for each new tenant we copy a host file & update the variables for that tenant. – wowbagger Apr 24 at 13:23
  • Alot of the host file creation is done with python and manually edited once, we generate tenant-specific online documentation out of those host files. The "core-hardly-ever-changing" variables are read using a common include role. We deploy all our tenants in parallel from Jenkins as one big pipeline with all the required steps calling ansible-role with the required hostfile. We created "remove" roles (as opposed to deploy roles) so we can completely wipe out an environment and rebuild it again in a few minutes without needing to wait on terraform to do low-level stuff. – wowbagger Apr 24 at 13:25
  • We created individual stop/start/check roles for each individual component. It's perhaps/most definitive not at all following the best-practice way from a strict Ansible point-of-view but from our application & infrastructure CI/CD point of view it's pretty solid, it allows us to do full deploys, rapid single-component hot fixes, partial or full roll backs, application start/stop/check etc per tenant and per component whilst keeping the variables clearly seperated and easily manageable. – wowbagger Apr 24 at 13:28
  • @wowbagger I just installed ansible-toolbox; I'd been using the include_role module in ad hoc mod, and it seems like toolbox is a more compact way of doing that. The insane amount of "per deployment" parameterization of my whole project is part of what makes it difficult to adapt components of it to things like updates/hotfixes, which is why I'm increasingly isolating specific sets of tasks into roles. When you say host file, do you mean an inventory file with variables in it? I'm also making a python script to help populate the vars.yml file. – Life5ign Apr 25 at 5:16
  • And do you keep reusing your hostfile for all the updates/rollbacks/hotfixes, after the initial deployment, so as to load all the same variables? – Life5ign Apr 25 at 5:17

Yes, an input host file for ansible looks like https://pastebin.com/Vtfw5FtV The upper part node stuff is generated using terraform, we can dynamically up/down scale the number of nodes with terraform and terraform outputs the yaml used for the hosts part of the file. The tenant-specifiek yml vars (about 150 additional lines of tenant specific config yml) are kept in a separate git location and a jenkins step/python concatenates them together and pushes them to the host directory location for each tenant and runs individual roles (hotfix) or all of 'em (full deploy) or perhaps just stop/start. The Jenkins job is pretty much a control screen with a lot of dropdowns, radio buttons and selectors. We can select how many nodes we want for a single service, which build to grab from nexus, do a full deploy, a single service hotfix or just a configuration hotfix. All this is selectable/executable per environment, per tenant and per individual module/service. The Jenkins screen is dynamically build from the host (targets) & roles (actions) directory.

Again, we're most likely abusing Ansible "best practices" a fair bit but for us this approach has been working remarkably well for last >3 years in terms of overall manageability and gives us maximum flexibility of the whole CI/CD pipeline.

  • This is awesome. I just started using terraform today (similar to Vagrant but for cloud stuff). I'm still confused about the beginning of the file you linked, and how terraform outputs that; can you point me to any documentation? I do understand the ansible inventory (yaml style) section. It seems like if you fed this file to ansible, it would throw an error, since it wouldn't be a valid inventory file? Or wait, is this file a pseudo code / tree representation of what your project's directory structure looks like? Also, what is this hostfile consumed by? – Life5ign Apr 25 at 22:32
  • pastebin.com/rGaydTh5 is a hostile.yml, the rest of the paste was just example. To deploy, for instance, the service component we would run ansible-role deploy/service -I hosts/tenant/hostfile.yml --hosts=SERVICE_NODES You do need to set the roles variable for the toolbox etc. – wowbagger Apr 26 at 8:16
  • I can't really remember perfectly how we went from terraform output to ansible but looking at the code we apparently parse the terraform *.tfstate file using jq cmdline. It starts from an "empty host file skeleton" as input, for each "node group" in that skeleton file a function gets called that parses the terraform state file and grabs the hostnames for that type of node. – wowbagger Apr 26 at 8:26
  • ah, I see it's a static inventory file. My current idea is to use terraform to run an ansible-playook command as a local provisioner. Maybe it would work to have terraform spin up a rig of instances, and provision them with my playbook(s), or individual roles with import_role, or include_tasks in ad hoc mode. At least then running test deployment wouldn't be like setting a sixteen legged monster loose in our vpc, only able to be destroyed manually in the console, with awscli, or by writing another teardown playbook.. then I could combine that with hostvars per host – Life5ign Apr 26 at 19:23
  • I guess this would effectively glue the hostvars to the terraform rig hosts; and thus, in my original question, it would be the ansible memory cache used during playbook execution that build the variable space; instead, it would be something like what you're describing: a way of writing a file of variables that are generated dynamically somewhere in the pipeline outside of ansible, that are associated with only certain hosts in the rig – Life5ign Apr 26 at 19:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.