├── LICENSE ├── README.md ├── ansible.cfg ├── cloudformation └── wordpress.json ├── group_vars └── all.yml ├── hosts ├── local_wordpress └── ansible │ ├── hosts │ ├── roles │ ├── common │ │ └── tasks │ │ │ └── main.yml │ ├── nginx │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ │ └── default.conf │ ├── php-fpm │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ │ └── wordpress.conf │ └── wordpress │ │ ├── tasks │ │ └── main.yml │ │ └── templates │ │ └── wp-config.php │ └── wordpress.yml ├── roles └── provision-ec2 │ └── tasks │ └── main.yml └── stack-wordpress.yml /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Allan Denot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Autoscaling Wordpress with Ansible and CloudFormation 2 | 3 | [http://allandenot.com/devops/2015/12/24/autoscaling-wordpress-with-ansible-and-cloudformation.html](http://allandenot.com/devops/2015/12/24/autoscaling-wordpress-with-ansible-and-cloudformation.html) 4 | 5 | ## The Goal 6 | 7 | - A WordPress site running in EC2 hosts and an RDS database. 8 | - EC2 hosts inside an autoscaling group 9 | - Easily deploy configuration changes 10 | 11 | ## How it works 12 | 13 | User runs Ansible playbook: 14 | 15 | ![blog-ansible-autoscaling-wordpress.png](https://allandenot.com/images/blog-ansible-autoscaling-wordpress.png) 16 | 17 | Notes: 18 | 19 | - Baking server exists as a template for the AMI that will be used in CloudFormation. 20 | - Baking server is an Ubuntu with Ansible and local playbooks are copied into it. 21 | - We choose to use baked AMIs to speedup the bootup process when autoscaling is creating new instances. Upon boot, these instances run their local Ansible playbooks and the webserver is configured. 22 | 23 | ## How can I use it 24 | 25 | Fork my repository: [https://github.com/adenot/blog-ansible-autoscaling-wordpress](https://github.com/adenot/blog-ansible-autoscaling-wordpress) 26 | 27 | Edit file: 28 | 29 | group_vars/all 30 | 31 | Set variables from your EC2 account. 32 | 33 | Make sure to create the required AWS resources for the baking server: 34 | 35 | - EC2 Keypair 36 | - VPC, Subnet (if not exists) 37 | - Security group - Port 22/tcp must be open 38 | 39 | Running it: 40 | 41 | ansible-playbook -vv stack-wordpress.yml 42 | 43 | ### What is happening? 44 | 45 | First time it runs: 46 | 47 | - a baking server is created 48 | - local playbooks are copied into baking server (from local_wordpress/ansible) 49 | - an AMI is created from the baking server 50 | - a CloudFormation stack is created 51 | 52 | Second time it runs: 53 | 54 | - local playbooks are copied into baking server (from local_wordpress/ansible) 55 | - an AMI is created from the baking server 56 | - CloudFormation stack is updated with new AMI 57 | - Autoscaling group replaces EC2 instances with new ones using new AMI, one by one 58 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | remote_user=ubuntu 3 | host_key_checking=False 4 | hostfile=hosts 5 | 6 | [ssh_connection] 7 | #control_path=%(directory)s/%%h-%%r 8 | ssh_args=-o ControlMaster=auto -o ControlPersist=60s -o ForwardAgent=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null 9 | -------------------------------------------------------------------------------- /cloudformation/wordpress.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | 4 | "Description" : "Autoscaling Wordpress with Ansible and CF by allandenot.com.", 5 | 6 | "Parameters" : { 7 | 8 | "KeyName": { 9 | "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances", 10 | "Type": "AWS::EC2::KeyPair::KeyName", 11 | "ConstraintDescription" : "must be the name of an existing EC2 KeyPair." 12 | }, 13 | 14 | "InstanceType" : { 15 | "Description" : "WebServer EC2 instance type", 16 | "Type" : "String", 17 | "Default" : "m3.large", 18 | "AllowedValues" : [ "t2.micro", "t2.small", "t2.medium", "m3.medium", "m3.large", "m3.xlarge", "m3.2xlarge", "c3.large", "c3.xlarge", "c3.2xlarge", "c3.4xlarge", "c3.8xlarge", "c4.large", "c4.xlarge", "c4.2xlarge", "c4.4xlarge", "c4.8xlarge", "r3.large", "r3.xlarge", "r3.2xlarge", "r3.4xlarge", "r3.8xlarge", "i2.xlarge", "i2.2xlarge", "i2.4xlarge", "i2.8xlarge", "d2.xlarge", "d2.2xlarge", "d2.4xlarge", "d2.8xlarge", "hi1.4xlarge", "hs1.8xlarge", "cr1.8xlarge", "cc2.8xlarge", "cg1.4xlarge"], 19 | "ConstraintDescription" : "must be a valid EC2 instance type." 20 | }, 21 | 22 | "DBClass" : { 23 | "Description" : "Database instance class", 24 | "Type" : "String", 25 | "Default" : "db.m3.medium", 26 | "AllowedValues" : [ "db.t2.micro", "db.t1.micro", "db.m1.small", "db.m1.medium", "db.m1.large", "db.m1.xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.m3.medium", "db.m3.large", "db.m3.xlarge", "db.m3.2xlarge", "db.r3.large", "db.r3.xlarge", "db.r3.2xlarge", "db.r3.4xlarge", "db.r3.8xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.cr1.8xlarge"], 27 | "ConstraintDescription" : "must select a valid database instance type." 28 | }, 29 | 30 | "SSHLocation": { 31 | "Description": "The IP address range that can be used to SSH to the EC2 instances", 32 | "Type": "String", 33 | "MinLength": "9", 34 | "MaxLength": "18", 35 | "Default": "0.0.0.0/0", 36 | "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", 37 | "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." 38 | }, 39 | 40 | "DBPassword" : { 41 | "NoEcho": "true", 42 | "Description" : "The WordPress database admin account password", 43 | "Type": "String", 44 | "MinLength": "8", 45 | "MaxLength": "41", 46 | "AllowedPattern" : "[a-zA-Z0-9]*", 47 | "ConstraintDescription" : "must contain only alphanumeric characters." 48 | }, 49 | 50 | "DBAllocatedStorage" : { 51 | "Default": "5", 52 | "Description" : "The size of the database (Gb)", 53 | "Type": "Number", 54 | "MinValue": "5", 55 | "MaxValue": "1024", 56 | "ConstraintDescription" : "must be between 5 and 1024Gb." 57 | }, 58 | 59 | "AMIId" : { 60 | "Description" : "AMI ID to use in the Launch configuration", 61 | "Type": "AWS::EC2::Image::Id", 62 | "ConstraintDescription" : "must be a valid AMI ID." 63 | }, 64 | 65 | "VPCId" : { 66 | "Description" : "VPC ID for Security Groups", 67 | "Type": "AWS::EC2::VPC::Id", 68 | "ConstraintDescription" : "must be a valid VPC ID." 69 | }, 70 | 71 | "SubnetIds" : { 72 | "Description" : "Subnets to add ELB", 73 | "Type" : "List", 74 | "ConstraintDescription" : "must be a valid list of subnet ids." 75 | } 76 | 77 | }, 78 | 79 | "Resources" : { 80 | "ELBSecurityGroup" : { 81 | "Type" : "AWS::EC2::SecurityGroup", 82 | "Properties" : { 83 | "GroupDescription" : "Enable HTTP access via port 80", 84 | "SecurityGroupIngress" : [ 85 | {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0"} 86 | ], 87 | "VpcId": { "Ref": "VPCId" } 88 | } 89 | }, 90 | 91 | "ElasticLoadBalancer" : { 92 | "Type" : "AWS::ElasticLoadBalancing::LoadBalancer", 93 | "Properties" : { 94 | "CrossZone" : "true", 95 | "SecurityGroups" : [ { "Fn::GetAtt": [ "ELBSecurityGroup", "GroupId" ] } ], 96 | "Subnets" : { "Ref": "SubnetIds" }, 97 | "LBCookieStickinessPolicy" : [ { 98 | "PolicyName" : "CookieBasedPolicy", 99 | "CookieExpirationPeriod" : "30" 100 | } ], 101 | "Listeners" : [ { 102 | "LoadBalancerPort" : "80", 103 | "InstancePort" : "80", 104 | "Protocol" : "HTTP", 105 | "PolicyNames" : [ "CookieBasedPolicy" ] 106 | } ], 107 | "HealthCheck" : { 108 | "Target" : "HTTP:80/wp-admin/install.php", 109 | "HealthyThreshold" : "2", 110 | "UnhealthyThreshold" : "5", 111 | "Interval" : "10", 112 | "Timeout" : "5" 113 | } 114 | } 115 | }, 116 | 117 | "WebServerSecurityGroup" : { 118 | "Type" : "AWS::EC2::SecurityGroup", 119 | "Properties" : { 120 | "GroupDescription" : "Enable HTTP access via port 80 locked down to the load balancer + SSH access", 121 | "VpcId": { "Ref": "VPCId" }, 122 | "SecurityGroupIngress" : [{ 123 | "IpProtocol" : "tcp", 124 | "FromPort" : "80", 125 | "ToPort" : "80", 126 | "SourceSecurityGroupId" : { 127 | "Ref" : "ELBSecurityGroup" 128 | } 129 | },{ 130 | "IpProtocol" : "tcp", 131 | "FromPort" : "22", 132 | "ToPort" : "22", 133 | "CidrIp" : { "Ref" : "SSHLocation"} 134 | }] 135 | } 136 | }, 137 | 138 | "WebServerGroup" : { 139 | "Type" : "AWS::AutoScaling::AutoScalingGroup", 140 | "Properties" : { 141 | "AvailabilityZones" : { "Fn::GetAZs" : "" }, 142 | "VPCZoneIdentifier": { "Ref": "SubnetIds" }, 143 | "LaunchConfigurationName" : { "Ref" : "LaunchConfig" }, 144 | "MinSize" : "1", 145 | "MaxSize" : "4", 146 | "DesiredCapacity" : "2", 147 | "LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ] 148 | }, 149 | "UpdatePolicy" : { 150 | "AutoScalingRollingUpdate" : { 151 | "MinInstancesInService" : "1", 152 | "PauseTime" : "PT5M" 153 | } 154 | } 155 | }, 156 | 157 | "LaunchConfig": { 158 | "Type" : "AWS::AutoScaling::LaunchConfiguration", 159 | "Properties": { 160 | "ImageId" : { "Ref" : "AMIId" }, 161 | "InstanceType" : { "Ref" : "InstanceType" }, 162 | "SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ], 163 | "KeyName" : { "Ref" : "KeyName" }, 164 | "UserData" : { "Fn::Base64" : { 165 | "Fn::Join" : [ 166 | "", 167 | [ 168 | "#!/bin/bash -v\n", 169 | "apt-get -y update\n", 170 | "apt-get -y install software-properties-common awscli\n", 171 | "apt-add-repository -y ppa:ansible/ansible\n", 172 | "apt-get -y update\n", 173 | "apt-get -y install ansible\n", 174 | "\n", 175 | "mkdir -p /etc/ansible/facts.d\n", 176 | "echo '[database]' >> /etc/ansible/facts.d/cloudformation.fact\n", 177 | "echo 'hostname=", { "Fn::GetAtt": [ "DBInstance", "Endpoint.Address" ] }, "' >> /etc/ansible/facts.d/cloudformation.fact\n", 178 | "echo 'password=", { "Ref" : "DBPassword" }, "' >> /etc/ansible/facts.d/cloudformation.fact\n", 179 | "echo '[elb]' >> /etc/ansible/facts.d/cloudformation.fact\n", 180 | "echo 'hostname=", { "Fn::GetAtt": [ "ElasticLoadBalancer", "DNSName" ] }, "' >> /etc/ansible/facts.d/cloudformation.fact\n", 181 | "\n", 182 | "cd /etc/ansible\n", 183 | "ansible-playbook wordpress.yml\n" 184 | ] 185 | ] 186 | } 187 | } 188 | } 189 | }, 190 | 191 | "DBEC2SecurityGroup": { 192 | "Type": "AWS::EC2::SecurityGroup", 193 | "Properties" : { 194 | "GroupDescription": "Open database for access", 195 | "VpcId": { "Ref": "VPCId" }, 196 | "SecurityGroupIngress" : [{ 197 | "IpProtocol" : "tcp", 198 | "FromPort" : "3306", 199 | "ToPort" : "3306", 200 | "SourceSecurityGroupId" : { "Ref" : "WebServerSecurityGroup" } 201 | }] 202 | } 203 | }, 204 | 205 | "DBSubnetGroup": { 206 | "Type" : "AWS::RDS::DBSubnetGroup", 207 | "Properties" : { 208 | "DBSubnetGroupDescription": "Subnet Group for Wordpress database", 209 | "SubnetIds" : { "Ref": "SubnetIds" } 210 | } 211 | }, 212 | 213 | "DBInstance" : { 214 | "Type": "AWS::RDS::DBInstance", 215 | "Properties": { 216 | "DBName" : "wordpress", 217 | "Engine" : "MySQL", 218 | "MasterUsername" : "root", 219 | "MasterUserPassword": { "Ref" : "DBPassword" }, 220 | "DBInstanceClass" : { "Ref" : "DBClass" }, 221 | "AllocatedStorage" : { "Ref" : "DBAllocatedStorage" }, 222 | "VPCSecurityGroups" : [ { "Fn::GetAtt": [ "DBEC2SecurityGroup", "GroupId" ] } ], 223 | "DBSubnetGroupName" : { "Ref" : "DBSubnetGroup" } 224 | } 225 | } 226 | }, 227 | 228 | "Outputs" : { 229 | "WebsiteURL" : { 230 | "Value" : { "Fn::Join" : ["", ["http://", { "Fn::GetAtt" : [ "ElasticLoadBalancer", "DNSName" ]} ]]}, 231 | "Description" : "WordPress Website" 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /group_vars/all.yml: -------------------------------------------------------------------------------- 1 | --- 2 | region: "ap-southeast-1" 3 | 4 | # Baking Server Variables 5 | 6 | ec2_keypair: "Allan-DEMO1" 7 | ansible_ssh_private_key_file: ~/{{ ec2_keypair }}.pem # File location for accessing host 8 | 9 | ec2_security_group: "sg-f7933792" 10 | ec2_instance_type: "t2.small" 11 | ec2_image: "ami-ca381398" # Ubuntu 12 | 13 | ec2_region: "{{ region }}" 14 | ec2_subnet_ids: ['subnet-ff934c9a','subnet-9b27d4ec'] 15 | ec2_tag_Name: "wordpress-bake-1" # Tag to identify the host 16 | # Change name when new baking need to be created 17 | 18 | ec2_tag_Type: "wordpress-bake" # Also the Ansible group name that host will be added. 19 | ec2_volume_size: 8 20 | 21 | # CloudFormation Stack Variables 22 | 23 | cf_keypair: "{{ ec2_keypair }}" 24 | cf_instance_type: "c3.large" 25 | cf_subnet_ids: "{{ ec2_subnet_ids }}" 26 | 27 | vpc_id: "vpc-e3b34886" 28 | 29 | region: "ap-southeast-1" 30 | 31 | version: "{{ lookup('pipe','date +%s') }}" # Using unixtime as version 32 | 33 | stack_name: "wordpress-staging-1" # Name of the stack, keep the same to prevent 34 | # creating new ones if same name exists 35 | # Use different names to set environments 36 | 37 | rds_class: "db.t2.micro" 38 | rds_password: "password123" 39 | rds_storage_size: 5 40 | -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | [localhost] 2 | 127.0.0.1 3 | -------------------------------------------------------------------------------- /local_wordpress/ansible/hosts: -------------------------------------------------------------------------------- 1 | [local] 2 | 127.0.0.1 ansible_connection=local 3 | -------------------------------------------------------------------------------- /local_wordpress/ansible/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Update apt cache 2 | sudo: yes 3 | apt: update_cache=yes 4 | 5 | - name: Install various packages 6 | apt: 7 | pkg: "{{ item }}" 8 | state: latest 9 | sudo: yes 10 | with_items: 11 | - git 12 | - vim 13 | - ntpdate 14 | 15 | - name: NTP update using cron 16 | sudo: yes 17 | cron: name="ntpdate" special_time=hourly job="/usr/sbin/ntpdate pool.ntp.org ntp.ubuntu.com" 18 | -------------------------------------------------------------------------------- /local_wordpress/ansible/roles/nginx/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart nginx 3 | service: name=nginx state=restarted enabled=yes 4 | -------------------------------------------------------------------------------- /local_wordpress/ansible/roles/nginx/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install nginx 3 | apt: name=nginx state=present 4 | 5 | - name: Copy nginx configuration for wordpress 6 | template: src=default.conf dest=/etc/nginx/sites-available/default 7 | notify: restart nginx 8 | 9 | - name: Start nginx Service 10 | service: name=nginx state=started enabled=yes 11 | -------------------------------------------------------------------------------- /local_wordpress/ansible/roles/nginx/templates/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | server_name {{ server_hostname }}; 4 | root /srv/wordpress/ ; 5 | 6 | client_max_body_size 64M; 7 | 8 | # Deny access to any files with a .php extension in the uploads directory 9 | location ~* /(?:uploads|files)/.*\.php$ { 10 | deny all; 11 | } 12 | 13 | location / { 14 | index index.php index.html index.htm; 15 | try_files $uri $uri/ /index.php?$args; 16 | } 17 | 18 | location ~* \.(gif|jpg|jpeg|png|css|js)$ { 19 | expires max; 20 | } 21 | 22 | location ~ \.php$ { 23 | try_files $uri =404; 24 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 25 | fastcgi_index index.php; 26 | fastcgi_pass unix:/var/run/php5-fpm-wordpress.sock; 27 | fastcgi_param SCRIPT_FILENAME 28 | $document_root$fastcgi_script_name; 29 | include fastcgi_params; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /local_wordpress/ansible/roles/php-fpm/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart php-fpm 3 | service: name=php5-fpm state=restarted 4 | -------------------------------------------------------------------------------- /local_wordpress/ansible/roles/php-fpm/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install php-fpm and deps 3 | apt: name={{ item }} state=present 4 | with_items: 5 | - php5 6 | - php5-fpm 7 | - php5-enchant 8 | - php5-mysql 9 | - python-mysqldb 10 | 11 | - name: Disable default pool 12 | command: mv /etc/php5/fpm/pool.d/www.conf /etc/php5/fpm/pool.d/www.disabled creates=/etc/php5/fpm/pool.d/www.disabled 13 | notify: restart php-fpm 14 | 15 | - name: Copy php-fpm configuration 16 | template: src=wordpress.conf dest=/etc/php5/fpm/pool.d/ 17 | notify: restart php-fpm 18 | -------------------------------------------------------------------------------- /local_wordpress/ansible/roles/php-fpm/templates/wordpress.conf: -------------------------------------------------------------------------------- 1 | [wordpress] 2 | listen = /var/run/php5-fpm-wordpress.sock 3 | listen.owner = www-data 4 | listen.group = www-data 5 | listen.mode = 0660 6 | user = wordpress 7 | group = wordpress 8 | pm = dynamic 9 | pm.max_children = 10 10 | pm.start_servers = 1 11 | pm.min_spare_servers = 1 12 | pm.max_spare_servers = 3 13 | pm.max_requests = 500 14 | chdir = /srv/wordpress/ 15 | php_admin_value[open_basedir] = /srv/wordpress/:/tmp 16 | -------------------------------------------------------------------------------- /local_wordpress/ansible/roles/wordpress/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Download WordPress 3 | get_url: 4 | url: "http://wordpress.org/wordpress-{{ wp_version }}.tar.gz" 5 | dest: "/srv/wordpress-{{ wp_version }}.tar.gz" 6 | sha256sum: "{{ wp_sha256sum }}" 7 | 8 | - name: Extract archive 9 | command: chdir=/srv/ /bin/tar xvf wordpress-{{ wp_version }}.tar.gz creates=/srv/wordpress 10 | 11 | - name: Add group "wordpress" 12 | group: name=wordpress 13 | 14 | - name: Add user "wordpress" 15 | user: name=wordpress group=wordpress home=/srv/wordpress/ 16 | 17 | - name: Fetch random salts for WordPress config 18 | local_action: command curl https://api.wordpress.org/secret-key/1.1/salt/ 19 | register: "wp_salt" 20 | sudo: no 21 | changed_when: false 22 | 23 | - name: Create WordPress database 24 | mysql_db: 25 | login_host: "{{ wp_db_host }}" 26 | login_user: "{{ wp_db_user }}" 27 | login_password: "{{ wp_db_password }}" 28 | login_port: "{{ mysql_port }}" 29 | name: "{{ wp_db_name }}" 30 | state: present 31 | 32 | - name: Copy WordPress config file 33 | template: src=wp-config.php dest=/srv/wordpress/ 34 | 35 | - name: Change ownership of WordPress installation 36 | file: path=/srv/wordpress/ owner=wordpress group=wordpress state=directory recurse=yes 37 | 38 | - name: Start php-fpm Service 39 | service: name=php5-fpm state=started enabled=yes 40 | -------------------------------------------------------------------------------- /local_wordpress/ansible/roles/wordpress/templates/wp-config.php: -------------------------------------------------------------------------------- 1 |