Here's another one: write a script to generate your playbook from your config (which I presume is another yaml file). I've been idly contemplating doing this kind of thing in the hope it might avoid the headaches mentioned below, but haven't spent lot of time pursuing it so far.
My own most recent shot at Apache with vhosts has been to define a role for the common stuff, and another for the vhost specifics which depends on the former (with no parameters, except via the back-door, i.e. setting global vars).
These roles (aim to) have sensible defaults - currently defined in vars/main.yml (since defaults/main.yml seemed to do odd things I didn't have time to resolve [1]).
I then invoke the latter once for each vhost I need, varying the parameters. A working example of this sort of thing (installs Drupal sites onto Debian):
https://github.com/wu-lee/test-playbooks/tree/drupal-playbook-with-roles
Constructive criticism welcome, this mostly works but it does seem to have problems.
Without going to to too much detail, my main headaches seem to arise from Ansible's var scoping and referencing semantics, which clearly I don't understand fully. Vars frequently baffle and confound me. For example, dollar references and Jinja2 expressions evidently have different semantics, and whilst the former are discouraged and don't seem to be undef checked, the latter have a tendency to stringify references and booleans, or throw "UndefinedError" despite my efforts to define them. Tracing these problems between roles can be tricky, since undefined variables may be caused by some upstream role. Inspecting the parameters passed to each role requires judiciously inserting debug actions - and even these can throw an error without indicating what the precise cause is.
Cheers,
N
1. IIRC, the supplying parameters to the role clobbers sibling parameters defined in defaults/, which are merged more normally in when they're in vars/, resulting in unexpanded variable references.
A hand-waving example: if defaults/main.yml said:
stuff:
default_foo: x
default_bar: y
And my invocation said:
roles:
- role: rolename
stuff:
default_foo: z
Then stuff.default_bar seemed to get clobbered, despite hash_behaviour = merge.