├── {{cookiecutter.project_slug}} ├── roles │ ├── users │ │ ├── files │ │ │ └── .keep │ │ └── tasks │ │ │ └── main.yml │ ├── pip │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── reboot-required │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── python-mysql │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── mariadb-repo │ │ ├── defaults │ │ │ └── main.yml │ │ ├── templates │ │ │ └── centos │ │ │ │ └── MariaDB.repo.j2 │ │ └── tasks │ │ │ └── main.yml │ ├── mariadb-client │ │ ├── files │ │ │ └── client.conf │ │ └── tasks │ │ │ └── main.yml │ ├── hostname │ │ ├── handlers │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── postfix │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ │ ├── ubuntu-precise │ │ │ └── main.cf.j2 │ │ │ ├── ubuntu-trusty │ │ │ └── main.cf.j2 │ │ │ ├── ubuntu-xenial │ │ │ └── main.cf.j2 │ │ │ └── centos-7 │ │ │ └── main.cf.j2 │ ├── mariadb-server │ │ ├── handlers │ │ │ └── main.yml │ │ ├── files │ │ │ ├── listen.cnf │ │ │ └── utf8.cnf │ │ └── tasks │ │ │ └── main.yml │ ├── apt-get-update │ │ └── tasks │ │ │ └── main.yml │ ├── hosts │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ │ ├── centos-7 │ │ │ └── hosts.j2 │ │ │ ├── ubuntu-trusty │ │ │ └── hosts.j2 │ │ │ ├── ubuntu-xenial │ │ │ └── hosts.j2 │ │ │ ├── ubuntu-precise │ │ │ └── hosts.j2 │ │ │ └── crappy-dns-replacement.j2 │ ├── ntp │ │ ├── handlers │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── nodejs │ │ ├── defaults │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── old-deploy-cleanup │ │ └── tasks │ │ │ └── main.yml │ ├── timezone │ │ ├── handlers │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── epel │ │ └── tasks │ │ │ └── main.yml │ ├── set-distribution-name-version │ │ └── tasks │ │ │ └── main.yml │ ├── rsyslog │ │ ├── tasks │ │ │ └── main.yml │ │ └── files │ │ │ ├── ubuntu-trusty │ │ │ └── rsyslog.conf │ │ │ ├── ubuntu-precise │ │ │ └── rsyslog.conf │ │ │ ├── ubuntu-xenial │ │ │ └── rsyslog.conf │ │ │ └── centos-7 │ │ │ └── rsyslog.conf │ ├── security-updates │ │ └── tasks │ │ │ └── main.yml │ ├── debug-tools │ │ └── tasks │ │ │ └── main.yml │ └── mysql-createdb │ │ └── tasks │ │ └── main.yml ├── .gitignore ├── group_vars │ ├── staging │ │ └── main.yml │ ├── production │ │ └── main.yml │ ├── all │ │ └── main.yml │ └── vagrant │ │ └── main.yml ├── requirements.yml ├── requirements.txt ├── .editorconfig ├── inventory │ └── vagrant.ini ├── Makefile ├── ansible.cfg ├── activate.sh ├── deploy.sh ├── site.yml ├── README.md └── Vagrantfile ├── .gitignore ├── docs ├── images │ ├── copy.png │ ├── hugs.jpg │ ├── usage.jpg │ ├── anatomy.jpg │ ├── no-idea.jpg │ ├── question.jpg │ ├── who-cat.jpg │ ├── idempotence.png │ └── custom-filters.png ├── simply-ansible-best-practices.pdf ├── simply-ansible-getting-started.pdf ├── simply-ansible-getting-started.marp.md └── simply-ansible-best-practices.marp.md ├── cookiecutter.json ├── LICENSE └── README.md /{{cookiecutter.project_slug}}/roles/users/files/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.retry 3 | .vagrant/ 4 | build/ 5 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/group_vars/staging/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | app_env: staging 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/pip/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pip_version: 8.1.2 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | boot/ 3 | lib_managed/ 4 | src_managed/ 5 | project/plugins/project/ -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/group_vars/production/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | app_env: production 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/reboot-required/defaults/main.yml: -------------------------------------------------------------------------------- 1 | really_reboot: False 2 | -------------------------------------------------------------------------------- /docs/images/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/images/copy.png -------------------------------------------------------------------------------- /docs/images/hugs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/images/hugs.jpg -------------------------------------------------------------------------------- /docs/images/usage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/images/usage.jpg -------------------------------------------------------------------------------- /docs/images/anatomy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/images/anatomy.jpg -------------------------------------------------------------------------------- /docs/images/no-idea.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/images/no-idea.jpg -------------------------------------------------------------------------------- /docs/images/question.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/images/question.jpg -------------------------------------------------------------------------------- /docs/images/who-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/images/who-cat.jpg -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/python-mysql/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | python_mysql_version: '1.2.5' 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mariadb-repo/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | mariadb_server_version: '10.1' 3 | -------------------------------------------------------------------------------- /docs/images/idempotence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/images/idempotence.png -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mariadb-client/files/client.conf: -------------------------------------------------------------------------------- 1 | [client] 2 | default-character-set = utf8mb4 3 | -------------------------------------------------------------------------------- /docs/images/custom-filters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/images/custom-filters.png -------------------------------------------------------------------------------- /docs/simply-ansible-best-practices.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/simply-ansible-best-practices.pdf -------------------------------------------------------------------------------- /docs/simply-ansible-getting-started.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/building5/simply-ansible/HEAD/docs/simply-ansible-getting-started.pdf -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/hostname/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: reboot 3 | file: path=/var/run/reboot-required state=touch 4 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/postfix/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart postfix 3 | service: name=postfix state=restarted 4 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # Ansible Galaxy modules 4 | # 5 | - src: rvm_io.rvm1-ruby 6 | version: v1.3.8 7 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mariadb-server/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart mysql 3 | service: name=mysql state=restarted 4 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mariadb-server/files/listen.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | # 3 | # Listen on all addresses 4 | # 5 | bind-address = 0.0.0.0 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/apt-get-update/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - when: ansible_distribution == 'Ubuntu' 3 | tags: apt-get-update 4 | name: apt-get update 5 | apt: update_cache=yes 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/hostname/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: hostname 3 | block: 4 | - name: set hostname 5 | hostname: name={{ inventory_hostname }} 6 | notify: reboot 7 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/hosts/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: hosts 3 | block: 4 | - name: cp hosts /etc/hosts 5 | template: src={{ distribution_name_version }}/hosts.j2 dest=/etc/hosts 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/ntp/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart ntp (Ubuntu) 3 | service: name=ntp state=restarted 4 | 5 | - name: restart ntpd (CentOS) 6 | service: name=ntpd state=restarted 7 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/python-mysql/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: python-mysql 3 | block: 4 | - name: pip install MYSQL-python 5 | pip: name=MYSQL-python version={{ python_mysql_version }} 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/nodejs/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | node_major_version: '6.x' 3 | node_version: '6.4.0' 4 | node_sha256: 92b592b506ae71fd13bdd43b972953d6baebdda65b64461b00412ad3a81a5a33 5 | npm_version: '3.10.6' 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/group_vars/all/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # default to direct mail delivery. if you have a relay, set it here. 3 | smtp_relay_host: '' 4 | 5 | # Use for NODE_ENV, RAILS_ENV, RACK_ENV, etc. 6 | app_env: development 7 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/group_vars/vagrant/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dns_domain: '{{ cookiecutter.project_slug }}.test' # Must match what's in Vagrantfile 3 | 4 | # Vagrant plugins to get DNS working do not work well 5 | use_hosts_file_as_crappy_replacement_for_dns: true 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mariadb-server/files/utf8.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | default-character-set = utf8mb4 3 | 4 | [mysqld] 5 | character-set-server = utf8mb4 6 | collation-server = utf8mb4_unicode_ci 7 | character_set_server = utf8mb4 8 | collation_server = utf8mb4_unicode_ci 9 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/requirements.txt: -------------------------------------------------------------------------------- 1 | ansible==2.1.1.0 2 | ansible-lint==3.2.5 3 | cffi==1.7.0 4 | cryptography==1.4 5 | enum34==1.1.6 6 | idna==2.1 7 | ipaddress==1.0.16 8 | Jinja2==2.8 9 | MarkupSafe==0.23 10 | paramiko==2.0.2 11 | pyasn1==0.1.9 12 | pycparser==2.14 13 | pycrypto==2.6.1 14 | PyYAML==3.11 15 | six==1.10.0 16 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/hosts/templates/centos-7/hosts.j2: -------------------------------------------------------------------------------- 1 | 127.0.1.1 {{ ansible_fqdn }} {{ ansible_hostname }} 2 | 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 3 | ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 4 | 5 | {% include 'roles/hosts/templates/crappy-dns-replacement.j2' %} 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/hosts/templates/ubuntu-trusty/hosts.j2: -------------------------------------------------------------------------------- 1 | 127.0.0.1 localhost 2 | 127.0.1.1 {{ ansible_fqdn }} {{ ansible_hostname }} 3 | ::1 ip6-localhost ip6-loopback 4 | fe00::0 ip6-localnet 5 | ff00::0 ip6-mcastprefix 6 | ff02::1 ip6-allnodes 7 | ff02::2 ip6-allrouters 8 | ff02::3 ip6-allhosts 9 | 10 | {% include 'roles/hosts/templates/crappy-dns-replacement.j2' %} 11 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/hosts/templates/ubuntu-xenial/hosts.j2: -------------------------------------------------------------------------------- 1 | 127.0.0.1 localhost 2 | 127.0.1.1 {{ ansible_fqdn }} {{ ansible_hostname }} 3 | ::1 ip6-localhost ip6-loopback 4 | fe00::0 ip6-localnet 5 | ff00::0 ip6-mcastprefix 6 | ff02::1 ip6-allnodes 7 | ff02::2 ip6-allrouters 8 | ff02::3 ip6-allhosts 9 | 10 | {% include 'roles/hosts/templates/crappy-dns-replacement.j2' %} 11 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mariadb-repo/templates/centos/MariaDB.repo.j2: -------------------------------------------------------------------------------- 1 | # MariaDB {{ mariadb_server_version }} CentOS repository list 2 | # http://downloads.mariadb.org/mariadb/repositories/ 3 | [mariadb] 4 | name = MariaDB 5 | baseurl = http://yum.mariadb.org/{{ mariadb_server_version }}/centos{{ ansible_distribution_major_version }}-amd64 6 | gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB 7 | gpgcheck=1 8 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "Simply Ansible", 3 | "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '-') }}", 4 | "description": "Ansible scripts for {{ cookiecutter.project_name }}.", 5 | "license": ["ISC License", "MIT license", "BSD license", "Apache Software License 2.0", "GNU General Public License v3", "Not open source"], 6 | "_copy_without_render": [ 7 | "roles" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/hosts/templates/ubuntu-precise/hosts.j2: -------------------------------------------------------------------------------- 1 | 127.0.0.1 localhost 2 | 127.0.1.1 {{ ansible_fqdn }} {{ ansible_hostname }} 3 | 4 | # The following lines are desirable for IPv6 capable hosts 5 | ::1 ip6-localhost ip6-loopback 6 | fe00::0 ip6-localnet 7 | ff00::0 ip6-mcastprefix 8 | ff02::1 ip6-allnodes 9 | ff02::2 ip6-allrouters 10 | 11 | {% include 'roles/hosts/templates/crappy-dns-replacement.j2' %} 12 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/old-deploy-cleanup/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # After being merged to master, and deployed to all the existing instances, 4 | # these tasks can be removed. 5 | # 6 | 7 | - tags: old-deploy-cleanup 8 | block: 9 | - when: ansible_distribution == 'Ubuntu' 10 | name: apt-get autoremove --purge 11 | apt: autoremove=yes purge=yes name=stupid-package-that-does-not-exist state=absent 12 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/postfix/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: postfix 3 | block: 4 | - when: ansible_distribution == 'Ubuntu' 5 | name: apt-get install postfix 6 | apt: name=postfix 7 | 8 | - when: ansible_distribution == 'CentOS' 9 | name: yum install postfix 10 | yum: name=postfix 11 | 12 | - name: copy main.cf /etc/postfix/ 13 | template: src={{ distribution_name_version }}/main.cf.j2 dest=/etc/postfix/main.cf 14 | notify: restart postfix 15 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/timezone/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: update timezone (Ubuntu) 3 | command: dpkg-reconfigure --frontend noninteractive tzdata 4 | notify: mark reboot required 5 | 6 | # 7 | # Once a process learns what timezone it is in, it usually caches it forever. 8 | # When updating the timezone, it's usually best to just reboot to make sure 9 | # everything picks up the change. 10 | # 11 | - name: mark reboot required 12 | file: state=touch path=/var/run/reboot-required 13 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/hosts/templates/crappy-dns-replacement.j2: -------------------------------------------------------------------------------- 1 | {% if use_hosts_file_as_crappy_replacement_for_dns %} 2 | # What makes DNS so hard? It's just naming things and cache invalidation ... 3 | # https://twitter.com/alindeman/status/741636409912623104 4 | {% for host in groups["all"] %} 5 | {% if host != inventory_hostname %} {# hosts file already has entry for self #} 6 | {{ hostvars[host].ansible_host }} {{ hostvars[host].ansible_fqdn }} 7 | {% endif %} 8 | {% endfor %} 9 | {% endif %} 10 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mariadb-server/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: mariadb-server 3 | block: 4 | 5 | # 6 | # The MariaDB repos only contain one version, so I guess that's what we'll 7 | # install 8 | # 9 | - name: apt-get install mariadb-server 10 | apt: name=mariadb-server 11 | 12 | - name: cp *.cnf /etc/mysql/conf.d/ 13 | copy: src={{ item }} dest=/etc/mysql/conf.d/ 14 | with_items: 15 | - listen.cnf 16 | - utf8.cnf 17 | notify: restart mysql 18 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/epel/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - when: ansible_distribution == 'CentOS' 3 | tags: epel 4 | block: 5 | - name: yum install epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm 6 | yum: name=https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm 7 | 8 | - name: rpm –import RPM-GPG-KEY-EPEL-{{ ansible_distribution_major_version }} 9 | rpm_key: key=/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-{{ ansible_distribution_major_version }} 10 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | max_line_length = 100 13 | tab_width = 8 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [*.md] 18 | indent_size = 4 19 | 20 | [Makefile] 21 | indent_style = tab 22 | indent_size = tab 23 | 24 | [.git/COMMIT_EDITMSG] 25 | max_line_length = 72 26 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/set-distribution-name-version/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # distribution_name_version is reliably unique for distribution and version 4 | # 5 | 6 | - tags: always 7 | block: 8 | - when: ansible_distribution == 'Ubuntu' 9 | name: Set distribution_name_version (Ubuntu) 10 | set_fact: 11 | distribution_name_version: ubuntu-{{ ansible_distribution_release }} 12 | 13 | - when: ansible_distribution == 'CentOS' 14 | name: Set distribution_name_version (Centos) 15 | set_fact: 16 | distribution_name_version: centos-{{ ansible_distribution_major_version }} 17 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/rsyslog/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: rsyslog 3 | block: 4 | - name: cp rsyslog.conf /etc/ 5 | copy: src={{ distribution_name_version }}/rsyslog.conf dest=/etc/ 6 | register: reconfigured 7 | 8 | # We restart here instead of a handler, because we need the syslog changes 9 | # to take effect immediately, instead of waiting for the end of the play to 10 | # run. This makes the biggest difference when running the entire playbook 11 | # for a new instance 12 | - name: restart rsyslog 13 | service: name=rsyslog state=restarted 14 | when: reconfigured.changed 15 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/ntp/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: ntp 3 | block: 4 | - when: ansible_distribution == 'Ubuntu' 5 | block: 6 | - name: apt-get install ntp 7 | apt: name=ntp 8 | notify: restart ntp (Ubuntu) 9 | 10 | - name: service start ntp 11 | service: name=ntp state=running enabled=yes 12 | 13 | - when: ansible_distribution == 'CentOS' 14 | block: 15 | - name: yum install ntp 16 | yum: name=ntp 17 | notify: restart ntpd (CentOS) 18 | 19 | - name: service start ntpd 20 | service: name=ntpd state=running enabled=yes 21 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/timezone/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: timezone 3 | block: 4 | - when: ansible_distribution == 'Ubuntu' 5 | name: Set timezone to UTC (Ubuntu) 6 | copy: dest=/etc/timezone content="Etc/UTC\n" 7 | notify: 8 | - update timezone (Ubuntu) 9 | 10 | - when: ansible_distribution == 'CentOS' 11 | name: Set timezone to UTC (Ubuntu) 12 | shell: | 13 | set -e 14 | if test $(date +%Z) != UTC; then 15 | echo SETTING TIMEZONE 16 | timedatectl --no-ask-password --no-pager set-timezone UTC 17 | else 18 | echo NO CHANGE 19 | fi 20 | register: tmp 21 | changed_when: tmp.stdout_lines[0] == 'SETTING TIMEZONE' 22 | notify: 23 | - mark reboot required 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, David M. Lee, II 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mariadb-client/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: mariadb-client 3 | block: 4 | - when: ansible_distribution == 'Ubuntu' 5 | block: 6 | - name: apt-get install libmariadbclient-dev mariadb-client 7 | apt: name={{ item }} 8 | with_items: 9 | - libmariadbclient-dev 10 | - mariadb-client 11 | 12 | - name: cp client.cnf /etc/mysql/conf.d 13 | copy: src=client.conf dest=/etc/mysql/conf.d 14 | 15 | - when: ansible_distribution == 'CentOS' 16 | block: 17 | - name: yum install MariaDB-client 18 | yum: name={{ item }} 19 | with_items: 20 | - MariaDB-client 21 | - MariaDB-devel 22 | 23 | - name: cp client.cnf /etc/my.cnf.d/ 24 | copy: src=client.conf dest=/etc/my.cnf.d/ 25 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/inventory/vagrant.ini: -------------------------------------------------------------------------------- 1 | [services] 2 | services ansible_host=192.168.89.100 ansible_user=ubuntu 3 | 4 | [legacy] 5 | legacy ansible_host=192.168.89.101 ansible_user=ubuntu 6 | 7 | [crazy-old] 8 | crazy-old ansible_host=192.168.89.102 ansible_user=ubuntu 9 | 10 | [weird] 11 | weird ansible_host=192.168.89.103 ansible_user=vagrant 12 | 13 | # It's empty in this example, but you would use the unmanaged group to 14 | # exclude hosts from tasks that you normally apply to everything. 15 | # hosts: all:!unmanaged 16 | [unmanaged] 17 | 18 | # 19 | # Because development, we'll run MariaDB on the service nodes 20 | # 21 | [mariadb:children] 22 | services 23 | legacy 24 | crazy-old 25 | 26 | # 27 | # By creating a vagrant group, we can have environment specific group_vars 28 | # 29 | [vagrant:children] 30 | services 31 | legacy 32 | crazy-old 33 | weird 34 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/security-updates/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: security-updates 3 | block: 4 | - when: ansible_distribution == 'Ubuntu' 5 | block: 6 | - name: apt-get install unattended-upgrades 7 | apt: name=unattended-upgrades 8 | 9 | - name: Install security updates (old Ubuntu) 10 | command: unattended-upgrades 11 | when: ansible_distribution_release == 'precise' 12 | 13 | - name: Install security updates (Ubuntu) 14 | command: unattended-upgrades --verbose 15 | when: ansible_distribution_release != 'precise' 16 | 17 | - when: ansible_distribution == 'CentOS' 18 | block: 19 | - name: Install security updates (CentOS) 20 | command: yum upgrade --security --quiet 21 | args: 22 | warn: no # Ansible's yum module doesn't have a --security option 23 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | .PHONY: all 3 | 4 | virtualenv: build/virtualenv/.built 5 | .PHONY: virtualenv 6 | 7 | galaxy: build/galaxy/.built 8 | .PHONY: galaxy 9 | 10 | check-env: 11 | @if ! test "$(shell which pip)" -ef "$(shell which build/virtualenv/bin/pip)"; then \ 12 | echo "ERROR: virtualenv not activated" >&2; \ 13 | echo "Run 'source ./activate.sh' and try again" >&2; \ 14 | exit 1; \ 15 | fi 16 | .PHONY: check-env 17 | 18 | build/virtualenv/.built: requirements.txt 19 | rm -rf build/virtualenv 20 | virtualenv build/virtualenv 21 | . build/virtualenv/bin/activate && pip install -r requirements.txt 22 | touch $@ 23 | 24 | build/galaxy/.built: requirements.yml 25 | @$(MAKE) check-env 26 | rm -rf build/galaxy 27 | ansible-galaxy install -r requirements.yml 28 | touch $@ 29 | 30 | test: 31 | @$(MAKE) check-env 32 | ansible-playbook site.yml --syntax-check 33 | ansible-lint --exclude build/ site.yml 34 | .PHONY: test 35 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | # Modern servers come and go too often for host key checking to be useful 3 | host_key_checking = False 4 | # Why is the default not smart? 5 | gathering = smart 6 | # For the rare cases where galaxy is useful 7 | roles_path = ./build/galaxy 8 | # Default location for vault file. Please use ansible-vault-tools to encrypt 9 | # https://github.com/building5/ansible-vault-tools 10 | vault_password_file = $HOME/.ansible/{{ cookiecutter.project_slug }}-vault 11 | # Default to the vagrant environment 12 | inventory = ./inventory/vagrant.ini 13 | # We don't want errors to skip handlers that have been triggered. 14 | # That would cause bugs, since the handlers would be skipped if play was 15 | # re-run. 16 | force_handlers = True 17 | 18 | [privilege_escalation] 19 | # Nearly everything requires sudo, so default on 20 | become = True 21 | 22 | [ssh_connection] 23 | # Speeds things up, but requires disabling requiretty in /etc/sudoers 24 | pipelining = True 25 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mariadb-repo/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # installing pinned version from archive site took nearly an hour :-( 3 | # DigitalOcean mirror of latest 5.5 took < 2 minutes. I guess we're installing 4 | # the latest 5.5, then. 5 | 6 | - tags: mariadb-repo 7 | block: 8 | - when: ansible_distribution == 'Ubuntu' 9 | block: 10 | - name: apt-key 11 | apt_key: keyserver=keyserver.ubuntu.com id={{ item }} 12 | with_items: 13 | - '0xF1656F24C74CD1D8' # newer signing key 14 | - '0xcbcb082a1bb943db' # older signing key; still in use 15 | 16 | - name: apt-add-repository mariadb-server 17 | apt_repository: repo='deb [arch=amd64,i386] http://nyc2.mirrors.digitalocean.com/mariadb/repo/{{ mariadb_server_version }}/ubuntu {{ ansible_distribution_release }} main' 18 | 19 | - when: ansible_distribution == 'CentOS' 20 | block: 21 | - name: cp MariaDB.repo /etc/yum.repos.d/ 22 | template: src=centos/MariaDB.repo.j2 dest=/etc/yum.repos.d/MariaDB.repo 23 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/debug-tools/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # This is the one file that violates my rule of "don't just throw a bunch of 4 | # stuff into a role." These are all really useful tools, with single line 5 | # installs 6 | # 7 | 8 | - tags: debug-tools 9 | block: 10 | - when: ansible_distribution == 'Ubuntu' 11 | name: apt-get install 12 | apt: name={{ item }} 13 | with_items: 14 | - curl 15 | - htop 16 | - iotop 17 | - jq 18 | - mailutils 19 | - nload 20 | - nmon 21 | - rsync 22 | - screen 23 | - sysstat 24 | - tcpdump 25 | - traceroute 26 | - tshark 27 | - unzip 28 | - vim 29 | - wget 30 | 31 | - when: ansible_distribution == 'CentOS' 32 | name: yum install 33 | yum: name={{ item }} 34 | with_items: 35 | - curl 36 | - htop 37 | - iotop 38 | - jq 39 | - lsof 40 | - mailx 41 | - nload 42 | - rsync 43 | - screen 44 | - sysstat 45 | - tcpdump 46 | - traceroute 47 | - wireshark 48 | - wget 49 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/activate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Activate an Ansible 2.0 virtualenv 5 | # 6 | 7 | if ! ps -o comm= -p $$ | grep -q -e 'bash$'; then 8 | echo "$0: only bash is supported" >&2 9 | exit 1 10 | fi 11 | 12 | if [[ ${BASH_VERSINFO[0]} -lt 3 ]]; then 13 | echo "$0: bash 3 or greater required" >&2 14 | exit 1 15 | fi 16 | 17 | PROGNAME=$(basename "${BASH_SOURCE[0]}") 18 | TOPDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 19 | 20 | usage() { 21 | cat <&2 53 | fi 54 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/users/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: users 3 | block: 4 | - name: groupadd --system sudo 5 | group: name=sudo system=yes 6 | 7 | - name: Disable sudo passwords 8 | copy: 9 | dest: /etc/sudoers.d/nopasswd 10 | content: | 11 | %sudo ALL=(ALL) NOPASSWD:ALL 12 | mode: 0440 13 | validate: visudo -cf %s 14 | 15 | # User list can be used by other roles; always build it 16 | - name: build user list 17 | become: False 18 | run_once: True 19 | local_action: command echo "{{ item | basename | replace('.pub', '') }}" 20 | with_fileglob: 21 | - "*.pub" 22 | register: users 23 | changed_when: False 24 | tags: always 25 | 26 | # now make user list friendly 27 | - name: set_fact users 28 | set_fact: 29 | users: "{{ users.results|map(attribute='stdout')|list }}" 30 | tags: always 31 | 32 | - name: useradd 33 | user: name="{{ item }}" groups=sudo append=yes shell=/bin/bash 34 | with_items: '{{ users }}' 35 | 36 | 37 | - name: Ensure public key is in authorized_keys 38 | authorized_key: 39 | key: "{{ lookup('file',item + '.pub') }}" 40 | user: "{{ item }}" 41 | with_items: '{{ users }}' 42 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/mysql-createdb/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: mysql-createdb 3 | block: 4 | - name: "CREATE DATABASE {{ database_name }}" 5 | become: False 6 | delegate_to: '{{ mysql_delegate }}' 7 | mysql_db: 8 | state: present 9 | name: '{{ database_name }}' 10 | login_host: '{{ mysql_login_host }}' 11 | login_user: '{{ mysql_root_user }}' 12 | login_password: '{{ mysql_root_password }}' 13 | register: create_db 14 | 15 | - name: 'CREATE USER {{ user_name }}' 16 | become: False 17 | delegate_to: '{{ mysql_delegate }}' 18 | mysql_user: 19 | state: present 20 | name: '{{ user_name }}' 21 | password: '{{ user_password }}' 22 | priv: '{{ database_name }}.*:ALL' 23 | host: '%' 24 | login_host: '{{ mysql_login_host }}' 25 | login_user: '{{ mysql_root_user }}' 26 | login_password: '{{ mysql_root_password }}' 27 | append_privs: yes # user may acquire more privs in other roles 28 | register: create_db_user 29 | 30 | # 31 | # Create some variables specific to this database/user 32 | # 33 | - set_fact: 'create_db_{{ database_name }}={{ create_db }}' 34 | - set_fact: 'create_db_user_{{ user_name }}={{ create_db_user }}' 35 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/reboot-required/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # So we don't go rebooting all willy-nilly, you have to pass 4 | # -e really_reboot=True to ansible to get the reboots to run 5 | # 6 | 7 | - tags: reboot-required 8 | block: 9 | # look for an explicit reboot-required from apt/deb, or any outdated libs 10 | - name: Look for reasons to reboot 11 | shell: "test -e /var/run/reboot-required || grep -q '/lib[0-9]*/.* (deleted)$' /proc/*/maps && echo REBOOT" 12 | register: reboot_required 13 | failed_when: False 14 | changed_when: reboot_required.stdout == 'REBOOT' 15 | 16 | # Reboot; see https://support.ansible.com/hc/en-us/articles/201958037-Reboot-a-server-and-wait-for-it-to-come-back 17 | # esp. the comments 18 | - name: reboot 19 | # sleep gives async task time to kick off 20 | shell: 'sleep 2 && shutdown -r now "ansible: reboot-required detected; restarting"' 21 | async: 5 22 | # poll 0 means "fire and forget" 23 | poll: 0 24 | register: reboot 25 | when: really_reboot and not reboot_required|skipped() and reboot_required.changed 26 | 27 | - name: wait for restart 28 | local_action: wait_for 29 | host={{ ansible_host }} 30 | port=22 31 | delay=15 32 | timeout=300 33 | become: no 34 | when: not reboot|skipped 35 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/nodejs/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: nodejs 3 | block: 4 | - name: wget https://deb.nodesource.com/node_{{ node_major_version }}/pool/main/n/nodejs/ 5 | get_url: 6 | url: 'https://deb.nodesource.com/node_{{ node_major_version }}/pool/main/n/nodejs/nodejs_{{ node_version }}-1nodesource1~{{ ansible_distribution_release }}1_amd64.deb' 7 | dest: /usr/local/src/nodejs.deb 8 | checksum: 'sha256:{{ node_sha256 }}' 9 | register: deb 10 | 11 | - name: dpkg -i /usr/local/src/nodejs.deb 12 | apt: deb=/usr/local/src/nodejs.deb 13 | 14 | # If NodeSource ever fixes https://github.com/nodesource/distributions/issues/33 15 | # then we can go back to just installing via apt, instead of manually 16 | # downloading the package 17 | 18 | #- name: apt-get install apt-transport-https 19 | # apt: name=apt-transport-https 20 | # 21 | #- name: apt-key nodesource 22 | # apt_key: url=https://deb.nodesource.com/gpgkey/nodesource.gpg.key id=68576280 23 | # 24 | #- name: apt-repository 25 | # apt_repository: repo="deb https://deb.nodesource.com/node_{{ node_major_version }} trusty main" 26 | # 27 | #- name: apt-get install nodejs 28 | # apt: name=nodejs={{ node_version }} 29 | 30 | - name: npm install -g npm@{{ npm_version }} 31 | npm: name=npm global=yes version={{ npm_version }} 32 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/postfix/templates/ubuntu-precise/main.cf.j2: -------------------------------------------------------------------------------- 1 | # See /usr/share/postfix/main.cf.dist for a commented, more complete version 2 | 3 | 4 | # Debian specific: Specifying a file name will cause the first 5 | # line of that file to be used as the name. The Debian default 6 | # is /etc/mailname. 7 | #myorigin = /etc/mailname 8 | 9 | smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) 10 | biff = no 11 | 12 | # appending .domain is the MUA's job. 13 | append_dot_mydomain = no 14 | 15 | # Uncomment the next line to generate "delayed mail" warnings 16 | #delay_warning_time = 4h 17 | 18 | readme_directory = no 19 | 20 | # TLS parameters 21 | smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem 22 | smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key 23 | smtpd_use_tls=yes 24 | smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache 25 | smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache 26 | 27 | # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for 28 | # information on enabling SSL in the smtp client. 29 | 30 | myhostname = {{ inventory_hostname }} 31 | alias_maps = hash:/etc/aliases 32 | alias_database = hash:/etc/aliases 33 | mydestination = {{ inventory_hostname }}, localhost.localdomain, , localhost 34 | relayhost = {{ smtp_relay_host|default('') }} 35 | mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 36 | mailbox_size_limit = 0 37 | recipient_delimiter = + 38 | inet_interfaces = 127.0.0.1 39 | inet_protocols = all 40 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/postfix/templates/ubuntu-trusty/main.cf.j2: -------------------------------------------------------------------------------- 1 | # See /usr/share/postfix/main.cf.dist for a commented, more complete version 2 | 3 | 4 | # Debian specific: Specifying a file name will cause the first 5 | # line of that file to be used as the name. The Debian default 6 | # is /etc/mailname. 7 | #myorigin = /etc/mailname 8 | 9 | smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) 10 | biff = no 11 | 12 | # appending .domain is the MUA's job. 13 | append_dot_mydomain = no 14 | 15 | # Uncomment the next line to generate "delayed mail" warnings 16 | #delay_warning_time = 4h 17 | 18 | readme_directory = no 19 | 20 | # TLS parameters 21 | smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem 22 | smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key 23 | smtpd_use_tls=yes 24 | smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache 25 | smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache 26 | 27 | # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for 28 | # information on enabling SSL in the smtp client. 29 | 30 | smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination 31 | myhostname = {{ inventory_hostname }} 32 | alias_maps = hash:/etc/aliases 33 | alias_database = hash:/etc/aliases 34 | myorigin = /etc/mailname 35 | mydestination = {{ inventory_hostname }}, localhost.localdomain, , localhost 36 | relayhost = {{ smtp_relay_host|default('') }} 37 | mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 38 | mailbox_size_limit = 0 39 | recipient_delimiter = + 40 | inet_interfaces = all 41 | inet_protocols = all 42 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/postfix/templates/ubuntu-xenial/main.cf.j2: -------------------------------------------------------------------------------- 1 | # See /usr/share/postfix/main.cf.dist for a commented, more complete version 2 | 3 | 4 | # Debian specific: Specifying a file name will cause the first 5 | # line of that file to be used as the name. The Debian default 6 | # is /etc/mailname. 7 | #myorigin = /etc/mailname 8 | 9 | smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) 10 | biff = no 11 | 12 | # appending .domain is the MUA's job. 13 | append_dot_mydomain = no 14 | 15 | # Uncomment the next line to generate "delayed mail" warnings 16 | #delay_warning_time = 4h 17 | 18 | readme_directory = no 19 | 20 | # TLS parameters 21 | smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem 22 | smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key 23 | smtpd_use_tls=yes 24 | smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache 25 | smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache 26 | 27 | # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for 28 | # information on enabling SSL in the smtp client. 29 | 30 | smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination 31 | myhostname = {{ inventory_hostname }} 32 | alias_maps = hash:/etc/aliases 33 | alias_database = hash:/etc/aliases 34 | myorigin = /etc/mailname 35 | mydestination = $myhostname, {{ inventory_hostname }}, localhost.localdomain, , localhost 36 | relayhost = {{ smtp_relay_host|default('') }} 37 | mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 38 | mailbox_size_limit = 0 39 | recipient_delimiter = + 40 | inet_interfaces = all 41 | inet_protocols = all 42 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/rsyslog/files/ubuntu-trusty/rsyslog.conf: -------------------------------------------------------------------------------- 1 | # /etc/rsyslog.conf Configuration file for rsyslog. 2 | # 3 | # For more information see 4 | # /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html 5 | # 6 | # Default logging rules can be found in /etc/rsyslog.d/50-default.conf 7 | 8 | 9 | ################# 10 | #### MODULES #### 11 | ################# 12 | 13 | $ModLoad imuxsock # provides support for local system logging 14 | $ModLoad imklog # provides kernel logging support 15 | #$ModLoad immark # provides --MARK-- message capability 16 | 17 | # provides UDP syslog reception 18 | #$ModLoad imudp 19 | #$UDPServerRun 514 20 | 21 | # provides TCP syslog reception 22 | #$ModLoad imtcp 23 | #$InputTCPServerRun 514 24 | 25 | # Enable non-kernel facility klog messages 26 | $KLogPermitNonKernelFacility on 27 | 28 | ########################### 29 | #### GLOBAL DIRECTIVES #### 30 | ########################### 31 | 32 | # 33 | # Use traditional timestamp format. 34 | # To enable high precision timestamps, comment out the following line. 35 | # 36 | # commented out to enable RFC 3339 timestamp 37 | #$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat 38 | 39 | # Filter duplicated messages 40 | $RepeatedMsgReduction on 41 | 42 | # 43 | # Set the default permissions for all log files. 44 | # 45 | $FileOwner syslog 46 | $FileGroup adm 47 | $FileCreateMode 0640 48 | $DirCreateMode 0755 49 | $Umask 0022 50 | $PrivDropToUser syslog 51 | $PrivDropToGroup syslog 52 | 53 | # 54 | # Where to place spool and state files 55 | # 56 | $WorkDirectory /var/spool/rsyslog 57 | 58 | # 59 | # Include all config files in /etc/rsyslog.d/ 60 | # 61 | $IncludeConfig /etc/rsyslog.d/*.conf 62 | 63 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/rsyslog/files/ubuntu-precise/rsyslog.conf: -------------------------------------------------------------------------------- 1 | # /etc/rsyslog.conf Configuration file for rsyslog. 2 | # 3 | # For more information see 4 | # /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html 5 | # 6 | # Default logging rules can be found in /etc/rsyslog.d/50-default.conf 7 | 8 | 9 | ################# 10 | #### MODULES #### 11 | ################# 12 | 13 | $ModLoad imuxsock # provides support for local system logging 14 | $ModLoad imklog # provides kernel logging support (previously done by rklogd) 15 | #$ModLoad immark # provides --MARK-- message capability 16 | 17 | # provides UDP syslog reception 18 | #$ModLoad imudp 19 | #$UDPServerRun 514 20 | 21 | # provides TCP syslog reception 22 | #$ModLoad imtcp 23 | #$InputTCPServerRun 514 24 | 25 | # Enable non-kernel facility klog messages 26 | $KLogPermitNonKernelFacility on 27 | 28 | ########################### 29 | #### GLOBAL DIRECTIVES #### 30 | ########################### 31 | 32 | # 33 | # Use traditional timestamp format. 34 | # To enable high precision timestamps, comment out the following line. 35 | # 36 | # commented out to enable RFC 3339 timestamp 37 | #$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat 38 | 39 | # Filter duplicated messages 40 | $RepeatedMsgReduction on 41 | 42 | # 43 | # Set the default permissions for all log files. 44 | # 45 | $FileOwner syslog 46 | $FileGroup adm 47 | $FileCreateMode 0640 48 | $DirCreateMode 0755 49 | $Umask 0022 50 | $PrivDropToUser syslog 51 | $PrivDropToGroup syslog 52 | 53 | # 54 | # Where to place spool files 55 | # 56 | $WorkDirectory /var/spool/rsyslog 57 | 58 | # 59 | # Include all config files in /etc/rsyslog.d/ 60 | # 61 | $IncludeConfig /etc/rsyslog.d/*.conf 62 | 63 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/rsyslog/files/ubuntu-xenial/rsyslog.conf: -------------------------------------------------------------------------------- 1 | # /etc/rsyslog.conf Configuration file for rsyslog. 2 | # 3 | # For more information see 4 | # /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html 5 | # 6 | # Default logging rules can be found in /etc/rsyslog.d/50-default.conf 7 | 8 | 9 | ################# 10 | #### MODULES #### 11 | ################# 12 | 13 | module(load="imuxsock") # provides support for local system logging 14 | module(load="imklog") # provides kernel logging support 15 | #module(load="immark") # provides --MARK-- message capability 16 | 17 | # provides UDP syslog reception 18 | #module(load="imudp") 19 | #input(type="imudp" port="514") 20 | 21 | # provides TCP syslog reception 22 | #module(load="imtcp") 23 | #input(type="imtcp" port="514") 24 | 25 | # Enable non-kernel facility klog messages 26 | $KLogPermitNonKernelFacility on 27 | 28 | ########################### 29 | #### GLOBAL DIRECTIVES #### 30 | ########################### 31 | 32 | # 33 | # Use traditional timestamp format. 34 | # To enable high precision timestamps, comment out the following line. 35 | # 36 | # commented out to enable RFC 3339 timestamp 37 | #$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat 38 | 39 | # Filter duplicated messages 40 | $RepeatedMsgReduction on 41 | 42 | # 43 | # Set the default permissions for all log files. 44 | # 45 | $FileOwner syslog 46 | $FileGroup adm 47 | $FileCreateMode 0640 48 | $DirCreateMode 0755 49 | $Umask 0022 50 | $PrivDropToUser syslog 51 | $PrivDropToGroup syslog 52 | 53 | # 54 | # Where to place spool and state files 55 | # 56 | $WorkDirectory /var/spool/rsyslog 57 | 58 | # 59 | # Include all config files in /etc/rsyslog.d/ 60 | # 61 | $IncludeConfig /etc/rsyslog.d/*.conf 62 | 63 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Deploy script 5 | # 6 | 7 | PROGNAME="$(basename "$0")" 8 | cd "$(dirname "$0")" 9 | 10 | if test $? -ne 0; then 11 | echo "${PROGNAME}: could not cd to $(dirname $0)" >&2 12 | exit 1 13 | fi 14 | 15 | usage() { 16 | cat <&2 71 | usage >&2 72 | exit 1 73 | ;; 74 | esac 75 | done 76 | 77 | set -- ${EXTRA} "$@" 78 | 79 | source activate.sh 80 | 81 | set -ex 82 | exec ansible-playbook \ 83 | -v \ 84 | --diff \ 85 | --inventory-file ./inventory/${ENV}.ini \ 86 | "$@" \ 87 | -- site.yml 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simply Ansible 2 | 3 | From what started out as a [slide deck][] describing getting started and best 4 | practices for [Ansible][], [Simply Ansible][] is now a [cookiecutter][] template 5 | for quickly getting started with a decent [Ansible][] project structure. It has 6 | a handful of roles that I've found useful, and is a decent starting point for 7 | new projects. 8 | 9 | For no other reason than to serve as an example, these scripts spin up: 10 | * Node.js on [Ubuntu][] xenial (representing new services) 11 | * Ruby 2.3.1 on [Ubuntu][] trusty (representing legacy services) 12 | * Ruby 2.1.10 on [Ubuntu][] precise (representing something crazy old you 13 | really ought to replace) 14 | * A [CentOS][] 7 VM, because there's always something that has to be different 15 | 16 | ## Usage 17 | 18 | ```bash 19 | $ pip install cookiecutter # or your favorite package manager 20 | 21 | $ cookiecutter gh:building5/simply-ansible 22 | ``` 23 | 24 | Or if you happen to have a local clone of this repo: 25 | 26 | ```bash 27 | $ cookiecutter ./simply-ansible 28 | ``` 29 | 30 | ## Features 31 | 32 | * Running Ansible in a [virtualenv][], to avoid surprises from version 33 | ` differences on different control machines 34 | * Wrapper scripts for consistently running `ansible-playbook` 35 | * [Vagrant][] for local development 36 | * Installs a role from [Ansible galaxy][] 37 | 38 | ## TODO 39 | 40 | * [AWS Cloudformation][] scripts for spinning up staging and production 41 | environments. 42 | * And inventory and associated `group_vars` 43 | * Test if `project_slug` is a valid domain name 44 | * Figure out how to avoid shared accounts. Ansible should SSH as the user 45 | running the playbook; not as a shared user like `vagrant` or `ubuntu` 46 | * But that shared user is necessary for initial spinups, since that's the 47 | only user on the VM. 48 | 49 | ## LICENSE 50 | 51 | [ISC License][] 52 | 53 | [ansible galaxy]: https://galaxy.ansible.com/ 54 | [ansible]: https://www.ansible.combativity/ 55 | [aws cloudformation]: https://aws.amazon.com/cloudformation/ 56 | [centos]: https://www.centos.org/ 57 | [cookiecutter]: https://cookiecutter.readthedocs.io/en/latest/ 58 | [isc license]: https://opensource.org/licenses/ISC 59 | [slide deck]: ./docs 60 | [ubuntu]: http://www.ubuntu.com/ 61 | [vagrant]: https://www.vagrantup.com/ 62 | [virtualenv]: https://virtualenv.pypa.io/en/stable/ 63 | [simply ansible]: https://github.com/building5/simply-ansible 64 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/pip/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - tags: pip 3 | block: 4 | - when: ansible_distribution == 'CentOS' 5 | block: 6 | - name: yum install 7 | yum: name={{ item }} 8 | with_items: 9 | - '@Development tools' 10 | - python-devel 11 | - libffi-devel 12 | - openssl-devel 13 | - libyaml-devel 14 | 15 | - name: yum install python-pip 16 | yum: name=python-pip 17 | 18 | - when: ansible_distribution == 'Ubuntu' 19 | block: 20 | - name: apt-get install 21 | apt: name={{ item }} 22 | with_items: 23 | - build-essential 24 | - python-dev 25 | - libffi-dev 26 | - libssl-dev 27 | - libyaml-dev 28 | 29 | - when: ansible_distribution_release != 'precise' 30 | name: apt-get install pip 31 | apt: name=python-pip 32 | 33 | # 34 | # On Ubuntu precise, system pip can be quite old, and not work well with 35 | # newer packages. Install directly for something more reasonable. 36 | # 37 | - when: ansible_distribution_release == 'precise' 38 | block: 39 | 40 | # 41 | # The cloud image comes with a bunch of extra python stuff 42 | # installed. System images do not place nice with pip, so remove 43 | # them. Not a complete list, but the things that cause trouble 44 | # 45 | - name: apt-get remove 46 | apt: name={{ item }} state=absent purge=yes autoremove=yes 47 | with_items: 48 | - python-boto 49 | - python-pyasn1 50 | - python-twisted* 51 | - python-yaml 52 | 53 | - name: copy get-pip.py /usr/local/src/ 54 | copy: src=get-pip.py dest=/usr/local/src mode=0755 55 | 56 | - name: get-pip.py 57 | shell: /usr/local/src/get-pip.py pip=={{ pip_version }} 58 | register: get_pip_result 59 | changed_when: 'not "Requirement already up-to-date" in get_pip_result.stdout' 60 | 61 | # 62 | # Fixes insecure platform and SNI missing warnings on older Pythons 63 | # See https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings 64 | # 65 | - name: pip install urllib3[secure] 66 | pip: name='urllib3[secure]' 67 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # Roles to run early, since they might cause reboots, or their handlers need 4 | # to run before the normal roles can execute 5 | # 6 | - name: pre-roles 7 | hosts: all:!unmanaged 8 | tags: [initial, common] 9 | # Occasionally useful, but super noisy 10 | #pre_tasks:[{ debug: 'var=hostvars[inventory_hostname]', tags: debug }] 11 | roles: 12 | - set-distribution-name-version 13 | - apt-get-update 14 | - security-updates 15 | - hostname 16 | - timezone 17 | - rsyslog 18 | - users 19 | 20 | # 21 | # Detect is a reboot is required 22 | # 23 | - name: reboot-required 24 | # reboot one host at a time, for rolling updates 25 | serial: 1 26 | hosts: all:!unmanaged 27 | tags: [initial, common] 28 | roles: 29 | - reboot-required 30 | 31 | # 32 | # Roles to run on every instance 33 | # 34 | - name: common 35 | hosts: all:!unmanaged 36 | tags: common 37 | roles: 38 | # run cleanup as early as possible, since current roles may conflict 39 | # with old stuff 40 | - old-deploy-cleanup 41 | - epel 42 | - pip 43 | - debug-tools 44 | - hosts 45 | - mariadb-repo 46 | - mariadb-client 47 | - ntp 48 | - postfix 49 | 50 | # 51 | # New services are written in Node.js 52 | # 53 | - name: node.js 54 | hosts: services 55 | tags: services 56 | roles: 57 | - nodejs 58 | 59 | # 60 | # Legacy services were written in Ruby... 61 | # 62 | - name: ruby 63 | hosts: legacy 64 | tags: ruby 65 | roles: 66 | # the galaxy role is actually useful 67 | - role: rvm_io.rvm1-ruby 68 | # The default keys.gnupg.net keyserver is unreliable. Using the SKS 69 | # keyserver pool should be better. 70 | # See http://security.stackexchange.com/a/48149/5868 71 | rvm1_gpg_key_server: hkp://pool.sks-keyservers.net 72 | rvm1_install_flags: '--auto-dotfiles' 73 | rvm1_autolib_mode: 4 # automatically install deps 74 | rvm1_rubies: ['ruby-2.3.1'] 75 | tags: rvm 76 | 77 | # 78 | # Some are more legacy than others 79 | # 80 | - name: ruby 81 | hosts: crazy-old 82 | tags: ruby 83 | roles: 84 | # the galaxy role is actually useful 85 | - role: rvm_io.rvm1-ruby 86 | # The default keys.gnupg.net keyserver is unreliable. Using the SKS 87 | # keyserver pool should be better. 88 | # See http://security.stackexchange.com/a/48149/5868 89 | rvm1_gpg_key_server: hkp://pool.sks-keyservers.net 90 | rvm1_install_flags: '--auto-dotfiles' 91 | rvm1_autolib_mode: 4 # automatically install deps 92 | rvm1_rubies: ['ruby-2.1.10'] 93 | tags: rvm 94 | 95 | - name: mariadb 96 | hosts: mariadb 97 | roles: 98 | - python-mysql 99 | - mariadb-server 100 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/rsyslog/files/centos-7/rsyslog.conf: -------------------------------------------------------------------------------- 1 | # rsyslog configuration file 2 | 3 | # For more information see /usr/share/doc/rsyslog-*/rsyslog_conf.html 4 | # If you experience problems, see http://www.rsyslog.com/doc/troubleshoot.html 5 | 6 | #### MODULES #### 7 | 8 | # The imjournal module bellow is now used as a message source instead of imuxsock. 9 | $ModLoad imuxsock # provides support for local system logging (e.g. via logger command) 10 | $ModLoad imjournal # provides access to the systemd journal 11 | #$ModLoad imklog # reads kernel messages (the same are read from journald) 12 | #$ModLoad immark # provides --MARK-- message capability 13 | 14 | # Provides UDP syslog reception 15 | #$ModLoad imudp 16 | #$UDPServerRun 514 17 | 18 | # Provides TCP syslog reception 19 | #$ModLoad imtcp 20 | #$InputTCPServerRun 514 21 | 22 | 23 | #### GLOBAL DIRECTIVES #### 24 | 25 | # Where to place auxiliary files 26 | $WorkDirectory /var/lib/rsyslog 27 | 28 | # Use default timestamp format 29 | # commented out to enable RFC 3339 timestamp 30 | #$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat 31 | 32 | # File syncing capability is disabled by default. This feature is usually not required, 33 | # not useful and an extreme performance hit 34 | #$ActionFileEnableSync on 35 | 36 | # Include all config files in /etc/rsyslog.d/ 37 | $IncludeConfig /etc/rsyslog.d/*.conf 38 | 39 | # Turn off message reception via local log socket; 40 | # local messages are retrieved through imjournal now. 41 | $OmitLocalLogging on 42 | 43 | # File to store the position in the journal 44 | $IMJournalStateFile imjournal.state 45 | 46 | 47 | #### RULES #### 48 | 49 | # Log all kernel messages to the console. 50 | # Logging much else clutters up the screen. 51 | #kern.* /dev/console 52 | 53 | # Log anything (except mail) of level info or higher. 54 | # Don't log private authentication messages! 55 | *.info;mail.none;authpriv.none;cron.none /var/log/messages 56 | 57 | # The authpriv file has restricted access. 58 | authpriv.* /var/log/secure 59 | 60 | # Log all the mail messages in one place. 61 | mail.* -/var/log/maillog 62 | 63 | 64 | # Log cron stuff 65 | cron.* /var/log/cron 66 | 67 | # Everybody gets emergency messages 68 | *.emerg :omusrmsg:* 69 | 70 | # Save news errors of level crit and higher in a special file. 71 | uucp,news.crit /var/log/spooler 72 | 73 | # Save boot messages also to boot.log 74 | local7.* /var/log/boot.log 75 | 76 | 77 | # ### begin forwarding rule ### 78 | # The statement between the begin ... end define a SINGLE forwarding 79 | # rule. They belong together, do NOT split them. If you create multiple 80 | # forwarding rules, duplicate the whole block! 81 | # Remote Logging (we use TCP for reliable delivery) 82 | # 83 | # An on-disk queue is created for this action. If the remote host is 84 | # down, messages are spooled to disk and sent when it is up again. 85 | #$ActionQueueFileName fwdRule1 # unique name prefix for spool files 86 | #$ActionQueueMaxDiskSpace 1g # 1gb space limit (use as much as possible) 87 | #$ActionQueueSaveOnShutdown on # save messages to disk on shutdown 88 | #$ActionQueueType LinkedList # run asynchronously 89 | #$ActionResumeRetryCount -1 # infinite retries if host is down 90 | # remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional 91 | #*.* @@remote-host:514 92 | # ### end of the forwarding rule ### 93 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/README.md: -------------------------------------------------------------------------------- 1 | # {{ cookiecutter.project_name }} 2 | 3 | {{ cookiecutter.description }} 4 | 5 | ## Build requirements 6 | 7 | ### General 8 | 9 | * [Python 2.7](https://www.python.org/) 10 | * Installed by default on OS X 11 | * [Virtualenv][] 12 | * `pip install virtualenv` 13 | * [Virtualbox][] 14 | * For development on local VMs 15 | * [Vagrant][] 16 | * For building those local VMs 17 | * Vagrant plugins 18 | * [vagrant-reload](https://github.com/aidanns/vagrant-reload) 19 | * `vagrant plugin install vagrant-reload` 20 | 21 | ## Basic usage 22 | 23 | There are a set of `.sh` scripts in the root directory, which wrap calls to 24 | `ansible-playbook`, so that we are calling it consistently. Each script has a 25 | `--help` option for more details. 26 | 27 | Each script also honors a `--` option, for passing options directly to 28 | `ansible-playbook`. This allows you to pass additional options for running the 29 | playbook. 30 | 31 | $ ./deploy.sh -- --limit mariadb 32 | 33 | ## Vagrant 34 | 35 | A `Vagrantfile` is provided to spin up local development VMs. When spinning up a 36 | new environment, be sure to run deploy with `--initial` once, so the new VMs can 37 | be properly deployed to. 38 | 39 | # Spin up VMs 40 | $ vagrant up 41 | 42 | # Initial deploy; only need to do once 43 | $ ./deploy.sh --env vagrant --initial 44 | 45 | # Full deploy 46 | $ ./deploy.sh --env vagrant 47 | 48 | ## Deploy 49 | 50 | $ ./deploy.sh --env vagrant 51 | 52 | ### `-- [ansible-options]` 53 | 54 | $ ./deploy.sh -- --limit services --check 55 | 56 | As mentioned above, to pass arbitrary option to the `ansible-playbook` call, you 57 | can set them after the `--` option. This is especially useful for passing along 58 | `--tags`, `--limit` or `--check`. 59 | 60 | ### `--really-reboot` 61 | 62 | $ ./deploy.sh --really-reboot 63 | 64 | The Ansible scripts detect when a reboot may be necessary to apply updates. 65 | Since rebooting in the middle of a deploy would violate the principle of least 66 | surprise, it will only happen when the `--really-reboot` option is present. 67 | 68 | ### `--initial` 69 | 70 | $ ./deploy --initial -- --limit something-new 71 | 72 | Normally, deploys use your username (or whatever is configured in 73 | `~/.ssh/config`) for managing target machines. But when an instance is first 74 | created, the only the `ubuntu` user is preconfigured. Passing the `--initial` 75 | option sets up the playbook run accordingly. 76 | 77 | The `--initial` option also implies `--really-reboot`, since rebooting on intial 78 | deployment is usually a really good idea. If there are only a few hosts which 79 | you'd like to initialize, use the `--limit` ansible option to ensure you do not 80 | reboot anything unexpectedly. 81 | 82 | ## Development 83 | 84 | The infrastructure scripts must be idempotent. It should be safe to simple run 85 | `./deploy.sh` at any time, without any ill effects or unexpected behavior. 86 | 87 | The automation scripts are primarily [Ansible][] scripts. A [Virtualenv][] is 88 | used to ensure that the proper version of Ansible and its dependencies are used. 89 | The contents of the virtualenv are in `requirements.txt`. The `activate.sh` 90 | manages the virtualenv, and activates it for the shell. 91 | 92 | Not many [Ansible Galaxy][] dependencies are used, but they are listed in 93 | `requirements.yml`. 94 | 95 | Please see [Simply Ansible][] for Ansible best practices for writing new 96 | roles/playbooks. 97 | 98 | # License 99 | 100 | {{ cookiecutter.license }} 101 | 102 | [ansible galaxy]: https://galaxy.ansible.com/ 103 | [ansible]: https://www.ansible.com/ 104 | [simply ansible]: https://github.com/building5/simply-ansible/tree/master/docs 105 | [vagrant]: https://www.vagrantup.com 106 | [virtualbox]: https://www.virtualbox.org/ 107 | [virtualenv]: https://virtualenv.pypa.io/en/stable/ 108 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | domain_name = '{{ cookiecutter.project_slug }}.test' 10 | 11 | # The most common configuration options are documented and commented below. 12 | # For a complete reference, please see the online documentation at 13 | # https://docs.vagrantup.com. 14 | 15 | # Every Vagrant development environment requires a box. You can search for 16 | # boxes at https://atlas.hashicorp.com/search. 17 | config.vm.box = "ubuntu/xenial64" 18 | 19 | # Disable automatic box update checking. If you disable this, then 20 | # boxes will only be checked for updates when the user runs 21 | # `vagrant box outdated`. This is not recommended. 22 | # config.vm.box_check_update = false 23 | 24 | # Create a forwarded port mapping which allows access to a specific port 25 | # within the machine from a port on the host machine. In the example below, 26 | # accessing "localhost:8080" will access port 80 on the guest machine. 27 | # config.vm.network "forwarded_port", guest: 80, host: 8080 28 | 29 | # Create a private network, which allows host-only access to the machine 30 | # using a specific IP. 31 | # config.vm.network "private_network", ip: "192.168.33.10" 32 | 33 | # Create a public network, which generally matched to bridged network. 34 | # Bridged networks make the machine appear as another physical device on 35 | # your network. 36 | # config.vm.network "public_network" 37 | 38 | # Share an additional folder to the guest VM. The first argument is 39 | # the path on the host to the actual folder. The second argument is 40 | # the path on the guest to mount the folder. And the optional third 41 | # argument is a set of non-required options. 42 | # config.vm.synced_folder "../data", "/vagrant_data" 43 | 44 | # Provider-specific configuration so you can fine-tune various 45 | # backing providers for Vagrant. These expose provider-specific options. 46 | # Example for VirtualBox: 47 | # 48 | # config.vm.provider "virtualbox" do |vb| 49 | # # Display the VirtualBox GUI when booting the machine 50 | # vb.gui = true 51 | # 52 | # # Customize the amount of memory on the VM: 53 | # vb.memory = "1024" 54 | # end 55 | # 56 | # View the documentation for the provider you are using for more 57 | # information on available options. 58 | 59 | vms = { 60 | 'services' => { 61 | ip: '192.168.89.100', 62 | }, 63 | 'legacy' => { 64 | ip: '192.168.89.101', 65 | box: 'ubuntu/trusty64', 66 | }, 67 | 'crazy-old' => { 68 | ip: '192.168.89.102', 69 | box: 'ubuntu/precise64', 70 | }, 71 | 'weird' => { 72 | ip: '192.168.89.103', 73 | # Warning: centos7 doesn't work w/ vagrant 1.8.5 74 | # see https://github.com/mitchellh/vagrant/issues/7610 75 | box: 'centos/7', 76 | }, 77 | } 78 | 79 | vms.each do |name, options| 80 | config.vm.define name do |node| 81 | node.vm.hostname = "#{name}.#{domain_name}" 82 | node.vm.network 'private_network', {ip: options.fetch(:ip)} 83 | node.vm.box = options.fetch(:box, 'ubuntu/xenial64') 84 | node.vm.provider 'virtualbox' do |v| 85 | v.cpus = options.fetch(:cpus, 1) 86 | v.memory = options.fetch(:memory, 512) 87 | end 88 | 89 | # default user for ubuntu instances 90 | user = 'ubuntu' 91 | 92 | if node.vm.box.start_with?('centos/') 93 | # CentOS boxes don't bring up eth1 automatically; and ifup eth1 doesn't 94 | # work consistently. If reboot doesn't work, I give up. 95 | node.vm.provision :reload 96 | 97 | # Default user for CentOS is vagrant. 98 | user = 'vagrant' 99 | end 100 | 101 | node.vm.provision 'shell' do |s| 102 | ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub").first.strip 103 | # ensure we can SSH directly to the VM 104 | s.inline = <<-SHELL 105 | set -ex 106 | echo #{ssh_pub_key} >> /home/#{user}/.ssh/authorized_keys 107 | SHELL 108 | end 109 | 110 | # xenial only ships with Python 3 by default, ansible requires 111 | # Python 2.7 112 | if node.vm.box == 'ubuntu/xenial64' 113 | node.vm.provision 'shell' do |s| 114 | s.inline = <<-SHELL 115 | set -ex 116 | export DEBIAN_FRONTEND=noninteractive 117 | apt-get update 118 | apt-get install -y python 119 | SHELL 120 | end 121 | end 122 | end 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /docs/simply-ansible-getting-started.marp.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Simply Ansible: Getting Started 5 | 6 | > Getting started with Ansible 7 | 8 | David M. Lee, II 9 | [@leedm777](https://github.com/leedm777) GitHub 10 | [@leedm777](https://twitter.com/leedm777) Twitter 11 | [leedm777@yahoo.com](mailto:leedm777@yahoo.com) 12 | 13 | --- 14 | 15 | # `new Ansible()` 16 | 17 | > Ansible is an IT automation tool. It can configure systems, deploy software, and orchestrate more advanced IT tasks such as continuous deployments or zero downtime rolling updates. 18 | 19 | --- 20 | 21 | # Why? 22 | 23 | Codifying infrastructure allows you to take advantage of software development principles and techniques. 24 | 25 | * Automation: do more with less 26 | * Improve reliability: it happens the same way every time 27 | * Peer review; revision control; automated validation; ... 28 | 29 | --- 30 | 31 | # Ansible basics 32 | 33 | * Ansible uses [YAML](http://www.yaml.org/spec/1.2/spec.html) for pretty much everything 34 | * Except for the occasional [INI format](https://docs.python.org/2/library/configparser.html) 35 | * Templates are written in [Jinja2](http://jinja.pocoo.org/docs/dev/) template language 36 | * Can show up in `.yml` files. Much fun! 37 | * Requires Python 2.x, on both control machine and managed nodes 38 | * No agent required on managed nodes 39 | * Modules may require certain libraries/apps to be installed 40 | 41 | --- 42 | 43 | # Using Ansible 44 | 45 | ![usage](./images/usage.jpg) 46 | 47 | --- 48 | 49 | ## `ansible` - ad hoc commands 50 | 51 | Handy, but use sparingly. Changes to systems should be done through playbooks. 52 | 53 | ```bash 54 | $ ansible all \ 55 | > --module-name command \ 56 | > --args "uname -a" 57 | services | SUCCESS | rc=0 >> 58 | Linux services 3.2.0-107-virtual #148-Ubuntu SMP Mon Jul 18 21:47:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 59 | 60 | mariadb | SUCCESS | rc=0 >> 61 | Linux mariadb 3.2.0-107-virtual #148-Ubuntu SMP Mon Jul 18 21:47:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 62 | 63 | pdns | SUCCESS | rc=0 >> 64 | Linux pdns 3.10.0-327.22.2.el7.x86_64 #1 SMP Thu Jun 23 17:05:11 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 65 | ``` 66 | 67 | --- 68 | 69 | ## `ansible-playbook` - what you really want 70 | 71 | The `ansible-playbook` command executes the tasks specified in the playbook, in order, against the managed hosts. 72 | 73 | ```bash 74 | $ ansible-playbook site.yml 75 | PLAY [Do the thing] ************************************************************ 76 | 77 | TASK [setup] ******************************************************************* 78 | ok: [mariadb] 79 | ok: [services] 80 | ok: [pdns] 81 | 82 | TASK [The thing] *************************************************************** 83 | ok: [mariadb] 84 | ok: [services] 85 | skipped: [pdns] 86 | ``` 87 | 88 | --- 89 | 90 | # Anatomy of an Ansible project 91 | 92 | ![anatomy](images/anatomy.jpg) 93 | 94 | --- 95 | 96 | # Inventory 97 | 98 | ## The list of managed hosts 99 | 100 | Can be static (in a `.ini` file) or dynamic (results of a `.py` inventory script). 101 | 102 | ``` 103 | # ./inventory/vagrant.ini 104 | [services] 105 | services ansible_host=192.168.98.100 106 | 107 | [mariadb] 108 | mariadb ansible_host=192.168.98.200 109 | ``` 110 | 111 | --- 112 | 113 | # Variables 114 | 115 | * Values that may vary per system 116 | * About a zillion different places to specify vars 117 | * Please don't use _all_ of them 118 | * Vaults: encrypted vars file 119 | 120 | ```yaml 121 | # ./group_vars/all/main.yml 122 | --- 123 | # default to direct mail delivery. 124 | # if you have a relay, set it here. 125 | smtp_relay_host: '' 126 | 127 | # Use for NODE_ENV, RAILS_ENV, RACK_ENV, etc. 128 | app_env: development 129 | ``` 130 | 131 | --- 132 | 133 | # Modules 134 | 135 | Programs that Ansible executes directly on remote hosts. 136 | 137 | * Ansible ships with >600 modules. 138 | * [Custom modules](http://docs.ansible.com/ansible/developing_modules.html) can be loaded in the `./library` directory. 139 | * Very [well documented](http://docs.ansible.com/ansible/list_of_all_modules.html) on ansible.com 140 | 141 | ![copy](./images/copy.png) 142 | 143 | --- 144 | 145 | # Tasks 146 | 147 | A task specifies a module and the parameters to invoke it with. 148 | 149 | ```yaml 150 | - name: perform an action 151 | # args can be name=value 152 | copy: src=blink.conf dest=/etc/angels 153 | 154 | - name: perform another action 155 | # or args can be an object 156 | copy: 157 | src: blink.conf 158 | dest: /etc/angels 159 | ``` 160 | 161 | --- 162 | 163 | # Blocks 164 | 165 | A block can used anywhere you can use a task. It allows you to more easily apply settings to a group of tasks. It also adds `rescue` blocks for error handling. New in Ansible 2.x. 166 | 167 | ```yaml 168 | - when: doctor == 10 169 | block: 170 | - name: Copy rose.conf to /etc/doctor/companions.d/ 171 | copy: src=rose.conf dest=/etc/doctor/companions.d/ 172 | - name: Set catchphrase to allons-y 173 | copy: src=allons-y.conf dest=/etc/doctor/catchphrase.conf 174 | 175 | ``` 176 | 177 | --- 178 | 179 | # Roles 180 | 181 | A role is a list of tasks, which are executed sequentially, in order. Plus some other stuff. 182 | 183 | ```bash 184 | site.yml 185 | roles/ 186 | some-role/ 187 | tasks/ 188 | main.yml # tasks go in here 189 | defaults/ 190 | main.yml # role-specific vars go in here 191 | handlers/ 192 | main.yml # tasks that are triggered optionally 193 | # i.e. service restarts 194 | files/ 195 | ... # content for copy/script tasks 196 | templates/ 197 | ... # content for template tasks 198 | # always append .j2 to filenames 199 | ``` 200 | 201 | --- 202 | 203 | # Plays 204 | 205 | A play specifies which roles (in order) to run on which hosts. The host list has [an elaborate syntax](http://docs.ansible.com/ansible/intro_patterns.html) which can specify pretty much anything you want. 206 | 207 | ```yaml 208 | - name: Some play 209 | hosts: some-hosts:!not-these-hosts 210 | roles: 211 | - some-role 212 | - some-other-role 213 | ``` 214 | 215 | --- 216 | 217 | # Playbooks 218 | 219 | A playbook is a list of plays, which are executed sequentially, in order. 220 | 221 | ```yaml 222 | # ./site.yml 223 | --- 224 | - name: First play 225 | hosts: some-hosts 226 | roles: 227 | - some-role 228 | 229 | - name: Second play 230 | # ... 231 | ``` 232 | 233 | --- 234 | 235 | ## Templates 236 | 237 | [Jinja2 syntax](http://jinja.pocoo.org/docs/dev/templates/) is used in `.j2` template files, or in task definitions. 238 | 239 | ```yaml 240 | - name: perform an action 241 | action: 242 | some_param: '{{ some_var }}' 243 | # ^ ^ 244 | # in YAML, quotes are 245 | # usually necessary to 246 | # avoid yaml/j2 confusion 247 | ``` 248 | 249 | --- 250 | 251 | ## Custom Jinja2 filters 252 | 253 | In addition to the standard Jinja2 filters, Ansible defines a [number of custom filters](https://docs.ansible.com/ansible/playbooks_filters.html). 254 | 255 | ![custom filters](./images/custom-filters.png) 256 | 257 | --- 258 | 259 | ## Handlers 260 | 261 | Handlers allow you to perform actions on change. 262 | 263 | * If handler isn't notified, it isn't run 264 | * If it's notified multiple times, it only runs once 265 | * When run, handlers run at the end of the play 266 | * **Caution**: Be sure to set `force_handlers = true` in `ansible.cfg` 267 | * Otherwise errors between notify and end of play cause handlers to be skipped 268 | 269 | ```yaml 270 | # ./some-role/tasks/main.yml 271 | - copy: src=missy.conf dest=/etc/master 272 | notify: restart master 273 | 274 | # ./some-role/handlers/main.yml 275 | - name: restart master 276 | service: name=master state=restarted 277 | ``` 278 | 279 | --- 280 | 281 | ## Parameterized Roles 282 | 283 | A role may be parameterized, meaning that it expects certain variables to be set. 284 | 285 | ```yaml 286 | - role: rvm_io.rvm1-ruby 287 | rvm1_autolib_mode: 4 # automatically install deps 288 | rvm1_rubies: ['ruby-2.3.1', 'ruby-2.2.5'] 289 | ``` 290 | 291 | --- 292 | 293 | ## Sharing roles 294 | 295 | * Common roles can be shared via [Ansible galaxy](https://galaxy.ansible.com/) or [git repo](http://docs.ansible.com/ansible/galaxy.html#installing-multiple-roles-from-multiple-files) 296 | * BUT... they tend to be very small, poorly versioned, and so poorly maintained, this is rarely useful 297 | * Don't use a role from Galaxy unless it has tagged versions 298 | * AND it looks like someone loves and cares for it 299 | * Otherwise, learn what you can from it and write your own 300 | 301 | --- 302 | 303 | # Tags and limits 304 | 305 | [Tags](http://docs.ansible.com/ansible/playbooks_tags.html) specify which tasks to run. [Limits](http://docs.ansible.com/ansible/intro_patterns.html) specifies which hosts to run those tasks on. 306 | 307 | ```bash 308 | # run entire playbook on single machine 309 | $ ansible-playbook site.yml --limit strax 310 | 311 | # run everything task tagged with sontaran 312 | $ ansible-playbook site.yml --tags sontaran 313 | 314 | # run sil and ood on the webservers not in San Diego 315 | $ ansible-playbook site.yml --tags sil,ood \ 316 | > --limit webservers:!san-diego 317 | ``` 318 | 319 | --- 320 | 321 | # Secret Variables: Ansible Vault 322 | 323 | The `ansible-vault` command can be used to manage encrypted files. Put any secrets for a given environment in a `vault.yml` file in that environment's `group_vars`. 324 | 325 | ```bash 326 | # create a vault 327 | $ ansible-vault create group_vars/${ENV}/vault.yml 328 | # edit a vault 329 | $ ansible-vault edit group_vars/${ENV}/vault.yml 330 | ``` 331 | 332 | --- 333 | 334 | # Usable vaults: [`ansible-vault-tools`](https://github.com/building5/ansible-vault-tools) 335 | 336 | Problem: vaults are just blobs of hex. `ansible-vault-tools` to the rescue! 337 | 338 | * `ansible-vault-merge` - magical vault merging 339 | * `ansible-vault-textconv` - magical vault diffing 340 | * And `git grep --textconv` can search in vaults! 341 | * `gpg-vault-password-file` - encrypt your vault password file 342 | 343 | ``` 344 | $ANSIBLE_VAULT;1.1;AES256 345 | deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead 346 | deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead 347 | ``` 348 | 349 | --- 350 | 351 | # Task attributes 352 | 353 | Task attributes can also be applied to blocks, roles and plays. 354 | 355 | * `no_log` - omit output when running the play 356 | * `become` - control which user runs the task(s) 357 | * `when` - conditionally runs the task(s) 358 | * `delegate_to`, `run_once`, `serial` - control task execution 359 | 360 | --- 361 | 362 | ## `no_log`: keep it secret, keep it safe 363 | 364 | Adding `no_log` to a task ensures that any secrets don't get printed to the console when you run the playbook. 365 | 366 | ```yaml 367 | - copy: content={{ doctors_name }} dest=/etc/doctor/name 368 | no_log: True 369 | ``` 370 | 371 | --- 372 | 373 | ## To `become`, or not `become` 374 | 375 | By default, Ansible runs modules as the unprivileged user. The `become` setting changes that. 376 | 377 | * Can be specified in `ansible.cfg`, on a play, on a task, on the command line 378 | * Can specify `become_user` to become a user other than `root` 379 | 380 | --- 381 | 382 | ## Conditionals 383 | 384 | On tasks, block, roles or plays, you can add a `when` clause to conditionally do something. 385 | 386 | ```yaml 387 | - when: ansible_distribution == 'Ubuntu' 388 | apt: name=postfix 389 | 390 | - when: ansible_distribution == 'CentOS' 391 | yum: name=postfix 392 | ``` 393 | 394 | --- 395 | 396 | ## Task execution control 397 | 398 | * `delegate_to` - run the task once, on the specified host 399 | * `run_once` - run the task once, on the first host in the group 400 | * useful for running database migrations 401 | * `serial` - run the task on batches of servers 402 | * `1` - run on one host at a time 403 | * `20%` - run on 20% of the hosts at a time 404 | 405 | --- 406 | 407 | # Questions 408 | 409 | ![question](images/question.jpg) 410 | 411 | --- 412 | 413 | # Thanks! 414 | 415 | ![hugs](images/hugs.jpg) 416 | 417 | David M. Lee, II 418 | [@leedm777](https://github.com/leedm777) GitHub 419 | [@leedm777](https://twitter.com/leedm777) Twitter 420 | [leedm777@yahoo.com](mailto:leedm777@yahoo.com) 421 | -------------------------------------------------------------------------------- /docs/simply-ansible-best-practices.marp.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Simply Ansible: Best Practices 5 | 6 | > Writing Ansible scripts that don't completely suck 7 | 8 | David M. Lee, II 9 | [@leedm777](https://github.com/leedm777) GitHub 10 | [@leedm777](https://twitter.com/leedm777) Twitter 11 | [leedm777@yahoo.com](mailto:leedm777@yahoo.com) 12 | 13 | --- 14 | 15 | # Fair warning: I might be wrong 16 | 17 | ![no-idea](./images/no-idea.jpg) 18 | 19 | --- 20 | 21 | # Nothing better than an example 22 | 23 | I have a [cookiecutter](https://github.com/audreyr/cookiecutter) template which sets up a good starting project demonstrating these practices. 24 | 25 | ```bash 26 | $ pip install cookiecutter 27 | 28 | $ cookiecutter gh:building5/simply-ansible 29 | ``` 30 | 31 | --- 32 | 33 | # PROTIPS 34 | 35 | ![who-cat](images/who-cat.jpg) 36 | 37 | --- 38 | 39 | ## Put tasks in roles, not the playbook 40 | 41 | While you can list tasks directly in a play, you're usually better off putting the task in a role. 42 | 43 | ```yaml 44 | # ./site.yml 45 | - name: Some play 46 | hosts: some-hosts 47 | pre_tasks: # meh 48 | - name: some pre_task 49 | roles: 50 | - some-role 51 | tasks: # really? 52 | - name: some task 53 | post_tasks: # PLEASE STOP! 54 | - name: some post_task 55 | ``` 56 | 57 | --- 58 | 59 | ## Really, keep tasks in roles 60 | 61 | Besides, content from Ansible Galaxy can only be brought in as a role, so trying to sort that into existing `{pre,post}_tasks` is a pain. 62 | 63 | ```yaml 64 | - name: Some play 65 | hosts: some-hosts 66 | pre_tasks: 67 | # but what if I wanted to call a galaxy role 68 | # before this? 69 | - name: some pre_task 70 | post_tasks: 71 | - name: some post_task 72 | # or maybe a role after this? 73 | ``` 74 | 75 | --- 76 | 77 | ## Keep roles small and single purpose 78 | 79 | If the playbook is your 'program', then roles are your 'functions'. 80 | 81 | * For example, `pip`, `firewall`, `nodejs`, `timezone`, `auton` 82 | * Not things like `common`, `general`, `misc`, ... 83 | * Keep them small; most will fit on the screen 84 | 85 | --- 86 | 87 | ## Bending the rule: `debug-tools` 88 | 89 | I usually have a rule with a bunch of one-line installers for things that are generally useful for debugging. But if you can't install it with a single `apt`, `pip`,`npm`, `gem`, etc., then it gets its own role. 90 | 91 | ```yaml 92 | # ./roles/debug-tools/tasks/main.yml 93 | - when: ansible_distribution == 'Ubuntu' 94 | name: apt-get install 95 | apt: name={{ item }} 96 | with_items: 97 | - curl 98 | - htop 99 | - iotop 100 | - jq 101 | # ... 102 | ``` 103 | 104 | --- 105 | 106 | ## Avoid repeating roles... 107 | 108 | Here both `webservers` and `db` need `zygon`. Since plays are sequential, `zygon` will be installed in sequence. Can really slow down the playbook. 109 | 110 | ```yaml 111 | - hosts: webservers 112 | roles: 113 | - zygon # Bad! 114 | - nginx 115 | 116 | - hosts: db 117 | roles: 118 | - zygon # Bad! 119 | - postgresql 120 | ``` 121 | 122 | --- 123 | 124 | ## i.e., consolidate roles to get parallelism 125 | 126 | Since the `zygon` role is in a single play, it will execute in parallel on both `webserver` and `db`. You can't always do this, but when you can, it helps tremendously. 127 | 128 | ```yaml 129 | - hosts: webserver,db # both webserver and db groups 130 | roles: 131 | - zygon # installs in parallel 132 | # across webserver & db nodes 133 | 134 | - hosts: webserver 135 | roles: 136 | - nginx 137 | 138 | - hosts: db 139 | roles: 140 | - postgresql 141 | ``` 142 | 143 | --- 144 | 145 | ## Tag each role with the role's name 146 | 147 | By keeping tag names consistent with role names, it's easier to keep track of what tags you have. 148 | 149 | 150 | ```yaml 151 | # ./roles/zygon/tasks/main.yml 152 | --- 153 | - tags: zygon 154 | block: # Ansible 2.x syntax 155 | - name: First zygon invasion 156 | # ... 157 | ``` 158 | 159 | Since roles are small and single purpose, you're usually debugging individual roles. 160 | 161 | ```bash 162 | $ ansible-playbook site.yml --tags zygon 163 | ``` 164 | 165 | --- 166 | 167 | ## Don't bother with `meta` 168 | 169 | Meta declares dependencies between roles, which seems nice. 170 | 171 | But... 172 | * Dependencies are executed in every play they are referenced from, which means they probably get executed multiple times 173 | * Plays/roles execute in order, so you can just put the dependency earlier in the list 174 | 175 | ```yaml 176 | # ./role/rani/meta/main.yml 177 | dependencies: [{ role: apt-get-update }] # Bad! 178 | # can cause apt-get-update to run 179 | # multiple times! 180 | 181 | # ./site.yml 182 | - roles: 183 | - apt-get-update # Much Better! 184 | - rani 185 | ``` 186 | 187 | --- 188 | 189 | ## Prefer `defaults` to `vars` 190 | 191 | Roles give you two places to define variables. They only differ in precedence, and `defaults` (lowest precedence of any variable) is the least confusing. 192 | 193 | ```bash 194 | roles/ 195 | some-role/ 196 | defaults/ 197 | main.yml # Good place for role's variables 198 | vars/ 199 | main.yml # Confusing place for role's variables 200 | ``` 201 | 202 | --- 203 | 204 | ## Don't put tons of tags on a task 205 | 206 | If you find yourself putting lots of tags on a task, you're probably doing it wrong. A tag should be there for a reason. 207 | 208 | ```yaml 209 | tags: 210 | - provision # okay, but every task in the 211 | # playbook is for provisioning 212 | - bamboo # and everything is for bamboo 213 | - bamboo-agent 214 | # what are you doing? 215 | - bamboo-agent-ubuntu 216 | # wait... 217 | - bamboo-agent-ubuntu-precise 218 | # MAKE IT STOP 219 | - bamboo-agent-ubuntu-precise-provision 220 | # *sigh* 221 | ``` 222 | 223 | --- 224 | 225 | ## Put variable definitions in the right spot 226 | 227 | ```bash 228 | site.yml # in the occasional vars block 229 | # for parameterized roles 230 | group_vars/ 231 | all/ 232 | main.yml # default values 233 | ... # can break into multiple files 234 | staging/ # Have a group for each environment 235 | main.yml # override defaults for env 236 | vault.yml # safe place for secrets 237 | roles/ 238 | kandy-man/ 239 | defaults/ 240 | main.yml # specific vars for the role 241 | # version numbers, SHA hashes, etc. 242 | ``` 243 | 244 | --- 245 | 246 | ## Vaults: too many vaults will kill you 247 | 248 | * We tried having single-var per vault 249 | * PRO: Avoids merge conflicts w/ vaults 250 | * CON: No one follows the rules 251 | * Vars end up in random vaults 252 | * Or var was renamed and vault wasn't 253 | * Grep is either powerless, or really slow 254 | * Use [ansible-vault-tools](https://github.com/building5/ansible-vault-tools) and as few vaults as possible 255 | * Single vault per `group_vars` 256 | * `ansible-vault-merge` - magical vault merging 257 | * `ansible-vault-textconv` - magical vault diffing 258 | * And `git grep --textconv` can search in vaults! 259 | 260 | --- 261 | 262 | ## !!!ENCRYPT YOUR VAULT PASSWORD!!! 263 | 264 | The `ansible-vault-tools` project also provides `gpg-vault-password-file`, which can use your PGP key to encrypt your vault password. 265 | * You do [have a PGP key](https://help.github.com/articles/generating-a-new-gpg-key/), right? 266 | * And it's [encrypted with a passphrase](https://xkcd.com/936/)? 267 | * And you use [gpg-agent](https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html) so you don't go insane typing the passphrase all the time? 268 | 269 | --- 270 | 271 | ## Put common settings in `ansible.cfg` 272 | 273 | Defaults should be what's best for the humans that run the playbooks by hand the most (probably devs). 274 | 275 | ``` 276 | # ./ansible.cfg 277 | [defaults] 278 | # Why is the default not smart? 279 | gathering = smart 280 | # Default for most common params devs would use 281 | inventory = ./inventory/vagrant.ini 282 | # Consistent configuration for vault passwords 283 | vault_password_file = $HOME/.ansible/some_project_vault 284 | 285 | [privilege_escalation] 286 | # Nearly everything requires sudo, so default to it 287 | become = True 288 | 289 | [ssh_connection] 290 | # Speeds things up, but requires disabling 291 | # requiretty in /etc/sudoers 292 | pipelining = True 293 | ``` 294 | 295 | --- 296 | 297 | ## Make sure roles are idempotent 298 | 299 | ![idempotence](images/idempotence.png) 300 | 301 | > The concept that change commands should only be applied when they need to be applied, and that it is better to describe the desired state of a system than the process of how to get to that state. 302 | 303 |   -- [Ansible Glossary](http://docs.ansible.com/ansible/glossary.html#term-idempotency) 304 | 305 | --- 306 | 307 | ## Set force_handlers = True in ansible.cfg 308 | 309 | *New*: By default, a failure in a playbook causes handlers to be skipped. This causes the handlers to not run idempotently, and cause strange behaviors when re-running playbooks after errors. 310 | 311 | ```yaml 312 | # ./site.yml 313 | - # ... 314 | roles: [ master, cyberman ] 315 | 316 | # ./roles/master/tasks/main.yml 317 | - cp: src=missy.conf dest=/etc/master/ 318 | notify: restart master 319 | # ^^ schedules restart of master if file is copied 320 | 321 | # ./roles/cyberman/tasks/main.yml 322 | - fail: msg="***HEAD EXPLODES***" 323 | # ^^ causes play to fail before restart master 324 | # w/o force_handlers = True, this would be skipped 325 | ``` 326 | 327 | --- 328 | 329 | ## Use a wrapper script so everyone runs the playbook consistently 330 | 331 | * Setup [virtualenv](https://virtualenv.pypa.io/en/stable/) so everyone runs the same version 332 | * Add some common/useful options (`-v`, `--diff`) 333 | * Add domain specific options, useful for your team 334 | * `--env` to specify environment 335 | * `--service` to deploy a specific service 336 | * `--` to break for `ansible-playbook` options 337 | 338 | ```bash 339 | $ ./deploy.sh --env staging -- --tags cyberman --check 340 | + exec ansible-playbook -v --diff \ 341 | + --inventory-file ./inventory/staging.ini \ 342 | + --tags cyberman \ 343 | + --check \ 344 | + -- site.yml 345 | Using /Users/dlee/prj/doctor/ansible.cfg as config file 346 | 347 | PLAY [pre-roles] *************************************************************** 348 | ``` 349 | 350 | --- 351 | 352 | ## Don't make a role portable until you have to 353 | 354 | > There is no such thing as portable code. Only code that's been ported. 355 | 356 | Avoid supporting multiple platforms as much as you can. It's better to standardize on a distro/version. 357 | 358 | That said, 359 | * You usually end up with one weird thing that needs to be on another distro 360 | * Or you'll have a transition time as you upgrade major versions 361 | * Don't port everything; only port what you need; one thing at a time. 362 | 363 | --- 364 | 365 | ## Logic goes in roles; not in playbooks 366 | 367 | Ansible allows you to split logic between playbooks and roles, but that just makes it harder to figure out what's going on. 368 | 369 | ```yaml 370 | # ./site.yml 371 | - # ... 372 | roles: 373 | - role: epel 374 | when: ansible_distribution == 'CentOS' # Bad! 375 | 376 | # ./roles/epel/tasks/main.yml 377 | - name: yum install epel-... 378 | when: ansible_distribution == 'CentOS' # Good! 379 | ``` 380 | --- 381 | 382 | ## Test your playbooks 383 | 384 | * A `make test` target is always useful 385 | * `ansible-playbook --syntax-check` finds syntax errors 386 | * [`ansible-lint`](https://github.com/willthames/ansible-lint) finds common errors 387 | 388 | ```Makefile 389 | test: virtualenv galaxy 390 | @$(MAKE) check-env # errors if we're 391 | # not in virtualenv 392 | ansible-playbook site.yml --syntax-check 393 | ansible-lint --exclude build/ site.yml 394 | .PHONY: test 395 | ``` 396 | 397 | --- 398 | 399 | ## Follow good programming principles 400 | 401 | * Use descriptive names 402 | * Keep playbooks in source control 403 | * Peer review changes before going live 404 | * Use continuous deployment/continous delivery/ChatOps 405 | * Testing! 406 | 407 | --- 408 | 409 | ## Old advice 410 | 411 | * OLD: Avoiding handles 412 | * I used to code my playbooks w/o handlers, so errors wouldn't skip them 413 | * NEW: Set `force_handlers = True` in `ansible.cfg` 414 | * OLD: Tag the roles in the playbook (`{ role: zygon, tags: zygon }`) 415 | * This was before blocks existed 416 | * NEW: Define a top-level block in the role and put the tag there 417 | 418 | --- 419 | 420 | # Questions 421 | 422 | ![question](images/question.jpg) 423 | 424 | --- 425 | 426 | # Thanks! 427 | 428 | ![hugs](images/hugs.jpg) 429 | 430 | David M. Lee, II 431 | [@leedm777](https://github.com/leedm777) GitHub 432 | [@leedm777](https://twitter.com/leedm777) Twitter 433 | [leedm777@yahoo.com](mailto:leedm777@yahoo.com) 434 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/roles/postfix/templates/centos-7/main.cf.j2: -------------------------------------------------------------------------------- 1 | # Global Postfix configuration file. This file lists only a subset 2 | # of all parameters. For the syntax, and for a complete parameter 3 | # list, see the postconf(5) manual page (command: "man 5 postconf"). 4 | # 5 | # For common configuration examples, see BASIC_CONFIGURATION_README 6 | # and STANDARD_CONFIGURATION_README. To find these documents, use 7 | # the command "postconf html_directory readme_directory", or go to 8 | # http://www.postfix.org/. 9 | # 10 | # For best results, change no more than 2-3 parameters at a time, 11 | # and test if Postfix still works after every change. 12 | 13 | # SOFT BOUNCE 14 | # 15 | # The soft_bounce parameter provides a limited safety net for 16 | # testing. When soft_bounce is enabled, mail will remain queued that 17 | # would otherwise bounce. This parameter disables locally-generated 18 | # bounces, and prevents the SMTP server from rejecting mail permanently 19 | # (by changing 5xx replies into 4xx replies). However, soft_bounce 20 | # is no cure for address rewriting mistakes or mail routing mistakes. 21 | # 22 | #soft_bounce = no 23 | 24 | # LOCAL PATHNAME INFORMATION 25 | # 26 | # The queue_directory specifies the location of the Postfix queue. 27 | # This is also the root directory of Postfix daemons that run chrooted. 28 | # See the files in examples/chroot-setup for setting up Postfix chroot 29 | # environments on different UNIX systems. 30 | # 31 | queue_directory = /var/spool/postfix 32 | 33 | # The command_directory parameter specifies the location of all 34 | # postXXX commands. 35 | # 36 | command_directory = /usr/sbin 37 | 38 | # The daemon_directory parameter specifies the location of all Postfix 39 | # daemon programs (i.e. programs listed in the master.cf file). This 40 | # directory must be owned by root. 41 | # 42 | daemon_directory = /usr/libexec/postfix 43 | 44 | # The data_directory parameter specifies the location of Postfix-writable 45 | # data files (caches, random numbers). This directory must be owned 46 | # by the mail_owner account (see below). 47 | # 48 | data_directory = /var/lib/postfix 49 | 50 | # QUEUE AND PROCESS OWNERSHIP 51 | # 52 | # The mail_owner parameter specifies the owner of the Postfix queue 53 | # and of most Postfix daemon processes. Specify the name of a user 54 | # account THAT DOES NOT SHARE ITS USER OR GROUP ID WITH OTHER ACCOUNTS 55 | # AND THAT OWNS NO OTHER FILES OR PROCESSES ON THE SYSTEM. In 56 | # particular, don't specify nobody or daemon. PLEASE USE A DEDICATED 57 | # USER. 58 | # 59 | mail_owner = postfix 60 | 61 | # The default_privs parameter specifies the default rights used by 62 | # the local delivery agent for delivery to external file or command. 63 | # These rights are used in the absence of a recipient user context. 64 | # DO NOT SPECIFY A PRIVILEGED USER OR THE POSTFIX OWNER. 65 | # 66 | #default_privs = nobody 67 | 68 | # INTERNET HOST AND DOMAIN NAMES 69 | # 70 | # The myhostname parameter specifies the internet hostname of this 71 | # mail system. The default is to use the fully-qualified domain name 72 | # from gethostname(). $myhostname is used as a default value for many 73 | # other configuration parameters. 74 | # 75 | #myhostname = host.domain.tld 76 | #myhostname = virtual.domain.tld 77 | 78 | # The mydomain parameter specifies the local internet domain name. 79 | # The default is to use $myhostname minus the first component. 80 | # $mydomain is used as a default value for many other configuration 81 | # parameters. 82 | # 83 | #mydomain = domain.tld 84 | 85 | # SENDING MAIL 86 | # 87 | # The myorigin parameter specifies the domain that locally-posted 88 | # mail appears to come from. The default is to append $myhostname, 89 | # which is fine for small sites. If you run a domain with multiple 90 | # machines, you should (1) change this to $mydomain and (2) set up 91 | # a domain-wide alias database that aliases each user to 92 | # user@that.users.mailhost. 93 | # 94 | # For the sake of consistency between sender and recipient addresses, 95 | # myorigin also specifies the default domain name that is appended 96 | # to recipient addresses that have no @domain part. 97 | # 98 | #myorigin = $myhostname 99 | #myorigin = $mydomain 100 | 101 | # RECEIVING MAIL 102 | 103 | # The inet_interfaces parameter specifies the network interface 104 | # addresses that this mail system receives mail on. By default, 105 | # the software claims all active interfaces on the machine. The 106 | # parameter also controls delivery of mail to user@[ip.address]. 107 | # 108 | # See also the proxy_interfaces parameter, for network addresses that 109 | # are forwarded to us via a proxy or network address translator. 110 | # 111 | # Note: you need to stop/start Postfix when this parameter changes. 112 | # 113 | #inet_interfaces = all 114 | #inet_interfaces = $myhostname 115 | #inet_interfaces = $myhostname, localhost 116 | inet_interfaces = localhost 117 | 118 | # Enable IPv4, and IPv6 if supported 119 | inet_protocols = all 120 | 121 | # The proxy_interfaces parameter specifies the network interface 122 | # addresses that this mail system receives mail on by way of a 123 | # proxy or network address translation unit. This setting extends 124 | # the address list specified with the inet_interfaces parameter. 125 | # 126 | # You must specify your proxy/NAT addresses when your system is a 127 | # backup MX host for other domains, otherwise mail delivery loops 128 | # will happen when the primary MX host is down. 129 | # 130 | #proxy_interfaces = 131 | #proxy_interfaces = 1.2.3.4 132 | 133 | # The mydestination parameter specifies the list of domains that this 134 | # machine considers itself the final destination for. 135 | # 136 | # These domains are routed to the delivery agent specified with the 137 | # local_transport parameter setting. By default, that is the UNIX 138 | # compatible delivery agent that lookups all recipients in /etc/passwd 139 | # and /etc/aliases or their equivalent. 140 | # 141 | # The default is $myhostname + localhost.$mydomain. On a mail domain 142 | # gateway, you should also include $mydomain. 143 | # 144 | # Do not specify the names of virtual domains - those domains are 145 | # specified elsewhere (see VIRTUAL_README). 146 | # 147 | # Do not specify the names of domains that this machine is backup MX 148 | # host for. Specify those names via the relay_domains settings for 149 | # the SMTP server, or use permit_mx_backup if you are lazy (see 150 | # STANDARD_CONFIGURATION_README). 151 | # 152 | # The local machine is always the final destination for mail addressed 153 | # to user@[the.net.work.address] of an interface that the mail system 154 | # receives mail on (see the inet_interfaces parameter). 155 | # 156 | # Specify a list of host or domain names, /file/name or type:table 157 | # patterns, separated by commas and/or whitespace. A /file/name 158 | # pattern is replaced by its contents; a type:table is matched when 159 | # a name matches a lookup key (the right-hand side is ignored). 160 | # Continue long lines by starting the next line with whitespace. 161 | # 162 | # See also below, section "REJECTING MAIL FOR UNKNOWN LOCAL USERS". 163 | # 164 | mydestination = $myhostname, localhost.$mydomain, localhost 165 | #mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain 166 | #mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, 167 | # mail.$mydomain, www.$mydomain, ftp.$mydomain 168 | 169 | # REJECTING MAIL FOR UNKNOWN LOCAL USERS 170 | # 171 | # The local_recipient_maps parameter specifies optional lookup tables 172 | # with all names or addresses of users that are local with respect 173 | # to $mydestination, $inet_interfaces or $proxy_interfaces. 174 | # 175 | # If this parameter is defined, then the SMTP server will reject 176 | # mail for unknown local users. This parameter is defined by default. 177 | # 178 | # To turn off local recipient checking in the SMTP server, specify 179 | # local_recipient_maps = (i.e. empty). 180 | # 181 | # The default setting assumes that you use the default Postfix local 182 | # delivery agent for local delivery. You need to update the 183 | # local_recipient_maps setting if: 184 | # 185 | # - You define $mydestination domain recipients in files other than 186 | # /etc/passwd, /etc/aliases, or the $virtual_alias_maps files. 187 | # For example, you define $mydestination domain recipients in 188 | # the $virtual_mailbox_maps files. 189 | # 190 | # - You redefine the local delivery agent in master.cf. 191 | # 192 | # - You redefine the "local_transport" setting in main.cf. 193 | # 194 | # - You use the "luser_relay", "mailbox_transport", or "fallback_transport" 195 | # feature of the Postfix local delivery agent (see local(8)). 196 | # 197 | # Details are described in the LOCAL_RECIPIENT_README file. 198 | # 199 | # Beware: if the Postfix SMTP server runs chrooted, you probably have 200 | # to access the passwd file via the proxymap service, in order to 201 | # overcome chroot restrictions. The alternative, having a copy of 202 | # the system passwd file in the chroot jail is just not practical. 203 | # 204 | # The right-hand side of the lookup tables is conveniently ignored. 205 | # In the left-hand side, specify a bare username, an @domain.tld 206 | # wild-card, or specify a user@domain.tld address. 207 | # 208 | #local_recipient_maps = unix:passwd.byname $alias_maps 209 | #local_recipient_maps = proxy:unix:passwd.byname $alias_maps 210 | #local_recipient_maps = 211 | 212 | # The unknown_local_recipient_reject_code specifies the SMTP server 213 | # response code when a recipient domain matches $mydestination or 214 | # ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty 215 | # and the recipient address or address local-part is not found. 216 | # 217 | # The default setting is 550 (reject mail) but it is safer to start 218 | # with 450 (try again later) until you are certain that your 219 | # local_recipient_maps settings are OK. 220 | # 221 | unknown_local_recipient_reject_code = 550 222 | 223 | # TRUST AND RELAY CONTROL 224 | 225 | # The mynetworks parameter specifies the list of "trusted" SMTP 226 | # clients that have more privileges than "strangers". 227 | # 228 | # In particular, "trusted" SMTP clients are allowed to relay mail 229 | # through Postfix. See the smtpd_recipient_restrictions parameter 230 | # in postconf(5). 231 | # 232 | # You can specify the list of "trusted" network addresses by hand 233 | # or you can let Postfix do it for you (which is the default). 234 | # 235 | # By default (mynetworks_style = subnet), Postfix "trusts" SMTP 236 | # clients in the same IP subnetworks as the local machine. 237 | # On Linux, this does works correctly only with interfaces specified 238 | # with the "ifconfig" command. 239 | # 240 | # Specify "mynetworks_style = class" when Postfix should "trust" SMTP 241 | # clients in the same IP class A/B/C networks as the local machine. 242 | # Don't do this with a dialup site - it would cause Postfix to "trust" 243 | # your entire provider's network. Instead, specify an explicit 244 | # mynetworks list by hand, as described below. 245 | # 246 | # Specify "mynetworks_style = host" when Postfix should "trust" 247 | # only the local machine. 248 | # 249 | #mynetworks_style = class 250 | #mynetworks_style = subnet 251 | #mynetworks_style = host 252 | 253 | # Alternatively, you can specify the mynetworks list by hand, in 254 | # which case Postfix ignores the mynetworks_style setting. 255 | # 256 | # Specify an explicit list of network/netmask patterns, where the 257 | # mask specifies the number of bits in the network part of a host 258 | # address. 259 | # 260 | # You can also specify the absolute pathname of a pattern file instead 261 | # of listing the patterns here. Specify type:table for table-based lookups 262 | # (the value on the table right-hand side is not used). 263 | # 264 | #mynetworks = 168.100.189.0/28, 127.0.0.0/8 265 | #mynetworks = $config_directory/mynetworks 266 | #mynetworks = hash:/etc/postfix/network_table 267 | 268 | # The relay_domains parameter restricts what destinations this system will 269 | # relay mail to. See the smtpd_recipient_restrictions description in 270 | # postconf(5) for detailed information. 271 | # 272 | # By default, Postfix relays mail 273 | # - from "trusted" clients (IP address matches $mynetworks) to any destination, 274 | # - from "untrusted" clients to destinations that match $relay_domains or 275 | # subdomains thereof, except addresses with sender-specified routing. 276 | # The default relay_domains value is $mydestination. 277 | # 278 | # In addition to the above, the Postfix SMTP server by default accepts mail 279 | # that Postfix is final destination for: 280 | # - destinations that match $inet_interfaces or $proxy_interfaces, 281 | # - destinations that match $mydestination 282 | # - destinations that match $virtual_alias_domains, 283 | # - destinations that match $virtual_mailbox_domains. 284 | # These destinations do not need to be listed in $relay_domains. 285 | # 286 | # Specify a list of hosts or domains, /file/name patterns or type:name 287 | # lookup tables, separated by commas and/or whitespace. Continue 288 | # long lines by starting the next line with whitespace. A file name 289 | # is replaced by its contents; a type:name table is matched when a 290 | # (parent) domain appears as lookup key. 291 | # 292 | # NOTE: Postfix will not automatically forward mail for domains that 293 | # list this system as their primary or backup MX host. See the 294 | # permit_mx_backup restriction description in postconf(5). 295 | # 296 | #relay_domains = $mydestination 297 | 298 | # INTERNET OR INTRANET 299 | 300 | # The relayhost parameter specifies the default host to send mail to 301 | # when no entry is matched in the optional transport(5) table. When 302 | # no relayhost is given, mail is routed directly to the destination. 303 | # 304 | # On an intranet, specify the organizational domain name. If your 305 | # internal DNS uses no MX records, specify the name of the intranet 306 | # gateway host instead. 307 | # 308 | # In the case of SMTP, specify a domain, host, host:port, [host]:port, 309 | # [address] or [address]:port; the form [host] turns off MX lookups. 310 | # 311 | # If you're connected via UUCP, see also the default_transport parameter. 312 | # 313 | #relayhost = $mydomain 314 | #relayhost = [gateway.my.domain] 315 | #relayhost = [mailserver.isp.tld] 316 | #relayhost = uucphost 317 | #relayhost = [an.ip.add.ress] 318 | relayhost = {{ smtp_relay_host|default('') }} 319 | 320 | # REJECTING UNKNOWN RELAY USERS 321 | # 322 | # The relay_recipient_maps parameter specifies optional lookup tables 323 | # with all addresses in the domains that match $relay_domains. 324 | # 325 | # If this parameter is defined, then the SMTP server will reject 326 | # mail for unknown relay users. This feature is off by default. 327 | # 328 | # The right-hand side of the lookup tables is conveniently ignored. 329 | # In the left-hand side, specify an @domain.tld wild-card, or specify 330 | # a user@domain.tld address. 331 | # 332 | #relay_recipient_maps = hash:/etc/postfix/relay_recipients 333 | 334 | # INPUT RATE CONTROL 335 | # 336 | # The in_flow_delay configuration parameter implements mail input 337 | # flow control. This feature is turned on by default, although it 338 | # still needs further development (it's disabled on SCO UNIX due 339 | # to an SCO bug). 340 | # 341 | # A Postfix process will pause for $in_flow_delay seconds before 342 | # accepting a new message, when the message arrival rate exceeds the 343 | # message delivery rate. With the default 100 SMTP server process 344 | # limit, this limits the mail inflow to 100 messages a second more 345 | # than the number of messages delivered per second. 346 | # 347 | # Specify 0 to disable the feature. Valid delays are 0..10. 348 | # 349 | #in_flow_delay = 1s 350 | 351 | # ADDRESS REWRITING 352 | # 353 | # The ADDRESS_REWRITING_README document gives information about 354 | # address masquerading or other forms of address rewriting including 355 | # username->Firstname.Lastname mapping. 356 | 357 | # ADDRESS REDIRECTION (VIRTUAL DOMAIN) 358 | # 359 | # The VIRTUAL_README document gives information about the many forms 360 | # of domain hosting that Postfix supports. 361 | 362 | # "USER HAS MOVED" BOUNCE MESSAGES 363 | # 364 | # See the discussion in the ADDRESS_REWRITING_README document. 365 | 366 | # TRANSPORT MAP 367 | # 368 | # See the discussion in the ADDRESS_REWRITING_README document. 369 | 370 | # ALIAS DATABASE 371 | # 372 | # The alias_maps parameter specifies the list of alias databases used 373 | # by the local delivery agent. The default list is system dependent. 374 | # 375 | # On systems with NIS, the default is to search the local alias 376 | # database, then the NIS alias database. See aliases(5) for syntax 377 | # details. 378 | # 379 | # If you change the alias database, run "postalias /etc/aliases" (or 380 | # wherever your system stores the mail alias file), or simply run 381 | # "newaliases" to build the necessary DBM or DB file. 382 | # 383 | # It will take a minute or so before changes become visible. Use 384 | # "postfix reload" to eliminate the delay. 385 | # 386 | #alias_maps = dbm:/etc/aliases 387 | alias_maps = hash:/etc/aliases 388 | #alias_maps = hash:/etc/aliases, nis:mail.aliases 389 | #alias_maps = netinfo:/aliases 390 | 391 | # The alias_database parameter specifies the alias database(s) that 392 | # are built with "newaliases" or "sendmail -bi". This is a separate 393 | # configuration parameter, because alias_maps (see above) may specify 394 | # tables that are not necessarily all under control by Postfix. 395 | # 396 | #alias_database = dbm:/etc/aliases 397 | #alias_database = dbm:/etc/mail/aliases 398 | alias_database = hash:/etc/aliases 399 | #alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases 400 | 401 | # ADDRESS EXTENSIONS (e.g., user+foo) 402 | # 403 | # The recipient_delimiter parameter specifies the separator between 404 | # user names and address extensions (user+foo). See canonical(5), 405 | # local(8), relocated(5) and virtual(5) for the effects this has on 406 | # aliases, canonical, virtual, relocated and .forward file lookups. 407 | # Basically, the software tries user+foo and .forward+foo before 408 | # trying user and .forward. 409 | # 410 | #recipient_delimiter = + 411 | 412 | # DELIVERY TO MAILBOX 413 | # 414 | # The home_mailbox parameter specifies the optional pathname of a 415 | # mailbox file relative to a user's home directory. The default 416 | # mailbox file is /var/spool/mail/user or /var/mail/user. Specify 417 | # "Maildir/" for qmail-style delivery (the / is required). 418 | # 419 | #home_mailbox = Mailbox 420 | #home_mailbox = Maildir/ 421 | 422 | # The mail_spool_directory parameter specifies the directory where 423 | # UNIX-style mailboxes are kept. The default setting depends on the 424 | # system type. 425 | # 426 | #mail_spool_directory = /var/mail 427 | #mail_spool_directory = /var/spool/mail 428 | 429 | # The mailbox_command parameter specifies the optional external 430 | # command to use instead of mailbox delivery. The command is run as 431 | # the recipient with proper HOME, SHELL and LOGNAME environment settings. 432 | # Exception: delivery for root is done as $default_user. 433 | # 434 | # Other environment variables of interest: USER (recipient username), 435 | # EXTENSION (address extension), DOMAIN (domain part of address), 436 | # and LOCAL (the address localpart). 437 | # 438 | # Unlike other Postfix configuration parameters, the mailbox_command 439 | # parameter is not subjected to $parameter substitutions. This is to 440 | # make it easier to specify shell syntax (see example below). 441 | # 442 | # Avoid shell meta characters because they will force Postfix to run 443 | # an expensive shell process. Procmail alone is expensive enough. 444 | # 445 | # IF YOU USE THIS TO DELIVER MAIL SYSTEM-WIDE, YOU MUST SET UP AN 446 | # ALIAS THAT FORWARDS MAIL FOR ROOT TO A REAL USER. 447 | # 448 | #mailbox_command = /some/where/procmail 449 | #mailbox_command = /some/where/procmail -a "$EXTENSION" 450 | 451 | # The mailbox_transport specifies the optional transport in master.cf 452 | # to use after processing aliases and .forward files. This parameter 453 | # has precedence over the mailbox_command, fallback_transport and 454 | # luser_relay parameters. 455 | # 456 | # Specify a string of the form transport:nexthop, where transport is 457 | # the name of a mail delivery transport defined in master.cf. The 458 | # :nexthop part is optional. For more details see the sample transport 459 | # configuration file. 460 | # 461 | # NOTE: if you use this feature for accounts not in the UNIX password 462 | # file, then you must update the "local_recipient_maps" setting in 463 | # the main.cf file, otherwise the SMTP server will reject mail for 464 | # non-UNIX accounts with "User unknown in local recipient table". 465 | # 466 | # Cyrus IMAP over LMTP. Specify ``lmtpunix cmd="lmtpd" 467 | # listen="/var/imap/socket/lmtp" prefork=0'' in cyrus.conf. 468 | #mailbox_transport = lmtp:unix:/var/lib/imap/socket/lmtp 469 | 470 | # If using the cyrus-imapd IMAP server deliver local mail to the IMAP 471 | # server using LMTP (Local Mail Transport Protocol), this is prefered 472 | # over the older cyrus deliver program by setting the 473 | # mailbox_transport as below: 474 | # 475 | # mailbox_transport = lmtp:unix:/var/lib/imap/socket/lmtp 476 | # 477 | # The efficiency of LMTP delivery for cyrus-imapd can be enhanced via 478 | # these settings. 479 | # 480 | # local_destination_recipient_limit = 300 481 | # local_destination_concurrency_limit = 5 482 | # 483 | # Of course you should adjust these settings as appropriate for the 484 | # capacity of the hardware you are using. The recipient limit setting 485 | # can be used to take advantage of the single instance message store 486 | # capability of Cyrus. The concurrency limit can be used to control 487 | # how many simultaneous LMTP sessions will be permitted to the Cyrus 488 | # message store. 489 | # 490 | # Cyrus IMAP via command line. Uncomment the "cyrus...pipe" and 491 | # subsequent line in master.cf. 492 | #mailbox_transport = cyrus 493 | 494 | # The fallback_transport specifies the optional transport in master.cf 495 | # to use for recipients that are not found in the UNIX passwd database. 496 | # This parameter has precedence over the luser_relay parameter. 497 | # 498 | # Specify a string of the form transport:nexthop, where transport is 499 | # the name of a mail delivery transport defined in master.cf. The 500 | # :nexthop part is optional. For more details see the sample transport 501 | # configuration file. 502 | # 503 | # NOTE: if you use this feature for accounts not in the UNIX password 504 | # file, then you must update the "local_recipient_maps" setting in 505 | # the main.cf file, otherwise the SMTP server will reject mail for 506 | # non-UNIX accounts with "User unknown in local recipient table". 507 | # 508 | #fallback_transport = lmtp:unix:/var/lib/imap/socket/lmtp 509 | #fallback_transport = 510 | 511 | # The luser_relay parameter specifies an optional destination address 512 | # for unknown recipients. By default, mail for unknown@$mydestination, 513 | # unknown@[$inet_interfaces] or unknown@[$proxy_interfaces] is returned 514 | # as undeliverable. 515 | # 516 | # The following expansions are done on luser_relay: $user (recipient 517 | # username), $shell (recipient shell), $home (recipient home directory), 518 | # $recipient (full recipient address), $extension (recipient address 519 | # extension), $domain (recipient domain), $local (entire recipient 520 | # localpart), $recipient_delimiter. Specify ${name?value} or 521 | # ${name:value} to expand value only when $name does (does not) exist. 522 | # 523 | # luser_relay works only for the default Postfix local delivery agent. 524 | # 525 | # NOTE: if you use this feature for accounts not in the UNIX password 526 | # file, then you must specify "local_recipient_maps =" (i.e. empty) in 527 | # the main.cf file, otherwise the SMTP server will reject mail for 528 | # non-UNIX accounts with "User unknown in local recipient table". 529 | # 530 | #luser_relay = $user@other.host 531 | #luser_relay = $local@other.host 532 | #luser_relay = admin+$local 533 | 534 | # JUNK MAIL CONTROLS 535 | # 536 | # The controls listed here are only a very small subset. The file 537 | # SMTPD_ACCESS_README provides an overview. 538 | 539 | # The header_checks parameter specifies an optional table with patterns 540 | # that each logical message header is matched against, including 541 | # headers that span multiple physical lines. 542 | # 543 | # By default, these patterns also apply to MIME headers and to the 544 | # headers of attached messages. With older Postfix versions, MIME and 545 | # attached message headers were treated as body text. 546 | # 547 | # For details, see "man header_checks". 548 | # 549 | #header_checks = regexp:/etc/postfix/header_checks 550 | 551 | # FAST ETRN SERVICE 552 | # 553 | # Postfix maintains per-destination logfiles with information about 554 | # deferred mail, so that mail can be flushed quickly with the SMTP 555 | # "ETRN domain.tld" command, or by executing "sendmail -qRdomain.tld". 556 | # See the ETRN_README document for a detailed description. 557 | # 558 | # The fast_flush_domains parameter controls what destinations are 559 | # eligible for this service. By default, they are all domains that 560 | # this server is willing to relay mail to. 561 | # 562 | #fast_flush_domains = $relay_domains 563 | 564 | # SHOW SOFTWARE VERSION OR NOT 565 | # 566 | # The smtpd_banner parameter specifies the text that follows the 220 567 | # code in the SMTP server's greeting banner. Some people like to see 568 | # the mail version advertised. By default, Postfix shows no version. 569 | # 570 | # You MUST specify $myhostname at the start of the text. That is an 571 | # RFC requirement. Postfix itself does not care. 572 | # 573 | #smtpd_banner = $myhostname ESMTP $mail_name 574 | #smtpd_banner = $myhostname ESMTP $mail_name ($mail_version) 575 | 576 | # PARALLEL DELIVERY TO THE SAME DESTINATION 577 | # 578 | # How many parallel deliveries to the same user or domain? With local 579 | # delivery, it does not make sense to do massively parallel delivery 580 | # to the same user, because mailbox updates must happen sequentially, 581 | # and expensive pipelines in .forward files can cause disasters when 582 | # too many are run at the same time. With SMTP deliveries, 10 583 | # simultaneous connections to the same domain could be sufficient to 584 | # raise eyebrows. 585 | # 586 | # Each message delivery transport has its XXX_destination_concurrency_limit 587 | # parameter. The default is $default_destination_concurrency_limit for 588 | # most delivery transports. For the local delivery agent the default is 2. 589 | 590 | #local_destination_concurrency_limit = 2 591 | #default_destination_concurrency_limit = 20 592 | 593 | # DEBUGGING CONTROL 594 | # 595 | # The debug_peer_level parameter specifies the increment in verbose 596 | # logging level when an SMTP client or server host name or address 597 | # matches a pattern in the debug_peer_list parameter. 598 | # 599 | debug_peer_level = 2 600 | 601 | # The debug_peer_list parameter specifies an optional list of domain 602 | # or network patterns, /file/name patterns or type:name tables. When 603 | # an SMTP client or server host name or address matches a pattern, 604 | # increase the verbose logging level by the amount specified in the 605 | # debug_peer_level parameter. 606 | # 607 | #debug_peer_list = 127.0.0.1 608 | #debug_peer_list = some.domain 609 | 610 | # The debugger_command specifies the external command that is executed 611 | # when a Postfix daemon program is run with the -D option. 612 | # 613 | # Use "command .. & sleep 5" so that the debugger can attach before 614 | # the process marches on. If you use an X-based debugger, be sure to 615 | # set up your XAUTHORITY environment variable before starting Postfix. 616 | # 617 | debugger_command = 618 | PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin 619 | ddd $daemon_directory/$process_name $process_id & sleep 5 620 | 621 | # If you can't use X, use this to capture the call stack when a 622 | # daemon crashes. The result is in a file in the configuration 623 | # directory, and is named after the process name and the process ID. 624 | # 625 | # debugger_command = 626 | # PATH=/bin:/usr/bin:/usr/local/bin; export PATH; (echo cont; 627 | # echo where) | gdb $daemon_directory/$process_name $process_id 2>&1 628 | # >$config_directory/$process_name.$process_id.log & sleep 5 629 | # 630 | # Another possibility is to run gdb under a detached screen session. 631 | # To attach to the screen sesssion, su root and run "screen -r 632 | # " where uniquely matches one of the detached 633 | # sessions (from "screen -list"). 634 | # 635 | # debugger_command = 636 | # PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH; screen 637 | # -dmS $process_name gdb $daemon_directory/$process_name 638 | # $process_id & sleep 1 639 | 640 | # INSTALL-TIME CONFIGURATION INFORMATION 641 | # 642 | # The following parameters are used when installing a new Postfix version. 643 | # 644 | # sendmail_path: The full pathname of the Postfix sendmail command. 645 | # This is the Sendmail-compatible mail posting interface. 646 | # 647 | sendmail_path = /usr/sbin/sendmail.postfix 648 | 649 | # newaliases_path: The full pathname of the Postfix newaliases command. 650 | # This is the Sendmail-compatible command to build alias databases. 651 | # 652 | newaliases_path = /usr/bin/newaliases.postfix 653 | 654 | # mailq_path: The full pathname of the Postfix mailq command. This 655 | # is the Sendmail-compatible mail queue listing command. 656 | # 657 | mailq_path = /usr/bin/mailq.postfix 658 | 659 | # setgid_group: The group for mail submission and queue management 660 | # commands. This must be a group name with a numerical group ID that 661 | # is not shared with other accounts, not even with the Postfix account. 662 | # 663 | setgid_group = postdrop 664 | 665 | # html_directory: The location of the Postfix HTML documentation. 666 | # 667 | html_directory = no 668 | 669 | # manpage_directory: The location of the Postfix on-line manual pages. 670 | # 671 | manpage_directory = /usr/share/man 672 | 673 | # sample_directory: The location of the Postfix sample configuration files. 674 | # This parameter is obsolete as of Postfix 2.1. 675 | # 676 | sample_directory = /usr/share/doc/postfix-2.10.1/samples 677 | 678 | # readme_directory: The location of the Postfix README files. 679 | # 680 | readme_directory = /usr/share/doc/postfix-2.10.1/README_FILES 681 | --------------------------------------------------------------------------------