1

Until recently I used tools like Cpanel or Webmin to manage Apache2 virtual hosts. I recently discovered how to create these manually under sites-available directory.

I can do this process manually time and again but when done manually it includes lot's of coping & pasting, repetitive string changes (once for the file name and of the domain+tld inside it, and further executions like a2ensite (see below).

I now seek to automize the process but I'm not sure what is the best way to change domain+tld both inside (in the Vhost) and outside (in the file name and further executions)

This is my way to manually create each Vhost:

1. Copy a ready Vhost template file (I have such a file which I name `d.t` and I clone it whenever I need to create a new Vhost):

    <VirtualHost *:80>
    DocumentRoot "/var/www/html/d.t"
    ServerName www.d.t
    <Directory "/var/www/html/d.t">
    Options +SymLinksIfOwnerMatch
    Require all granted
    </Directory>
    ServerAlias www.d.t
    </VirtualHost>

3. Search and replace "d.t", with domain+tld:

    Do in nano.

4. Replace filename (d.t) with domain+tld:

    mv /etc/apache2/sites-available/d.t /etc/apache2/sites-available/domain.tld

5. Enable domain.tld.conf & restart the Apache service:

    a2ensite domain.tld.conf && systemctl restart apache2.service

This might seem simple but when I manually do this algorithm from a manual file it might take even more time to rewrite each example like d.t into the relevant domain+tld.

My question:

I am looking for a way to run a script with these actions: The script will actually look quite similar but in this script, d.t will be replaced with the script's very file name. For example, if the script's filename is MyNewVhost.com, each d.t occurrence inside it will become MyNewVhost.com.

I guess some variable work can serve that purpose, but I might be wrong; If so, a way to tell the program "Put the file name in each d.t" seems to be what I need.

Notes:

  1. I believe an answer could also give a good direction for people with a similar problem in Nginx Server blocks (Nginx Sblocks).
  • See the answer I marked correctly and if you want a GNU make alternative (a Bash script), see here: https://unix.stackexchange.com/questions/363169/makefile-alternative-for-those-who-dont-want-tab-indenting-all-content-under-th –  Aug 03 '17 at 17:44

3 Answers3

3

GNU make works well

You could use GNU make1, where creating a new virtual host is as simple as executing make example.com

Short and simple: one static template

First, create /etc/apache2/sites-available/Makefile containing:

% :
    printf '%s\n'                         \
    '<VirtualHost *:80>'                  \
    '  DocumentRoot "/var/www/html/$@"'  \
    '  ServerName www.$@'                 \
    '  <Directory "/var/www/html/$@">'    \
    '    Options +SymLinksIfOwnerMatch'   \
    '    Require all granted'             \
    '  </Directory>'                      \
    '  ServerAlias www.$@'                \
    '</VirtualHost>'                      \
    > "$@"
    a2ensite "$@"
    systemctl restart apache2.service

Note that each line after % : begins with a tab, not spaces. Also note that the majority of this is effectively your template file, but with d.t replaced by $@.

After this, you can create a new virtual host called domain.tld:

cd /etc/apache2/sites-available
make domain.tld

Adding options: Multiple templates

That was one of the simplest ways to use make. For a slightly more complex configuration, suppose you have two (or more) templates. I will show one here, which I called basic.template, as a pattern:

<VirtualHost *:80>
  DocumentRoot "/var/www/html/$domain$"
  ServerName www.$domain$
  <Directory "/var/www/html/$domain$">
    Options +SymLinksIfOwnerMatch
    Require all granted
  </Directory>
  ServerAlias www.$domain$
</VirtualHost>

You would then modify (simplify!) your Makefile:

% :
    sed 's/\$$domain\$$/$@/g' < "$<" > "$@"
    a2ensite "$@"
    systemctl restart apache2.service

.PHONY : all
all :
    @true

Here the recipe involves using sed to replace a variable of the form $domain$ with the intended domain name. Further, I have added a PHONY target called all, whose use will soon become apparent.

To create a virtual host now, you will have to specify which template to use. To me, it seems best to wrap this in a script.

#!/bin/sh
# File update-makefile
# Call as:
#    ./update-makefile domain.tld basic.template
sed '/^all :/s/$/ '"$1"'/' < Makefile > Makefile.new
printf '%s\n' "$1 : $2" >> Makefile.new
mv Makefile.new Makefile

Note that this has an additional step of adding2 the new virtual host to the Makefile. This lets make see which template domain.tld depends on. Then you could do:

cd /etc/apache2/sites-available
./update-makefile domain.tld basic.template && make

With this setup, every site will be updated if its corresponding template has changed.

It is important that all be the first named target. This makes it the default when make is called with no arguments. The update-makefile script adds the new domain as a dependency of all, so make will recreate it if it becomes outdated with respect to its own dependency, the template file.


1 make is a tool that is used to create target files from a set of recipes and dependencies that are documented in a Makefile. In GNU syntax,

% :
    # do stuff

is a pattern rule. It states that a file with any name (%) can be created as a target, and that it has no dependencies. The # do stuff is the recipe (any shell script), indented by one tab. There are variables you can use to refer to the name of the target ($@), the first dependency ($<), or all dependencies ($^), among others. For more information, see Wikipedia.

Your system may not have make installed, in which case you would have to install it yourself. Further, some systems use a BSD variant of make. The concepts are the same, but the syntax for rules differs slightly from GNU versions.

2 Make absolutely certain you use >> to append rather than > to overwrite!

Fox
  • 8,013
  • 1
  • 26
  • 44
  • Hi! Thanks! Can you please access the answer a bit more by editing it and explaining the sentence ```"Create /etc/apache2/sites-available/Makefile containing:"```? I think I miss how it differs from ```make domain.tld```. –  Mar 28 '17 at 17:06
  • Please, one last question a theroetical one. Say I do ```cd /etc/apache2/sites-available && make domain.tld``` in case I have ***2*** different template files in the same dir. How could the ```make``` utility now with which of the 2 to work? –  Mar 28 '17 at 21:31
  • Yes, thanks very much for all the help so far! Definitely going to try the answer right now and thumbed up. –  Mar 28 '17 at 21:44
  • I did ```apt-get install make -y```, installed it and then ```nano /etc/apache2/sites-available/Makefile```, pasted the whole syntax (after reading it well) and saved. I then did ```cd /etc/apache2/sites-available && make domain.tld``` but I get ```Makefile:2: *** mixed implicit and normal rules: deprecated syntax make: *** No rule to make target '80>'', needed by 'Makefile'. Stop.```... I try to solve it but you still might have any idea? –  Mar 28 '17 at 22:14
  • From the answer.. –  Mar 28 '17 at 22:28
  • 1
    Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/56209/discussion-between-fox-and-benia). – Fox Mar 28 '17 at 22:31
  • Replied there... –  Mar 28 '17 at 23:11
  • Oh btw, what is the ```> "$@"```? And also, it seems ```a2ensite "$@"``` didn't work for me and I hat to make it manually... –  Mar 30 '17 at 01:24
  • 1
    @Benia `> "$@"` means "send the output of this command into a file whose name is the name of the target". According to the manpage, `a2ensite` just makes a symlink in `sites-enabled`. `$@` is replaced by `make`, the rest runs in a shell. (Does the file need to end in `.conf`? If so, that is a smallish edit) – Fox Mar 30 '17 at 01:33
  • Reporting a prob I struggeled with: ```' DocumentRoot "/var/www/html/$@">' \``` should be ```' DocumentRoot "/var/www/html/$@"' \```. The extra greater than sign negated me of restarting successfully after ```a2ensite```. It should be removed (please edit, I can't edit for one letter lol). –  Mar 30 '17 at 11:02
  • 1
    Sorry about that. I had typo'd elsewhere in the XML and added that in fixing it. But that line didn't need it – Fox Mar 30 '17 at 12:03
1

Congratulations, you're taking the first steps towards configuration management!

There are many possible outcomes here, but at the root of it, you are making a template of a snippet of configuration, and creating instances of that template.

You are correct in that a few variables in your template can fit your need; it's just a matter of picking some programming language (even simple shell) to drive filling out the template with the needed values. Fox's answer using Make is a nice short example; and you could amend it by declaring all the sites later in the Makefile to rebuild all of them when you wanted to update the template.

That's at the simple end of configuration management (keep your script that fills out the template with variables, in case you want to update lots of configurations later).

Moving toward more complexity are configuration management tools, such as Puppet, Chef, Cfengine, Salt, & more.

Here's an off-the-shelf example of using Puppet, with the PuppetLabs Apache module, and populating it with data from Hiera (a structured set of your variable values).

robbat2
  • 3,599
  • 20
  • 32
0

You could use ansible, it utilises Jinja2 for templating, and it gives you the possible to automate a lot of other stuff also.

  • To make sure the answer is more accessible (and hence could have more thumb ups) I suggest writing a stepped explanation for how it is done with Ansible. –  Mar 28 '17 at 18:34