├── ansible-modules ├── README.md ├── ansible.cfg ├── examples_common_modules.yml ├── hosts ├── library │ └── epoch_converter.py └── test_custom_module.yml ├── ansible-roles ├── README.md ├── Vagrantfile ├── ansible.cfg ├── hosts ├── main_playbook.yml └── roles │ ├── test_role │ ├── .travis.yml │ ├── README.md │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ ├── tests │ │ ├── inventory │ │ └── test.yml │ └── vars │ │ └── main.yml │ └── webserver │ ├── README.md │ ├── defaults │ └── main.yml │ ├── files │ └── index.html │ ├── handlers │ └── main.yml │ ├── meta │ └── main.yml │ ├── tasks │ └── main.yml │ ├── templates │ └── nginx.conf.j2 │ └── vars │ └── main.yml ├── ansible-variables ├── .gitignore ├── README.md ├── Vagrantfile ├── ansible.cfg ├── example-anchors-aliases.yml ├── example-external-vars.yml ├── example-register-variable-conditionals.yml ├── example-register-variable.yml ├── example-simple-variable.yml ├── example-variable-types.yml ├── group_vars │ ├── all │ └── webservers ├── host_vars │ ├── host1 │ └── host2 ├── hosts ├── roles │ └── common │ │ ├── defaults │ │ └── main.yml │ │ └── vars │ │ └── main.yml └── vars │ └── variables.yml ├── az-ad-group-module ├── ad_group │ ├── main.tf │ └── variables.tf ├── azure-pipeline.yaml ├── main.tf ├── terraform.tfvars └── variables.tf ├── cdktf-aws-python-webserver ├── .gitignore ├── Pipfile ├── Pipfile.lock ├── README.md ├── cdktf.json ├── help ├── main-test.py └── main.py ├── eks-blueprints-terraform ├── README.md ├── dev_teams.tf ├── eks_blueprints_addons.tf ├── initial_skeleton │ ├── main.tf │ ├── variables.tf │ └── versions.tf ├── karpenter.tf ├── main.tf ├── outputs.tf ├── platform_team.tf ├── variables.tf └── versions.tf ├── iac-examples ├── README.MD ├── storage.bicep ├── storage.json ├── storage.tf └── storage_pulumi.py ├── terraform-best-practices ├── README.md ├── separate-environments-project-structure │ ├── environment │ │ ├── production │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ ├── terraform.tfvars │ │ │ └── variables.tf │ │ ├── staging │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ ├── terraform.tfvars │ │ │ └── variables.tf │ │ └── test │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ ├── terraform.tfvars │ │ │ └── variables.tf │ └── modules │ │ └── vpc │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── varariables.tf └── small-terraform-project-structure │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── terraform.tfvars │ └── variables.tf ├── terraform-github-actions ├── .github │ └── workflows │ │ └── terraform.yml ├── README.md └── terraform │ └── main.tf ├── terraform-output ├── README.md ├── main.tf ├── modules │ ├── aws-web-server-instance │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── aws-web-server-vpc │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── outputs.tf ├── terraform.tfvars └── variables.tf ├── terraform-tfvars ├── dev.auto.tfvars ├── main.tf ├── modules │ ├── main.tf │ ├── terraform.tfvars │ └── variable.tf ├── prod.tfvars └── variable.tf └── working-with-ansible-playbooks ├── README.md ├── Vagrantfile ├── ansible.cfg ├── example-conditionals-playbook.yml ├── example-handlers-playbook.yml ├── example-loops-playbook.yml ├── example-secrets-playbook.yml ├── example-simple-playbook.yml ├── example-variables-2-playbook.yml ├── example-variables-playbook.yml ├── example_file └── hosts /ansible-modules/README.md: -------------------------------------------------------------------------------- 1 | This repository includes content related to the [Ansible Modules]() demo on Spacelift blog. -------------------------------------------------------------------------------- /ansible-modules/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory=./hosts 3 | deprecation_warnings=False 4 | nocows = 1 5 | duplicate_dict_key=ignore 6 | action_warnings=False -------------------------------------------------------------------------------- /ansible-modules/examples_common_modules.yml: -------------------------------------------------------------------------------- 1 | - name: Examples Common Modules 2 | hosts: all 3 | vars: 4 | nginx_version: 1.18.0-0ubuntu1.3 5 | 6 | tasks: 7 | - name: "Install Nginx to version {{ nginx_version }} with apt module" 8 | ansible.builtin.apt: 9 | name: "nginx={{ nginx_version }}" 10 | state: present 11 | 12 | - name: Update the repository cache and update package "nginx" to latest version 13 | ansible.builtin.apt: 14 | name: nginx 15 | state: latest 16 | update_cache: yes 17 | 18 | - name: Update the repository cache and update package "nginx" to latest version 19 | ansible.builtin.yum: 20 | name: nginx 21 | state: latest 22 | update_cache: yes 23 | 24 | - name: Restart docker service 25 | ansible.builtin.service: 26 | name: docker 27 | state: restarted 28 | 29 | - name: Create the directory "/etc/test" if it doesnt exist and set permissions 30 | ansible.builtin.file: 31 | path: /etc/test 32 | state: directory 33 | mode: '0750' 34 | 35 | - name: Copy file with owner and permissions 36 | ansible.builtin.copy: 37 | src: /example_directory/test 38 | dest: /target_directory/test 39 | owner: joe 40 | group: admins 41 | mode: '0755' 42 | 43 | - name: Copy and template the Nginx configuration file to the host 44 | ansible.builtin.template: 45 | src: templates/nginx.conf.j2 46 | dest: /etc/nginx/sites-available/default 47 | 48 | - name: Add a line to a file if it doesnt exist 49 | ansible.builtin.lineinfile: 50 | path: /tmp/example_file 51 | line: "This line must exist in the file" 52 | state: present 53 | 54 | - name: Add a block of configuration options at the end of the file if it doesnt exist 55 | ansible.builtin.blockinfile: 56 | path: /etc/example_dictory/example.conf 57 | block: | 58 | feature1_enabled: true 59 | feature2_enabled: false 60 | feature2_enabled: true 61 | insertafter: EOF 62 | 63 | - name: Run daily DB backup script at 00:00 64 | ansible.builtin.cron: 65 | name: "Run daily DB backup script at 00:00" 66 | minute: "0" 67 | hour: "0" 68 | job: "/usr/local/bin/db_backup_script.sh > /var/log/db_backup_script.sh.log 2>&1" 69 | 70 | - name: Wait until a string is in the file before continuing 71 | ansible.builtin.wait_for: 72 | path: /tmp/example_file 73 | search_regex: "String exists, continue" 74 | 75 | - name: Execute a script in remote shell and capture the output to file 76 | ansible.builtin.shell: script.sh >> script_output.log 77 | 78 | - name: Install Docker dependencies 79 | ansible.builtin.apt: 80 | name: 81 | - curl 82 | - ca-certificates 83 | - gnupg2 84 | - lsb-release 85 | state: latest -------------------------------------------------------------------------------- /ansible-modules/hosts: -------------------------------------------------------------------------------- 1 | [testgroup] 2 | host1 ansible_host=127.0.0.1 -------------------------------------------------------------------------------- /ansible-modules/library/epoch_converter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from __future__ import (absolute_import, division, print_function) 4 | __metaclass__ = type 5 | import datetime 6 | 7 | DOCUMENTATION = r''' 8 | --- 9 | module: epoch_converter 10 | 11 | short_description: This module converts an epoch timestamp to human-readable date. 12 | 13 | # If this is part of a collection, you need to use semantic versioning, 14 | # i.e. the version is of the form "2.5.0" and not "2.4". 15 | version_added: "1.0.0" 16 | 17 | description: This modules takes a string that represents a Unix epoch timestamp and displays its human-readable date equivalent. 18 | 19 | options: 20 | epoch_timestamp: 21 | description: This is the string that represents a Unix epoch timestamp. 22 | required: true 23 | type: str 24 | state_changed: 25 | description: This string simulates a modification of the target's state. 26 | required: false 27 | type: bool 28 | 29 | author: 30 | - Ioannis Moustakis (@Imoustak) 31 | ''' 32 | 33 | EXAMPLES = r''' 34 | # Convert an epoch timestamp 35 | - name: Convert an epoch timestamp 36 | epoch_converter: 37 | epoch_timestamp: 1657382362 38 | ''' 39 | 40 | RETURN = r''' 41 | # These are examples of possible return values, and in general should use other names for return values. 42 | human_readable_date: 43 | description: The human-readable equivalent of the epoch timestamp input. 44 | type: str 45 | returned: always 46 | sample: '2022-07-09T17:59:22' 47 | original_timestamp: 48 | description: The original epoch timestamp input. 49 | type: str 50 | returned: always 51 | sample: '16573823622' 52 | 53 | ''' 54 | 55 | from ansible.module_utils.basic import AnsibleModule 56 | 57 | 58 | def run_module(): 59 | # define available arguments/parameters a user can pass to the module 60 | module_args = dict( 61 | epoch_timestamp=dict(type='str', required=True), 62 | state_changed=dict(type='bool', required=False) 63 | ) 64 | 65 | # seed the result dict in the object 66 | # we primarily care about changed and state 67 | # changed is if this module effectively modified the target 68 | # state will include any data that you want your module to pass back 69 | # for consumption, for example, in a subsequent task 70 | result = dict( 71 | changed=False, 72 | human_readable_date='', 73 | original_timestamp='' 74 | ) 75 | 76 | # the AnsibleModule object will be our abstraction working with Ansible 77 | # this includes instantiation, a couple of common attr would be the 78 | # args/params passed to the execution, as well as if the module 79 | # supports check mode 80 | module = AnsibleModule( 81 | argument_spec=module_args, 82 | supports_check_mode=True 83 | ) 84 | 85 | # if the user is working with this module in only check mode we do not 86 | # want to make any changes to the environment, just return the current 87 | # state with no modifications 88 | if module.check_mode: 89 | module.exit_json(**result) 90 | 91 | # manipulate or modify the state as needed (this is going to be the 92 | # part where your module will do what it needs to do) 93 | result['original_timestamp'] = module.params['epoch_timestamp'] 94 | result['human_readable_date'] = datetime.datetime.fromtimestamp(int(module.params['epoch_timestamp'])) 95 | 96 | # use whatever logic you need to determine whether or not this module 97 | # made any modifications to your target 98 | if module.params['state_changed']: 99 | result['changed'] = True 100 | 101 | # during the execution of the module, if there is an exception or a 102 | # conditional state that effectively causes a failure, run 103 | # AnsibleModule.fail_json() to pass in the message and the result 104 | if module.params['epoch_timestamp'] == 'fail': 105 | module.fail_json(msg='You requested this to fail', **result) 106 | 107 | # in the event of a successful module execution, you will want to 108 | # simple AnsibleModule.exit_json(), passing the key/value results 109 | module.exit_json(**result) 110 | 111 | 112 | def main(): 113 | run_module() 114 | 115 | 116 | if __name__ == '__main__': 117 | main() 118 | -------------------------------------------------------------------------------- /ansible-modules/test_custom_module.yml: -------------------------------------------------------------------------------- 1 | - name: Test my new module 2 | hosts: localhost 3 | tasks: 4 | - name: Run the new module 5 | epoch_converter: 6 | epoch_timestamp: '1657382362' 7 | state_changed: yes 8 | register: show_output 9 | - name: Show Output 10 | debug: 11 | msg: '{{ show_output }}' -------------------------------------------------------------------------------- /ansible-roles/README.md: -------------------------------------------------------------------------------- 1 | This repository includes content related to the [Ansible Roles](https://spacelift.io/blog/ansible-roles) demo on Spacelift blog. -------------------------------------------------------------------------------- /ansible-roles/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://vagrantcloud.com/search. 15 | 16 | config.vm.define "host1" do |host1| 17 | host1.vm.box = "ubuntu/focal64" 18 | end 19 | 20 | 21 | # Disable automatic box update checking. If you disable this, then 22 | # boxes will only be checked for updates when the user runs 23 | # `vagrant box outdated`. This is not recommended. 24 | # config.vm.box_check_update = false 25 | 26 | # Create a forwarded port mapping which allows access to a specific port 27 | # within the machine from a port on the host machine. In the example below, 28 | # accessing "localhost:8080" will access port 80 on the guest machine. 29 | # NOTE: This will enable public access to the opened port 30 | # config.vm.network "forwarded_port", guest: 80, host: 8080 31 | 32 | # Create a forwarded port mapping which allows access to a specific port 33 | # within the machine from a port on the host machine and only allow access 34 | # via 127.0.0.1 to disable public access 35 | # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" 36 | 37 | # Create a private network, which allows host-only access to the machine 38 | # using a specific IP. 39 | # config.vm.network "private_network", ip: "192.168.33.10" 40 | 41 | # Create a public network, which generally matched to bridged network. 42 | # Bridged networks make the machine appear as another physical device on 43 | # your network. 44 | # config.vm.network "public_network" 45 | 46 | # Share an additional folder to the guest VM. The first argument is 47 | # the path on the host to the actual folder. The second argument is 48 | # the path on the guest to mount the folder. And the optional third 49 | # argument is a set of non-required options. 50 | # config.vm.synced_folder "../data", "/vagrant_data" 51 | 52 | # Provider-specific configuration so you can fine-tune various 53 | # backing providers for Vagrant. These expose provider-specific options. 54 | # Example for VirtualBox: 55 | # 56 | # config.vm.provider "virtualbox" do |vb| 57 | # # Display the VirtualBox GUI when booting the machine 58 | # vb.gui = true 59 | # 60 | # # Customize the amount of memory on the VM: 61 | # vb.memory = "1024" 62 | # end 63 | # 64 | # View the documentation for the provider you are using for more 65 | # information on available options. 66 | 67 | # Enable provisioning with a shell script. Additional provisioners such as 68 | # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the 69 | # documentation for more information about their specific syntax and use. 70 | # config.vm.provision "shell", inline: <<-SHELL 71 | # apt-get update 72 | # apt-get install -y apache2 73 | # SHELL 74 | end 75 | -------------------------------------------------------------------------------- /ansible-roles/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory=./hosts 3 | deprecation_warnings=False 4 | nocows = 1 5 | duplicate_dict_key=ignore 6 | action_warnings=False -------------------------------------------------------------------------------- /ansible-roles/hosts: -------------------------------------------------------------------------------- 1 | [webservers] 2 | host1 ansible_host=127.0.0.1 ansible_user=vagrant ansible_port=2222 ansible_ssh_private_key_file=./.vagrant/machines/host1/virtualbox/private_key -------------------------------------------------------------------------------- /ansible-roles/main_playbook.yml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | become: true 3 | roles: 4 | - role: webserver -------------------------------------------------------------------------------- /ansible-roles/roles/test_role/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | 5 | # Use the new container infrastructure 6 | sudo: false 7 | 8 | # Install ansible 9 | addons: 10 | apt: 11 | packages: 12 | - python-pip 13 | 14 | install: 15 | # Install ansible 16 | - pip install ansible 17 | 18 | # Check ansible version 19 | - ansible --version 20 | 21 | # Create ansible.cfg with correct roles_path 22 | - printf '[defaults]\nroles_path=../' >ansible.cfg 23 | 24 | script: 25 | # Basic role syntax check 26 | - ansible-playbook tests/test.yml -i tests/inventory --syntax-check 27 | 28 | notifications: 29 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ -------------------------------------------------------------------------------- /ansible-roles/roles/test_role/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 20 | 21 | Example Playbook 22 | ---------------- 23 | 24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 25 | 26 | - hosts: servers 27 | roles: 28 | - { role: username.rolename, x: 42 } 29 | 30 | License 31 | ------- 32 | 33 | BSD 34 | 35 | Author Information 36 | ------------------ 37 | 38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 39 | -------------------------------------------------------------------------------- /ansible-roles/roles/test_role/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for test_role 3 | -------------------------------------------------------------------------------- /ansible-roles/roles/test_role/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for test_role 3 | -------------------------------------------------------------------------------- /ansible-roles/roles/test_role/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: your name 3 | description: your role description 4 | company: your company (optional) 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Choose a valid license ID from https://spdx.org - some suggested licenses: 11 | # - BSD-3-Clause (default) 12 | # - MIT 13 | # - GPL-2.0-or-later 14 | # - GPL-3.0-only 15 | # - Apache-2.0 16 | # - CC-BY-4.0 17 | license: license (GPL-2.0-or-later, MIT, etc) 18 | 19 | min_ansible_version: 2.1 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # 25 | # Provide a list of supported platforms, and for each platform a list of versions. 26 | # If you don't wish to enumerate all versions for a particular platform, use 'all'. 27 | # To view available platforms and versions (or releases), visit: 28 | # https://galaxy.ansible.com/api/v1/platforms/ 29 | # 30 | # platforms: 31 | # - name: Fedora 32 | # versions: 33 | # - all 34 | # - 25 35 | # - name: SomePlatform 36 | # versions: 37 | # - all 38 | # - 1.0 39 | # - 7 40 | # - 99.99 41 | 42 | galaxy_tags: [] 43 | # List tags for your role here, one per line. A tag is a keyword that describes 44 | # and categorizes the role. Users find roles by searching for tags. Be sure to 45 | # remove the '[]' above, if you add tags to this list. 46 | # 47 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 48 | # Maximum 20 tags per role. 49 | 50 | dependencies: [] 51 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 52 | # if you add dependencies to this list. 53 | -------------------------------------------------------------------------------- /ansible-roles/roles/test_role/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for test_role 3 | -------------------------------------------------------------------------------- /ansible-roles/roles/test_role/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /ansible-roles/roles/test_role/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - test_role 6 | -------------------------------------------------------------------------------- /ansible-roles/roles/test_role/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for test_role 3 | -------------------------------------------------------------------------------- /ansible-roles/roles/webserver/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | This is a role created for demonstration purposes that configures a basic nginx webserver with a minimal configuration. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any prerequisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | * Ansible 12 | * Jinja2 13 | 14 | Role Variables 15 | -------------- 16 | 17 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 18 | 19 | ### defaults/main.yml 20 | Default nginx installation variables. 21 | 22 | * nginx_version: Specific version of nginx to install 23 | * nginx_custom_directory: Custom directory for nginx installation 24 | 25 | ### vars/main.yml 26 | Here we define variables that have high precedence and aren't intended to be changed by the play. 27 | 28 | * nginx_custom_directory: Custom directory for nginx installation 29 | 30 | Dependencies 31 | ------------ 32 | 33 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 34 | 35 | Example Playbook 36 | ---------------- 37 | 38 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 39 | 40 | - hosts: all 41 | become: true 42 | roles: 43 | - webserver 44 | 45 | License 46 | ------- 47 | 48 | Apache-2.0 49 | 50 | Author Information 51 | ------------------ 52 | 53 | Ioannis Moustakis 54 | -------------------------------------------------------------------------------- /ansible-roles/roles/webserver/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for webserver 3 | nginx_version: 1.18.0-0ubuntu1.3 4 | nginx_custom_directory: /var/www/example_domain -------------------------------------------------------------------------------- /ansible-roles/roles/webserver/files/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello from Ngnix 4 | 5 | 6 |

This is our test webserver

7 |

This Nginx web server was deployed by Ansible.

8 | 9 | -------------------------------------------------------------------------------- /ansible-roles/roles/webserver/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for webserver 3 | - name: Restart the Nginx service 4 | service: 5 | name: nginx 6 | state: restarted -------------------------------------------------------------------------------- /ansible-roles/roles/webserver/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Ioannis Mosutakis 3 | description: Installs nginx and configures a minimal test webserver 4 | company: ACME Corp 5 | license: Apache-2.0 6 | role_name: websercer 7 | 8 | min_ansible_version: "2.1" 9 | 10 | # If this is a Container Enabled role, provide the minimum Ansible Container version. 11 | # min_ansible_container_version: 12 | 13 | # 14 | # Provide a list of supported platforms, and for each platform a list of versions. 15 | # If you don't wish to enumerate all versions for a particular platform, use 'all'. 16 | # To view available platforms and versions (or releases), visit: 17 | # https://galaxy.ansible.com/api/v1/platforms/ 18 | # 19 | platforms: 20 | - name: Ubuntu 21 | versions: 22 | - bionic 23 | - focal 24 | 25 | galaxy_tags: 26 | - nginx 27 | - webserver 28 | - development 29 | - test 30 | 31 | dependencies: [] 32 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 33 | # if you add dependencies to this list. 34 | -------------------------------------------------------------------------------- /ansible-roles/roles/webserver/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for webserver 3 | - name: Update and upgrade apt 4 | ansible.builtin.apt: 5 | update_cache: yes 6 | cache_valid_time: 3600 7 | upgrade: yes 8 | 9 | - name: "Install Nginx to version {{ nginx_version }}" 10 | ansible.builtin.apt: 11 | name: "nginx={{ nginx_version }}" 12 | state: present 13 | 14 | - name: Copy the Nginx configuration file to the host 15 | template: 16 | src: templates/nginx.conf.j2 17 | dest: /etc/nginx/sites-available/default 18 | 19 | - name: Create link to the new config to enable it 20 | file: 21 | dest: /etc/nginx/sites-enabled/default 22 | src: /etc/nginx/sites-available/default 23 | state: link 24 | 25 | - name: Create Nginx directory 26 | ansible.builtin.file: 27 | path: "{{ nginx_custom_directory }}" 28 | state: directory 29 | 30 | - name: Copy index.html to the Nginx directory 31 | copy: 32 | src: files/index.html 33 | dest: "{{ nginx_custom_directory }}/index.html" 34 | notify: Restart the Nginx service -------------------------------------------------------------------------------- /ansible-roles/roles/webserver/templates/nginx.conf.j2: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | root {{ nginx_custom_directory }}; 5 | index index.html; 6 | location / { 7 | try_files $uri $uri/ =404; 8 | } 9 | } -------------------------------------------------------------------------------- /ansible-roles/roles/webserver/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for webserver 3 | nginx_custom_directory: /home/ubuntu/nginx -------------------------------------------------------------------------------- /ansible-variables/.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant/ -------------------------------------------------------------------------------- /ansible-variables/README.md: -------------------------------------------------------------------------------- 1 | # Working with Ansible Playbooks 2 | This repository includes all the files related to the [Ansible Variables](https://spacelift.io/blog/ansible-variables) blog post. If you want to run the examples on this repo you can execute `vagrant up` from the top of this repository in order to spin up two local vms to target with Ansible. Requirements to follow along: [Vagrant](https://www.vagrantup.com/docs/installation) and [VirtualBox](https://www.virtualbox.org/). -------------------------------------------------------------------------------- /ansible-variables/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://vagrantcloud.com/search. 15 | 16 | config.vm.define "host1" do |host1| 17 | host1.vm.box = "hashicorp/bionic64" 18 | end 19 | 20 | config.vm.define "host2" do |host2| 21 | host2.vm.box = "hashicorp/bionic64" 22 | end 23 | 24 | # Disable automatic box update checking. If you disable this, then 25 | # boxes will only be checked for updates when the user runs 26 | # `vagrant box outdated`. This is not recommended. 27 | # config.vm.box_check_update = false 28 | 29 | # Create a forwarded port mapping which allows access to a specific port 30 | # within the machine from a port on the host machine. In the example below, 31 | # accessing "localhost:8080" will access port 80 on the guest machine. 32 | # NOTE: This will enable public access to the opened port 33 | # config.vm.network "forwarded_port", guest: 80, host: 8080 34 | 35 | # Create a forwarded port mapping which allows access to a specific port 36 | # within the machine from a port on the host machine and only allow access 37 | # via 127.0.0.1 to disable public access 38 | # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" 39 | 40 | # Create a private network, which allows host-only access to the machine 41 | # using a specific IP. 42 | # config.vm.network "private_network", ip: "192.168.33.10" 43 | 44 | # Create a public network, which generally matched to bridged network. 45 | # Bridged networks make the machine appear as another physical device on 46 | # your network. 47 | # config.vm.network "public_network" 48 | 49 | # Share an additional folder to the guest VM. The first argument is 50 | # the path on the host to the actual folder. The second argument is 51 | # the path on the guest to mount the folder. And the optional third 52 | # argument is a set of non-required options. 53 | # config.vm.synced_folder "../data", "/vagrant_data" 54 | 55 | # Provider-specific configuration so you can fine-tune various 56 | # backing providers for Vagrant. These expose provider-specific options. 57 | # Example for VirtualBox: 58 | # 59 | # config.vm.provider "virtualbox" do |vb| 60 | # # Display the VirtualBox GUI when booting the machine 61 | # vb.gui = true 62 | # 63 | # # Customize the amount of memory on the VM: 64 | # vb.memory = "1024" 65 | # end 66 | # 67 | # View the documentation for the provider you are using for more 68 | # information on available options. 69 | 70 | # Enable provisioning with a shell script. Additional provisioners such as 71 | # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the 72 | # documentation for more information about their specific syntax and use. 73 | # config.vm.provision "shell", inline: <<-SHELL 74 | # apt-get update 75 | # apt-get install -y apache2 76 | # SHELL 77 | end 78 | -------------------------------------------------------------------------------- /ansible-variables/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory=./hosts 3 | deprecation_warnings=False 4 | nocows = 1 5 | duplicate_dict_key=ignore -------------------------------------------------------------------------------- /ansible-variables/example-anchors-aliases.yml: -------------------------------------------------------------------------------- 1 | - name: Example Anchors and Aliases 2 | hosts: all 3 | become: yes 4 | vars: 5 | user_groups: &user_groups 6 | - devs 7 | - support 8 | user_1: 9 | user_info: &user_info 10 | name: bob 11 | groups: *user_groups 12 | state: present 13 | create_home: yes 14 | user_2: 15 | user_info: 16 | <<: *user_info 17 | name: christina 18 | user_3: 19 | user_info: 20 | <<: *user_info 21 | name: jessica 22 | groups: support 23 | 24 | tasks: 25 | - name: Add several groups 26 | ansible.builtin.group: 27 | name: "{{ item }}" 28 | state: present 29 | loop: "{{ user_groups }}" 30 | 31 | - name: Add several users 32 | ansible.builtin.user: 33 | <<: *user_info 34 | name: "{{ item.user_info.name }}" 35 | groups: "{{ item.user_info.groups }}" 36 | loop: 37 | - "{{ user_1 }}" 38 | - "{{ user_2 }}" 39 | - "{{ user_3 }}" -------------------------------------------------------------------------------- /ansible-variables/example-external-vars.yml: -------------------------------------------------------------------------------- 1 | - name: Example External Variables file 2 | hosts: all 3 | vars_files: 4 | - ./vars/variables.yml 5 | 6 | tasks: 7 | - name: Print the value of variable docker_version 8 | debug: 9 | msg: "{{ docker_version}} " 10 | 11 | - name: Print the value of group variable http_port 12 | debug: 13 | msg: "{{ http_port}} " 14 | 15 | - name: Print the value of host variable app_version 16 | debug: 17 | msg: "{{ app_version}} " -------------------------------------------------------------------------------- /ansible-variables/example-register-variable-conditionals.yml: -------------------------------------------------------------------------------- 1 | - name: Example Registered Variables Conditionals 2 | hosts: all 3 | 4 | tasks: 5 | - name: Register an example variable 6 | shell: cat /etc/hosts 7 | register: hosts_contents 8 | 9 | - name: Check if hosts file contains the word "localhost" 10 | debug: 11 | msg: "/etc/hosts file contains the word localhost" 12 | when: hosts_contents.stdout.find("localhost") != -1 -------------------------------------------------------------------------------- /ansible-variables/example-register-variable.yml: -------------------------------------------------------------------------------- 1 | - name: Example Register Variable Playbook 2 | hosts: all 3 | 4 | tasks: 5 | - name: Run a script and register the output as a variable 6 | shell: "find hosts" 7 | args: 8 | chdir: "/etc" 9 | register: find_hosts_output 10 | - name: Use the output variable of the previous task 11 | debug: 12 | var: find_hosts_output -------------------------------------------------------------------------------- /ansible-variables/example-simple-variable.yml: -------------------------------------------------------------------------------- 1 | - name: Example Simple Variable 2 | hosts: all 3 | become: yes 4 | vars: 5 | username: bob 6 | 7 | tasks: 8 | - name: Add the user {{ username }} 9 | ansible.builtin.user: 10 | name: "{{ username }}" 11 | state: present -------------------------------------------------------------------------------- /ansible-variables/example-variable-types.yml: -------------------------------------------------------------------------------- 1 | - name: Example Variable Types 2 | hosts: all 3 | vars: 4 | username: bob 5 | version: 6 | - v1 7 | - v2 8 | - v3 9 | users: 10 | - user_1: maria 11 | - user_2: peter 12 | - user_3: sophie 13 | cidr_blocks: 14 | production: 15 | vpc_cidr: "172.31.0.0/16" 16 | staging: 17 | vpc_cidr: "10.0.0.0/24" 18 | 19 | 20 | 21 | tasks: 22 | - name: Add the user {{ username }} 23 | ansible.builtin.user: 24 | name: "{{ username }}" 25 | state: present 26 | become: yes 27 | 28 | - name: Print versions 29 | ansible.builtin.debug: 30 | var: version 31 | 32 | - name: Print users 33 | ansible.builtin.debug: 34 | var: users 35 | 36 | - name: Print production vpc_cidr 37 | ansible.builtin.debug: 38 | var: cidr_blocks['production']['vpc_cidr'] -------------------------------------------------------------------------------- /ansible-variables/group_vars/all: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacelift-io-blog-posts/Blog-Technical-Content/d3fab6d9c6cb030e3e617b0c42e9a46fe797826f/ansible-variables/group_vars/all -------------------------------------------------------------------------------- /ansible-variables/group_vars/webservers: -------------------------------------------------------------------------------- 1 | http_port: 80 2 | ansible_host: 127.0.0.1 3 | ansible_user: vagrant -------------------------------------------------------------------------------- /ansible-variables/host_vars/host1: -------------------------------------------------------------------------------- 1 | app_version: 1.0.1 2 | ansible_port: 2222 3 | ansible_ssh_private_key_file: ./.vagrant/machines/host1/virtualbox/private_key -------------------------------------------------------------------------------- /ansible-variables/host_vars/host2: -------------------------------------------------------------------------------- 1 | app_version: 1.0.2 2 | ansible_port: 2200 3 | ansible_ssh_private_key_file: ./.vagrant/machines/host2/virtualbox/private_key -------------------------------------------------------------------------------- /ansible-variables/hosts: -------------------------------------------------------------------------------- 1 | [webservers] 2 | host1 3 | host2 -------------------------------------------------------------------------------- /ansible-variables/roles/common/defaults/main.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacelift-io-blog-posts/Blog-Technical-Content/d3fab6d9c6cb030e3e617b0c42e9a46fe797826f/ansible-variables/roles/common/defaults/main.yml -------------------------------------------------------------------------------- /ansible-variables/roles/common/vars/main.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacelift-io-blog-posts/Blog-Technical-Content/d3fab6d9c6cb030e3e617b0c42e9a46fe797826f/ansible-variables/roles/common/vars/main.yml -------------------------------------------------------------------------------- /ansible-variables/vars/variables.yml: -------------------------------------------------------------------------------- 1 | docker_version: 20.10.12 -------------------------------------------------------------------------------- /az-ad-group-module/ad_group/main.tf: -------------------------------------------------------------------------------- 1 | resource "azuread_group" "ad_group" { 2 | count = 3 3 | display_name = var.ad_group_names[count.index] 4 | security_enabled = true 5 | mail_enabled = false 6 | } -------------------------------------------------------------------------------- /az-ad-group-module/ad_group/variables.tf: -------------------------------------------------------------------------------- 1 | variable "ad_group_names" { 2 | type = list(string) 3 | description = "List of all the AD Group names" 4 | } -------------------------------------------------------------------------------- /az-ad-group-module/azure-pipeline.yaml: -------------------------------------------------------------------------------- 1 | stages: 2 | - stage: Build 3 | displayName: Terraform-Plan 4 | jobs: 5 | - job: TerraformPlan 6 | displayName: Terraform-Plan 7 | pool: 8 | name: Private-Build-Agents 9 | steps: 10 | - checkout: self 11 | - script: ls $(System.DefaultWorkingDirectory) 12 | - task: TerraformInstaller@0 13 | displayName: 'Use Terraform latest' 14 | 15 | - task: TerraformCLI@0 16 | displayName: Terraform-Init 17 | inputs: 18 | command: 'init' 19 | workingDirectory: '$(System.DefaultWorkingDirectory)' 20 | backendType: 'azurerm' 21 | backendServiceArm: 'IAC Service Connection-Azure' 22 | backendAzureRmResourceGroupName: 'tf-rg' 23 | backendAzureRmStorageAccountName: 'jacktfstatesa' 24 | backendAzureRmContainerName: 'terraform' 25 | backendAzureRmKey: 'adgroups.tfstate' 26 | 27 | - task: TerraformCLI@0 28 | displayName: Terraform-Plan 29 | inputs: 30 | command: 'plan' 31 | workingDirectory: '$(System.DefaultWorkingDirectory)' 32 | environmentServiceName: 'RG Service Connection' -------------------------------------------------------------------------------- /az-ad-group-module/main.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | features {} 3 | } 4 | 5 | terraform { 6 | required_providers { 7 | azurerm = { 8 | source = "hashicorp/azurerm" 9 | version = ">=2.95.0" 10 | } 11 | azuread = { 12 | source = "hashicorp/azuread" 13 | } 14 | } 15 | backend "azurerm" { 16 | resource_group_name = "tf-rg" 17 | storage_account_name = "jacktfstatesa" 18 | container_name = "terraform" 19 | key = "adgroups.tfstate" 20 | } 21 | } 22 | 23 | module "ad_group" { 24 | source = "./ad_group" 25 | ad_group_names = var.ad_group_names 26 | } -------------------------------------------------------------------------------- /az-ad-group-module/terraform.tfvars: -------------------------------------------------------------------------------- 1 | ad_group_names = ["Group1", "Group2", "Group3"] 2 | -------------------------------------------------------------------------------- /az-ad-group-module/variables.tf: -------------------------------------------------------------------------------- 1 | variable "ad_group_names" { 2 | type = list(string) 3 | description = "List of all the AD Group names" 4 | } -------------------------------------------------------------------------------- /cdktf-aws-python-webserver/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | imports/* 3 | !imports/__init__.py 4 | .terraform 5 | cdktf.out 6 | cdktf.log 7 | *terraform.*.tfstate* -------------------------------------------------------------------------------- /cdktf-aws-python-webserver/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [requires] 7 | python_version = "3" 8 | 9 | [packages] 10 | cdktf = "~=0.15.2" 11 | pytest = "*" 12 | cdktf-cdktf-provider-aws = "~=12.0.2" 13 | constructs = "*" 14 | -------------------------------------------------------------------------------- /cdktf-aws-python-webserver/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "e26c0c005abc1232f7e0ee8183364511d0f0754da0bb7798fd54a61f559b4f55" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "attrs": { 20 | "hashes": [ 21 | "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", 22 | "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" 23 | ], 24 | "markers": "python_version >= '3.6'", 25 | "version": "==22.2.0" 26 | }, 27 | "cattrs": { 28 | "hashes": [ 29 | "sha256:bc12b1f0d000b9f9bee83335887d532a1d3e99a833d1bf0882151c97d3e68c21", 30 | "sha256:f0eed5642399423cf656e7b66ce92cdc5b963ecafd041d1b24d136fdde7acf6d" 31 | ], 32 | "markers": "python_version >= '3.7'", 33 | "version": "==22.2.0" 34 | }, 35 | "cdktf": { 36 | "hashes": [ 37 | "sha256:3cd6ad7b918ad4a87fe0477545b35d27c63540068b222460c1037736472920ca", 38 | "sha256:ec5e1838b732bcbc9bbc8e3c4a26e658ffcb0a75cb6eae20b972c93e8131c0a5" 39 | ], 40 | "index": "pypi", 41 | "version": "==0.15.2" 42 | }, 43 | "cdktf-cdktf-provider-aws": { 44 | "hashes": [ 45 | "sha256:356d7474e4401d1422c28144a7081d41c5d08826ee50fcf77fc909f86ba27c52", 46 | "sha256:567d6f6be42cdd77e25e292e0786109fb7d1982ce9b10e2d67d4bd7b6eba061d" 47 | ], 48 | "index": "pypi", 49 | "version": "==12.0.2" 50 | }, 51 | "constructs": { 52 | "hashes": [ 53 | "sha256:85f6d715d69ae68deaa5f701c2df24a3a663cc96ac6ebc8b19fd6fe7f23ebab9", 54 | "sha256:9436ab72590d302ccec87ccfee7d936f72184330e459c19f2b2b37c42d5d622a" 55 | ], 56 | "markers": "python_version ~= '3.7'", 57 | "version": "==10.1.241" 58 | }, 59 | "exceptiongroup": { 60 | "hashes": [ 61 | "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e", 62 | "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23" 63 | ], 64 | "markers": "python_version < '3.11'", 65 | "version": "==1.1.0" 66 | }, 67 | "iniconfig": { 68 | "hashes": [ 69 | "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", 70 | "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" 71 | ], 72 | "markers": "python_version >= '3.7'", 73 | "version": "==2.0.0" 74 | }, 75 | "jsii": { 76 | "hashes": [ 77 | "sha256:575131396ad34f8f6e9f2604953ecbf4f3368625656a828b13089e4abb81b443", 78 | "sha256:ee76781fe66106c367fbb3bb383db4f5e9b8ff3d3c4c0f34624c050211f040be" 79 | ], 80 | "markers": "python_version ~= '3.7'", 81 | "version": "==1.74.0" 82 | }, 83 | "packaging": { 84 | "hashes": [ 85 | "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2", 86 | "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97" 87 | ], 88 | "markers": "python_version >= '3.7'", 89 | "version": "==23.0" 90 | }, 91 | "pluggy": { 92 | "hashes": [ 93 | "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", 94 | "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" 95 | ], 96 | "markers": "python_version >= '3.6'", 97 | "version": "==1.0.0" 98 | }, 99 | "publication": { 100 | "hashes": [ 101 | "sha256:0248885351febc11d8a1098d5c8e3ab2dabcf3e8c0c96db1e17ecd12b53afbe6", 102 | "sha256:68416a0de76dddcdd2930d1c8ef853a743cc96c82416c4e4d3b5d901c6276dc4" 103 | ], 104 | "version": "==0.0.3" 105 | }, 106 | "pytest": { 107 | "hashes": [ 108 | "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5", 109 | "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42" 110 | ], 111 | "index": "pypi", 112 | "version": "==7.2.1" 113 | }, 114 | "python-dateutil": { 115 | "hashes": [ 116 | "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", 117 | "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" 118 | ], 119 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 120 | "version": "==2.8.2" 121 | }, 122 | "six": { 123 | "hashes": [ 124 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", 125 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" 126 | ], 127 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 128 | "version": "==1.16.0" 129 | }, 130 | "tomli": { 131 | "hashes": [ 132 | "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", 133 | "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" 134 | ], 135 | "markers": "python_version < '3.11'", 136 | "version": "==2.0.1" 137 | }, 138 | "typeguard": { 139 | "hashes": [ 140 | "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4", 141 | "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1" 142 | ], 143 | "markers": "python_full_version >= '3.5.3'", 144 | "version": "==2.13.3" 145 | }, 146 | "typing-extensions": { 147 | "hashes": [ 148 | "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", 149 | "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" 150 | ], 151 | "markers": "python_version >= '3.7'", 152 | "version": "==4.4.0" 153 | } 154 | }, 155 | "develop": {} 156 | } 157 | -------------------------------------------------------------------------------- /cdktf-aws-python-webserver/README.md: -------------------------------------------------------------------------------- 1 | # cdktf-aws-python-webserver 2 | This repository containers CDK for Terraform files to provision AWS infrastructure for a web server with a loadbalancer and other necessary resources via Python 3 | -------------------------------------------------------------------------------- /cdktf-aws-python-webserver/cdktf.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "python", 3 | "app": "pipenv run python main.py", 4 | "projectId": "ec6738d5-5da2-4f77-9bc4-42480495df6d", 5 | "sendCrashReports": "false", 6 | "terraformProviders": [], 7 | "terraformModules": [ 8 | { 9 | "name": "vpc", 10 | "source": "terraform-aws-modules/vpc/aws", 11 | "version": "3.19.0" 12 | } 13 | ], 14 | "codeMakerOutput": "imports", 15 | "context": { 16 | "excludeStackIdFromLogicalIds": "true", 17 | "allowSepCharsInLogicalIds": "true" 18 | } 19 | } -------------------------------------------------------------------------------- /cdktf-aws-python-webserver/help: -------------------------------------------------------------------------------- 1 | ======================================================================================================== 2 | 3 | Your cdktf Python project is ready! 4 | 5 | cat help Prints this message 6 | 7 | Compile: 8 | pipenv run ./main.py Compile and run the python code. 9 | 10 | Synthesize: 11 | cdktf synth [stack] Synthesize Terraform resources to cdktf.out/ 12 | 13 | Diff: 14 | cdktf diff [stack] Perform a diff (terraform plan) for the given stack 15 | 16 | Deploy: 17 | cdktf deploy [stack] Deploy the given stack 18 | 19 | Destroy: 20 | cdktf destroy [stack] Destroy the given stack 21 | 22 | Learn more about using modules and providers https://cdk.tf/modules-and-providers 23 | 24 | Use Providers: 25 | 26 | You can add prebuilt providers (if available) or locally generated ones using the add command: 27 | 28 | cdktf provider add "aws@~>3.0" null kreuzwerker/docker 29 | 30 | You can find all prebuilt providers on PyPI: https://pypi.org/user/cdktf-team/ 31 | You can also install these providers directly through pipenv: 32 | 33 | pipenv install cdktf-cdktf-provider-aws 34 | pipenv install cdktf-cdktf-provider-google 35 | pipenv install cdktf-cdktf-provider-azurerm 36 | pipenv install cdktf-cdktf-provider-docker 37 | pipenv install cdktf-cdktf-provider-github 38 | pipenv install cdktf-cdktf-provider-null 39 | 40 | You can also build any module or provider locally. Learn more: https://cdk.tf/modules-and-providers 41 | 42 | ======================================================================================================== -------------------------------------------------------------------------------- /cdktf-aws-python-webserver/main-test.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from cdktf import Testing 3 | 4 | # The tests below are example tests, you can find more information at 5 | # https://cdk.tf/testing 6 | 7 | class TestMain: 8 | 9 | def test_my_app(self): 10 | assert True 11 | 12 | #stack = TerraformStack(Testing.app(), "stack") 13 | #app_abstraction = MyApplicationsAbstraction(stack, "app-abstraction") 14 | #synthesized = Testing.synth(stack) 15 | 16 | #def test_should_contain_container(self): 17 | # assert Testing.to_have_resource(self.synthesized, Container.TF_RESOURCE_TYPE) 18 | 19 | #def test_should_use_an_ubuntu_image(self): 20 | # assert Testing.to_have_resource_with_properties(self.synthesized, Image.TF_RESOURCE_TYPE, { 21 | # "name": "ubuntu:latest", 22 | # }) 23 | 24 | #def test_check_validity(self): 25 | # assert Testing.to_be_valid_terraform(Testing.full_synth(stack)) -------------------------------------------------------------------------------- /cdktf-aws-python-webserver/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from constructs import Construct 3 | from cdktf import App, TerraformStack, TerraformOutput, TerraformVariable, Token, Fn 4 | from cdktf_cdktf_provider_aws.provider import AwsProvider 5 | from imports.vpc import Vpc 6 | import cdktf_cdktf_provider_aws.alb as Alb_ 7 | import cdktf_cdktf_provider_aws.alb_listener as AlbListener_ 8 | import cdktf_cdktf_provider_aws.alb_target_group_attachment as AlbTargetGroupAttachment_ 9 | import cdktf_cdktf_provider_aws.alb_target_group as AlbTargetGroup_ 10 | import cdktf_cdktf_provider_aws.security_group as SecurityGroup_ 11 | from cdktf_cdktf_provider_aws.instance import Instance 12 | 13 | 14 | class MyStack(TerraformStack): 15 | def __init__(self, scope: Construct, id: str): 16 | super().__init__(scope, id) 17 | 18 | AwsProvider(self, "AWS", region="eu-west-2") 19 | 20 | image_id = TerraformVariable(self, "image_id", 21 | type="string", 22 | default="ami-08cd358d745620807", 23 | description="AMI for the web server" 24 | ) 25 | 26 | instance_type = TerraformVariable(self, "instance_type", 27 | type="string", 28 | default="t2.micro", 29 | description="Instance type for the web server" 30 | ) 31 | 32 | webserver_vpc = Vpc(self, "vpc_name", 33 | name="web_server", 34 | cidr="10.10.0.0/16", 35 | azs=["eu-west-2a", "eu-west-2b", "eu-west-2c"], 36 | public_subnets=["10.10.1.0/24", 37 | "10.10.2.0/24", "10.10.3.0/24"], 38 | private_subnets=["10.10.4.0/24", 39 | "10.10.5.0/24", "10.10.6.0/24"], 40 | enable_nat_gateway=True 41 | ) 42 | 43 | alb_security_group = SecurityGroup_.SecurityGroup(self, "alb_security_group", 44 | name="alb_sg", 45 | description="Security group for alb with HTTP ports open from anywhere", 46 | vpc_id=Token().as_string(webserver_vpc.vpc_id_output), 47 | ingress=[SecurityGroup_.SecurityGroupIngress( 48 | from_port=80, to_port=80, protocol="tcp", cidr_blocks=["0.0.0.0/0"])], 49 | egress=[SecurityGroup_.SecurityGroupEgress( 50 | from_port=0, to_port=0, protocol="-1", cidr_blocks=["0.0.0.0/0"])] 51 | ) 52 | 53 | web_server_security_group = SecurityGroup_.SecurityGroup(self, "web_server_security_group", 54 | name="web_server_sg", 55 | description="Security group for web-server with HTTP ports open from alb", 56 | vpc_id=Token().as_string(webserver_vpc.vpc_id_output), 57 | ingress=[SecurityGroup_.SecurityGroupIngress( 58 | from_port=80, to_port=80, protocol="tcp", security_groups=[alb_security_group.id])], 59 | egress=[SecurityGroup_.SecurityGroupEgress( 60 | from_port=0, to_port=0, protocol="-1", cidr_blocks=["0.0.0.0/0"])] 61 | ) 62 | 63 | web_server = Instance(self, "compute", 64 | ami=image_id.default, 65 | instance_type=instance_type.default, 66 | vpc_security_group_ids=[ 67 | web_server_security_group.id], 68 | subnet_id=Fn.element(Token().as_list( 69 | webserver_vpc.public_subnets_output), 0), 70 | user_data="""#!/bin/bash 71 | sudo yum update -y 72 | sudo yum install httpd -y 73 | sudo systemctl enable httpd 74 | sudo systemctl start httpd 75 | echo "
Congrats you provisioned this web server with CDK for Terraform!
" > /var/www/html/index.html""" 76 | 77 | ) 78 | 79 | web_server_alb = Alb_.Alb(self, "web_server_alb", 80 | load_balancer_type="application", 81 | subnets=Token().as_list(webserver_vpc.public_subnets_output), 82 | security_groups=[alb_security_group.id] 83 | 84 | ) 85 | 86 | web_server_alb_target_group = AlbTargetGroup_.AlbTargetGroup( 87 | self, 88 | "web_server_alb_target_group", 89 | name="webServerAlbTargetGroup", 90 | port=80, 91 | vpc_id=Token().as_string(webserver_vpc.vpc_id_output), 92 | protocol="HTTP", 93 | target_type="instance" 94 | ) 95 | 96 | web_server_alb_listener = AlbListener_.AlbListener(self, "web_server_alb_listener", 97 | load_balancer_arn=web_server_alb.id, 98 | port=80, 99 | protocol="HTTP", 100 | default_action=[AlbListener_.AlbListenerDefaultAction( 101 | type="forward", target_group_arn=web_server_alb_target_group.arn)] 102 | ) 103 | 104 | web_server_alb_target_group_attachment = AlbTargetGroupAttachment_.AlbTargetGroupAttachment(self, "web_server_alb_target_group_attachment", 105 | target_group_arn=web_server_alb_target_group.arn, 106 | target_id=web_server.id, 107 | port=80 108 | ) 109 | 110 | TerraformOutput(self, "alb_dns", 111 | value=web_server_alb.dns_name 112 | ) 113 | 114 | 115 | app = App() 116 | MyStack(app, "cdktf-aws-python-webserver") 117 | 118 | app.synth() 119 | -------------------------------------------------------------------------------- /eks-blueprints-terraform/README.md: -------------------------------------------------------------------------------- 1 | # eks-blueprints-terraform-demo 2 | This repository includes all the files related to this EKS Blueprints for Terraform blog post. -------------------------------------------------------------------------------- /eks-blueprints-terraform/dev_teams.tf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | #Dev Teams 3 | ############################################################################### 4 | 5 | module "eks_blueprints_dev_teams" { 6 | source = "aws-ia/eks-blueprints-teams/aws" 7 | version = "~> 1.0" 8 | 9 | for_each = { 10 | a = { 11 | labels = { 12 | "elbv2.k8s.aws/pod-readiness-gate-inject" = "enabled", 13 | "appName" = "team-a-app", 14 | "projectName" = "project-a", 15 | } 16 | } 17 | b = { 18 | labels = { 19 | "elbv2.k8s.aws/pod-readiness-gate-inject" = "enabled", 20 | "appName" = "team-b-app", 21 | "projectName" = "project-b", 22 | } 23 | } 24 | } 25 | name = "team-${each.key}" 26 | 27 | users = [data.aws_caller_identity.current.arn] 28 | cluster_arn = module.eks.cluster_arn 29 | oidc_provider_arn = module.eks.oidc_provider_arn 30 | 31 | labels = merge( 32 | { 33 | team = each.key 34 | }, 35 | try(each.value.labels, {}) 36 | ) 37 | 38 | annotations = { 39 | team = each.key 40 | } 41 | 42 | namespaces = { 43 | "team-${each.key}" = { 44 | labels = merge( 45 | { 46 | team = each.key 47 | }, 48 | try(each.value.labels, {}) 49 | ) 50 | 51 | resource_quota = { 52 | hard = { 53 | "requests.cpu" = "100", 54 | "requests.memory" = "20Gi", 55 | "limits.cpu" = "200", 56 | "limits.memory" = "50Gi", 57 | "pods" = "15", 58 | "secrets" = "10", 59 | "services" = "20" 60 | } 61 | } 62 | 63 | limit_range = { 64 | limit = [ 65 | { 66 | type = "Pod" 67 | max = { 68 | cpu = "2" 69 | memory = "1Gi" 70 | } 71 | min = { 72 | cpu = "10m" 73 | memory = "4Mi" 74 | } 75 | }, 76 | { 77 | type = "PersistentVolumeClaim" 78 | min = { 79 | storage = "24M" 80 | } 81 | }, 82 | { 83 | type = "Container" 84 | default = { 85 | cpu = "50m" 86 | memory = "24Mi" 87 | } 88 | } 89 | ] 90 | } 91 | } 92 | } 93 | 94 | tags = local.tags 95 | 96 | } 97 | -------------------------------------------------------------------------------- /eks-blueprints-terraform/eks_blueprints_addons.tf: -------------------------------------------------------------------------------- 1 | provider "helm" { 2 | kubernetes { 3 | host = module.eks.cluster_endpoint 4 | cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) 5 | 6 | exec { 7 | api_version = "client.authentication.k8s.io/v1beta1" 8 | command = "aws" 9 | # This requires the awscli to be installed locally where Terraform is executed 10 | args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] 11 | } 12 | } 13 | } 14 | 15 | 16 | module "eks_blueprints_addons" { 17 | source = "aws-ia/eks-blueprints-addons/aws" 18 | version = "~> 1.0" 19 | 20 | cluster_name = module.eks.cluster_name 21 | cluster_endpoint = module.eks.cluster_endpoint 22 | cluster_version = module.eks.cluster_version 23 | oidc_provider_arn = module.eks.oidc_provider_arn 24 | 25 | enable_aws_load_balancer_controller = true 26 | enable_metrics_server = true 27 | enable_karpenter = true 28 | karpenter = { 29 | repository_username = data.aws_ecrpublic_authorization_token.token.user_name 30 | repository_password = data.aws_ecrpublic_authorization_token.token.password 31 | } 32 | 33 | tags = local.tags 34 | } 35 | 36 | -------------------------------------------------------------------------------- /eks-blueprints-terraform/initial_skeleton/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | } 4 | 5 | 6 | provider "kubernetes" { 7 | host = module.eks.cluster_endpoint 8 | cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) 9 | exec { 10 | api_version = "client.authentication.k8s.io/v1beta1" 11 | command = "aws" 12 | # This requires the awscli to be installed locally where Terraform is executed 13 | args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] 14 | } 15 | } 16 | 17 | 18 | data "aws_availability_zones" "available" {} 19 | 20 | 21 | # Find the user currently in use by AWS 22 | data "aws_caller_identity" "current" {} 23 | 24 | 25 | data "aws_iam_role" "eks_admin_role_name" { 26 | count = local.eks_admin_role_name != "" ? 1 : 0 27 | name = local.eks_admin_role_name 28 | } 29 | 30 | 31 | data "aws_ecrpublic_authorization_token" "token" {} 32 | 33 | 34 | locals { 35 | name = var.environment_name 36 | region = var.aws_region 37 | vpc_cidr = var.vpc_cidr 38 | num_of_subnets = min(length(data.aws_availability_zones.available.names), 3) 39 | azs = slice(data.aws_availability_zones.available.names, 0, local.num_of_subnets) 40 | eks_admin_role_name = var.eks_admin_role_name 41 | tags = { 42 | Blueprint = local.name 43 | GithubRepo = "github.com/aws-ia/terraform-aws-eks-blueprints" 44 | } 45 | } 46 | 47 | 48 | ################################################################################ 49 | # VPC 50 | ################################################################################ 51 | module "vpc" { 52 | source = "terraform-aws-modules/vpc/aws" 53 | version = "~> 5.0.0" 54 | name = "eks_vpc" 55 | cidr = var.vpc_cidr 56 | azs = local.azs 57 | public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 6, k)] 58 | private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 6, k + 10)] 59 | 60 | 61 | enable_nat_gateway = true 62 | create_igw = true 63 | enable_dns_hostnames = true 64 | single_nat_gateway = true 65 | 66 | 67 | manage_default_network_acl = true 68 | default_network_acl_tags = { Name = "${local.name}-default" } 69 | manage_default_route_table = true 70 | default_route_table_tags = { Name = "${local.name}-default" } 71 | manage_default_security_group = true 72 | default_security_group_tags = { Name = "${local.name}-default" } 73 | 74 | 75 | public_subnet_tags = { 76 | "kubernetes.io/role/elb" = 1 77 | "kubernetes.io/cluster/${var.environment_name}" = "owned" 78 | } 79 | 80 | 81 | private_subnet_tags = { 82 | "kubernetes.io/role/internal-elb" = 1 83 | "kubernetes.io/cluster/${var.environment_name}" = "owned" 84 | "karpenter.sh/discovery" = local.name 85 | } 86 | } 87 | 88 | 89 | ################################################################################ 90 | # Cluster 91 | ################################################################################ 92 | 93 | 94 | module "eks" { 95 | source = "terraform-aws-modules/eks/aws" 96 | version = "~> 19.15" 97 | 98 | 99 | cluster_name = local.name 100 | cluster_version = "1.27" 101 | cluster_endpoint_public_access = true 102 | 103 | 104 | # EKS Addons 105 | cluster_addons = { 106 | coredns = {} 107 | kube-proxy = {} 108 | vpc-cni = {} 109 | aws-ebs-csi-driver = { 110 | service_account_role_arn = module.ebs_csi_driver_irsa.iam_role_arn 111 | } 112 | 113 | 114 | } 115 | 116 | 117 | vpc_id = module.vpc.vpc_id 118 | subnet_ids = module.vpc.private_subnets 119 | 120 | 121 | eks_managed_node_groups = { 122 | initial = { 123 | instance_types = ["m5.large"] 124 | 125 | 126 | min_size = 1 127 | max_size = 5 128 | desired_size = 2 129 | } 130 | } 131 | 132 | manage_aws_auth_configmap = true 133 | aws_auth_roles = flatten([ 134 | { 135 | # The ARN of the IAM role 136 | rolearn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${local.eks_admin_role_name}" 137 | # The user name within Kubernetes to map to the IAM role 138 | username = "ops-role" 139 | # A list of groups within Kubernetes to which the role is mapped; Checkout K8s Role and Rolebindings 140 | groups = ["system:masters"] 141 | } 142 | ]) 143 | 144 | 145 | tags = merge(local.tags, { 146 | # NOTE - if creating multiple security groups with this module, only tag the 147 | # security group that Karpenter should utilize with the following tag 148 | # (i.e. - at most, only one security group should have this tag in your account) 149 | "karpenter.sh/discovery" = local.name 150 | }) 151 | } 152 | module "ebs_csi_driver_irsa" { 153 | source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" 154 | version = "~> 5.20" 155 | 156 | 157 | # create_role = false 158 | role_name_prefix = "${module.eks.cluster_name}-ebs-csi-driver-" 159 | 160 | 161 | attach_ebs_csi_policy = true 162 | 163 | 164 | oidc_providers = { 165 | main = { 166 | rovider_arn = module.eks.oidc_provider_arn 167 | namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] 168 | } 169 | } 170 | 171 | 172 | tags = local.tags 173 | } 174 | -------------------------------------------------------------------------------- /eks-blueprints-terraform/initial_skeleton/variables.tf: -------------------------------------------------------------------------------- 1 | variable "environment_name" { 2 | description = "The name of environment." 3 | type = string 4 | default = "eks_blueprints" 5 | } 6 | 7 | 8 | variable "aws_region" { 9 | description = "AWS Region" 10 | type = string 11 | default = "us-east-1" 12 | } 13 | 14 | 15 | variable "vpc_cidr" { 16 | description = "CIDR block for VPC" 17 | type = string 18 | default = "10.0.0.0/16" 19 | } 20 | 21 | 22 | variable "eks_admin_role_name" { 23 | type = string 24 | description = "Additional IAM role to be admin in the cluster" 25 | default = "" 26 | } 27 | -------------------------------------------------------------------------------- /eks-blueprints-terraform/initial_skeleton/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.47" 8 | } 9 | helm = { 10 | source = "hashicorp/helm" 11 | version = ">= 2.9" 12 | } 13 | kubernetes = { 14 | source = "hashicorp/kubernetes" 15 | version = ">= 2.20" 16 | } 17 | random = { 18 | source = "hashicorp/random" 19 | version = ">= 3.5" 20 | } 21 | bcrypt = { 22 | source = "viktorradnai/bcrypt" 23 | version = ">= 0.1.2" 24 | } 25 | kubectl = { 26 | source = "gavinbunney/kubectl" 27 | version = ">= 1.14" 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /eks-blueprints-terraform/karpenter.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Karpenter 3 | ################################################################################ 4 | 5 | resource "kubectl_manifest" "karpenter_provisioner" { 6 | yaml_body = <<-YAML 7 | apiVersion: karpenter.sh/v1alpha5 8 | kind: Provisioner 9 | metadata: 10 | name: default 11 | spec: 12 | requirements: 13 | - key: "karpenter.k8s.aws/instance-category" 14 | operator: In 15 | values: ["c", "m"] 16 | - key: "karpenter.k8s.aws/instance-cpu" 17 | operator: In 18 | values: ["8", "16", "32"] 19 | - key: "karpenter.k8s.aws/instance-hypervisor" 20 | operator: In 21 | values: ["nitro"] 22 | - key: "topology.kubernetes.io/zone" 23 | operator: In 24 | values: ${jsonencode(local.azs)} 25 | - key: "kubernetes.io/arch" 26 | operator: In 27 | values: ["arm64", "amd64"] 28 | - key: "karpenter.sh/capacity-type" # If not included, the webhook for the AWS cloud provider will default to on-demand 29 | operator: In 30 | values: ["spot", "on-demand"] 31 | kubeletConfiguration: 32 | containerRuntime: containerd 33 | maxPods: 110 34 | limits: 35 | resources: 36 | cpu: 1000 37 | consolidation: 38 | enabled: true 39 | providerRef: 40 | name: default 41 | ttlSecondsUntilExpired: 604800 # 7 Days = 7 * 24 * 60 * 60 Seconds 42 | YAML 43 | 44 | depends_on = [ 45 | module.eks_blueprints_addons 46 | ] 47 | } 48 | 49 | resource "kubectl_manifest" "karpenter_node_template" { 50 | yaml_body = <<-YAML 51 | apiVersion: karpenter.k8s.aws/v1alpha1 52 | kind: AWSNodeTemplate 53 | metadata: 54 | name: default 55 | spec: 56 | subnetSelector: 57 | karpenter.sh/discovery: ${module.eks.cluster_name} 58 | securityGroupSelector: 59 | karpenter.sh/discovery: ${module.eks.cluster_name} 60 | instanceProfile: ${module.eks_blueprints_addons.karpenter.node_instance_profile_name} 61 | tags: 62 | karpenter.sh/discovery: ${module.eks.cluster_name} 63 | YAML 64 | } -------------------------------------------------------------------------------- /eks-blueprints-terraform/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | } 4 | 5 | provider "kubernetes" { 6 | host = module.eks.cluster_endpoint 7 | cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) 8 | exec { 9 | api_version = "client.authentication.k8s.io/v1beta1" 10 | command = "aws" 11 | # This requires the awscli to be installed locally where Terraform is executed 12 | args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] 13 | } 14 | } 15 | 16 | data "aws_availability_zones" "available" {} 17 | 18 | # Find the user currently in use by AWS 19 | data "aws_caller_identity" "current" {} 20 | 21 | data "aws_iam_role" "eks_admin_role_name" { 22 | count = local.eks_admin_role_name != "" ? 1 : 0 23 | name = local.eks_admin_role_name 24 | } 25 | 26 | data "aws_ecrpublic_authorization_token" "token" {} 27 | 28 | locals { 29 | name = var.environment_name 30 | region = var.aws_region 31 | vpc_cidr = var.vpc_cidr 32 | num_of_subnets = min(length(data.aws_availability_zones.available.names), 3) 33 | azs = slice(data.aws_availability_zones.available.names, 0, local.num_of_subnets) 34 | eks_admin_role_name = var.eks_admin_role_name 35 | tags = { 36 | Blueprint = local.name 37 | GithubRepo = "github.com/aws-ia/terraform-aws-eks-blueprints" 38 | } 39 | } 40 | 41 | ################################################################################ 42 | # VPC 43 | ################################################################################ 44 | module "vpc" { 45 | source = "terraform-aws-modules/vpc/aws" 46 | version = "~> 5.0.0" 47 | name = "eks_vpc" 48 | cidr = var.vpc_cidr 49 | azs = local.azs 50 | public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 6, k)] 51 | private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 6, k + 10)] 52 | 53 | enable_nat_gateway = true 54 | create_igw = true 55 | enable_dns_hostnames = true 56 | single_nat_gateway = true 57 | 58 | manage_default_network_acl = true 59 | default_network_acl_tags = { Name = "${local.name}-default" } 60 | manage_default_route_table = true 61 | default_route_table_tags = { Name = "${local.name}-default" } 62 | manage_default_security_group = true 63 | default_security_group_tags = { Name = "${local.name}-default" } 64 | 65 | public_subnet_tags = { 66 | "kubernetes.io/role/elb" = 1 67 | "kubernetes.io/cluster/${var.environment_name}" = "owned" 68 | } 69 | 70 | private_subnet_tags = { 71 | "kubernetes.io/role/internal-elb" = 1 72 | "kubernetes.io/cluster/${var.environment_name}" = "owned" 73 | "karpenter.sh/discovery" = local.name 74 | } 75 | } 76 | 77 | ################################################################################ 78 | # Cluster 79 | ################################################################################ 80 | 81 | module "eks" { 82 | source = "terraform-aws-modules/eks/aws" 83 | version = "~> 19.15" 84 | 85 | cluster_name = local.name 86 | cluster_version = "1.27" 87 | cluster_endpoint_public_access = true 88 | 89 | # EKS Addons 90 | cluster_addons = { 91 | coredns = {} 92 | kube-proxy = {} 93 | vpc-cni = {} 94 | aws-ebs-csi-driver = { 95 | service_account_role_arn = module.ebs_csi_driver_irsa.iam_role_arn 96 | } 97 | } 98 | 99 | vpc_id = module.vpc.vpc_id 100 | subnet_ids = module.vpc.private_subnets 101 | 102 | eks_managed_node_groups = { 103 | initial = { 104 | instance_types = ["m5.large"] 105 | 106 | min_size = 1 107 | max_size = 5 108 | desired_size = 2 109 | } 110 | } 111 | 112 | manage_aws_auth_configmap = true 113 | aws_auth_roles = flatten([ 114 | { 115 | # The ARN of the IAM role 116 | rolearn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${local.eks_admin_role_name}" 117 | # The user name within Kubernetes to map to the IAM role 118 | username = "ops-role" 119 | # A list of groups within Kubernetes to which the role is mapped; Checkout K8s Role and Rolebindings 120 | groups = ["system:masters"] 121 | }, 122 | module.eks_blueprints_platform_team.aws_auth_configmap_role, 123 | [for team in module.eks_blueprints_dev_teams : team.aws_auth_configmap_role], 124 | { 125 | rolearn = module.eks_blueprints_addons.karpenter.node_iam_role_arn 126 | username = "system:node:{{EC2PrivateDNSName}}" 127 | groups = [ 128 | "system:bootstrappers", 129 | "system:nodes", 130 | ] 131 | } 132 | ]) 133 | 134 | tags = merge(local.tags, { 135 | # NOTE - if creating multiple security groups with this module, only tag the 136 | # security group that Karpenter should utilize with the following tag 137 | # (i.e. - at most, only one security group should have this tag in your account) 138 | "karpenter.sh/discovery" = local.name 139 | }) 140 | } 141 | 142 | module "ebs_csi_driver_irsa" { 143 | source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" 144 | version = "~> 5.20" 145 | 146 | # create_role = false 147 | role_name_prefix = "${module.eks.cluster_name}-ebs-csi-driver-" 148 | 149 | attach_ebs_csi_policy = true 150 | 151 | oidc_providers = { 152 | main = { 153 | provider_arn = module.eks.oidc_provider_arn 154 | namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] 155 | } 156 | } 157 | 158 | tags = local.tags 159 | } 160 | -------------------------------------------------------------------------------- /eks-blueprints-terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "eks_blueprints_platform_teams_configure_kubectl" { 2 | description = "Configure kubectl for Platform Team: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig" 3 | value = "aws eks --region ${var.aws_region} update-kubeconfig --name ${module.eks.cluster_name} --role-arn ${module.eks_blueprints_platform_team.iam_role_arn}" 4 | } 5 | 6 | output "eks_blueprints_dev_teams_configure_kubectl" { 7 | description = "Configure kubectl for each Dev Application Teams: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig" 8 | value = [for team in module.eks_blueprints_dev_teams : "aws eks --region ${var.aws_region} update-kubeconfig --name ${module.eks.cluster_name} --role-arn ${team.iam_role_arn}"] 9 | } -------------------------------------------------------------------------------- /eks-blueprints-terraform/platform_team.tf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | #Team Management 3 | ############################################################################### 4 | 5 | provider "kubectl" { 6 | apply_retry_count = 10 7 | host = module.eks.cluster_endpoint 8 | cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) 9 | load_config_file = false 10 | 11 | exec { 12 | api_version = "client.authentication.k8s.io/v1beta1" 13 | command = "aws" 14 | # This requires the awscli to be installed locally where Terraform is executed 15 | args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name] 16 | } 17 | } 18 | 19 | 20 | ############################################################################### 21 | #Platform Team 22 | ############################################################################### 23 | 24 | module "eks_blueprints_platform_team" { 25 | source = "aws-ia/eks-blueprints-teams/aws" 26 | version = "~> 1.0" 27 | 28 | name = "platform-team" 29 | 30 | # Enables elevated, admin privileges for this team 31 | enable_admin = true 32 | 33 | # Define who can impersonate the team-platform Role 34 | users = [ 35 | data.aws_caller_identity.current.arn, 36 | try(data.aws_iam_role.eks_admin_role_name[0].arn, data.aws_caller_identity.current.arn), 37 | ] 38 | cluster_arn = module.eks.cluster_arn 39 | oidc_provider_arn = module.eks.oidc_provider_arn 40 | 41 | labels = { 42 | "elbv2.k8s.aws/pod-readiness-gate-inject" = "enabled", 43 | "appName" = "platform-team-app", 44 | "projectName" = "project-platform", 45 | } 46 | 47 | annotations = { 48 | team = "platform" 49 | } 50 | 51 | namespaces = { 52 | "team-platform" = { 53 | 54 | resource_quota = { 55 | hard = { 56 | "requests.cpu" = "10000m", 57 | "requests.memory" = "20Gi", 58 | "limits.cpu" = "20000m", 59 | "limits.memory" = "50Gi", 60 | "pods" = "20", 61 | "secrets" = "20", 62 | "services" = "20" 63 | } 64 | } 65 | 66 | limit_range = { 67 | limit = [ 68 | { 69 | type = "Pod" 70 | max = { 71 | cpu = "1000m" 72 | memory = "1Gi" 73 | }, 74 | min = { 75 | cpu = "10m" 76 | memory = "4Mi" 77 | } 78 | }, 79 | { 80 | type = "PersistentVolumeClaim" 81 | min = { 82 | storage = "24M" 83 | } 84 | } 85 | ] 86 | } 87 | } 88 | 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /eks-blueprints-terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "environment_name" { 2 | description = "The name of environment." 3 | type = string 4 | default = "eks_blueprints" 5 | } 6 | 7 | variable "aws_region" { 8 | description = "AWS Region" 9 | type = string 10 | default = "us-east-1" 11 | } 12 | 13 | variable "vpc_cidr" { 14 | description = "CIDR block for VPC" 15 | type = string 16 | default = "10.0.0.0/16" 17 | } 18 | 19 | variable "eks_admin_role_name" { 20 | type = string 21 | description = "Additional IAM role to be admin in the cluster" 22 | default = "" 23 | } -------------------------------------------------------------------------------- /eks-blueprints-terraform/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 4.47" 8 | } 9 | helm = { 10 | source = "hashicorp/helm" 11 | version = ">= 2.9" 12 | } 13 | kubernetes = { 14 | source = "hashicorp/kubernetes" 15 | version = ">= 2.20" 16 | } 17 | random = { 18 | source = "hashicorp/random" 19 | version = ">= 3.5" 20 | } 21 | bcrypt = { 22 | source = "viktorradnai/bcrypt" 23 | version = ">= 0.1.2" 24 | } 25 | kubectl = { 26 | source = "gavinbunney/kubectl" 27 | version = ">= 1.14" 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /iac-examples/README.MD: -------------------------------------------------------------------------------- 1 | Examples from the 'Infrastructure as Code' blog article -------------------------------------------------------------------------------- /iac-examples/storage.bicep: -------------------------------------------------------------------------------- 1 | param storageAccountName string 2 | param containerName string = 'logs' 3 | param location string = resourceGroup().location 4 | 5 | resource sa 'Microsoft.Storage/storageAccounts@2019-06-01' = { 6 | name: storageAccountName 7 | location: location 8 | sku: { 9 | name: 'Standard_LRS' 10 | } 11 | kind: 'StorageV2' 12 | properties: { 13 | accessTier: 'Hot' 14 | } 15 | } 16 | 17 | resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2019-06-01' = { 18 | name: '${sa.name}/default/${containerName}' 19 | } 20 | -------------------------------------------------------------------------------- /iac-examples/storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "metadata": { 5 | "_generator": { 6 | "name": "bicep", 7 | "version": "0.4.1008.15138", 8 | "templateHash": "8636947863337745424" 9 | } 10 | }, 11 | "parameters": { 12 | "storageAccountName": { 13 | "type": "string" 14 | }, 15 | "containerName": { 16 | "type": "string", 17 | "defaultValue": "logs" 18 | }, 19 | "location": { 20 | "type": "string", 21 | "defaultValue": "[resourceGroup().location]" 22 | } 23 | }, 24 | "functions": [], 25 | "resources": [ 26 | { 27 | "type": "Microsoft.Storage/storageAccounts", 28 | "apiVersion": "2019-06-01", 29 | "name": "[parameters('storageAccountName')]", 30 | "location": "[parameters('location')]", 31 | "sku": { 32 | "name": "Standard_LRS" 33 | }, 34 | "kind": "StorageV2", 35 | "properties": { 36 | "accessTier": "Hot" 37 | } 38 | }, 39 | { 40 | "type": "Microsoft.Storage/storageAccounts/blobServices/containers", 41 | "apiVersion": "2019-06-01", 42 | "name": "[format('{0}/default/{1}', parameters('storageAccountName'), parameters('containerName'))]", 43 | "dependsOn": [ 44 | "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" 45 | ] 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /iac-examples/storage.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_storage_account" "storage_acount" { 2 | name = "storageAccountName" 3 | resource_group_name = azurerm_resource_group.example.name 4 | location = azurerm_resource_group.example.location 5 | account_tier = "Standard" 6 | account_replication_type = "LRS" 7 | } 8 | 9 | resource "azurerm_storage_container" "storage_account_container" { 10 | name = "logs" 11 | storage_account_name = azurerm_storage_account.example.name 12 | container_access_type = "private" 13 | } -------------------------------------------------------------------------------- /iac-examples/storage_pulumi.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | import pulumi_azure as azure 3 | 4 | storage_acount = azure.storage.Account("storageAcount", 5 | resource_group_name=azurerm_resource_group["example"]["name"], 6 | location=azurerm_resource_group["example"]["location"], 7 | account_tier="Standard", 8 | account_replication_type="LRS") 9 | storage_account_container = azure.storage.Container("storageAccountContainer", 10 | storage_account_name=azurerm_storage_account["example"]["name"], 11 | container_access_type="private") -------------------------------------------------------------------------------- /terraform-best-practices/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Best Practices Examples 2 | This repository includes some example Terraform configurations that are referenced in this [Terraform Best Practices](https://spacelift.io/blog/terraform-best-practices) blog post. -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/production/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "3.63.0" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = var.aws_region 12 | } 13 | 14 | module "vpc" { 15 | source = "../../modules/vpc" 16 | 17 | vpc_name = var.vpc_name 18 | vpc_cidr = var.vpc_cidr 19 | azs = var.azs 20 | vpc_public_subnets = var.vpc_public_subnets 21 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/production/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "ID of the VPC" 3 | value = module.vpc.vpc_id 4 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/production/terraform.tfvars: -------------------------------------------------------------------------------- 1 | aws_region = "us-east-1" 2 | vpc_name = "production" 3 | vpc_cidr = "172.31.0.0/16" 4 | azs = ["us-east-1a", "us-east-1b", "us-east-1c"] 5 | vpc_public_subnets = ["172.31.0.0/22", "172.31.4.0/22", "172.31.8.0/22"] -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/production/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | description = "AWS region" 3 | type = string 4 | } 5 | 6 | variable "vpc_name" { 7 | description = "Name for the VPC" 8 | type = string 9 | } 10 | 11 | variable "vpc_cidr" { 12 | description = "Default value for VPC CIDR" 13 | type = string 14 | default = "172.31.0.0/16" 15 | } 16 | 17 | variable "azs" { 18 | description = "Availability zones for the region" 19 | type = list(string) 20 | default = [] 21 | } 22 | 23 | variable "vpc_public_subnets" { 24 | description = "Public subnets for the VPC" 25 | type = list(string) 26 | default = [] 27 | } 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/staging/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "3.63.0" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = var.aws_region 12 | } 13 | 14 | module "vpc" { 15 | source = "../../modules/vpc" 16 | 17 | vpc_name = var.vpc_name 18 | vpc_cidr = var.vpc_cidr 19 | azs = var.azs 20 | vpc_public_subnets = var.vpc_public_subnets 21 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/staging/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "ID of the VPC" 3 | value = module.vpc.vpc_id 4 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/staging/terraform.tfvars: -------------------------------------------------------------------------------- 1 | aws_region = "us-east-2" 2 | vpc_name = "staging" 3 | vpc_cidr = "10.0.0.0/16" 4 | azs = ["us-east-2a", "us-east-2b", "us-east-2c"] 5 | vpc_public_subnets = ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"] -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/staging/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | description = "AWS region" 3 | type = string 4 | } 5 | 6 | variable "vpc_name" { 7 | description = "Name for the VPC" 8 | type = string 9 | } 10 | 11 | variable "vpc_cidr" { 12 | description = "Default value for VPC CIDR" 13 | type = string 14 | default = "172.31.0.0/16" 15 | } 16 | 17 | variable "azs" { 18 | description = "Availability zones for the region" 19 | type = list(string) 20 | default = [] 21 | } 22 | 23 | variable "vpc_public_subnets" { 24 | description = "Public subnets for the VPC" 25 | type = list(string) 26 | default = [] 27 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/test/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "3.63.0" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = var.aws_region 12 | } 13 | 14 | module "vpc" { 15 | source = "../../modules/vpc" 16 | 17 | vpc_name = var.vpc_name 18 | vpc_cidr = var.vpc_cidr 19 | azs = var.azs 20 | vpc_public_subnets = var.vpc_public_subnets 21 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/test/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "ID of the VPC" 3 | value = module.vpc.vpc_id 4 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/test/terraform.tfvars: -------------------------------------------------------------------------------- 1 | aws_region = "eu-central-1" 2 | vpc_name = "test" 3 | vpc_cidr = "10.1.0.0/16" 4 | azs = ["eu-central-1a", "eu-central-1b", "eu-central-1c"] 5 | vpc_public_subnets = ["10.1.0.0/24", "10.1.1.0/24", "10.1.2.0/24"] -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/environment/test/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | description = "AWS region" 3 | type = string 4 | } 5 | 6 | variable "vpc_name" { 7 | description = "Name for the VPC" 8 | type = string 9 | } 10 | 11 | variable "vpc_cidr" { 12 | description = "Default value for VPC CIDR" 13 | type = string 14 | default = "172.31.0.0/16" 15 | } 16 | 17 | variable "azs" { 18 | description = "Availability zones for the region" 19 | type = list(string) 20 | default = [] 21 | } 22 | 23 | variable "vpc_public_subnets" { 24 | description = "Public subnets for the VPC" 25 | type = list(string) 26 | default = [] 27 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/modules/vpc/main.tf: -------------------------------------------------------------------------------- 1 | module "vpc" { 2 | source = "terraform-aws-modules/vpc/aws" 3 | version = "3.14" 4 | 5 | name = var.vpc_name 6 | cidr = var.vpc_cidr 7 | azs = var.azs 8 | public_subnets = var.vpc_public_subnets 9 | 10 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/modules/vpc/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "ID of the VPC" 3 | value = module.vpc.vpc_id 4 | } -------------------------------------------------------------------------------- /terraform-best-practices/separate-environments-project-structure/modules/vpc/varariables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_name" { 2 | description = "Name for the VPC" 3 | type = string 4 | } 5 | 6 | variable "vpc_cidr" { 7 | description = "Default value for VPC CIDR" 8 | type = string 9 | default = "172.31.0.0/16" 10 | } 11 | 12 | variable "azs" { 13 | description = "Availability zones for the region" 14 | type = list(string) 15 | default = [] 16 | } 17 | 18 | variable "vpc_public_subnets" { 19 | description = "Public subnets for the VPC" 20 | type = list(string) 21 | default = [] 22 | } -------------------------------------------------------------------------------- /terraform-best-practices/small-terraform-project-structure/README.md: -------------------------------------------------------------------------------- 1 | This file should contain information about your project. Be clear about the intentions and functionality of it. Include information about how to interact, use, or contribute to the project. -------------------------------------------------------------------------------- /terraform-best-practices/small-terraform-project-structure/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "3.16.0" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = var.aws_region 12 | } 13 | 14 | data "aws_caller_identity" "current" {} 15 | 16 | locals { 17 | account_id = data.aws_caller_identity.current.account_id 18 | environment = "Stage" 19 | team_name = "Infrastructure" 20 | service_name = "Blue" 21 | } 22 | 23 | locals { 24 | default_tags = { 25 | Environment = local.environment 26 | Team = local.team_name 27 | Service = local.service_name 28 | } 29 | } 30 | 31 | resource "aws_vpc" "this" { 32 | cidr_block = var.vpc_cidr 33 | instance_tenancy = var.instance_tenancy 34 | 35 | tags = local.default_tags 36 | } 37 | 38 | -------------------------------------------------------------------------------- /terraform-best-practices/small-terraform-project-structure/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "ID of the VPC" 3 | value = aws_vpc.this.id 4 | } -------------------------------------------------------------------------------- /terraform-best-practices/small-terraform-project-structure/terraform.tfvars: -------------------------------------------------------------------------------- 1 | aws_region = "us-east-1" 2 | vpc_cidr = "10.0.0.0/16" -------------------------------------------------------------------------------- /terraform-best-practices/small-terraform-project-structure/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | description = "AWS region" 3 | type = string 4 | } 5 | 6 | variable "vpc_cidr" { 7 | description = "Default value for VPC CIDR" 8 | type = string 9 | default = "172.31.0.0/16" 10 | } 11 | 12 | variable "instance_tenancy" { 13 | description = "Tenancy for ec2 instances" 14 | type = string 15 | default = "default" 16 | } -------------------------------------------------------------------------------- /terraform-github-actions/.github/workflows/terraform.yml: -------------------------------------------------------------------------------- 1 | name: "Terraform Infrastructure Change Management Pipeline with GitHub Actions" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - terraform/** 9 | pull_request: 10 | branches: 11 | - main 12 | paths: 13 | - terraform/** 14 | 15 | env: 16 | TF_LOG: INFO 17 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 18 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 19 | BUCKET_TF_STATE: ${{ secrets.BUCKET_TF_STATE}} 20 | 21 | jobs: 22 | terraform: 23 | name: "Terraform Infrastructure Change Management" 24 | runs-on: ubuntu-latest 25 | defaults: 26 | run: 27 | shell: bash 28 | # We keep Terraform files in the terraform directory. 29 | working-directory: ./terraform 30 | 31 | steps: 32 | - name: Checkout the repository to the runner 33 | uses: actions/checkout@v2 34 | 35 | - name: Setup Terraform with specified version on the runner 36 | uses: hashicorp/setup-terraform@v2 37 | with: 38 | terraform_version: 1.3.0 39 | 40 | - name: Terraform init 41 | id: init 42 | run: terraform init -backend-config="bucket=$BUCKET_TF_STATE" 43 | 44 | - name: Terraform format 45 | id: fmt 46 | run: terraform fmt -check 47 | 48 | - name: Terraform validate 49 | id: validate 50 | run: terraform validate 51 | 52 | - name: Terraform plan 53 | id: plan 54 | if: github.event_name == 'pull_request' 55 | run: terraform plan -no-color -input=false 56 | continue-on-error: true 57 | 58 | - uses: actions/github-script@v6 59 | if: github.event_name == 'pull_request' 60 | env: 61 | PLAN: "terraform\n${{ steps.plan.outputs.stdout }}" 62 | with: 63 | github-token: ${{ secrets.GITHUB_TOKEN }} 64 | script: | 65 | const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` 66 | #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` 67 | #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` 68 | #### Terraform Plan 📖\`${{ steps.plan.outcome }}\` 69 | 70 |
Show Plan 71 | 72 | \`\`\`\n 73 | ${process.env.PLAN} 74 | \`\`\` 75 | 76 |
77 | *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; 78 | 79 | github.rest.issues.createComment({ 80 | issue_number: context.issue.number, 81 | owner: context.repo.owner, 82 | repo: context.repo.repo, 83 | body: output 84 | }) 85 | 86 | - name: Terraform Plan Status 87 | if: steps.plan.outcome == 'failure' 88 | run: exit 1 89 | 90 | - name: Terraform Apply 91 | if: github.ref == 'refs/heads/main' && github.event_name == 'push' 92 | run: terraform apply -auto-approve -input=false 93 | -------------------------------------------------------------------------------- /terraform-github-actions/README.md: -------------------------------------------------------------------------------- 1 | # Manage Infrastrucutre with Terraform and GitHub Actions 2 | This demo shows an example of a GitHub Actions pipeline to manage the Terraform infrastructure lifecycle. -------------------------------------------------------------------------------- /terraform-github-actions/terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | } 6 | } 7 | 8 | backend "s3" { 9 | region = "us-west-2" 10 | key = "terraform.tfstate" 11 | } 12 | } 13 | 14 | provider "aws" { 15 | region = "us-west-2" 16 | } 17 | 18 | resource "aws_instance" "test_instance" { 19 | ami = "ami-830c94e3" 20 | instance_type = "t2.nano" 21 | tags = { 22 | Name = "test_instance" 23 | } 24 | } -------------------------------------------------------------------------------- /terraform-output/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Ouputs 2 | This repository includes all the files related to the [Terraform Outputs](https://spacelift.io/blog/terraform-output) blog post. If you want to run the examples on this repo you need [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli) installed and an [AWS account](https://aws.amazon.com/console/). -------------------------------------------------------------------------------- /terraform-output/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "3.16.0" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = var.aws_region 12 | } 13 | 14 | module "aws_web_server_vpc" { 15 | source = "./modules/aws-web-server-vpc" 16 | } 17 | 18 | module "aws_web_server_instance" { 19 | source = "./modules/aws-web-server-instance" 20 | ec2_instance_type = var.ec2_instance_type 21 | vpc_id = module.aws_web_server_vpc.vpc_id 22 | subnet_id = module.aws_web_server_vpc.subnet_id 23 | } -------------------------------------------------------------------------------- /terraform-output/modules/aws-web-server-instance/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "amazon_linux" { 2 | most_recent = true 3 | owners = ["amazon"] 4 | filter { 5 | name = "name" 6 | values = ["amzn2-ami-hvm-*-x86_64-gp2"] 7 | } 8 | } 9 | 10 | resource "aws_security_group" "web_server" { 11 | name = var.ec2_security_group_name 12 | description = var.ec2_security_group_description 13 | vpc_id = var.vpc_id 14 | 15 | ingress { 16 | description = "Allow traffic on port 80 from everywhere" 17 | from_port = 80 18 | to_port = 80 19 | protocol = "tcp" 20 | cidr_blocks = ["0.0.0.0/0"] 21 | ipv6_cidr_blocks = ["::/0"] 22 | } 23 | 24 | egress { 25 | from_port = 0 26 | to_port = 0 27 | protocol = "-1" 28 | cidr_blocks = ["0.0.0.0/0"] 29 | ipv6_cidr_blocks = ["::/0"] 30 | } 31 | 32 | tags = { 33 | Name = var.ec2_security_group_name 34 | } 35 | } 36 | 37 | resource "aws_instance" "web_server" { 38 | ami = data.aws_ami.amazon_linux.id 39 | instance_type = var.ec2_instance_type 40 | 41 | subnet_id = var.subnet_id 42 | vpc_security_group_ids = [aws_security_group.web_server.id] 43 | 44 | tags = { 45 | Name = var.ec2_instance_name 46 | } 47 | 48 | user_data = <<-EOF 49 | #!/bin/bash 50 | sudo yum update -y 51 | sudo yum install httpd -y 52 | sudo systemctl enable httpd 53 | sudo systemctl start httpd 54 | echo "
This is a test webserver!
" > /var/www/html/index.html 55 | EOF 56 | } 57 | -------------------------------------------------------------------------------- /terraform-output/modules/aws-web-server-instance/outputs.tf: -------------------------------------------------------------------------------- 1 | output "instance_id" { 2 | description = "ID of EC2 instance" 3 | value = aws_instance.web_server.id 4 | } 5 | 6 | output "instance_public_ip" { 7 | description = "Public IP of EC2 instance" 8 | value = aws_instance.web_server.public_ip 9 | } 10 | -------------------------------------------------------------------------------- /terraform-output/modules/aws-web-server-instance/variables.tf: -------------------------------------------------------------------------------- 1 | variable "ec2_instance_name" { 2 | description = "Name for web server EC2 instance" 3 | type = string 4 | default = "web_server" 5 | } 6 | 7 | variable "ec2_instance_type" { 8 | description = "Instance type for web server EC2 instance" 9 | type = string 10 | default = "t2.micro" 11 | } 12 | 13 | variable "ec2_security_group_name" { 14 | description = "Security group name for web server EC2 instance" 15 | type = string 16 | default = "web_server" 17 | 18 | } 19 | 20 | variable "ec2_security_group_description" { 21 | description = "Security group description for web server EC2 instance" 22 | type = string 23 | default = "Allow traffic for webserver" 24 | } 25 | 26 | variable "vpc_id" { 27 | description = "VPC id for web server EC2 instance" 28 | type = string 29 | } 30 | 31 | variable "subnet_id" { 32 | description = "Subnet id for web server EC2 instance" 33 | type = string 34 | } 35 | -------------------------------------------------------------------------------- /terraform-output/modules/aws-web-server-vpc/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "web_server" { 2 | cidr_block = var.vpc_cidr_block 3 | instance_tenancy = "default" 4 | 5 | tags = { 6 | Name = var.vpc_name 7 | } 8 | } 9 | 10 | resource "aws_subnet" "web_server" { 11 | vpc_id = aws_vpc.web_server.id 12 | cidr_block = var.subnet_cidr_block 13 | map_public_ip_on_launch = true 14 | availability_zone = var.aws_az 15 | 16 | tags = { 17 | Name = var.subnet_name 18 | } 19 | } 20 | 21 | resource "aws_internet_gateway" "web_server" { 22 | vpc_id = aws_vpc.web_server.id 23 | 24 | tags = { 25 | Name = var.igw_name 26 | } 27 | } 28 | 29 | resource "aws_route_table" "web_server" { 30 | vpc_id = aws_vpc.web_server.id 31 | route { 32 | cidr_block = "0.0.0.0/0" 33 | gateway_id = aws_internet_gateway.web_server.id 34 | } 35 | 36 | tags = { 37 | Name = var.rt_name 38 | } 39 | } 40 | 41 | resource "aws_route_table_association" "web_server" { 42 | subnet_id = aws_subnet.web_server.id 43 | route_table_id = aws_route_table.web_server.id 44 | } -------------------------------------------------------------------------------- /terraform-output/modules/aws-web-server-vpc/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "ID of the VPC" 3 | value = aws_vpc.web_server.id 4 | } 5 | 6 | output "subnet_id" { 7 | description = "ID of the VPC subnet" 8 | value = aws_subnet.web_server.id 9 | } -------------------------------------------------------------------------------- /terraform-output/modules/aws-web-server-vpc/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_cidr_block" { 2 | description = "CIDR block for webserver VPC" 3 | type = string 4 | default = "10.0.0.0/16" 5 | } 6 | 7 | variable "vpc_name" { 8 | description = "Name of the vpc" 9 | type = string 10 | default = "web_server" 11 | } 12 | 13 | variable "subnet_cidr_block" { 14 | description = "CIDR block for the webserver subnet" 15 | type = string 16 | default = "10.0.0.0/24" 17 | } 18 | 19 | variable "subnet_name" { 20 | description = "Name for the webserver subnet" 21 | type = string 22 | default = "web_server" 23 | } 24 | 25 | variable "aws_az" { 26 | description = "Availability Zone for the webserver subnet" 27 | type = string 28 | default = "us-east-1a" 29 | } 30 | 31 | variable "igw_name" { 32 | description = "Name for the Internet Gateway of the webserver vpc" 33 | type = string 34 | default = "web_server" 35 | } 36 | 37 | variable "rt_name" { 38 | description = "Name for the route table of the webserver vpc" 39 | type = string 40 | default = "web_server" 41 | } 42 | -------------------------------------------------------------------------------- /terraform-output/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "ID of the vpc" 3 | value = module.aws_web_server_vpc.vpc_id 4 | } 5 | 6 | output "instance_id" { 7 | description = "ID of EC2 instance" 8 | value = module.aws_web_server_instance.instance_id 9 | } 10 | 11 | output "instance_public_ip" { 12 | description = "Public IP of EC2 instance" 13 | value = module.aws_web_server_instance.instance_public_ip 14 | } -------------------------------------------------------------------------------- /terraform-output/terraform.tfvars: -------------------------------------------------------------------------------- 1 | aws_region = "us-east-1" 2 | ec2_instance_type = "t2.nano" 3 | -------------------------------------------------------------------------------- /terraform-output/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | description = "AWS region" 3 | type = string 4 | } 5 | 6 | variable "ec2_instance_type" { 7 | description = "Instance type for EC2 instances" 8 | type = string 9 | default = "t2.small" 10 | } -------------------------------------------------------------------------------- /terraform-tfvars/dev.auto.tfvars: -------------------------------------------------------------------------------- 1 | instance_type = "t2.large" 2 | -------------------------------------------------------------------------------- /terraform-tfvars/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "~> 3.0" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = "us-east-1" 12 | } 13 | 14 | //referencing ubuntu AMI 15 | data "aws_ami" "ubuntu" { 16 | most_recent = true 17 | owners = ["099720109477"] #Canonical 18 | 19 | filter { 20 | name = "name" 21 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 22 | } 23 | 24 | filter { 25 | name = "virtualization-type" 26 | values = ["hvm"] 27 | } 28 | } 29 | 30 | //spinning up billing server 31 | resource "aws_instance" "billing_server" { 32 | ami = data.aws_ami.ubuntu.id 33 | instance_type = var.instance_type 34 | tags = { 35 | "service" = "billing" 36 | } 37 | } 38 | 39 | module "child" { 40 | source = "./modules" 41 | instance_type = var.instance_type 42 | } -------------------------------------------------------------------------------- /terraform-tfvars/modules/main.tf: -------------------------------------------------------------------------------- 1 | //referencing ubuntu AMI 2 | data "aws_ami" "ubuntu" { 3 | most_recent = true 4 | owners = ["099720109477"] #Canonical 5 | 6 | filter { 7 | name = "name" 8 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 9 | } 10 | 11 | filter { 12 | name = "virtualization-type" 13 | values = ["hvm"] 14 | } 15 | } 16 | 17 | //spinning up another billing server 18 | resource "aws_instance" "billing_server" { 19 | ami = data.aws_ami.ubuntu.id 20 | instance_type = var.instance_type 21 | tags = { 22 | "service" = "billing" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /terraform-tfvars/modules/terraform.tfvars: -------------------------------------------------------------------------------- 1 | instance_type = "t2.small" 2 | -------------------------------------------------------------------------------- /terraform-tfvars/modules/variable.tf: -------------------------------------------------------------------------------- 1 | variable "instance_type" { 2 | type = string 3 | description = "EC2 instance type" 4 | } 5 | -------------------------------------------------------------------------------- /terraform-tfvars/prod.tfvars: -------------------------------------------------------------------------------- 1 | instance_type = "t2.xlarge" 2 | -------------------------------------------------------------------------------- /terraform-tfvars/variable.tf: -------------------------------------------------------------------------------- 1 | variable "instance_type" { 2 | type = string 3 | default = "t2.micro" 4 | description = "EC2 instance type" 5 | } 6 | -------------------------------------------------------------------------------- /working-with-ansible-playbooks/README.md: -------------------------------------------------------------------------------- 1 | # Working with Ansible Playbooks 2 | This repository includes all the files related to the [Working with Ansible Playbooks](https://spacelift.io/blog/ansible-playbooks) blog post. If you want to run the examples on this repo you can execute `vagrant up` from the top of this repository in order to spin up a local vm to target with Ansible. Requirements to follow along: [Vagrant](https://www.vagrantup.com/docs/installation) and [VirtualBox](https://www.virtualbox.org/). -------------------------------------------------------------------------------- /working-with-ansible-playbooks/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://vagrantcloud.com/search. 15 | 16 | config.vm.define "host1" do |host1| 17 | host1.vm.box = "hashicorp/bionic64" 18 | end 19 | 20 | config.vm.define "host2" do |host2| 21 | host2.vm.box = "hashicorp/bionic64" 22 | end 23 | 24 | # Disable automatic box update checking. If you disable this, then 25 | # boxes will only be checked for updates when the user runs 26 | # `vagrant box outdated`. This is not recommended. 27 | # config.vm.box_check_update = false 28 | 29 | # Create a forwarded port mapping which allows access to a specific port 30 | # within the machine from a port on the host machine. In the example below, 31 | # accessing "localhost:8080" will access port 80 on the guest machine. 32 | # NOTE: This will enable public access to the opened port 33 | # config.vm.network "forwarded_port", guest: 80, host: 8080 34 | 35 | # Create a forwarded port mapping which allows access to a specific port 36 | # within the machine from a port on the host machine and only allow access 37 | # via 127.0.0.1 to disable public access 38 | # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" 39 | 40 | # Create a private network, which allows host-only access to the machine 41 | # using a specific IP. 42 | # config.vm.network "private_network", ip: "192.168.33.10" 43 | 44 | # Create a public network, which generally matched to bridged network. 45 | # Bridged networks make the machine appear as another physical device on 46 | # your network. 47 | # config.vm.network "public_network" 48 | 49 | # Share an additional folder to the guest VM. The first argument is 50 | # the path on the host to the actual folder. The second argument is 51 | # the path on the guest to mount the folder. And the optional third 52 | # argument is a set of non-required options. 53 | # config.vm.synced_folder "../data", "/vagrant_data" 54 | 55 | # Provider-specific configuration so you can fine-tune various 56 | # backing providers for Vagrant. These expose provider-specific options. 57 | # Example for VirtualBox: 58 | # 59 | # config.vm.provider "virtualbox" do |vb| 60 | # # Display the VirtualBox GUI when booting the machine 61 | # vb.gui = true 62 | # 63 | # # Customize the amount of memory on the VM: 64 | # vb.memory = "1024" 65 | # end 66 | # 67 | # View the documentation for the provider you are using for more 68 | # information on available options. 69 | 70 | # Enable provisioning with a shell script. Additional provisioners such as 71 | # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the 72 | # documentation for more information about their specific syntax and use. 73 | # config.vm.provision "shell", inline: <<-SHELL 74 | # apt-get update 75 | # apt-get install -y apache2 76 | # SHELL 77 | end 78 | -------------------------------------------------------------------------------- /working-with-ansible-playbooks/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory=./hosts 3 | deprecation_warnings=False 4 | nocows = 1 -------------------------------------------------------------------------------- /working-with-ansible-playbooks/example-conditionals-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Example Simple Conditional 2 | hosts: all 3 | vars: 4 | trigger_task: true 5 | 6 | tasks: 7 | - name: Install nginx 8 | apt: 9 | name: "nginx" 10 | state: present 11 | when: trigger_task 12 | 13 | - name: Example Facts Conditionals 14 | hosts: all 15 | vars: 16 | supported_os: 17 | - RedHat 18 | - Fedora 19 | 20 | tasks: 21 | - name: Install nginx 22 | yum: 23 | name: "nginx" 24 | state: present 25 | when: ansible_facts['distribution'] in supported_os 26 | 27 | - name: Example Registered Variables Conditionals 28 | hosts: all 29 | 30 | tasks: 31 | - name: Register an example variable 32 | ansible.builtin.shell: cat /etc/hosts 33 | register: hosts_contents 34 | 35 | - name: Check if hosts file contains "localhost" 36 | ansible.builtin.shell: echo "/etc/hosts contains localhost" 37 | when: hosts_contents.stdout.find(localhost) != -1 38 | -------------------------------------------------------------------------------- /working-with-ansible-playbooks/example-handlers-playbook.yml: -------------------------------------------------------------------------------- 1 | 2 | - name: Example with handler - Update apache config 3 | hosts: webservers 4 | 5 | tasks: 6 | - name: Write the apache config file 7 | ansible.builtin.template: 8 | src: /srv/httpd.j2 9 | dest: /etc/httpd.conf 10 | notify: 11 | - Restart apache 12 | 13 | handlers: 14 | - name: Restart apache 15 | ansible.builtin.service: 16 | name: httpd 17 | state: restarted -------------------------------------------------------------------------------- /working-with-ansible-playbooks/example-loops-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Examples with Loops 2 | hosts: all 3 | 4 | tasks: 5 | - name: "Create some files" 6 | ansible.builtin.file: 7 | state: touch 8 | path: /tmp/{{ item }} 9 | loop: 10 | - example_file1 11 | - example_file2 12 | - example_file3 13 | 14 | - name: "Create some files with dictionaries" 15 | ansible.builtin.file: 16 | state: touch 17 | path: "/tmp/{{ item.filename }}" 18 | mode: "{{ item.mode }}" 19 | loop: 20 | - { filename: 'example_file1', mode: '755'} 21 | - { filename: 'example_file2', mode: '775'} 22 | - { filename: 'example_file3', mode: '777'} 23 | 24 | - name: Show all the hosts in the inventory 25 | ansible.builtin.debug: 26 | msg: "{{ item }}" 27 | loop: "{{ groups['databases'] }}" 28 | 29 | - name: Execute when values in list are lower than 10 30 | ansible.builtin.command: echo {{ item }} 31 | loop: [ 100, 200, 3, 600, 7, 11 ] 32 | when: item < 10 33 | 34 | 35 | - name: Retry a task until we find the word “success” in the logs 36 | shell: cat /var/log/example_log 37 | register: result 38 | until: result.stdout.find("success") != -1 39 | retries: 10 40 | delay: 15 41 | -------------------------------------------------------------------------------- /working-with-ansible-playbooks/example-secrets-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Example decrypt Ansible Vault variable 2 | hosts: all 3 | vars: 4 | db_password: !vault | 5 | $ANSIBLE_VAULT;1.1;AES256 6 | 64613161626261666130396330323463333336663363613065346330643138346335346563633837 7 | 3234373132313362636263666538383436633332323034620a656333396639383532366634333764 8 | 39396233303164353763386430343836373938643831613365366366333335386633633936373035 9 | 3832363735643032630a343439396632363536633439393762343965636163343061383735333963 10 | 3435 11 | 12 | tasks: 13 | - name: Use the db_password value 14 | debug: 15 | msg: "This is a test to validate that we can decrypt correctly the example encrypted variable db_password: {{db_password}} " -------------------------------------------------------------------------------- /working-with-ansible-playbooks/example-simple-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Example Simple Playbook 2 | hosts: all 3 | become: yes 4 | 5 | tasks: 6 | - name: Copy file example_file to /tmp with permissions 7 | ansible.builtin.copy: 8 | src: ./example_file 9 | dest: /tmp/example_file 10 | mode: '0644' 11 | 12 | - name: Add the user 'bob' with a specific uid 13 | ansible.builtin.user: 14 | name: bob 15 | state: present 16 | uid: 1040 17 | 18 | - name: Update postgres servers 19 | hosts: databases 20 | become: yes 21 | 22 | tasks: 23 | - name: Ensure postgres DB is at the latest version 24 | ansible.builtin.yum: 25 | name: postgresql 26 | state: latest 27 | 28 | - name: Ensure that postgresql is started 29 | ansible.builtin.service: 30 | name: postgresql 31 | state: started 32 | -------------------------------------------------------------------------------- /working-with-ansible-playbooks/example-variables-2-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Example-2 Variables Playbook 2 | hosts: all 3 | 4 | tasks: 5 | - name: Run a script and register the output as a variable 6 | shell: "find example_file" 7 | args: 8 | chdir: "/tmp" 9 | register: example_script_output 10 | - name: Use the output variable of the previous task 11 | debug: 12 | var: example_script_output -------------------------------------------------------------------------------- /working-with-ansible-playbooks/example-variables-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Example Variables Playbook 2 | hosts: all 3 | vars: 4 | username: bob 5 | 6 | tasks: 7 | - name: Add the user {{ username }} 8 | ansible.builtin.user: 9 | name: "{{ username }}" 10 | state: present -------------------------------------------------------------------------------- /working-with-ansible-playbooks/example_file: -------------------------------------------------------------------------------- 1 | This is an example -------------------------------------------------------------------------------- /working-with-ansible-playbooks/hosts: -------------------------------------------------------------------------------- 1 | host1 ansible_host=127.0.0.1 ansible_user=vagrant ansible_port=2201 ansible_ssh_private_key_file=./.vagrant/machines/host1/virtualbox/private_key --------------------------------------------------------------------------------