├── inventory └── hosts ├── aws.yml ├── .github └── workflows │ └── ansible-lint.yml ├── roles ├── ec2sg │ ├── defaults │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── ec2key │ ├── defaults │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── ec2instance │ ├── defaults │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── elb │ ├── tasks │ │ └── main.yml │ └── defaults │ │ └── main.yml ├── rds │ ├── defaults │ │ └── main.yml │ └── tasks │ │ └── main.yml └── vpc │ ├── defaults │ └── main.yml │ └── tasks │ └── main.yml ├── filter_plugins ├── get_ec2_info.py └── get_public_subnets_ids.py ├── lookup_plugins └── get_sg_id_from_name.py ├── LICENSE ├── README.md └── secret_vars └── secret.yml /inventory/hosts: -------------------------------------------------------------------------------- 1 | [localhost] 2 | 127.0.0.1 3 | 4 | [webserver] 5 | -------------------------------------------------------------------------------- /aws.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | connection: local 4 | gather_facts: no 5 | roles: 6 | - vpc 7 | - ec2sg 8 | - ec2key 9 | - ec2instance 10 | - elb 11 | - rds 12 | -------------------------------------------------------------------------------- /.github/workflows/ansible-lint.yml: -------------------------------------------------------------------------------- 1 | name: Ansible Lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Lint Ansible Roles 11 | uses: ansible/ansible-lint-action@master 12 | with: 13 | targets: "roles" 14 | args: "" 15 | -------------------------------------------------------------------------------- /roles/ec2sg/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables that can provide as extra vars 3 | VPC_NAME: test 4 | VPC_REGION: us-east-1 # N.Virginia 5 | ENVIRONMENT: "test" 6 | EC2_SECURITY_GROUPS: 7 | - name: test-sg 8 | description: "This sg is just a test sg" 9 | rules: 10 | - proto: tcp 11 | from_port: 22 12 | to_port: 22 13 | cidr_ip: 0.0.0.0/0 14 | 15 | # Use inside the tasks 16 | vpc_name: "{{ VPC_NAME }}" 17 | vpc_region: "{{ VPC_REGION }}" 18 | Environment: "{{ ENVIRONMENT }}" 19 | ec2_security_groups: "{{ EC2_SECURITY_GROUPS }}" -------------------------------------------------------------------------------- /filter_plugins/get_ec2_info.py: -------------------------------------------------------------------------------- 1 | from jinja2.utils import soft_unicode 2 | 3 | ''' 4 | USAGE: 5 | - debug: 6 | msg: "{{ ec2.results | get_ec2_info('id') }}" 7 | 8 | Some useful ec2 keys: 9 | id 10 | dns_name 11 | public_ip 12 | private_ip 13 | ''' 14 | 15 | class FilterModule(object): 16 | def filters(self): 17 | return { 18 | 'get_ec2_info': get_ec2_info, 19 | } 20 | 21 | def get_ec2_info(list, ec2_key): 22 | ec2_info = [] 23 | for item in list: 24 | for ec2 in item['instances']: 25 | ec2_info.append(ec2[ec2_key]) 26 | return ec2_info 27 | -------------------------------------------------------------------------------- /filter_plugins/get_public_subnets_ids.py: -------------------------------------------------------------------------------- 1 | from jinja2.utils import soft_unicode 2 | 3 | ''' 4 | USAGE: 5 | - debug: 6 | msg: "{{ vpc.subnets | get_public_subnets_ids('Type','Public') }}" 7 | ''' 8 | 9 | class FilterModule(object): 10 | def filters(self): 11 | return { 12 | 'get_public_subnets_ids': get_public_subnets_ids, 13 | } 14 | 15 | def get_public_subnets_ids(list, tag_key, tag_value): 16 | subnets_ids = [] 17 | for item in list: 18 | for key, value in item['resource_tags'].iteritems(): 19 | if key == tag_key and value == tag_value: 20 | subnets_ids.append(item['id']) 21 | 22 | return subnets_ids -------------------------------------------------------------------------------- /roles/ec2sg/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Creating an EC2 Security Group inside the Mentioned VPC 3 | ec2_group: 4 | name: "{{ item.name }}" 5 | description: "{{ item.description }}" 6 | region: "{{ vpc_region }}" 7 | vpc_id: "{{ VPC_ID }}" # Came from ENV specific secret file 8 | state: present 9 | rules: "{{ item.rules }}" 10 | with_items: "{{ ec2_security_groups }}" 11 | register: aws_sg 12 | 13 | - name: Tag the Security Groups with Distinguished Name 14 | ec2_tag: 15 | resource: "{{ item.group_id }}" 16 | region: "{{ vpc_region }}" 17 | state: present 18 | tags: 19 | Name: "{{ item.item.name }}" 20 | Environment: "{{ Environment }}" 21 | with_items: "{{ aws_sg.results }}" -------------------------------------------------------------------------------- /lookup_plugins/get_sg_id_from_name.py: -------------------------------------------------------------------------------- 1 | ''' 2 | USAGE: 3 | - debug: 4 | msg: "{{ lookup('get_sg_id_from_name', (vpc_region, rds_sg_name)) }}" 5 | ''' 6 | 7 | from ansible.errors import * 8 | from ansible.plugins.lookup import LookupBase 9 | 10 | try: 11 | import boto 12 | import boto.ec2 13 | except ImportError: 14 | raise AnsibleError("get_sg_id_from_name lookup cannot be run without boto installed") 15 | 16 | class LookupModule(LookupBase): 17 | 18 | def run(self, terms, variables=None, **kwargs): 19 | region = terms[0][0] 20 | sg_name = terms[0][1] 21 | if isinstance(sg_name, basestring): 22 | sg_name = sg_name 23 | ec2_conn = boto.ec2.connect_to_region(region) 24 | sg = ec2_conn.get_all_security_groups(filters={'group_name': sg_name})[0] 25 | return [sg.id] -------------------------------------------------------------------------------- /roles/ec2key/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables that can provide as extra vars 3 | VPC_REGION: us-east-1 # N.Virginia 4 | GITHUB_USERNAME: no 5 | EC2_KEY_NAME: "my-default-key" 6 | LOCAL_USER_SSH_KEY: yes 7 | 8 | # If you don't want to use any of this service, just set it's value to 'none' 9 | # For example: 10 | # If you want to use github ssh key as your key on the EC2, just mentioned your username 11 | # 12 | # github_username: arbabnazar 13 | # 14 | # and set the other variable as none or if you want to use local key over EC2 then do like this 15 | # 16 | # local_ssh_key: yes 17 | # 18 | # and 19 | # 20 | # github_username: no 21 | 22 | # Use inside the tasks 23 | vpc_region: "{{ VPC_REGION }}" 24 | github_username: "{{ GITHUB_USERNAME }}" 25 | local_user_ssh_key: "{{ LOCAL_USER_SSH_KEY }}" 26 | ec2_key_name: "{{ EC2_KEY_NAME }}" -------------------------------------------------------------------------------- /roles/ec2instance/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables that can provide as extra vars 3 | VPC_NAME: test 4 | VPC_REGION: us-east-1 # N.Virginia 5 | EC2_INSTANCE_TYPE: t2.micro 6 | EC2_KEY_NAME: "my-default-key" 7 | EC2_SECURITY_GROUP_NAME: "test" 8 | EC2_COUNT: 1 9 | EC2_VOLUME_SIZE: 8 10 | EC2_SUBNET_ID: [] 11 | # Example of EC2_SUBNET_ID 12 | # EC2_SUBNET_ID: 13 | # - "subnet-0c3e0b7b" 14 | # - "subnet-bf672ae6" 15 | AMI_NAME: "ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*" 16 | AMI_OWNER: "099720109477" 17 | 18 | # Tags 19 | ENVIRONMENT: test 20 | SERVER_ROLE: test 21 | 22 | # Use inside the tasks 23 | vpc_name: "{{ VPC_NAME }}" 24 | vpc_region: "{{ VPC_REGION }}" 25 | ec2_instance_type: "{{ EC2_INSTANCE_TYPE }}" 26 | ec2_key_name: "{{ EC2_KEY_NAME }}" 27 | ec2_security_group_name: "{{ EC2_SECURITY_GROUP_NAME }}" 28 | ec2_volume_size: "{{ EC2_VOLUME_SIZE }}" 29 | ec2_count: "{{ EC2_COUNT }}" 30 | ec2_subnet_id: "{{ EC2_SUBNET_ID }}" 31 | 32 | # Please don't change the variables below, until you know what you are doing 33 | # Only Ubuntu distribution is supported 34 | 35 | ami_name: "{{ AMI_NAME }}" 36 | ami_owner: "{{ AMI_OWNER }}" 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Arbab Nazar 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 | -------------------------------------------------------------------------------- /roles/elb/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure load balancer exists 3 | ec2_elb_lb: 4 | name: "{{ elb_name }}" 5 | security_group_names: "{{ elb_security_group_name }}" 6 | state: present 7 | region: "{{ vpc_region }}" 8 | subnets: "{{ elb_subnet_id }}" 9 | purge_subnets: "{{ elb_purge_subnets }}" 10 | cross_az_load_balancing: "{{ elb_cross_az_load_balancing }}" 11 | connection_draining_timeout: "{{ elb_connection_draining_timeout }}" 12 | listeners: "{{ elb_listeners }}" 13 | stickiness: "{{ elb_stickiness }}" 14 | health_check: "{{ elb_health_check }}" 15 | register: elb 16 | 17 | - name: Adding EC2 instance(s) to the ELB 18 | ec2_elb: 19 | region: "{{ vpc_region }}" 20 | ec2_elbs: "{{ elb_name }}" 21 | instance_id: "{{ item }}" 22 | wait: yes 23 | state: present 24 | with_items: "{{ instances_id_list }}" 25 | when: not(instances_id_list is undefined or instances_id_list is none or instances_id_list | trim == '') 26 | 27 | - name: "Write ELB url to {{ vpc_name }}.yml file inside the secret_vars directory" 28 | lineinfile: 29 | dest: "secret_vars/{{ vpc_name }}.yml" 30 | regexp: "^ELB_URL" 31 | line: "ELB_URL: {{ '\"' + elb.elb.dns_name + '\"' }}" 32 | 33 | - include_vars: "secret_vars/{{ vpc_name }}.yml" 34 | 35 | -------------------------------------------------------------------------------- /roles/rds/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables that can provide as extra vars 3 | VPC_REGION: us-east-1 # N.Virginia 4 | RDS_SUBNET_GROUP_NAME: "my_test_subnet_group" 5 | RDS_SG_DESCRIPTION: "My Subnet Group for wordpress rds instance" 6 | RDS_SG_SUBNETS: [] 7 | RDS_MULTI_ZONE_OPTION: no 8 | RDS_SG_NAME: "test_rd_sg" 9 | RDS_INSTANCE_NAME: "my-test-rds" 10 | RDS_DB_ENGINE: MySQL 11 | RDS_DB_SIZE: 5 12 | RDS_DB_NAME: "rds_test" 13 | RDS_INSTANCE_TYPE: "db.t2.micro" 14 | RDS_DB_USERNAME: admin 15 | RDS_DB_PASSWORD: test 16 | RDS_BACKUP_RETENTION_PERIOD: 0 17 | RDS_PUBLICLY_ACCESSIBLE: yes 18 | RDS_WAIT_TIMEOUT: 300 19 | 20 | # Use inside the tasks 21 | vpc_region: "{{ VPC_REGION }}" 22 | rds_subnet_group_name: "{{ RDS_SUBNET_GROUP_NAME }}" 23 | rds_sg_description: "{{ RDS_SG_DESCRIPTION }}" 24 | rds_sg_subnets: "{{ RDS_SG_SUBNETS }}" 25 | rds_multi_zone_option: "{{ RDS_MULTI_ZONE_OPTION }}" 26 | rds_sg_name: "{{ RDS_SG_NAME }}" 27 | rds_instance_name: "{{ RDS_INSTANCE_NAME }}" 28 | rds_db_engine: "{{ RDS_DB_ENGINE }}" 29 | rds_db_size: "{{ RDS_DB_SIZE }}" 30 | rds_db_name: "{{ RDS_DB_NAME }}" 31 | rds_instance_type: "{{ RDS_INSTANCE_TYPE }}" 32 | rds_db_username: "{{ RDS_DB_USERNAME }}" 33 | rds_db_password: "{{ RDS_DB_PASSWORD }}" 34 | rds_backup_retention_period: "{{ RDS_BACKUP_RETENTION_PERIOD }}" 35 | rds_publicly_accessible: "{{ RDS_PUBLICLY_ACCESSIBLE }}" 36 | rds_wait_timeout: "{{ RDS_WAIT_TIMEOUT }}" -------------------------------------------------------------------------------- /roles/rds/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create RDS Subnet Group 3 | rds_subnet_group: 4 | region: "{{ vpc_region }}" 5 | state: present 6 | name: "{{ rds_subnet_group_name }}" 7 | description: "{{ rds_sg_description }}" 8 | subnets: "{{ rds_sg_subnets }}" 9 | 10 | - name: Provisioning RDS Instance 11 | rds: 12 | command: create 13 | region: "{{ vpc_region }}" 14 | multi_zone: "{{ rds_multi_zone_option }}" 15 | subnet: "{{ rds_subnet_group_name }}" 16 | vpc_security_groups: "{{ lookup('get_sg_id_from_name', (vpc_region, rds_sg_name)) }}" 17 | instance_name: "{{ rds_instance_name }}" 18 | db_engine: "{{ rds_db_engine }}" 19 | size: "{{ rds_db_size }}" 20 | db_name: "{{ rds_db_name }}" 21 | instance_type: "{{ rds_instance_type }}" 22 | username: "{{ rds_db_username }}" 23 | password: "{{ rds_db_password }}" 24 | backup_retention: "{{ rds_backup_retention_period }}" 25 | wait: yes 26 | publicly_accessible: "{{ rds_publicly_accessible }}" 27 | wait_timeout: "{{ rds_wait_timeout }}" 28 | tags: 29 | Environment: "{{ ENVIRONMENT }}" 30 | register: rds 31 | 32 | - name: "Write RDS endpoint to {{ vpc_name }}.yml file inside the secret_vars directory" 33 | lineinfile: 34 | dest: "secret_vars/{{ vpc_name }}.yml" 35 | regexp: "^RDS_ENDPOINT" 36 | line: "RDS_ENDPOINT: {{ '\"' + rds.instance.endpoint + '\"' }}" 37 | 38 | - include_vars: "secret_vars/{{ vpc_name }}.yml" 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ansible Roles for AWS Infrastructure Creation 2 | ---------------------- 3 | There's a series of blog posts that I wrote to go along with these roles. [Check it out!](https://rbgeek.wordpress.com/2016/01/04/aws-infrastructure-creation-with-ansible-part-1/) 4 | 5 | The purpose of these Ansible roles are to create the complete infrastructure over AWS using Ansible, which include: 6 | 7 | - VPC with public and private subnets in different AZ 8 | - EC2 Key Pair 9 | - Security Groups for EC2,RDS and ELB with tags 10 | - EC2 instance inside the desired AZs 11 | - ELB with SSL support 12 | - RDS instance 13 | 14 | I have also written so really small plugins to get the desired information. 15 | 16 | Requirement to use these roles: 17 | 18 | - Ansible v2.0 19 | - boto 20 | - AWS admin access 21 | 22 | Specifically, these are the versions of mentioned software that I am using: 23 | 24 | ```shell 25 | arbab@ansible2:~$ ansible --version 26 | ansible 2.0.0 27 | config file = 28 | configured module search path = Default w/o overrides 29 | arbab@ansible2:~$ 30 | arbab@ansible2:~$ python -c 'import boto;print(boto.Version)' 31 | 2.38.0 32 | arbab@ansible2:~$ 33 | ``` 34 | 35 | Ansible uses python-boto library to call AWS API, and boto needs AWS credentials in order to perform all the functions. There are many ways to configure your AWS credentials. The easiest way is to crate a .boto file under your user home directory: 36 | 37 | Then add the following: 38 | ```shell 39 | [Credentials] 40 | aws_access_key_id = 41 | aws_secret_access_key = 42 | ``` -------------------------------------------------------------------------------- /roles/elb/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables that can provide as extra vars 3 | VPC_REGION: us-east-1 # N.Virginia 4 | ELB_NAME: "test" 5 | ELB_SUBNET_ID: [] 6 | ELB_PURGE_SUBNETS: no 7 | ELB_CROSS_AZ_LOAD_BALANCING: yes 8 | ELB_PING_PROTOCOL: tcp 9 | ELB_PING_PORT: 80 10 | ELB_RESPONSE_TIMEOUT: 5 11 | ELB_INTERVAL: 30 12 | ELB_UNHEALTHY_THRESHOLD: 2 13 | ELB_HEALTHY_THRESHOLD: 10 14 | ELB_CONNECTION_DRAINING_TIMEOUT: 60 15 | ELB_SECURITY_GROUP_NAME: "test" 16 | ELB_STICKINESS_TYPE: "loadbalancer" 17 | ELB_STICKINESS_ENABLED: no 18 | ELB_STICKINESS_EXPIRATION: 150 19 | ELB_LISTENERS: 20 | - protocol: http 21 | load_balancer_port: 80 22 | instance_protocol: http 23 | instance_port: 80 24 | 25 | # Use inside the tasks 26 | vpc_region: "{{ VPC_REGION }}" 27 | elb_name: "{{ ELB_NAME }}" 28 | elb_subnet_id: "{{ ELB_SUBNET_ID }}" 29 | elb_purge_subnets: "{{ ELB_PURGE_SUBNETS }}" 30 | elb_cross_az_load_balancing: "{{ ELB_CROSS_AZ_LOAD_BALANCING }}" 31 | elb_connection_draining_timeout: "{{ ELB_CONNECTION_DRAINING_TIMEOUT }}" 32 | elb_security_group_name: "{{ ELB_SECURITY_GROUP_NAME }}" 33 | elb_listeners: "{{ ELB_LISTENERS }}" 34 | elb_stickiness: 35 | type: "{{ ELB_STICKINESS_TYPE }}" 36 | enabled: "{{ ELB_STICKINESS_ENABLED }}" 37 | expiration: "{{ ELB_STICKINESS_EXPIRATION }}" 38 | elb_health_check: 39 | ping_protocol: "{{ ELB_PING_PROTOCOL }}" 40 | ping_port: "{{ ELB_PING_PORT }}" 41 | response_timeout: "{{ ELB_RESPONSE_TIMEOUT }}" 42 | interval: "{{ ELB_INTERVAL }}" 43 | unhealthy_threshold: "{{ ELB_UNHEALTHY_THRESHOLD }}" 44 | healthy_threshold: "{{ ELB_HEALTHY_THRESHOLD }}" -------------------------------------------------------------------------------- /roles/ec2key/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - block: 3 | - name: "Grab key for {{ github_username }} user from github.com" 4 | uri: 5 | url: "https://github.com/{{ github_username }}.keys" 6 | return_content: yes 7 | register: github_key_result 8 | 9 | - name: Creates an new ec2 key with mentioned name if not present 10 | ec2_key: 11 | region: "{{ vpc_region }}" 12 | name: "{{ ec2_key_name }}" 13 | key_material: "{{ github_key_result.content }}" 14 | 15 | when: github_username and github_username |lower != 'no' and not local_user_ssh_key 16 | 17 | - block: 18 | - name: Find the current login user 19 | command: whoami 20 | register: login_user 21 | changed_when: no 22 | 23 | - name: Set the Key location as fact 24 | set_fact: 25 | ssh_key_location: "/home/{{ login_user.stdout }}/.ssh/id_rsa.pub" 26 | 27 | - name: "Check that the SSH Key exists for {{ login_user.stdout }} user" 28 | stat: 29 | path: "{{ ssh_key_location }}" 30 | register: sshkey_result 31 | 32 | - name: "Generating a new SSH key for {{ login_user.stdout }} user it does not exists already" 33 | user: 34 | name: "{{ login_user.stdout }}" 35 | generate_ssh_key: yes 36 | ssh_key_bits: 2048 37 | when: sshkey_result.stat.exists == False 38 | 39 | - name: Creates an new ec2 key with mentioned name if not present 40 | ec2_key: 41 | region: "{{ vpc_region }}" 42 | name: "{{ ec2_key_name }}" 43 | key_material: "{{ lookup('file', ssh_key_location ) }}" 44 | 45 | when: local_user_ssh_key and not github_username 46 | 47 | -------------------------------------------------------------------------------- /roles/vpc/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables that can provide as extra vars 3 | VPC_NAME: test 4 | VPC_REGION: us-east-1 # N.Virginia 5 | VPC_CIDR: "172.25.0.0/16" 6 | VPC_CLASS_DEFAULT: "172.25" 7 | 8 | # Variables for VPC 9 | vpc_name: "{{ VPC_NAME }}" 10 | vpc_region: "{{ VPC_REGION }}" 11 | vpc_cidr_block: "{{ VPC_CIDR }}" 12 | public_cidr_1: "{{ VPC_CLASS_DEFAULT }}.10.0/24" 13 | public_az_1: "{{ vpc_region }}a" 14 | public_cidr_2: "{{ VPC_CLASS_DEFAULT }}.20.0/24" 15 | public_az_2: "{{ vpc_region }}b" 16 | private_cidr_1: "{{ VPC_CLASS_DEFAULT }}.30.0/24" 17 | private_az_1: "{{ vpc_region }}a" 18 | private_cidr_2: "{{ VPC_CLASS_DEFAULT }}.40.0/24" 19 | private_az_2: "{{ vpc_region }}b" 20 | 21 | # Please don't change the variables below, until you know what you are doing 22 | # 23 | # Subnets Defination for VPC 24 | vpc_subnets: 25 | - cidr: "{{ public_cidr_1 }}" # Public Subnet-1 26 | az: "{{ public_az_1 }}" 27 | resource_tags: { "Name":"{{ vpc_name }}-{{ public_az_1 }}-public_subnet-1", "Type":"Public", "Alias":"Public_Subnet_1" } 28 | - cidr: "{{ public_cidr_2 }}" # Public Subnet-2 29 | az: "{{ public_az_2 }}" 30 | resource_tags: { "Name":"{{ vpc_name }}-{{ public_az_2 }}-public-subnet-2", "Type":"Public", "Alias":"Public_Subnet_2" } 31 | - cidr: "{{ private_cidr_1 }}" # Private Subnet-1 32 | az: "{{ private_az_1 }}" 33 | resource_tags: { "Name":"{{ vpc_name }}-{{ private_az_1 }}-private-subnet-1", "Type":"Private", "Alias":"Private_Subnet_1" } 34 | - cidr: "{{ private_cidr_2 }}" # Private Subnet-2 35 | az: "{{ private_az_2 }}" 36 | resource_tags: { "Name":"{{ vpc_name }}-{{ private_az_2 }}-private-subnet-2", "Type":"Private", "Alias":"Private_Subnet_2" } 37 | -------------------------------------------------------------------------------- /roles/ec2instance/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Search for the latest Ubuntu 14.04 AMI 3 | ec2_ami_find: 4 | region: "{{ vpc_region }}" 5 | name: "{{ ami_name }}" 6 | owner: "{{ ami_owner }}" 7 | sort: name 8 | sort_order: descending 9 | sort_end: 1 10 | no_result_action: fail 11 | register: ami_find_result 12 | 13 | - name: Create EC2 Instance(s) 14 | ec2: 15 | region: "{{ vpc_region }}" 16 | group: "{{ ec2_security_group_name }}" 17 | keypair: "{{ ec2_key_name }}" 18 | instance_type: "{{ ec2_instance_type }}" 19 | image: "{{ ami_find_result.results[0].ami_id }}" 20 | vpc_subnet_id: "{{ item.1 }}" 21 | assign_public_ip: yes 22 | wait: True 23 | wait_timeout: 600 24 | user_data: | 25 | #!/bin/sh 26 | sudo apt-get install nginx -y 27 | instance_tags: 28 | Name: "{{ vpc_name }}-{{ SERVER_ROLE }}-0{{ item.0 + 1 }}" 29 | Environment: "{{ ENVIRONMENT }}" 30 | Server_Role: "{{ SERVER_ROLE }}" 31 | volumes: 32 | - device_name: /dev/sda1 33 | volume_type: gp2 34 | volume_size: "{{ ec2_volume_size }}" 35 | delete_on_termination: yes 36 | count: "{{ ec2_count }}" 37 | with_indexed_items: "{{ ec2_subnet_id }}" 38 | register: ec2 39 | 40 | - set_fact: 41 | instances_id_list: "{{ ec2.results | get_ec2_info('id') }}" 42 | 43 | - name: Add the newly created EC2 instance(s) to the local host group (located at ./inventory/hosts) 44 | lineinfile: 45 | dest: "./inventory/hosts" 46 | regexp: "{{ item }}" 47 | insertafter: "[webserver]" 48 | line: "{{ item }}" 49 | with_items: "{{ ec2.results | get_ec2_info('public_ip') }}" 50 | 51 | - name: Wait for SSH to come up on EC2 Instance(s) 52 | wait_for: 53 | host: "{{ item }}" 54 | port: 22 55 | search_regex: OpenSSH 56 | delay: 10 57 | with_items: "{{ ec2.results | get_ec2_info('public_ip') }}" 58 | -------------------------------------------------------------------------------- /roles/vpc/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Creating an AWS VPC inside mentioned Region 3 | ec2_vpc: 4 | region: "{{ vpc_region }}" 5 | state: present 6 | cidr_block: "{{ vpc_cidr_block }}" 7 | resource_tags: { "Name":"{{ vpc_name }}-vpc", "Environment":"{{ ENVIRONMENT }}" } 8 | subnets: "{{ vpc_subnets }}" 9 | internet_gateway: yes 10 | register: vpc 11 | 12 | - name: Tag the Internet Gateway 13 | ec2_tag: 14 | resource: "{{ vpc.igw_id }}" 15 | region: "{{ vpc_region }}" 16 | state: present 17 | tags: 18 | Name: "{{ vpc_name }}-igw" 19 | register: igw 20 | 21 | - name: Set up Public Subnets Route Table 22 | ec2_vpc_route_table: 23 | vpc_id: "{{ vpc.vpc_id }}" 24 | region: "{{ vpc_region }}" 25 | state: present 26 | tags: 27 | Name: "Public-RT-for-{{ vpc_name }}-vpc" 28 | subnets: 29 | "{{ vpc.subnets | get_public_subnets_ids('Type','Public') }}" 30 | routes: 31 | - dest: 0.0.0.0/0 32 | gateway_id: "{{ vpc.igw_id }}" 33 | register: public_rt 34 | 35 | - name: "Create {{ vpc_name }}.yml file inside the secret_vars directory" 36 | lineinfile: 37 | dest: "secret_vars/{{ vpc_name }}.yml" 38 | line: "---" 39 | create: yes 40 | 41 | - name: "Write vpc info to {{ vpc_name }}.yml file inside the secret_vars directory" 42 | lineinfile: 43 | dest: "secret_vars/{{ vpc_name }}.yml" 44 | regexp: "^{{ item.regexp | upper }}" 45 | line: "{{ item.regexp | upper }}: {{ '\"' + item.line + '\"' }}" 46 | with_items: 47 | - { regexp: 'vpc_id', line: '{{ vpc.vpc_id }}' } 48 | - { regexp: 'igw', line: '{{ vpc.igw_id }}' } 49 | - { regexp: 'route_table_id', line: '{{ public_rt.route_table.id }}' } 50 | 51 | - name: "Write public and private subnets ids to {{ vpc_name }}.yml file inside the secret_vars directory" 52 | lineinfile: 53 | dest: "secret_vars/{{ vpc_name }}.yml" 54 | regexp: "^{{ item.resource_tags.Alias | upper }}" 55 | line: "{{ item.resource_tags.Alias | upper }}: {{ '\"' + item.id + '\"' }}" 56 | with_items: "{{ vpc.subnets }}" 57 | 58 | - include_vars: "secret_vars/{{ vpc_name }}.yml" -------------------------------------------------------------------------------- /secret_vars/secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Environment specific variables 3 | COMPANY: rbgeek 4 | ENVIRONMENT: dev 5 | SERVER_ROLE: web 6 | 7 | # VPC specific variables 8 | VPC_NAME: "{{ COMPANY }}-{{ ENVIRONMENT }}" 9 | VPC_REGION: eu-west-1 # Ireland 10 | VPC_CIDR: "10.10.0.0/16" 11 | VPC_CLASS_DEFAULT: "10.10" 12 | 13 | #Github username for creating EC2 KEY Pair 14 | GITHUB_USERNAME: "arbabnazar" 15 | EC2_KEY_NAME: "{{ GITHUB_USERNAME }}-github-key" 16 | LOCAL_USER_SSH_KEY: no 17 | 18 | # Ubuntu AMI specific variables 19 | AMI_NAME: "ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*" 20 | AMI_OWNER: "099720109477" 21 | 22 | # EC2 instances specific variables 23 | EC2_INSTANCE_TYPE: t2.nano 24 | EC2_SECURITY_GROUP_NAME: 25 | - "{{ VPC_NAME }}-{{ SSH_SG_NAME }}" 26 | - "{{ VPC_NAME }}-{{ WEB_SG_NAME }}" 27 | EC2_VOLUME_SIZE: 10 28 | EC2_COUNT: 1 29 | EC2_SUBNET_ID: 30 | - "{{ PUBLIC_SUBNET_1 }}" 31 | - "{{ PUBLIC_SUBNET_2 }}" 32 | 33 | # RDS specific variables 34 | RDS_SUBNET_GROUP_NAME: "{{ VPC_NAME }}-subnet-group" 35 | RDS_SG_DESCRIPTION: "Subnet Group for rds instances inside {{ VPC_NAME }} VPC" 36 | RDS_SG_SUBNETS: 37 | - "{{ PRIVATE_SUBNET_1 }}" 38 | - "{{ PRIVATE_SUBNET_2 }}" 39 | RDS_SG_NAME: "{{ VPC_NAME }}-{{ DATABASE_SG_NAME }}" 40 | RDS_MULTI_ZONE_OPTION: no 41 | RDS_INSTANCE_NAME: "{{ COMPANY }}-{{ ENVIRONMENT }}-rds" 42 | RDS_DB_ENGINE: MySQL 43 | RDS_DB_SIZE: 10 44 | RDS_DB_NAME: "mydatabase" 45 | RDS_INSTANCE_TYPE: "db.t2.micro" 46 | RDS_DB_USERNAME: root 47 | RDS_DB_PASSWORD: "verystrongpassword" 48 | RDS_BACKUP_RETENTION_PERIOD: 1 49 | RDS_PUBLICLY_ACCESSIBLE: no 50 | RDS_WAIT_TIMEOUT: 1800 51 | 52 | # Elastic Load Balancer specific variables 53 | ELB_NAME: "{{ COMPANY }}-{{ ENVIRONMENT }}-{{ SERVER_ROLE }}-elb" 54 | ELB_SUBNET_ID: 55 | - "{{ PUBLIC_SUBNET_1 }}" 56 | - "{{ PUBLIC_SUBNET_2 }}" 57 | ELB_PURGE_SUBNETS: yes 58 | ELB_CROSS_AZ_LOAD_BALANCING: yes 59 | ELB_PING_PROTOCOL: tcp 60 | ELB_PING_PORT: 80 61 | ELB_RESPONSE_TIMEOUT: 5 62 | ELB_INTERVAL: 30 63 | ELB_UNHEALTHY_THRESHOLD: 2 64 | ELB_HEALTHY_THRESHOLD: 10 65 | ELB_CONNECTION_DRAINING_TIMEOUT: 300 66 | ELB_SECURITY_GROUP_NAME: "{{ VPC_NAME }}-{{ ELB_SG_NAME }}" 67 | ELB_STICKINESS_TYPE: "loadbalancer" 68 | ELB_STICKINESS_ENABLED: yes 69 | ELB_STICKINESS_EXPIRATION: 300 70 | ELB_LISTENERS: 71 | - protocol: http 72 | load_balancer_port: 80 73 | instance_protocol: http 74 | instance_port: 80 75 | - protocol: https 76 | load_balancer_port: 443 77 | instance_protocol: http 78 | instance_port: 80 79 | ssl_certificate_id: "arn:aws:iam::xxxxxxxxxxxx:server-certificate/tendo-crt" 80 | 81 | # EC2 Security Groups specific variables 82 | WEB_SG_NAME: "webserver-sg" 83 | DATABASE_SG_NAME: "rds-sg" 84 | SSH_SG_NAME: "ssh-sg" 85 | ELB_SG_NAME: "elb-sg" 86 | 87 | # Security Groups 88 | EC2_SECURITY_GROUPS: "{{ SSH_SG + WEB_SG + DATABASE_SG + ELB_SG }}" 89 | 90 | # Secrity Groups info(Name, Description and Rules) for Web, RDS and ELB 91 | SSH_SG: 92 | - name: "{{ VPC_NAME }}-{{ SSH_SG_NAME }}" 93 | description: "This sg is for remote access to instances inside {{ VPC_NAME }} VPC" 94 | rules: 95 | - proto: tcp 96 | from_port: 22 97 | to_port: 22 98 | cidr_ip: 0.0.0.0/0 99 | 100 | WEB_SG: 101 | - name: "{{ VPC_NAME }}-{{ WEB_SG_NAME }}" 102 | description: "This sg is for web instances inside {{ VPC_NAME }} VPC" 103 | rules: 104 | - proto: tcp 105 | from_port: 80 106 | to_port: 80 107 | cidr_ip: 0.0.0.0/0 108 | - proto: tcp 109 | from_port: 443 110 | to_port: 443 111 | cidr_ip: 0.0.0.0/0 112 | 113 | DATABASE_SG: 114 | - name: "{{ VPC_NAME }}-{{ DATABASE_SG_NAME }}" 115 | description: "This sg is for rds instances inside {{ VPC_NAME }} VPC" 116 | rules: 117 | - proto: tcp 118 | from_port: 3306 119 | to_port: 3306 120 | group_name: "{{ VPC_NAME }}-{{ WEB_SG_NAME }}" 121 | 122 | ELB_SG: 123 | - name: "{{ VPC_NAME }}-{{ ELB_SG_NAME }}" 124 | description: "This sg is for ELB inside {{ VPC_NAME }} VPC" 125 | rules: 126 | - proto: tcp 127 | from_port: 80 128 | to_port: 80 129 | cidr_ip: 0.0.0.0/0 130 | --------------------------------------------------------------------------------