├── .gitignore ├── README.md ├── ansible ├── ansible.cfg ├── dynamic_inventory.sh ├── playbooks │ ├── db.yml │ └── web.yml ├── site.yml └── terraform.py └── terraform ├── backend.tf.example ├── data.tf ├── main.tf ├── modules ├── base │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── db │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── key_pair │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── web │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── outputs.tf ├── terraform.tfvars.example └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | *.retry 2 | *.tfstate* 3 | *.tfstate.backup* 4 | *.tfvars 5 | *.terraform 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terraform-ansible-example 2 | Quick start on how to provision with ansible inside terraform 3 | 4 | ## Project structure 5 | * ansible - folder with ansible playbooks, inventories and configuration 6 | * terraform - folder with terraform infrastructure files 7 | 8 | ## Getting stated 9 | Prepend environment for using ansible dynamic inventory with Amazon ec2: 10 | ``` 11 | $ pip install boto 12 | $ chmod +x ansible/ec2.py 13 | ``` 14 | Of course, you'll need to have AWS credentials. By default you can find it in ~/.aws/credentials 15 | ``` 16 | $ cat ~/.aws/credentials 17 | [default] 18 | aws_access_key_id = 19 | aws_secret_access_key = 20 | ... 21 | ``` 22 | 23 | In file ansible/ec2.ini define your regions: 24 | ``` 25 | ... 26 | regions = eu-central-1 27 | regions_exclude = us-gov-west-1, cn-north-1 28 | ... 29 | ``` 30 | 31 | ## Usage 32 | if you want just up example infrastructure you need set your variables in .tfvars files 33 | ``` 34 | pub_key_path = "~/.ssh/express42.pub" 35 | private_key_path = "~/.ssh/express42" 36 | key_name = "astarostenko" 37 | env = "astarostenko" 38 | ``` 39 | 40 | Go to terraform folder and download all modules to .terraform folder (for local modules it just creates symlinks) 41 | ``` 42 | $ cd terraform 43 | $ terraform get 44 | ``` 45 | 46 | If your want to see plan of your own infrastructure 47 | 48 | ``` 49 | $ terraform plan 50 | Refreshing Terraform state in-memory prior to plan... 51 | The refreshed state will be used to calculate this plan, but will not be 52 | persisted to local or remote state storage. 53 | 54 | data.aws_ami.image: Refreshing state... 55 | The Terraform execution plan has been generated and is shown below. 56 | Resources are shown in alphabetical order for quick scanning. Green resources 57 | will be created (or destroyed and then created if an existing resource 58 | exists), yellow resources are being changed in-place, and red resources 59 | will be destroyed. Cyan entries are data sources to be read. 60 | 61 | Note: You didn't specify an "-out" parameter to save this plan, so when 62 | "apply" is called, Terraform can't guarantee this is what will execute. 63 | 64 | + null_resource.ansible_db 65 | 66 | + null_resource.ansible_web 67 | 68 | + module.db.aws_instance.db 69 | ... 70 | ... 71 | ... 72 | Plan: 9 to add, 0 to change, 0 to destroy. 73 | 74 | ``` 75 | To create all resources and provision all services 76 | ``` 77 | $ terraform apply 78 | ``` 79 | To delete all created resources 80 | ``` 81 | $ terraform destroy 82 | ``` 83 | # Terraform structure 84 | 85 | #### main.tf - contain general infrastructure description 86 | We describe used provider, can create resources, call some modules, and can also define provision 87 | action 88 | ``` 89 | provider "aws" { 90 | region = "${var.region}" 91 | } 92 | ... 93 | module "base_linux" { 94 | source = "./modules/base" 95 | } 96 | ... 97 | resource null_resource "ansible_web" { 98 | depends_on = ["module.web"] 99 | 100 | provisioner "local-exec" { 101 | command = "cd ../ansible && ansible-playbook playbooks/apache.yml -e env=${var.env} -e group_name=${var.web_server_params["name"]}" 102 | } 103 | } 104 | ... 105 | ``` 106 | 107 | 108 | #### variables.tf - define all required variables, its description(optional) default values(optional) 109 | 110 | There are three types of variables in terraform: 111 | 112 | * string 113 | ``` 114 | variable "string_var_name" { 115 | default = "string_value" 116 | } 117 | ``` 118 | * map 119 | ``` 120 | variable "map_var_name" { 121 | default = { 122 | key-1 = "image-1234" 123 | key-2 = "image-4567" 124 | } 125 | ``` 126 | * list 127 | ``` 128 | variable "list_var_name" { 129 | default = ["us-east-1a", "us-east-1b"] 130 | } 131 | ``` 132 | 133 | Variables can be defined in 134 | * variables.tf or any other .tf file 135 | ``` 136 | variable env { 137 | description = "current environment (dev, prod, stage)" 138 | default = "dev" 139 | } 140 | ``` 141 | You can just create it but not define in .tf file, but then you'll need to define it anywhere 142 | ``` 143 | variable "name" {} 144 | ``` 145 | 146 | * terraform.tfvars (default) or any other .tfvars file with flag -var-file 147 | ``` 148 | $ terraform plan \ 149 | -var-file="secret.tfvars" \ 150 | -var-file="production.tfvars" 151 | ``` 152 | * input argument to terraform with flag -var 153 | ``` 154 | $ terraform plan -var 'access_key=foo' 155 | ``` 156 | 157 | [More information about variables](https://www.terraform.io/docs/configuration/variables.html) 158 | 159 | #### outputs.tf - define all important output data like variables 160 | 161 | ``` 162 | output "web_address" { 163 | value = "${module.web.public_ip}" 164 | } 165 | ``` 166 | 167 | #### modules/ 168 | Modules are used in Terraform to modularize and encapsulate groups of resources in your infrastructure. 169 | Every module is a little terraform project - it can contain its own variables and outputs 170 | 171 | Module structure: 172 | * base - module used in every infrastructure 173 | * key_pair - upload public keys to cloud provider 174 | * db - build database resources 175 | * web - build web-service resources 176 | 177 | We can use module just like: 178 | ``` 179 | module "exemplar_name" { 180 | source ./modules/web 181 | name = server_name 182 | env = "${var.env_name}" 183 | } 184 | ``` 185 | [More information about modules](https://www.terraform.io/docs/modules/index.html) 186 | -------------------------------------------------------------------------------- /ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | 3 | inventory = ./dynamic_inventory.sh 4 | 5 | private_key_file = ~/.ssh/express42 6 | 7 | remote_user = ubuntu 8 | 9 | host_key_checking = False 10 | -------------------------------------------------------------------------------- /ansible/dynamic_inventory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # used to pull the current state from S3 and 3 | # use this information for provisioning 4 | playbook=${1} 5 | state_file_name="terraform.tfstate" 6 | 7 | # fetch current state form s3 8 | (cd ../terraform && terraform state pull) > ${state_file_name} 9 | ./terraform.py ${1} 10 | 11 | rm ${state_file_name} 12 | -------------------------------------------------------------------------------- /ansible/playbooks/db.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: aws_tag_Group=dev_db_cluster 4 | gather_facts: false 5 | become: yes 6 | 7 | pre_tasks: 8 | - name: Install python for Ansible 9 | raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal) 10 | changed_when: False 11 | 12 | tasks: 13 | - name: Install packages 14 | apt: 15 | name: "{{ item }}" 16 | state: present 17 | with_items: 18 | - mysql-server 19 | 20 | - name: Start mysql service 21 | service: 22 | enabled: yes 23 | name: mysql 24 | state: started 25 | -------------------------------------------------------------------------------- /ansible/playbooks/web.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: aws_tag_Group=dev_web_cluster 4 | gather_facts: false 5 | become: yes 6 | 7 | pre_tasks: 8 | - name: Install python for Ansible 9 | raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal) 10 | changed_when: False 11 | 12 | tasks: 13 | - name: Install packages 14 | apt: 15 | name: "{{ item }}" 16 | state: present 17 | with_items: 18 | - apache2 19 | 20 | - name: Start apache2 service 21 | service: 22 | enabled: yes 23 | name: apache2 24 | state: started 25 | -------------------------------------------------------------------------------- /ansible/site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - include: playbooks/db.yml 4 | - include: playbooks/web.yml 5 | -------------------------------------------------------------------------------- /ansible/terraform.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2015 Cisco Systems, Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | """\ 17 | Dynamic inventory for Terraform - finds all `.tfstate` files below the working 18 | directory and generates an inventory based on them. 19 | """ 20 | from __future__ import unicode_literals, print_function 21 | import argparse 22 | from collections import defaultdict 23 | from functools import wraps 24 | import json 25 | import os 26 | import re 27 | 28 | VERSION = '0.3.0pre' 29 | 30 | 31 | def tfstates(root=None): 32 | root = root or os.getcwd() 33 | for dirpath, _, filenames in os.walk(root): 34 | for name in filenames: 35 | if os.path.splitext(name)[-1] == '.tfstate': 36 | yield os.path.join(dirpath, name) 37 | 38 | 39 | def iterresources(filenames): 40 | for filename in filenames: 41 | with open(filename, 'r') as json_file: 42 | state = json.load(json_file) 43 | for module in state['modules']: 44 | name = module['path'][-1] 45 | for key, resource in module['resources'].items(): 46 | yield name, key, resource 47 | 48 | # READ RESOURCES 49 | PARSERS = {} 50 | 51 | 52 | def _clean_dc(dcname): 53 | # Consul DCs are strictly alphanumeric with underscores and hyphens - 54 | # ensure that the consul_dc attribute meets these requirements. 55 | return re.sub('[^\w_\-]', '-', dcname) 56 | 57 | 58 | def iterhosts(resources): 59 | '''yield host tuples of (name, attributes, groups)''' 60 | for module_name, key, resource in resources: 61 | resource_type, name = key.split('.', 1) 62 | try: 63 | parser = PARSERS[resource_type] 64 | except KeyError: 65 | continue 66 | 67 | yield parser(resource, module_name) 68 | 69 | 70 | def parses(prefix): 71 | def inner(func): 72 | PARSERS[prefix] = func 73 | return func 74 | 75 | return inner 76 | 77 | 78 | def calculate_mantl_vars(func): 79 | """calculate Mantl vars""" 80 | 81 | @wraps(func) 82 | def inner(*args, **kwargs): 83 | name, attrs, groups = func(*args, **kwargs) 84 | 85 | # attrs 86 | if attrs.get('role', '') == 'control': 87 | attrs['consul_is_server'] = True 88 | else: 89 | attrs['consul_is_server'] = False 90 | 91 | # groups 92 | if attrs.get('publicly_routable', False): 93 | groups.append('publicly_routable') 94 | 95 | return name, attrs, groups 96 | 97 | return inner 98 | 99 | 100 | def _parse_prefix(source, prefix, sep='.'): 101 | for compkey, value in source.items(): 102 | try: 103 | curprefix, rest = compkey.split(sep, 1) 104 | except ValueError: 105 | continue 106 | 107 | if curprefix != prefix or rest == '#': 108 | continue 109 | 110 | yield rest, value 111 | 112 | 113 | def parse_attr_list(source, prefix, sep='.'): 114 | attrs = defaultdict(dict) 115 | for compkey, value in _parse_prefix(source, prefix, sep): 116 | idx, key = compkey.split(sep, 1) 117 | attrs[idx][key] = value 118 | 119 | return attrs.values() 120 | 121 | 122 | def parse_dict(source, prefix, sep='.'): 123 | return dict(_parse_prefix(source, prefix, sep)) 124 | 125 | 126 | def parse_list(source, prefix, sep='.'): 127 | return [value for _, value in _parse_prefix(source, prefix, sep)] 128 | 129 | 130 | def parse_bool(string_form): 131 | token = string_form.lower()[0] 132 | 133 | if token == 't': 134 | return True 135 | elif token == 'f': 136 | return False 137 | else: 138 | raise ValueError('could not convert %r to a bool' % string_form) 139 | 140 | 141 | @parses('ddcloud_server') 142 | @calculate_mantl_vars 143 | def ddcloud_server(resource, module_name): 144 | raw_attrs = resource['primary']['attributes'] 145 | name = raw_attrs.get('name') 146 | groups = [] 147 | 148 | tags = {} 149 | raw_tags = parse_attr_list(raw_attrs, 'tag') 150 | for raw_tag in raw_tags: 151 | tags[raw_tag['name']] = raw_tag['value'] 152 | 153 | attrs = { 154 | 'id': raw_attrs['id'], 155 | 'name': raw_attrs['name'], 156 | 'primary_ip': raw_attrs['public_ipv4'], # not available immediately after creation; you'll need to run terraform refresh first. 157 | 'tags': tags, 158 | 159 | 'network_domain': raw_attrs['networkdomain'], 160 | 161 | # ansible 162 | 'ansible_ssh_host': raw_attrs['public_ipv4'], 163 | 'ansible_ssh_port': 22, 164 | 'ansible_ssh_user': 'root', # it's always "root" on CloudControl images 165 | 166 | # generic 167 | 'private_ipv4': raw_attrs['primary_adapter_ipv4'], 168 | 'public_ipv4': raw_attrs['public_ipv4'], 169 | 'primary_ipv6': raw_attrs['primary_adapter_ipv6'], 170 | 171 | 'provider': 'ddcloud', 172 | } 173 | 174 | # image details 175 | if 'os_image_id' in raw_attrs: 176 | attrs['image_type'] = 'os' 177 | attrs['image_id'] = raw_attrs['os_image_id'] 178 | attrs['image_name'] = raw_attrs['os_image_name'] 179 | else: 180 | attrs['image_type'] = 'customer' 181 | attrs['image_id'] = raw_attrs['customer_image_id'] 182 | attrs['image_name'] = aw_attrs['customer_image_name'] 183 | 184 | # attrs specific to Mantl 185 | attrs.update({ 186 | 'role': tags.get('role', 'none'), 187 | 'consul_dc': _clean_dc(tags.get('consul_dc', 'none')) 188 | }) 189 | 190 | # groups specific to Mantl 191 | groups.append('role=' + attrs['role']) 192 | groups.append('dc=' + attrs['consul_dc']) 193 | 194 | return name, attrs, groups 195 | 196 | 197 | @parses('triton_machine') 198 | @calculate_mantl_vars 199 | def triton_machine(resource, module_name): 200 | raw_attrs = resource['primary']['attributes'] 201 | name = raw_attrs.get('name') 202 | groups = [] 203 | 204 | attrs = { 205 | 'id': raw_attrs['id'], 206 | 'dataset': raw_attrs['dataset'], 207 | 'disk': raw_attrs['disk'], 208 | 'firewall_enabled': parse_bool(raw_attrs['firewall_enabled']), 209 | 'image': raw_attrs['image'], 210 | 'ips': parse_list(raw_attrs, 'ips'), 211 | 'memory': raw_attrs['memory'], 212 | 'name': raw_attrs['name'], 213 | 'networks': parse_list(raw_attrs, 'networks'), 214 | 'package': raw_attrs['package'], 215 | 'primary_ip': raw_attrs['primaryip'], 216 | 'root_authorized_keys': raw_attrs['root_authorized_keys'], 217 | 'state': raw_attrs['state'], 218 | 'tags': parse_dict(raw_attrs, 'tags'), 219 | 'type': raw_attrs['type'], 220 | 'user_data': raw_attrs['user_data'], 221 | 'user_script': raw_attrs['user_script'], 222 | 223 | # ansible 224 | 'ansible_ssh_host': raw_attrs['primaryip'], 225 | 'ansible_ssh_user': 'root', # it's "root" on Triton by default 226 | 227 | # generic 228 | 'public_ipv4': raw_attrs['primaryip'], 229 | 'provider': 'triton', 230 | } 231 | 232 | # private IPv4 233 | for ip in attrs['ips']: 234 | if ip.startswith('10') or ip.startswith('192.168'): # private IPs 235 | attrs['private_ipv4'] = ip 236 | break 237 | 238 | if 'private_ipv4' not in attrs: 239 | attrs['private_ipv4'] = attrs['public_ipv4'] 240 | 241 | # attrs specific to Mantl 242 | attrs.update({ 243 | 'consul_dc': _clean_dc(attrs['tags'].get('dc', 'none')), 244 | 'role': attrs['tags'].get('role', 'none'), 245 | 'ansible_python_interpreter': attrs['tags'].get('python_bin', 'python') 246 | }) 247 | 248 | # add groups based on attrs 249 | groups.append('triton_image=' + attrs['image']) 250 | groups.append('triton_package=' + attrs['package']) 251 | groups.append('triton_state=' + attrs['state']) 252 | groups.append('triton_firewall_enabled=%s' % attrs['firewall_enabled']) 253 | groups.extend('triton_tags_%s=%s' % item 254 | for item in attrs['tags'].items()) 255 | groups.extend('triton_network=' + network 256 | for network in attrs['networks']) 257 | 258 | # groups specific to Mantl 259 | groups.append('role=' + attrs['role']) 260 | groups.append('dc=' + attrs['consul_dc']) 261 | 262 | return name, attrs, groups 263 | 264 | 265 | @parses('packet_device') 266 | @calculate_mantl_vars 267 | def packet_device(resource, tfvars=None): 268 | raw_attrs = resource['primary']['attributes'] 269 | name = raw_attrs['id'] 270 | groups = [] 271 | 272 | attrs = { 273 | 'id': raw_attrs['id'], 274 | 'facility': raw_attrs['facility'], 275 | 'hostname': raw_attrs['hostname'], 276 | 'operating_system': raw_attrs['operating_system'], 277 | 'locked': parse_bool(raw_attrs['locked']), 278 | 'metadata': json.loads(raw_attrs.get('user_data', '{}')), 279 | 'plan': raw_attrs['plan'], 280 | 'project_id': raw_attrs['project_id'], 281 | 'state': raw_attrs['state'], 282 | # ansible 283 | 'ansible_ssh_host': raw_attrs['network.0.address'], 284 | 'ansible_ssh_port': 22, 285 | 'ansible_ssh_user': 'root', # it's always "root" on Packet 286 | # generic 287 | 'ipv4_address': raw_attrs['network.0.address'], 288 | 'public_ipv4': raw_attrs['network.0.address'], 289 | 'ipv6_address': raw_attrs['network.1.address'], 290 | 'public_ipv6': raw_attrs['network.1.address'], 291 | 'private_ipv4': raw_attrs['network.2.address'], 292 | 'provider': 'packet', 293 | } 294 | 295 | # attrs specific to Mantl 296 | attrs.update({ 297 | 'consul_dc': _clean_dc(attrs['metadata'].get('dc', attrs['facility'])), 298 | 'role': attrs['metadata'].get('role', 'none'), 299 | 'ansible_python_interpreter': attrs['metadata'] 300 | .get('python_bin', 'python') 301 | }) 302 | 303 | # add groups based on attrs 304 | groups.append('packet_facility=' + attrs['facility']) 305 | groups.append('packet_operating_system=' + attrs['operating_system']) 306 | groups.append('packet_locked=%s' % attrs['locked']) 307 | groups.append('packet_state=' + attrs['state']) 308 | groups.append('packet_plan=' + attrs['plan']) 309 | groups.extend('packet_metadata_%s=%s' % item 310 | for item in attrs['metadata'].items()) 311 | 312 | # groups specific to Mantl 313 | groups.append('role=' + attrs['role']) 314 | groups.append('dc=' + attrs['consul_dc']) 315 | 316 | return name, attrs, groups 317 | 318 | 319 | @parses('digitalocean_droplet') 320 | @calculate_mantl_vars 321 | def digitalocean_host(resource, tfvars=None): 322 | raw_attrs = resource['primary']['attributes'] 323 | name = raw_attrs['name'] 324 | groups = [] 325 | 326 | attrs = { 327 | 'id': raw_attrs['id'], 328 | 'image': raw_attrs['image'], 329 | 'ipv4_address': raw_attrs['ipv4_address'], 330 | 'locked': parse_bool(raw_attrs['locked']), 331 | 'metadata': json.loads(raw_attrs.get('user_data', '{}')), 332 | 'region': raw_attrs['region'], 333 | 'size': raw_attrs['size'], 334 | 'ssh_keys': parse_list(raw_attrs, 'ssh_keys'), 335 | 'status': raw_attrs['status'], 336 | 'tags': parse_list(raw_attrs, 'tags'), 337 | # ansible 338 | 'ansible_ssh_host': raw_attrs['ipv4_address'], 339 | 'ansible_ssh_user': 'root', # it's always "root" on DO 340 | # generic 341 | 'public_ipv4': raw_attrs['ipv4_address'], 342 | 'private_ipv4': raw_attrs.get('ipv4_address_private', 343 | raw_attrs['ipv4_address']), 344 | 'provider': 'digitalocean', 345 | } 346 | 347 | # attrs specific to Mantl 348 | attrs.update({ 349 | 'consul_dc': _clean_dc(attrs['metadata'].get('dc', attrs['region'])), 350 | 'role': attrs['metadata'].get('role', 'none'), 351 | 'ansible_python_interpreter': attrs['metadata'] 352 | .get('python_bin', 'python') 353 | }) 354 | 355 | # add groups based on attrs 356 | groups.append('do_image=' + attrs['image']) 357 | groups.append('do_locked=%s' % attrs['locked']) 358 | groups.append('do_region=' + attrs['region']) 359 | groups.append('do_size=' + attrs['size']) 360 | groups.append('do_status=' + attrs['status']) 361 | groups.extend('do_metadata_%s=%s' % item 362 | for item in attrs['metadata'].items()) 363 | groups.extend('do_tag=%s' % item 364 | for item in attrs['tags']) 365 | 366 | # groups specific to Mantl 367 | groups.append('role=' + attrs['role']) 368 | groups.append('dc=' + attrs['consul_dc']) 369 | 370 | return name, attrs, groups 371 | 372 | 373 | @parses('softlayer_virtualserver') 374 | @calculate_mantl_vars 375 | def softlayer_host(resource, module_name): 376 | raw_attrs = resource['primary']['attributes'] 377 | name = raw_attrs['name'] 378 | groups = [] 379 | 380 | attrs = { 381 | 'id': raw_attrs['id'], 382 | 'image': raw_attrs['image'], 383 | 'ipv4_address': raw_attrs['ipv4_address'], 384 | 'metadata': json.loads(raw_attrs.get('user_data', '{}')), 385 | 'region': raw_attrs['region'], 386 | 'ram': raw_attrs['ram'], 387 | 'cpu': raw_attrs['cpu'], 388 | 'ssh_keys': parse_list(raw_attrs, 'ssh_keys'), 389 | 'public_ipv4': raw_attrs['ipv4_address'], 390 | 'private_ipv4': raw_attrs['ipv4_address_private'], 391 | 'ansible_ssh_host': raw_attrs['ipv4_address'], 392 | 'ansible_ssh_user': 'root', 393 | 'provider': 'softlayer', 394 | } 395 | 396 | # attrs specific to Mantl 397 | attrs.update({ 398 | 'consul_dc': _clean_dc(attrs['metadata'].get('dc', attrs['region'])), 399 | 'role': attrs['metadata'].get('role', 'none'), 400 | 'ansible_python_interpreter': attrs['metadata'] 401 | .get('python_bin', 'python') 402 | }) 403 | 404 | # groups specific to Mantl 405 | groups.append('role=' + attrs['role']) 406 | groups.append('dc=' + attrs['consul_dc']) 407 | 408 | return name, attrs, groups 409 | 410 | 411 | @parses('openstack_compute_instance_v2') 412 | @calculate_mantl_vars 413 | def openstack_host(resource, module_name): 414 | raw_attrs = resource['primary']['attributes'] 415 | name = raw_attrs['name'] 416 | groups = [] 417 | 418 | attrs = { 419 | 'access_ip_v4': raw_attrs['access_ip_v4'], 420 | 'access_ip_v6': raw_attrs['access_ip_v6'], 421 | 'flavor': parse_dict(raw_attrs, 'flavor', 422 | sep='_'), 423 | 'id': raw_attrs['id'], 424 | 'image': parse_dict(raw_attrs, 'image', 425 | sep='_'), 426 | 'key_pair': raw_attrs['key_pair'], 427 | 'metadata': parse_dict(raw_attrs, 'metadata'), 428 | 'network': parse_attr_list(raw_attrs, 'network'), 429 | 'region': raw_attrs.get('region', ''), 430 | 'security_groups': parse_list(raw_attrs, 'security_groups'), 431 | # ansible 432 | # workaround for an OpenStack bug where hosts have a different domain 433 | # after they're restarted 434 | 'host_domain': 'novalocal', 435 | 'use_host_domain': True, 436 | # generic 437 | 'public_ipv4': raw_attrs['access_ip_v4'], 438 | 'private_ipv4': raw_attrs['access_ip_v4'], 439 | 'provider': 'openstack', 440 | } 441 | 442 | if 'floating_ip' in raw_attrs: 443 | attrs['private_ipv4'] = raw_attrs['network.0.fixed_ip_v4'] 444 | 445 | try: 446 | attrs.update({ 447 | 'ansible_ssh_host': raw_attrs['access_ip_v4'], 448 | 'publicly_routable': True, 449 | }) 450 | except (KeyError, ValueError): 451 | attrs.update({'ansible_ssh_host': '', 'publicly_routable': False}) 452 | 453 | # attrs specific to Ansible 454 | if 'metadata.ssh_user' in raw_attrs: 455 | attrs['ansible_ssh_user'] = raw_attrs['metadata.ssh_user'] 456 | 457 | # attrs specific to Mantl 458 | attrs.update({ 459 | 'consul_dc': _clean_dc(attrs['metadata'].get('dc', module_name)), 460 | 'role': attrs['metadata'].get('role', 'none'), 461 | 'ansible_python_interpreter': attrs['metadata'] 462 | .get('python_bin', 'python') 463 | }) 464 | 465 | # add groups based on attrs 466 | if 'name' in attrs['image'].keys(): 467 | groups.append('os_image=' + attrs['image']['name']) 468 | groups.append('os_flavor=' + attrs['flavor']['name']) 469 | groups.extend('os_metadata_%s=%s' % item 470 | for item in attrs['metadata'].items()) 471 | groups.append('os_region=' + attrs['region']) 472 | 473 | # groups specific to Mantl 474 | groups.append('role=' + attrs['metadata'].get('role', 'none')) 475 | groups.append('dc=' + attrs['consul_dc']) 476 | 477 | return name, attrs, groups 478 | 479 | 480 | @parses('aws_instance') 481 | @calculate_mantl_vars 482 | def aws_host(resource, module_name): 483 | name = resource['primary']['attributes']['tags.Name'] 484 | raw_attrs = resource['primary']['attributes'] 485 | 486 | groups = [] 487 | 488 | attrs = { 489 | 'ami': raw_attrs['ami'], 490 | 'availability_zone': raw_attrs['availability_zone'], 491 | 'ebs_block_device': parse_attr_list(raw_attrs, 'ebs_block_device'), 492 | 'ebs_optimized': parse_bool(raw_attrs['ebs_optimized']), 493 | 'ephemeral_block_device': parse_attr_list(raw_attrs, 494 | 'ephemeral_block_device'), 495 | 'id': raw_attrs['id'], 496 | 'key_name': raw_attrs['key_name'], 497 | 'private': parse_dict(raw_attrs, 'private', 498 | sep='_'), 499 | 'public': parse_dict(raw_attrs, 'public', 500 | sep='_'), 501 | 'root_block_device': parse_attr_list(raw_attrs, 'root_block_device'), 502 | 'security_groups': parse_list(raw_attrs, 'security_groups'), 503 | 'subnet': parse_dict(raw_attrs, 'subnet', 504 | sep='_'), 505 | 'tags': parse_dict(raw_attrs, 'tags'), 506 | 'tenancy': raw_attrs['tenancy'], 507 | 'vpc_security_group_ids': parse_list(raw_attrs, 508 | 'vpc_security_group_ids'), 509 | # ansible-specific 510 | 'ansible_ssh_host': raw_attrs['public_ip'], 511 | # generic 512 | 'public_ipv4': raw_attrs['public_ip'], 513 | 'private_ipv4': raw_attrs['private_ip'], 514 | 'provider': 'aws', 515 | } 516 | 517 | # attrs specific to Ansible 518 | if 'tags.sshUser' in raw_attrs: 519 | attrs['ansible_ssh_user'] = raw_attrs['tags.sshUser'] 520 | if 'tags.sshPrivateIp' in raw_attrs: 521 | attrs['ansible_ssh_host'] = raw_attrs['private_ip'] 522 | 523 | # add to groups by comma separated tag(s) 524 | if 'tags.groups' in raw_attrs: 525 | for group in raw_attrs['tags.groups'].split(','): 526 | groups.append(group) 527 | 528 | 529 | # attrs specific to Mantl 530 | attrs.update({ 531 | 'consul_dc': _clean_dc(attrs['tags'].get('dc', module_name)), 532 | 'role': attrs['tags'].get('role', 'none'), 533 | 'ansible_python_interpreter': attrs['tags'] 534 | .get('python_bin', 'python') 535 | }) 536 | 537 | # groups specific to Mantl 538 | groups.extend(['aws_ami=' + attrs['ami'], 539 | 'aws_az=' + attrs['availability_zone'], 540 | 'aws_key_name=' + attrs['key_name'], 541 | 'aws_tenancy=' + attrs['tenancy']]) 542 | groups.extend('aws_tag_%s=%s' % item for item in attrs['tags'].items()) 543 | groups.extend('aws_vpc_security_group=' + group 544 | for group in attrs['vpc_security_group_ids']) 545 | groups.extend('aws_subnet_%s=%s' % subnet 546 | for subnet in attrs['subnet'].items()) 547 | 548 | # groups specific to Mantl 549 | groups.append('role=' + attrs['role']) 550 | groups.append('dc=' + attrs['consul_dc']) 551 | 552 | return name, attrs, groups 553 | 554 | 555 | @parses('google_compute_instance') 556 | @calculate_mantl_vars 557 | def gce_host(resource, module_name): 558 | name = resource['primary']['id'] 559 | raw_attrs = resource['primary']['attributes'] 560 | groups = [] 561 | 562 | # network interfaces 563 | interfaces = parse_attr_list(raw_attrs, 'network_interface') 564 | for interface in interfaces: 565 | interface['access_config'] = parse_attr_list(interface, 566 | 'access_config') 567 | for key in interface.keys(): 568 | if '.' in key: 569 | del interface[key] 570 | 571 | # general attrs 572 | attrs = { 573 | 'can_ip_forward': raw_attrs['can_ip_forward'] == 'true', 574 | 'disks': parse_attr_list(raw_attrs, 'disk'), 575 | 'machine_type': raw_attrs['machine_type'], 576 | 'metadata': parse_dict(raw_attrs, 'metadata'), 577 | 'network': parse_attr_list(raw_attrs, 'network'), 578 | 'network_interface': interfaces, 579 | 'self_link': raw_attrs['self_link'], 580 | 'service_account': parse_attr_list(raw_attrs, 'service_account'), 581 | 'tags': parse_list(raw_attrs, 'tags'), 582 | 'zone': raw_attrs['zone'], 583 | # ansible 584 | 'provider': 'gce', 585 | } 586 | 587 | # attrs specific to Ansible 588 | if 'metadata.ssh_user' in raw_attrs: 589 | attrs['ansible_ssh_user'] = raw_attrs['metadata.ssh_user'] 590 | 591 | # attrs specific to Mantl 592 | attrs.update({ 593 | 'consul_dc': _clean_dc(attrs['metadata'].get('dc', module_name)), 594 | 'role': attrs['metadata'].get('role', 'none'), 595 | 'ansible_python_interpreter': attrs['metadata'] 596 | .get('python_bin', 'python') 597 | }) 598 | 599 | try: 600 | attrs.update({ 601 | 'ansible_ssh_host': interfaces[0]['access_config'][0]['nat_ip'] or 602 | interfaces[0]['access_config'][0]['assigned_nat_ip'], 603 | 'public_ipv4': interfaces[0]['access_config'][0]['nat_ip'] or 604 | interfaces[0]['access_config'][0]['assigned_nat_ip'], 605 | 'private_ipv4': interfaces[0]['address'], 606 | 'publicly_routable': True, 607 | }) 608 | except (KeyError, ValueError): 609 | attrs.update({'ansible_ssh_host': '', 'publicly_routable': False}) 610 | 611 | # add groups based on attrs 612 | groups.extend('gce_image=' + disk['image'] for disk in attrs['disks']) 613 | groups.append('gce_machine_type=' + attrs['machine_type']) 614 | groups.extend('gce_metadata_%s=%s' % (key, value) 615 | for (key, value) in attrs['metadata'].items() 616 | if key not in set(['sshKeys'])) 617 | groups.extend('gce_tag=' + tag for tag in attrs['tags']) 618 | groups.append('gce_zone=' + attrs['zone']) 619 | 620 | if attrs['can_ip_forward']: 621 | groups.append('gce_ip_forward') 622 | if attrs['publicly_routable']: 623 | groups.append('gce_publicly_routable') 624 | 625 | # groups specific to Mantl 626 | groups.append('role=' + attrs['metadata'].get('role', 'none')) 627 | groups.append('dc=' + attrs['consul_dc']) 628 | 629 | return name, attrs, groups 630 | 631 | 632 | @parses('vsphere_virtual_machine') 633 | @calculate_mantl_vars 634 | def vsphere_host(resource, module_name): 635 | raw_attrs = resource['primary']['attributes'] 636 | network_attrs = parse_dict(raw_attrs, 'network_interface') 637 | network = parse_dict(network_attrs, '0') 638 | ip_address = network.get('ipv4_address', network['ip_address']) 639 | name = raw_attrs['name'] 640 | groups = [] 641 | 642 | attrs = { 643 | 'id': raw_attrs['id'], 644 | 'ip_address': ip_address, 645 | 'private_ipv4': ip_address, 646 | 'public_ipv4': ip_address, 647 | 'metadata': parse_dict(raw_attrs, 'custom_configuration_parameters'), 648 | 'provider': 'vsphere', 649 | } 650 | 651 | try: 652 | attrs.update({ 653 | 'ansible_ssh_host': ip_address, 654 | }) 655 | except (KeyError, ValueError): 656 | attrs.update({'ansible_ssh_host': '', }) 657 | 658 | attrs.update({ 659 | 'consul_dc': _clean_dc(attrs['metadata'] 660 | .get('consul_dc', module_name)), 661 | 'role': attrs['metadata'].get('role', 'none'), 662 | 'ansible_python_interpreter': attrs['metadata'] 663 | .get('python_bin', 'python') 664 | }) 665 | 666 | # attrs specific to Ansible 667 | if 'ssh_user' in attrs['metadata']: 668 | attrs['ansible_ssh_user'] = attrs['metadata']['ssh_user'] 669 | 670 | groups.append('role=' + attrs['role']) 671 | groups.append('dc=' + attrs['consul_dc']) 672 | 673 | return name, attrs, groups 674 | 675 | 676 | @parses('azurerm_virtual_machine') 677 | @calculate_mantl_vars 678 | def azurerm_host(resource, module_name): 679 | name = resource['primary']['attributes']['name'] 680 | raw_attrs = resource['primary']['attributes'] 681 | 682 | groups = [] 683 | 684 | attrs = { 685 | 'id': raw_attrs['id'], 686 | 'name': raw_attrs['name'], 687 | # ansible 688 | 'ansible_ssh_port': 22, 689 | 'ansible_ssh_user': raw_attrs.get('tags.ssh_user', ''), 690 | 'ansible_ssh_host': raw_attrs.get('tags.ssh_ip', ''), 691 | } 692 | 693 | groups.append('role=' + raw_attrs.get('tags.role', '')) 694 | 695 | return name, attrs, groups 696 | 697 | 698 | @parses('azure_instance') 699 | @calculate_mantl_vars 700 | def azure_host(resource, module_name): 701 | name = resource['primary']['attributes']['name'] 702 | raw_attrs = resource['primary']['attributes'] 703 | 704 | groups = [] 705 | 706 | attrs = { 707 | 'automatic_updates': raw_attrs['automatic_updates'], 708 | 'description': raw_attrs['description'], 709 | 'hosted_service_name': raw_attrs['hosted_service_name'], 710 | 'id': raw_attrs['id'], 711 | 'image': raw_attrs['image'], 712 | 'ip_address': raw_attrs['ip_address'], 713 | 'location': raw_attrs['location'], 714 | 'name': raw_attrs['name'], 715 | 'reverse_dns': raw_attrs['reverse_dns'], 716 | 'security_group': raw_attrs['security_group'], 717 | 'size': raw_attrs['size'], 718 | 'ssh_key_thumbprint': raw_attrs['ssh_key_thumbprint'], 719 | 'subnet': raw_attrs['subnet'], 720 | 'username': raw_attrs['username'], 721 | 'vip_address': raw_attrs['vip_address'], 722 | 'virtual_network': raw_attrs['virtual_network'], 723 | 'endpoint': parse_attr_list(raw_attrs, 'endpoint'), 724 | # ansible 725 | 'ansible_ssh_user': raw_attrs['username'], 726 | 'ansible_ssh_host': raw_attrs['vip_address'], 727 | } 728 | 729 | for ep in attrs['endpoint']: 730 | if ep['name'] == 'SSH': 731 | attrs['ansible_ssh_port'] = int(ep['public_port']) 732 | 733 | # attrs specific to mantl 734 | attrs.update({ 735 | 'consul_dc': attrs['location'].lower().replace(" ", "-"), 736 | 'role': attrs['description'] 737 | }) 738 | 739 | # groups specific to mantl 740 | groups.extend(['azure_image=' + attrs['image'], 741 | 'azure_location=' + attrs['location'] 742 | .lower().replace(" ", "-"), 743 | 'azure_username=' + attrs['username'], 744 | 'azure_security_group=' + attrs['security_group']]) 745 | 746 | # groups specific to mantl 747 | groups.append('role=' + attrs['role']) 748 | groups.append('dc=' + attrs['consul_dc']) 749 | 750 | return name, attrs, groups 751 | 752 | 753 | @parses('clc_server') 754 | @calculate_mantl_vars 755 | def clc_server(resource, module_name): 756 | raw_attrs = resource['primary']['attributes'] 757 | name = raw_attrs.get('id') 758 | groups = [] 759 | md = parse_dict(raw_attrs, 'metadata') 760 | attrs = { 761 | 'metadata': md, 762 | 'ansible_ssh_user': md.get('ssh_user', 'root'), 763 | 'provider': 'clc', 764 | 'publicly_routable': False, 765 | } 766 | 767 | if 'ssh_port' in md: 768 | attrs['ansible_ssh_port'] = md.get('ssh_port') 769 | 770 | try: 771 | attrs.update({ 772 | 'public_ipv4': raw_attrs['public_ip_address'], 773 | 'private_ipv4': raw_attrs['private_ip_address'], 774 | 'ansible_ssh_host': raw_attrs['public_ip_address'], 775 | 'publicly_routable': True, 776 | }) 777 | except (KeyError, ValueError): 778 | attrs.update({ 779 | 'ansible_ssh_host': raw_attrs['private_ip_address'], 780 | 'private_ipv4': raw_attrs['private_ip_address'], 781 | }) 782 | 783 | attrs.update({ 784 | 'consul_dc': _clean_dc(attrs['metadata'].get('dc', module_name)), 785 | 'role': attrs['metadata'].get('role', 'none'), 786 | }) 787 | 788 | groups.append('role=' + attrs['role']) 789 | groups.append('dc=' + attrs['consul_dc']) 790 | return name, attrs, groups 791 | 792 | 793 | @parses('ucs_service_profile') 794 | @calculate_mantl_vars 795 | def ucs_host(resource, module_name): 796 | name = resource['primary']['id'] 797 | raw_attrs = resource['primary']['attributes'] 798 | groups = [] 799 | 800 | # general attrs 801 | attrs = { 802 | 'metadata': parse_dict(raw_attrs, 'metadata'), 803 | 'provider': 'ucs', 804 | } 805 | 806 | # attrs specific to mantl 807 | attrs.update({ 808 | 'consul_dc': _clean_dc(attrs['metadata'].get('dc', module_name)), 809 | 'role': attrs['metadata'].get('role', 'none'), 810 | }) 811 | 812 | try: 813 | attrs.update({ 814 | 'ansible_ssh_host': raw_attrs['vNIC.0.ip'], 815 | 'public_ipv4': raw_attrs['vNIC.0.ip'], 816 | 'private_ipv4': raw_attrs['vNIC.0.ip'] 817 | }) 818 | except (KeyError, ValueError): 819 | attrs.update({'ansible_ssh_host': '', 'publicly_routable': False}) 820 | 821 | # add groups based on attrs 822 | groups.append('role=' + attrs['role']) # .get('role', 'none')) 823 | 824 | # groups.append('all:children') 825 | groups.append('dc=' + attrs['consul_dc']) 826 | 827 | return name, attrs, groups 828 | 829 | 830 | # QUERY TYPES 831 | def query_host(hosts, target): 832 | for name, attrs, _ in hosts: 833 | if name == target: 834 | return attrs 835 | 836 | return {} 837 | 838 | 839 | def query_list(hosts): 840 | groups = defaultdict(dict) 841 | meta = {} 842 | 843 | for name, attrs, hostgroups in hosts: 844 | for group in set(hostgroups): 845 | groups[group].setdefault('hosts', []) 846 | groups[group]['hosts'].append(name) 847 | 848 | meta[name] = attrs 849 | 850 | groups['_meta'] = {'hostvars': meta} 851 | return groups 852 | 853 | 854 | def query_hostfile(hosts): 855 | out = ['## begin hosts generated by terraform.py ##'] 856 | out.extend( 857 | '{}\t{}'.format(attrs['ansible_ssh_host'].ljust(16), name) 858 | for name, attrs, _ in hosts 859 | ) 860 | 861 | out.append('## end hosts generated by terraform.py ##') 862 | return '\n'.join(out) 863 | 864 | 865 | def main(): 866 | parser = argparse.ArgumentParser( 867 | __file__, __doc__, 868 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) 869 | modes = parser.add_mutually_exclusive_group(required=True) 870 | modes.add_argument('--list', 871 | action='store_true', 872 | help='list all variables') 873 | modes.add_argument('--host', help='list variables for a single host') 874 | modes.add_argument('--version', 875 | action='store_true', 876 | help='print version and exit') 877 | modes.add_argument('--hostfile', 878 | action='store_true', 879 | help='print hosts as a /etc/hosts snippet') 880 | parser.add_argument('--pretty', 881 | action='store_true', 882 | help='pretty-print output JSON') 883 | parser.add_argument('--nometa', 884 | action='store_true', 885 | help='with --list, exclude hostvars') 886 | default_root = os.environ.get('TERRAFORM_STATE_ROOT', 887 | os.path.abspath( 888 | os.path.join(os.path.dirname(__file__)))) 889 | parser.add_argument('--root', 890 | default=default_root, 891 | help='custom root to search for `.tfstate`s in') 892 | 893 | args = parser.parse_args() 894 | 895 | if args.version: 896 | print('%s %s' % (__file__, VERSION)) 897 | parser.exit() 898 | 899 | hosts = iterhosts(iterresources(tfstates(args.root))) 900 | if args.list: 901 | output = query_list(hosts) 902 | if args.nometa: 903 | del output['_meta'] 904 | print(json.dumps(output, indent=4 if args.pretty else None)) 905 | elif args.host: 906 | output = query_host(hosts, args.host) 907 | print(json.dumps(output, indent=4 if args.pretty else None)) 908 | elif args.hostfile: 909 | output = query_hostfile(hosts) 910 | print(output) 911 | 912 | parser.exit() 913 | 914 | 915 | if __name__ == '__main__': 916 | main() 917 | -------------------------------------------------------------------------------- /terraform/backend.tf.example: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | bucket = "mk-aws-interns" 4 | key = "artemkin/remote_state" 5 | region = "eu-central-1" 6 | #lock_table = "terraform-state-lock" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /terraform/data.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "image" { 2 | most_recent = true 3 | 4 | filter { 5 | name = "name" 6 | values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64*"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /terraform/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.region}" 3 | } 4 | 5 | module "key_pair" { 6 | source = "modules/key_pair" 7 | key_name = "${var.key_name}" 8 | pub_key_path = "${var.pub_key_path}" 9 | } 10 | 11 | module "base_linux" { 12 | source = "modules/base" 13 | env = "${var.env}" 14 | } 15 | 16 | module "web" { 17 | ami = "${data.aws_ami.image.id}" 18 | source = "modules/web" 19 | pub_key_path = "${var.pub_key_path}" 20 | ssh_user = "${var.ssh_user}" 21 | private_key_path = "${var.private_key_path}" 22 | env = "${var.env}" 23 | key_name = "${module.key_pair.key_name}" 24 | sg_ids = "${module.base_linux.sg_id}" 25 | name = "${var.web_server_params["name"]}" 26 | count = "${var.web_server_params["count"]}" 27 | } 28 | 29 | module "db" { 30 | ami = "${data.aws_ami.image.id}" 31 | source = "modules/db" 32 | pub_key_path = "${var.pub_key_path}" 33 | ssh_user = "${var.ssh_user}" 34 | private_key_path = "${var.private_key_path}" 35 | env = "${var.env}" 36 | key_name = "${module.key_pair.key_name}" 37 | sg_ids = "${module.base_linux.sg_id}" 38 | name = "${var.db_server_params["name"]}" 39 | count = "${var.db_server_params["count"]}" 40 | } 41 | -------------------------------------------------------------------------------- /terraform/modules/base/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_security_group" "base_linux" { 2 | name = "${var.env}_example_base_linux_sg" 3 | description = "Allow SSH access" 4 | 5 | ingress { 6 | from_port = 22 7 | to_port = 22 8 | protocol = "tcp" 9 | cidr_blocks = ["0.0.0.0/0"] 10 | } 11 | 12 | egress { 13 | from_port = 0 14 | to_port = 0 15 | protocol = -1 16 | cidr_blocks = ["0.0.0.0/0"] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /terraform/modules/base/outputs.tf: -------------------------------------------------------------------------------- 1 | output "sg_id" { 2 | value = "${aws_security_group.base_linux.id}" 3 | } 4 | -------------------------------------------------------------------------------- /terraform/modules/base/variables.tf: -------------------------------------------------------------------------------- 1 | variable env { 2 | description = "Environment prefix" 3 | } 4 | -------------------------------------------------------------------------------- /terraform/modules/db/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_instance" "db" { 2 | ami = "${var.ami}" 3 | count = "${var.count}" 4 | instance_type = "${var.instance_type}" 5 | key_name = "${var.key_name}" 6 | 7 | vpc_security_group_ids = [ 8 | "${var.sg_ids}", 9 | "${aws_security_group.db.id}", 10 | ] 11 | 12 | tags { 13 | Name = "${var.env}_${format("${var.name}%02d", count.index+1)}" 14 | Group = "${var.env}_${var.name}_cluster" 15 | } 16 | 17 | provisioner "remote-exec" { 18 | inline = "#Connected!" 19 | 20 | connection { 21 | agent = false 22 | type = "ssh" 23 | user = "${var.ssh_user}" 24 | private_key = "${file(var.private_key_path)}" 25 | } 26 | } 27 | } 28 | 29 | resource "aws_security_group" "db" { 30 | name = "${var.env}_example_db_sg" 31 | description = "Allow DB access" 32 | 33 | ingress { 34 | from_port = 3306 35 | to_port = 3306 36 | protocol = "tcp" 37 | cidr_blocks = ["0.0.0.0/0"] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /terraform/modules/db/outputs.tf: -------------------------------------------------------------------------------- 1 | output "public_ip" { 2 | value = ["${aws_instance.db.*.public_ip}"] 3 | } 4 | 5 | output "id_list" { 6 | value = ["${aws_instance.db.*.id}"] 7 | } 8 | -------------------------------------------------------------------------------- /terraform/modules/db/variables.tf: -------------------------------------------------------------------------------- 1 | variable pub_key_path { 2 | description = "Path to ssh public key used to create this key on AWS" 3 | } 4 | 5 | variable ssh_user { 6 | description = "User used to log in to instance" 7 | } 8 | 9 | variable private_key_path { 10 | description = "Path to the private key used to connect to instance" 11 | } 12 | 13 | variable env { 14 | description = "Environment prefix" 15 | } 16 | 17 | variable ami { 18 | description = "Instance AMI" 19 | } 20 | 21 | variable instance_type { 22 | description = "Instance type" 23 | default = "t2.micro" 24 | } 25 | 26 | variable sg_ids { 27 | description = "List of security groups ids" 28 | } 29 | 30 | variable key_name { 31 | description = "Name of ssh key to create" 32 | } 33 | 34 | variable count { 35 | description = "Number of instances to create" 36 | default = "1" 37 | } 38 | 39 | variable name { 40 | description = "Server name" 41 | default = "db" 42 | } 43 | -------------------------------------------------------------------------------- /terraform/modules/key_pair/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_key_pair" "auth" { 2 | key_name = "${var.key_name}" 3 | public_key = "${file("${var.pub_key_path}")}" 4 | } 5 | -------------------------------------------------------------------------------- /terraform/modules/key_pair/outputs.tf: -------------------------------------------------------------------------------- 1 | output "key_name" { 2 | value = "${aws_key_pair.auth.key_name}" 3 | } 4 | -------------------------------------------------------------------------------- /terraform/modules/key_pair/variables.tf: -------------------------------------------------------------------------------- 1 | variable key_name { 2 | description = "Name of ssh key to create" 3 | } 4 | 5 | variable pub_key_path { 6 | description = "Path to ssh public key used to create this key on AWS" 7 | } 8 | -------------------------------------------------------------------------------- /terraform/modules/web/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_instance" "web" { 2 | ami = "${var.ami}" 3 | count = "${var.count}" 4 | instance_type = "${var.instance_type}" 5 | key_name = "${var.key_name}" 6 | 7 | vpc_security_group_ids = [ 8 | "${var.sg_ids}", 9 | "${aws_security_group.web.id}", 10 | ] 11 | 12 | tags { 13 | Name = "${var.env}_${format("${var.name}%02d", count.index+1)}" 14 | Group = "${var.env}_${var.name}_cluster" 15 | } 16 | 17 | provisioner "remote-exec" { 18 | inline = "#Connected!" 19 | 20 | connection { 21 | agent = false 22 | type = "ssh" 23 | user = "${var.ssh_user}" 24 | private_key = "${file(var.private_key_path)}" 25 | } 26 | } 27 | } 28 | 29 | resource "aws_security_group" "web" { 30 | name = "${var.env}_example_web_sg" 31 | description = "Allow HTTP access" 32 | 33 | ingress { 34 | from_port = 80 35 | to_port = 80 36 | protocol = "tcp" 37 | cidr_blocks = ["0.0.0.0/0"] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /terraform/modules/web/outputs.tf: -------------------------------------------------------------------------------- 1 | output "public_ip" { 2 | value = ["${aws_instance.web.*.public_ip}"] 3 | } 4 | 5 | output "id_list" { 6 | value = ["${aws_instance.web.*.id}"] 7 | } 8 | -------------------------------------------------------------------------------- /terraform/modules/web/variables.tf: -------------------------------------------------------------------------------- 1 | variable pub_key_path { 2 | description = "Path to ssh public key used to create this key on AWS" 3 | } 4 | 5 | variable ssh_user { 6 | description = "User used to log in to instance" 7 | } 8 | 9 | variable private_key_path { 10 | description = "Path to the private key used to connect to instance" 11 | } 12 | 13 | variable env { 14 | description = "Environment prefix" 15 | } 16 | 17 | variable ami { 18 | description = "Instance AMI" 19 | } 20 | 21 | variable instance_type { 22 | description = "instance type" 23 | default = "t2.micro" 24 | } 25 | 26 | variable sg_ids { 27 | description = "List of security groups ids" 28 | } 29 | 30 | variable key_name { 31 | description = "Name of ssh key to create" 32 | } 33 | 34 | variable count { 35 | description = "Number of instances to create" 36 | default = "1" 37 | } 38 | 39 | variable name { 40 | description = "Server name" 41 | default = "web" 42 | } 43 | -------------------------------------------------------------------------------- /terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "web_address" { 2 | value = ["${module.web.public_ip}"] 3 | } 4 | 5 | output "db_address" { 6 | value = ["${module.db.public_ip}"] 7 | } 8 | -------------------------------------------------------------------------------- /terraform/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | pub_key_path = "~/.ssh/express42.pub" 2 | private_key_path = "~/.ssh/express42" 3 | key_name = "astarostenko" 4 | 5 | web_server_params { 6 | count = "1" 7 | } 8 | 9 | db_server_params { 10 | count = "1" 11 | } 12 | -------------------------------------------------------------------------------- /terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable pub_key_path { 2 | description = "Path to ssh public key used to create this key on AWS" 3 | } 4 | 5 | variable ssh_user { 6 | description = "User used to log in to instance" 7 | default = "ubuntu" 8 | } 9 | 10 | variable private_key_path { 11 | description = "Path to the private key used to connect to instance" 12 | } 13 | 14 | variable region { 15 | description = "Region" 16 | default = "eu-central-1" 17 | } 18 | 19 | variable env { 20 | description = "Environment prefix" 21 | default = "dev" 22 | } 23 | 24 | variable db_server_params { 25 | default = { 26 | "name" = "db" 27 | "count" = "1" 28 | } 29 | } 30 | 31 | variable web_server_params { 32 | default = { 33 | "name" = "web" 34 | "count" = "1" 35 | } 36 | } 37 | 38 | variable key_name { 39 | description = "name of ssh key to create" 40 | } 41 | --------------------------------------------------------------------------------