prakhar1985
2020-01-20 5adc63ed0e8a1a4d00d2d17db0fc965f9099cc02
Osp tower implementation (#1002)

* novello config ansible-tower-implementation

* novello config ansible-tower-implementation

Co-authored-by: Mitesh The Mouse <44154255+miteshrh@users.noreply.github.com>
21 files added
2324 ■■■■■ changed files
ansible/configs/ansible-tower-implementation/README.adoc 3 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/destroy_env.yml 3 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/env_vars.yml 266 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/files/cloud_providers/azure_cloud_template.j2 428 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/files/cloud_providers/ec2_cloud_template.j2 287 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/files/cloud_providers/ec2_cloud_template_json.j2 792 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/files/cloud_providers/terraform_ec2_cloud_template.tf.j2 164 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/files/cloud_providers/terraform_ec2_cloud_template.tfvars.j2 2 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/files/hosts_template.j2 45 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/files/repos_template.j2 32 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/post_infra.yml 25 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/post_software.yml 36 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/pre_infra.yml 30 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/pre_software.yml 47 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/requirements.yml 6 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/sample_vars.yml 39 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/sample_vars_osp.yml 58 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/software.yml 19 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/start.yml 21 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/stop.yml 21 ●●●●● patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/topology.png patch | view | raw | blame | history
ansible/configs/ansible-tower-implementation/README.adoc
New file
@@ -0,0 +1,3 @@
== Ansible Tower Implementation
* novello based config
ansible/configs/ansible-tower-implementation/destroy_env.yml
New file
@@ -0,0 +1,3 @@
---
- name: Import default destroy playbook
  import_playbook: ../../cloud_providers/{{cloud_provider}}_destroy_env.yml
ansible/configs/ansible-tower-implementation/env_vars.yml
New file
@@ -0,0 +1,266 @@
## TODO: What variables can we strip out of here to build complex variables?
## i.e. what can we add into group_vars as opposed to config_vars?
## Example: We don't really need "subdomain_base_short". If we want to use this,
## should just toss in group_vars/all.
### Also, we should probably just create a variable reference in the README.md
### For now, just tagging comments in line with configuration file.
### Vars that can be removed:
# use_satellite: true
# use_subscription_manager: false
# use_own_repos: false
###### VARIABLES YOU SHOULD CONFIGURE FOR YOUR DEPLOYEMNT
###### OR PASS as "-e" args to ansible-playbook command
### Common Host settings
repo_method: file # Other Options are: file, satellite and rhn
install_ipa_client: false
install_student_user: false
# Do you want to run a full yum update
update_packages: false
#If using repo_method: satellite, you must set these values as well.
# satellite_url: satellite.example.com
# satellite_org: Sat_org_name
# satellite_activationkey: "rhel7basic"
## guid is the deployment unique identifier, it will be appended to all tags,
## files and anything that identifies this environment from another "just like it"
guid: defaultguid
# This var is used to identify stack (cloudformation, azure resourcegroup, ...)
project_tag: "{{ env_type }}-{{ guid }}"
# This is where the ssh_config file will be created, this file is used to
# define the communication method to all the hosts in the deployment
deploy_local_ssh_config_location: "{{output_dir}}/"
install_bastion: true
install_common: true
## SB Don't set software_to_deploy from here, always use extra vars (-e) or "none" will be used
#software_to_deploy:: none
repo_version: "3.5.0-1"
tower_version: "{{repo_version}}"
### If you want a Key Pair name created and injected into the hosts,
# set `set_env_authorized_key` to true and set the keyname in `env_authorized_key`
# you can use the key used to create the environment or use your own self generated key
# if you set "use_own_key" to false your PRIVATE key will be copied to the bastion. (This is {{key_name}})
use_own_key: true
env_authorized_key: "{{guid}}key"
ansible_ssh_private_key_file: ~/.ssh/{{key_name}}.pem
set_env_authorized_key: true
# Is this running from Red Hat Ansible Tower
tower_run: false
### Azure
# Create a dedicated resourceGroup for this deployment
az_destroy_method: resource_group
az_resource_group: "{{ project_tag }}"
# you can operate differently: if you share on resourceGroup for all you deployments,
# you can specify a different resourceGroup and method:
#az_destroy_method: deployment
#az_resource_group: my-shared-resource-group
### AWS EC2 Environment settings
### Route 53 Zone ID (AWS)
# This is the Route53 HostedZoneId where you will create your Public DNS entries
# This only needs to be defined if your CF template uses route53
HostedZoneId: Z3IHLWJZOU9SRT
# The region to be used, if not specified by -e in the command line
aws_region: ap-southeast-2
# The key that is used to
key_name: "default_key_name"
## Networking (AWS)
subdomain_base_short: "{{ guid }}"
subdomain_base_suffix: ".example.opentlc.com"
subdomain_base: "{{subdomain_base_short}}{{subdomain_base_suffix}}"
## Environment Sizing
bastion_instance_type: "t2.medium"
tower_instance_type: "t2.small"
server_instance_type: "{{tower_instance_type}}"
tower_instance_count: 1
server_instance_count: 2
rootfs_size_bastion: 50
bastion_instance_image: RHELAMI
tower_instance_image: RHELAMI
server_instance_image: RHELAMI
instances:
  - name: bastion
    count: 1
    unique: true
    public_dns: true
    dns_loadbalancer: true
    floating_ip: true
    image_id: "{{ bastion_instance_image }}"
    flavor:
      ec2: "{{bastion_instance_type}}"
      osp: "{{bastion_instance_type}}"
      azure: Standard_A2_V2
    tags:
      - key: "AnsibleGroup"
        value: "bastions"
      - key: "ostype"
        value: "linux"
      - key: "instance_filter"
        value: "{{ env_type }}-{{ email }}"
    rootfs_size: "{{ rootfs_size_bastion }}"
    security_groups:
      - BastionSG
  - name: "tower"
    count: "{{tower_instance_count}}"
    public_dns: true
    dns_loadbalancer: true
    image_id: "{{ tower_instance_image }}"
    flavor:
      ec2: "{{tower_instance_type}}"
      osp: "{{tower_instance_type}}"
      azure: "Standard_A2_V2"
    tags:
      - key: "AnsibleGroup"
        value: "towers"
      - key: "ostype"
        value: "linux"
      - key: "instance_filter"
        value: "{{ env_type }}-{{ email }}"
    security_groups:
      - DefaultSG
  - name: "server"
    count: "{{server_instance_count}}"
    public_dns: false
    image_id: "{{ server_instance_image }}"
    flavor:
      ec2: "{{server_instance_type}}"
      osp: "{{server_instance_type}}"
      azure: "Standard_A2_V2"
    tags:
      - key: "AnsibleGroup"
        value: "servers"
      - key: "ostype"
        value: "rhel"
      - key: "instance_filter"
        value: "{{ env_type }}-{{ email }}"
    key_name: "{{key_name}}"
    security_groups:
      - DefaultSG
###### VARIABLES YOU SHOULD ***NOT*** CONFIGURE FOR YOUR DEPLOYEMNT
###### You can, but you usually wouldn't need to.
ansible_user: ec2-user
remote_user: ec2-user
common_packages:
  - python
  - unzip
  - bash-completion
  - tmux
  - bind-utils
  - wget
  - git
  - vim-enhanced
  - at
  - ansible
rhel_repos:
  - rhel-7-server-rpms
  - rhel-7-server-extras-rpms
  - epel-release-latest-7
## Currently there is no NFS created for this Environment - See ocp-workshop for clues.
# ## NFS Server settings
# nfs_vg: nfsvg
# nfs_pvs: /dev/xvdb
# nfs_export_path: /srv/nfs
#
# nfs_shares:
#   - es-storage
#   - user-vols
#   - jenkins
#   - nexus
#   - nexus2
zone_internal_dns: "{{guid}}.internal."
chomped_zone_internal_dns: "{{guid}}.internal"
tower_public_dns: "towerlb.{{subdomain_base}}."
#tower_public_dns: "tower.{{subdomain_base}}."
bastion_public_dns: "bastion.{{subdomain_base}}."
bastion_public_dns_chomped: "bastion.{{subdomain_base}}"
vpcid_cidr_block: "192.168.0.0/16"
vpcid_name_tag: "{{subdomain_base}}"
az_1_name: "{{ aws_region }}a"
az_2_name: "{{ aws_region }}b"
subnet_private_1_cidr_block: "192.168.2.0/24"
subnet_private_1_az: "{{ az_2_name }}"
subnet_private_1_name_tag: "{{subdomain_base}}-private"
subnet_private_2_cidr_block: "192.168.1.0/24"
subnet_private_2_az: "{{ az_1_name }}"
subnet_private_2_name_tag: "{{subdomain_base}}-private"
subnet_public_1_cidr_block: "192.168.10.0/24"
subnet_public_1_az: "{{ az_1_name }}"
subnet_public_1_name_tag: "{{subdomain_base}}-public"
subnet_public_2_cidr_block: "192.168.20.0/24"
subnet_public_2_az: "{{ az_2_name }}"
subnet_public_2_name_tag: "{{subdomain_base}}-public"
dopt_domain_name: "{{ aws_region }}.compute.internal"
rtb_public_name_tag: "{{subdomain_base}}-public"
rtb_private_name_tag: "{{subdomain_base}}-private"
cf_template_description: "{{ env_type }}-{{ guid }} Ansible Agnostic Deployer "
#### OSP ####
# See cloud_providers/osp_default_vars.yml
# See roles/infra-osp-project-create/defaults/main.yml
# Set this to true if you need to create a new project in OpenStack
# This should almost always be set to true for OpenShift installations
# If it is set to false, the {{ osp_project_name }} must already exist and
# should be able to run whatever you are deploying
#osp_project_create: true
# If osp_project_create is set to yes, define those:
# Quotas to set for new project that is created
#quota_num_instances: 15
#quota_num_cores: 72
#quota_memory: 131072 # in MB
#quota_num_volumes: 25
#quota_volumes_gigs: 500
#quota_loadbalancers: #when Octavia is available
#quota_pool: #when Octavia is available
#quota_networks: 3
#quota_subnets: 3
#quota_routers: 3
quota_fip: 7
#quota_sg: 10
#quota_sg_rules: 100
ansible/configs/ansible-tower-implementation/files/cloud_providers/azure_cloud_template.j2
New file
@@ -0,0 +1,428 @@
{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters" : {
        "guid": {
            "type" : "string",
            "minLength" : 3,
            "metadata" : {
                "description" : "GUID of the environment"
            }
        },
        "DNSZone": {
            "type" : "string",
            "minLength" : 3,
            "metadata" : {
                "description" : "dns zone of the environment, to update or create"
            }
        },
        "adminUsername" : {
            "type" : "string",
            "minLength" : 1,
            "defaultValue" : "azure",
            "metadata" : {
                "description" : "User name for the Virtual Machine."
            }
        },
        "sshKeyData" : {
            "type" : "securestring",
            "metadata" : {
                "description" : "SSH RSA public key file as a string."
            }
        },
        "vmSize" : {
            "type" : "string",
            "defaultValue" : "Basic_A2",
            "allowedValues" : [
                "Basic_A2",
                "Standard_A2",
                "Standard_A3",
                "Standard_A4",
                "Standard_A5",
                "Standard_A6",
                "Standard_A7",
                "Standard_A8",
                "Standard_A9",
                "Standard_A10",
                "Standard_A11",
                "Standard_D2",
                "Standard_D3",
                "Standard_D4",
                "Standard_D11",
                "Standard_D12",
                "Standard_D13",
                "Standard_D14",
                "Standard_D2_v2",
                "Standard_D3_v2",
                "Standard_D4_v2",
                "Standard_D5_v2",
                "Standard_D11_v2",
                "Standard_D12_v2",
                "Standard_D13_v2",
                "Standard_D14_v2",
                "Standard_G1",
                "Standard_G2",
                "Standard_G3",
                "Standard_G4",
                "Standard_G5",
                "Standard_DS2",
                "Standard_DS3",
                "Standard_DS4",
                "Standard_DS11",
                "Standard_DS12",
                "Standard_DS13",
                "Standard_DS14",
                "Standard_DS2_v2",
                "Standard_DS3_v2",
                "Standard_DS4_v2",
                "Standard_DS5_v2",
                "Standard_DS11_v2",
                "Standard_DS12_v2",
                "Standard_DS13_v2",
                "Standard_DS14_v2",
                "Standard_GS1",
                "Standard_GS2",
                "Standard_GS3",
                "Standard_GS4",
                "Standard_GS5"
            ],
            "metadata" : {
                "description" : "The size of the each Node Virtual Machine."
            }
        }
    },
    "variables" : {
        "subzone": "[concat('{{guid}}.',parameters('DNSZone'))]",
        "location" : "[resourceGroup().location]",
        "virtualNetworkName" : "[concat('VNet', parameters('guid'))]",
        "addressPrefix" : "10.0.0.0/16",
        "vnetId" : "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]",
        "rhel" : {
            "publisher" : "Redhat",
            "offer" : "RHEL",
            "sku" : "7-RAW",
            "version" : "latest"
        },
        "tenantId" : "[subscription().tenantId]",
        "apiVersion" : "2015-06-15",
        "apiVersionCompute" : "2015-06-15",
        "apiVersionNetwork" : "2016-03-30",
        "tmApiVersion" : "2015-11-01",
        "apiVersionStorage" : "2015-06-15",
        "apiVersionLinkTemplate" : "2015-01-01",
        "nicName" : "OneVmNic",
        "publicIPAddressType" : "Dynamic",
        "subnetRef" : "[concat(variables('vnetID'),'/subnets/',variables('virtualNetworkName'))]",
        "sshKeyPath" : "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
        "sQuote" : "\"",
        "vmStorageAccountContainerName": "vhds",
        "storageAccountType": "Standard_LRS",
        "vhdStorageType" : "Premium_LRS",
        "storageAccountName": "[concat('vsts8',uniquestring(parameters('guid')))]"
    },
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "name": "[variables('StorageAccountName')]",
            "apiVersion": "2016-01-01",
            "location": "[resourceGroup().location]",
            "sku": {
                "name": "[variables('storageAccountType')]"
            },
            "kind": "Storage",
            "properties": {
            }
        },
{% for instance in instances %}
{% if instance['dns_loadbalancer']|d(false)|bool and not instance['unique']|d(false)|bool %}
        {
            "type": "Microsoft.Network/dnszones/a",
            "name": "[concat(variables('subzone'), '/', '{{instance['name']}}')]",
            "apiVersion": "2016-04-01",
            "dependsOn": [
{% for c in range(1,(instance['count'] |int)+1) %}
  {% if instance['unique']|d(false)|bool %}
    {% set instancename = instance['name'] %}
    {% else %}
    {% set instancename = instance['name'] + (loop.index|string) %}
  {% endif %}
                "[resourceId('Microsoft.Network/publicIPAddresses/', '{{instancename}}-PublicIP')]",
{% endfor %}
                "[resourceId('Microsoft.Network/dnsZones/', variables('subzone'))]",
            ],
            "properties": {
                "TTL": 3600,
                "ARecords": [
                    {
{% for c in range(1,(instance['count'] |int)+1) %}
  {% if instance['unique']|d(false)|bool %}
    {% set instancename = instance['name'] %}
    {% else %}
    {% set instancename = instance['name'] + (loop.index|string) %}
  {% endif %}
                        "ipv4Address": "[reference('{{instancename}}-PublicIP').ipAddress]"
{% endfor %}
                    }
                ]
            }
        },
{% endif %}
{% for c in range(1,(instance['count'] |int)+1) %}
  {% if instance['unique']|d(false)|bool %}
    {% set instancename = instance['name'] %}
    {% else %}
    {% set instancename = instance['name'] + (loop.index|string) %}
  {% endif %}
{% if instance['public_dns']|d(false)|bool %}
        {
            "type": "Microsoft.Network/dnszones/a",
            "name": "[concat(variables('subzone'), '/', '{{instancename}}')]",
            "apiVersion": "2016-04-01",
            "dependsOn": [
                "[resourceId('Microsoft.Network/publicIPAddresses/', '{{instancename}}-PublicIP')]",
                "[resourceId('Microsoft.Network/dnsZones/', variables('subzone'))]",
            ],
            "properties": {
                "TTL": 3600,
                "ARecords": [
                    {
                        "ipv4Address": "[reference('{{instancename}}-PublicIP').ipAddress]"
                    }
                ]
            }
        },
        {
            "apiVersion" : "2017-04-01",
            "type" : "Microsoft.Network/publicIPAddresses",
            "name" : "{{instancename}}-PublicIP",
            "location" : "[resourceGroup().location]",
            "properties" : {
                "publicIPAllocationMethod" : "Static",
                "dnsSettings" : {
                    "domainNameLabel" : "{{instancename}}-{{guid}}"
                }
            }
        },
        {
            "apiVersion" : "2017-04-01",
            "type" : "Microsoft.Network/networkInterfaces",
            "name" : "{{instancename}}-Interface",
            "location" : "[resourceGroup().location]",
            "dependsOn" : [
                "[resourceId('Microsoft.Network/publicIPAddresses/', '{{instancename}}-PublicIP')]",
                "[resourceId('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
            ],
            "properties" : {
                "ipConfigurations" : [
                    {
                        "name" : "ipconfig1",
                        "properties" : {
                            "privateIPAllocationMethod" : "Dynamic",
                            "publicIPAddress" : {
                                "id" : "[resourceId('Microsoft.Network/publicIPAddresses','{{instancename}}-PublicIP')]"
                            },
                            "subnet" : {
                                "id" : "[variables('subnetRef')]"
                            }
                        }
                    }
                ]
            }
        },
{% endif %}
        {
            "apiVersion" : "2017-03-30",
            "type" : "Microsoft.Compute/virtualMachines",
            "name" : "{{instancename}}",
            "location" : "[resourceGroup().location]",
            "dependsOn" : [
                "[resourceId('Microsoft.Network/networkInterfaces/', '{{instancename}}-Interface')]"
            ],
            "tags": {
                "Name": "{{instancename}}",
                "internaldns": "{{instancename}}.{{chomped_zone_internal_dns}}",
                "owner": "{{ email | default('unknownuser') }}",
                "Project": "{{project_tag}}",
{% for tag in instance['tags'] %}
                "{{tag['key']}}": "{{tag['value']}}",
{% endfor %}
                "{{project_tag}}": "{{ instance['name'] }}"
            },
            "properties" : {
                "hardwareProfile" : {
                    "vmSize" : "{{instance['flavor'][cloud_provider]}}"
                },
                "osProfile" : {
                    "computerName" : "{{instancename}}",
                    "adminUsername" : "[parameters('adminUsername')]",
                    "linuxConfiguration" : {
                        "disablePasswordAuthentication" : "true",
                        "ssh" : {
                            "publicKeys" : [
                                {
                                    "path" : "[variables('sshKeyPath')]",
                                    "keyData" : "[parameters('sshKeyData')]"
                                }
                            ]
                        }
                    }
                },
                "storageProfile" : {
                    "imageReference" : "[variables('rhel')]",
                    "osDisk" : {
                        "caching" : "ReadWrite",
                        "name" : "{{instancename}}-osdisk",
                        "createOption" : "FromImage",
                        "diskSizeGB" : "{{instance['rootfs_size']|d('50')}}"
                    },
                    "dataDisks" : [
{% for vol in instance['volumes']|default([]) %}
                        {
                            "caching" : "None",
                            "createOption" : "Empty",
                            "lun" : "{{loop.index}}",
                            "name": "{{instancename}}-{{vol['device_name']}}",
                            "diskSizeGB" : "{{vol['volume_size']}}"
                        },
{% endfor %}
                    ]
                },
                "networkProfile" : {
                    "networkInterfaces" : [
                        {
                            "id" : "[resourceId('Microsoft.Network/networkInterfaces','{{instancename}}-Interface')]"
                        }
                    ]
                },
                "diagnosticsProfile" : {
                    "bootDiagnostics" : {
                        "enabled" : "false",
                        "storageUri" : "[concat(reference(concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName')), '2016-01-01').primaryEndpoints.blob)]"
                    }
                }
            }
        },
{% endfor %}
{% endfor %}
        {
            "name": "[variables('subzone')]",
            "type": "Microsoft.Network/dnsZones",
            "apiVersion": "2017-09-01",
            "location" : "global",
        },
        {
            "apiVersion": "2017-05-10",
            "name": "nestedTemplate",
            "type": "Microsoft.Resources/deployments",
            "resourceGroup": "dns",
            "properties": {
                "mode": "Incremental",
                "template": {
                    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                    "contentVersion": "1.0.0.0",
                    "parameters": {},
                    "variables": {},
                    "resources": [
                    ]
                },
                "parameters": {}
            }
        },
        {
            "apiVersion" : "[variables('apiVersion')]",
            "type" : "Microsoft.Network/virtualNetworks",
            "name" : "[variables('virtualNetworkName')]",
            "location" : "[variables('location')]",
            "tags" : {
                "displayName" : "VirtualNetwork"
            },
            "properties" : {
                "addressSpace" : {
                    "addressPrefixes" : [
                        "[variables('addressPrefix')]"
                    ]
                },
                "subnets" : [
                    {
                        "name" : "[variables('virtualNetworkName')]",
                        "properties" : {
                            "addressPrefix" : "[variables('addressPrefix')]"
                        }
                    }
                ]
            }
        },
        {
            "type" : "Microsoft.Network/networkSecurityGroups",
            "name" : "[concat(resourceGroup().name, 'nsg')]",
            "tags" : {
                "displayName" : "NetworkSecurityGroup"
            },
            "apiVersion" : "[variables('apiVersion')]",
            "location" : "[resourceGroup().location]",
            "properties" : {
                "securityRules" : [
                    {
                        "name" : "default-allow-openshift-router-https",
                        "properties" : {
                            "protocol" : "Tcp",
                            "sourcePortRange" : "*",
                            "destinationPortRange" : "443",
                            "sourceAddressPrefix" : "*",
                            "destinationAddressPrefix" : "*",
                            "access" : "Allow",
                            "priority" : 2000,
                            "direction" : "Inbound"
                        }
                    },
                    {
                        "name" : "default-allow-openshift-router-http\n",
                        "properties" : {
                            "protocol" : "Tcp",
                            "sourcePortRange" : "*",
                            "destinationPortRange" : "80",
                            "sourceAddressPrefix" : "*",
                            "destinationAddressPrefix" : "*",
                            "access" : "Allow",
                            "priority" : 2001,
                            "direction" : "Inbound"
                        }
                    },
                    {
                        "name" : "default-allow-openshift-master",
                        "properties" : {
                            "protocol" : "Tcp",
                            "sourcePortRange" : "*",
                            "destinationPortRange" : "8443",
                            "sourceAddressPrefix" : "*",
                            "destinationAddressPrefix" : "*",
                            "access" : "Allow",
                            "priority" : 2002,
                            "direction" : "Inbound"
                        }
                    },
                    {
                        "name" : "default-allow-ssh",
                        "properties" : {
                            "protocol" : "Tcp",
                            "sourcePortRange" : "*",
                            "destinationPortRange" : "22",
                            "sourceAddressPrefix" : "*",
                            "destinationAddressPrefix" : "*",
                            "access" : "Allow",
                            "priority" : 2003,
                            "direction" : "Inbound"
                        }
                    }
                ]
            }
        }
    ],
    "outputs" : {
    }
}
ansible/configs/ansible-tower-implementation/files/cloud_providers/ec2_cloud_template.j2
New file
@@ -0,0 +1,287 @@
AWSTemplateFormatVersion: "2010-09-09"
Mappings:
  RegionMapping:
    us-east-1:
      RHELAMI: ami-c998b6b2
      WIN2012R2AMI: ami-0dcdd073eeabb0101
    us-east-2:
      RHELAMI: ami-cfdafaaa
      WIN2012R2AMI: ami-72745d17
    us-west-1:
      RHELAMI: ami-66eec506
      WIN2012R2AMI: ami-ef95ae8f
    us-west-2:
      RHELAMI: ami-9fa343e7
      WIN2012R2AMI: ami-af5980d7
    eu-west-1:
      RHELAMI: ami-bb9a6bc2
      WIN2012R2AMI: ami-24f04d5d
    eu-west-2:
      WIN2012R2AMI: ami-6a746a0e
    ca-central-1:
      WIN2012R2AMI: ami-4e4cf72a
    eu-central-1:
      RHELAMI: ami-d74be5b8
      WIN2012R2AMI: ami-88c14ee7
    ap-northeast-1:
      RHELAMI: ami-30ef0556
      WIN2012R2AMI: ami-dcdd66ba
    ap-northeast-2:
      RHELAMI: ami-0f5a8361
      WIN2012R2AMI: ami-681cbb06
    ap-southeast-1:
      RHELAMI: ami-10bb2373
      WIN2012R2AMI: ami-f887d59b
    ap-southeast-2:
      RHELAMI: ami-ccecf5af
      WIN2012R2AMI: ami-f601f494
    sa-east-1:
      RHELAMI: ami-a789ffcb
      WIN2012R2AMI: ami-42a5e12e
    ap-south-1:
      RHELAMI: ami-cdbdd7a2
      WIN2012R2AMI: ami-b9e8a6d6
  DNSMapping:
    "us-east-1":
      domain: "us-east-1.compute.internal"
    "us-west-1":
      domain: "us-west-1.compute.internal"
    "us-west-2":
      domain: "us-west-2.compute.internal"
    "eu-west-1":
      domain: "eu-west-1.compute.internal"
    "eu-central-1":
      domain: "eu-central-1.compute.internal"
    "ap-northeast-1":
      domain: "ap-northeast-1.compute.internal"
    "ap-northeast-2":
      domain: "ap-northeast-2.compute.internal"
    "ap-southeast-1":
      domain: "ap-southeast-1.compute.internal"
    "ap-southeast-2":
      domain: "ap-southeast-2.compute.internal"
    "sa-east-1":
      domain: "sa-east-1.compute.internal"
    "ap-south-1":
      domain: "ap-south-1.compute.internal"
Resources:
   Vpc:
    Type: "AWS::EC2::VPC"
    Properties:
     CidrBlock: "192.168.0.0/16"
     EnableDnsSupport: true
     EnableDnsHostnames: true
     Tags:
      - Key: Name
        Value: "{{vpcid_name_tag}}"
      - Key: Hostlication
        Value:
          Ref: "AWS::StackId"
   VpcInternetGateway:
    Type: "AWS::EC2::InternetGateway"
   VpcGA:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
     InternetGatewayId:
      Ref: VpcInternetGateway
     VpcId:
      Ref: Vpc
   VpcRouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
     VpcId:
      Ref: Vpc
   VPCRouteInternetGateway:
    DependsOn: VpcGA
    Type: "AWS::EC2::Route"
    Properties:
     GatewayId:
      Ref: VpcInternetGateway
     DestinationCidrBlock: "0.0.0.0/0"
     RouteTableId:
      Ref: VpcRouteTable
   PublicSubnet:
    Type: "AWS::EC2::Subnet"
    DependsOn:
     - Vpc
    Properties:
     CidrBlock: "192.168.0.0/24"
     Tags:
      - Key: Name
        Value: "{{project_tag}}"
      - Key: Hostlication
        Value:
          Ref: "AWS::StackId"
     MapPublicIpOnLaunch: true
     VpcId:
      Ref: Vpc
   PublicSubnetRTA:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
     RouteTableId:
      Ref: VpcRouteTable
     SubnetId:
      Ref: PublicSubnet
   HostSG:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
     GroupDescription: Host
     VpcId:
      Ref: Vpc
     Tags:
      - Key: Name
        Value: host_sg
   HostUDPPorts:
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties:
     GroupId:
      "Fn::GetAtt":
       - HostSG
       - GroupId
     IpProtocol: udp
     FromPort: 0
     ToPort: 65535
     CidrIp: "0.0.0.0/0"
   HostTCPPorts:
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties:
     GroupId:
      "Fn::GetAtt":
       - HostSG
       - GroupId
     IpProtocol: tcp
     FromPort: 0
     ToPort: 65535
     CidrIp: "0.0.0.0/0"
   zoneinternalidns:
    Type: "AWS::Route53::HostedZone"
    Properties:
     Name: "{{ zone_internal_dns }}"
     VPCs:
      - VPCId:
          Ref: Vpc
        VPCRegion:
          Ref: "AWS::Region"
     HostedZoneConfig:
      Comment: "Created By ansible agnostic deployer"
{% for instance in instances %}
{% if instance['dns_loadbalancer']|d(false)|bool and not instance['unique']|d(false)|bool %}
   {{instance['name']}}DNSLoadBalancer:
    Type: "AWS::Route53::RecordSetGroup"
    DependsOn:
{% for c in range(1,(instance['count'] |int)+1) %}
    - {{instance['name']}}{{c}}EIP
{% endfor %}
    Properties:
     HostedZoneId: {{HostedZoneId}}
     RecordSets:
      - Name: "{{instance['name']}}.{{subdomain_base}}."
        Type: A
        TTL: 900
        ResourceRecords:
{% for c in range(1,(instance['count'] |int)+1) %}
          - "Fn::GetAtt":
            - {{instance['name']}}{{loop.index}}
            - PublicIp
{% endfor %}
{% endif %}
{% for c in range(1,(instance['count'] |int)+1) %}
   {{instance['name']}}{{loop.index}}:
    Type: "AWS::EC2::Instance"
    Properties:
     ImageId:
      "Fn::FindInMap":
       - RegionMapping
       - Ref: "AWS::Region"
       - {{ instance['image_id'] | default('RHELAMI') }}
     InstanceType: "{{instance['flavor'][cloud_provider]}}"
     KeyName: "{{instance['key_name'] | default(key_name)}}"
{% if instance['UserData'] is defined %}
     {{instance['UserData']}}
{% endif %}
     SecurityGroupIds:
        - "Fn::GetAtt":
          - HostSG
          - GroupId
     SubnetId:
      Ref: PublicSubnet
     Tags:
{% if instance['unique'] | d(false) | bool %}
      - Key: Name
        Value: {{instance['name']}}
      - Key: internaldns
        Value: {{instance['name']}}.{{chomped_zone_internal_dns}}
{% else %}
      - Key: Name
        Value: {{instance['name']}}{{loop.index}}
      - Key: internaldns
        Value: {{instance['name']}}{{loop.index}}.{{chomped_zone_internal_dns}}
{% endif %}
      - Key: "owner"
        Value: "{{ email | default('unknownuser') }}"
      - Key: "Project"
        Value: "{{project_tag}}"
      - Key: "{{project_tag}}"
        Value: "{{ instance['name'] }}"
{% for tag in instance['tags'] %}
      - Key: {{tag['key']}}
        Value: {{tag['value']}}
{% endfor %}
     BlockDeviceMappings:
        - DeviceName: "/dev/sda1"
          Ebs:
            VolumeSize: 50
        - DeviceName: "/dev/xvdb"
          Ebs:
            VolumeType: gp2
            VolumeSize: 20
   {{instance['name']}}{{loop.index}}InternalDNS:
    Type: "AWS::Route53::RecordSetGroup"
    Properties:
     HostedZoneId:
      Ref: zoneinternalidns
     RecordSets:
{% if instance['unique'] | d(false) | bool %}
      - Name: "{{instance['name']}}.{{zone_internal_dns}}"
{% else %}
      - Name: "{{instance['name']}}{{loop.index}}.{{zone_internal_dns}}"
{% endif %}
        Type: A
        TTL: 10
        ResourceRecords:
          - "Fn::GetAtt":
            - {{instance['name']}}{{loop.index}}
            - PrivateIp
{% if instance['public_dns'] %}
   {{instance['name']}}{{loop.index}}EIP:
     Type: "AWS::EC2::EIP"
     DependsOn:
      - VpcGA
     Properties:
      InstanceId:
       Ref: {{instance['name']}}{{loop.index}}
   {{instance['name']}}{{loop.index}}PubliclDNS:
     Type: "AWS::Route53::RecordSetGroup"
     DependsOn:
        - {{instance['name']}}{{loop.index}}EIP
     Properties:
      HostedZoneId: {{HostedZoneId}}
      RecordSets:
{% if instance['unique'] | d(false) | bool %}
        - Name: "{{instance['name']}}.{{subdomain_base}}."
{% else %}
        - Name: "{{instance['name']}}{{loop.index}}.{{subdomain_base}}."
{% endif %}
          Type: A
          TTL: 10
          ResourceRecords:
          - "Fn::GetAtt":
            - {{instance['name']}}{{loop.index}}
            - PublicIp
{% endif %}
{% endfor %}
{% endfor %}
ansible/configs/ansible-tower-implementation/files/cloud_providers/ec2_cloud_template_json.j2
New file
@@ -0,0 +1,792 @@
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": { },
  "Mappings": {
    "RegionMapping": {
      "us-east-1": {
        "RHELAMI": "ami-b63769a1", "WIN2012R2AMI": "ami-7da4ab6b"
      },
      "us-east-2": {
        "RHELAMI": "ami-0932686c", "WIN2012R2AMI": "ami-ffae8f9a"
      },
      "us-west-1": {
        "RHELAMI": "ami-2cade64c", "WIN2012R2AMI": "ami-a11836c1"
      },
      "us-west-2": {
        "RHELAMI": "ami-6f68cf0f", "WIN2012R2AMI": "ami-a1c1ddd8"
      },
      "eu-west-1": {
        "RHELAMI": "ami-02ace471", "WIN2012R2AMI": "ami-cc8e98a8"
      },
      "eu-central-1": {
        "RHELAMI": "ami-e4c63e8b", "WIN2012R2AMI": "ami-da1ebdb5"
      },
      "ap-northeast-1": {
        "RHELAMI": "ami-5de0433c", "WIN2012R2AMI": "ami-4312cc2d"
      },
      "ap-northeast-2": {
        "RHELAMI": "ami-44db152a", "WIN2012R2AMI": "ami-68756f0f"
      },
      "ap-southeast-1": {
        "RHELAMI": "ami-2c95344f", "WIN2012R2AMI": "ami-7644d315"
      },
      "ap-southeast-2": {
        "RHELAMI": "ami-39ac915a", "WIN2012R2AMI": "ami-468f9225"
      },
      "sa-east-1": {
        "RHELAMI": "ami-7de77b11", "WIN2012R2AMI": "ami-c8285ca4"
      },
      "ap-south-1": {
        "RHELAMI": "ami-cdbdd7a2", "WIN2012R2AMI": "ami-8eafd6e1"
      }
    },
    "DNSMapping": {
      "us-east-1": {
        "domain": "us-east-1.compute.internal"
      },
      "us-west-1": {
        "domain": "us-west-1.compute.internal"
      },
      "us-west-2": {
        "domain": "us-west-2.compute.internal"
      },
      "eu-west-1": {
        "domain": "eu-west-1.compute.internal"
      },
      "eu-central-1": {
        "domain": "eu-central-1.compute.internal"
      },
      "ap-northeast-1": {
        "domain": "ap-northeast-1.compute.internal"
      },
      "ap-northeast-2": {
        "domain": "ap-northeast-2.compute.internal"
      },
      "ap-southeast-1": {
        "domain": "ap-southeast-1.compute.internal"
      },
      "ap-southeast-2": {
        "domain": "ap-southeast-2.compute.internal"
      },
      "sa-east-1": {
        "domain": "sa-east-1.compute.internal"
      },
      "ap-south-1": {
        "domain": "ap-south-1.compute.internal"
      }
    }
  },
  "Resources": {
    "Vpc": {
      "Type": "AWS::EC2::VPC",
      "Properties": {
        "CidrBlock": "192.168.0.0/16",
        "EnableDnsSupport": "true",
        "EnableDnsHostnames": "true",
        "Tags": [
          {
            "Key": "Name",
            "Value": "{{vpcid_name_tag}}"
          },
          {
            "Key": "Hostlication",
            "Value": {
              "Ref": "AWS::StackId"
            }
          }
        ]
      }
    },
    "VpcInternetGateway": {
      "Type": "AWS::EC2::InternetGateway",
      "Properties": {}
    },
    "VpcGA": {
      "Type": "AWS::EC2::VPCGatewayAttachment",
      "Properties": {
        "InternetGatewayId": {
          "Ref": "VpcInternetGateway"
        },
        "VpcId": {
          "Ref": "Vpc"
        }
      }
    },
    "VpcRouteTable": {
      "Type": "AWS::EC2::RouteTable",
      "Properties": {
        "VpcId": {
          "Ref": "Vpc"
        }
      }
    },
    "VPCRouteInternetGateway": {
      "DependsOn" : "VpcGA",
  "Type": "AWS::EC2::Route",
      "Properties": {
        "GatewayId": {
          "Ref": "VpcInternetGateway"
        },
        "DestinationCidrBlock": "0.0.0.0/0",
        "RouteTableId": {
          "Ref": "VpcRouteTable"
        }
      }
    },
    "PublicSubnet": {
      "Type": "AWS::EC2::Subnet",
      "DependsOn": [
        "Vpc"
      ],
      "Properties": {
        "CidrBlock": "192.168.0.0/24",
        "Tags": [
          {
            "Key": "Name",
            "Value": "{{project_tag}}"
          },
          {
            "Key": "Hostlication",
            "Value": {
              "Ref": "AWS::StackId"
            }
          }
        ],
        "MapPublicIpOnLaunch": "true",
        "VpcId": {
          "Ref": "Vpc"
        }
      }
    },
    "PublicSubnetRTA": {
      "Type": "AWS::EC2::SubnetRouteTableAssociation",
      "Properties": {
        "RouteTableId": {
          "Ref": "VpcRouteTable"
        },
        "SubnetId": {
          "Ref": "PublicSubnet"
        }
      }
    },
    "HostSG": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "Host",
        "VpcId": {
          "Ref": "Vpc"
        },
        "Tags": [
          {
            "Key": "Name",
            "Value": "host_sg"
          }
        ]
      }
    },
    "HostUDPPorts": {
      "Type": "AWS::EC2::SecurityGroupIngress",
      "Properties": {
        "GroupId": {
          "Fn::GetAtt": [
            "HostSG",
            "GroupId"
          ]
        },
        "IpProtocol": "udp",
        "FromPort": "0",
        "ToPort": "65535",
        "CidrIp": "0.0.0.0/0"
      }
    },
    "HostTCPPorts": {
      "Type": "AWS::EC2::SecurityGroupIngress",
      "Properties": {
        "GroupId": {
          "Fn::GetAtt": [
            "HostSG",
            "GroupId"
          ]
        },
        "IpProtocol": "tcp",
        "FromPort": "0",
        "ToPort": "65535",
        "CidrIp": "0.0.0.0/0"
      }
    },
    "zoneinternalidns": {
      "Type": "AWS::Route53::HostedZone",
      "Properties": {
        "Name": "{{ zone_internal_dns }}",
        "VPCs" :  [{
      "VPCId": { "Ref" : "Vpc" },
      "VPCRegion": { "Ref": "AWS::Region" } } ],
        "HostedZoneConfig": {
          "Comment": "Created By ansible agnostic deployer"
        }
      }
    },
    "BastionDNS": {
      "Type": "AWS::Route53::RecordSetGroup",
      "DependsOn": [ "BastionEIP" ],
      "Properties": {
        "HostedZoneId": "{{HostedZoneId}}",
        "RecordSets": [
          {
            "Name": "{{bastion_public_dns}}",
            "Type": "A",
            "TTL": "10",
            "ResourceRecords": [
              {
                "Fn::GetAtt": [
                  "Bastion",
                  "PublicIp"
                ]
              }
            ]
          }
        ]
      }
    },
    "CloudDNS": {
      "Type": "AWS::Route53::RecordSetGroup",
      "DependsOn": [ "BastionEIP" ],
      "Properties": {
        "HostedZoneId": "{{HostedZoneId}}",
        "RecordSets": [
          {
            "Name": "{{cloudapps_dns}}",
            "Type": "A",
            "TTL": "10",
            "ResourceRecords": [
              {
                "Fn::GetAtt": [
                  "Bastion",
                  "PublicIp"
                ]
              }
            ]
          }
        ]
      }
    },
    "FrontendDNSLoadBalancer": {
      "Type": "AWS::Route53::RecordSetGroup",
      "DependsOn": "frontend{{frontend_instance_count}}EIP",
      "Properties": {
        "HostedZoneId": "{{HostedZoneId}}",
        "RecordSets": [
        {
          "Name" : "{{frontend_public_dns}}",
          "Type" : "A",
          "TTL" : "900",
          "ResourceRecords" : [
{% for c in range(1,(frontend_instance_count|int)+1) %}
{ "Fn::GetAtt": [ "frontend{{loop.index}}", "PublicIp" ] }{% if loop.index < frontend_instance_count   %},{% endif %}
{% endfor %}
          ]}]
    }},
    {% for c in range(1,(frontend_instance_count|int)+1) %}
    "PublicHostDNS{{loop.index}}": {
      "Type": "AWS::Route53::RecordSetGroup",
      "DependsOn": "frontend{{frontend_instance_count}}EIP",
      "Properties": {
        "HostedZoneId": "{{HostedZoneId}}",
        "RecordSets": [
        {
          "Name" : "frontend{{loop.index}}.{{subdomain_base}}.",
          "Type" : "A",
          "TTL" : "900",
          "ResourceRecords" : [
{ "Fn::GetAtt": [ "frontend{{loop.index}}", "PublicIp" ] }
          ]}]
    }},
{% endfor %}
    "Bastion": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "ImageId": {
          "Fn::FindInMap": [
            "RegionMapping",
            {
              "Ref": "AWS::Region"
            },
            "RHELAMI"
          ]
        },
        "InstanceType": "{{bastion_instance_type}}",
        "KeyName": "{{key_name}}",
        "UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
          "#!/bin/bash -xe\n",
          "sed -i '2i ldap_group_nesting_level=0' /etc/sssd/sssd.conf \n",
          "systemctl restart sssd \n",
          "echo 'PermitRootLogin without-password' >> /etc/ssh/sshd_config \n",
          "sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config \n",
          "sed -i 's/^ChallengeResponseAuthentication yes/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config \n",
          "systemctl restart sshd \n"
          ]]}},
        "SecurityGroupIds": [
          {
            "Fn::GetAtt": [
              "HostSG",
              "GroupId"
            ]
          }
        ],
        "SubnetId": {
          "Ref": "PublicSubnet"
        },
        "Tags": [
          {
            "Key": "Name",
            "Value": "bastion"
          },
          {
            "Key": "AnsibleGroup",
            "Value": "bastions"
          },
          {
            "Key": "Project",
            "Value": "{{project_tag}}"
          },
          {
            "Key": "{{ project_tag }}",
            "Value": "bastion"
          },
          {
            "Key": "internaldns",
            "Value": "bastion.{{chomped_zone_internal_dns}}"
          },
          {
            "Key": "owner",
            "Value": "{{ email | default('unknown')}}"
          }
        ]
      }
    },
      "BastionEIP" : {
          "Type" : "AWS::EC2::EIP",
          "DependsOn": [ "VpcGA" ],
          "Properties" : {
              "InstanceId" : { "Ref" : "Bastion" }
          }
      },
    "BastionInternalDNS": {
      "Type": "AWS::Route53::RecordSetGroup",
      "Properties": {
      "HostedZoneId" : { "Ref" : "zoneinternalidns" },
        "RecordSets": [
          {
            "Name": "bastion.{{zone_internal_dns}}",
            "Type": "A",
            "TTL": "10",
            "ResourceRecords": [
              {
                "Fn::GetAtt": [
                  "Bastion",
                  "PrivateIp"
                ]
              }
            ]
          }
        ]
      }
    },
    {% for c in range(1,(frontend_instance_count|int)+1) %}
        "frontend{{loop.index}}": {
          "Type": "AWS::EC2::Instance",
          "Properties": {
            "ImageId": {
              "Fn::FindInMap": [
                "RegionMapping",
                {
                  "Ref": "AWS::Region"
                },
                "RHELAMI"
              ]
            },
            "InstanceType": "{{frontend_instance_type}}",
            "KeyName": "{{key_name}}",
            "SecurityGroupIds": [
              {
                "Fn::GetAtt": [
                  "HostSG",
                  "GroupId"
                ]
              }
            ],
            "SubnetId": {
              "Ref": "PublicSubnet"
            },
            "Tags": [
              {
                "Key": "Name",
                "Value": "frontend{{loop.index}}"
              },
              {
                "Key": "AnsibleGroup",
                "Value": "frontends"
              },
              {
                "Key": "Project",
                "Value": "{{project_tag}}"
              },
              {
                "Key": "{{ project_tag }}",
                "Value": "frontend"
              },
              {
                "Key": "internaldns",
                "Value": "frontend{{loop.index}}.{{chomped_zone_internal_dns}}"
              },
              {
                "Key": "owner",
                "Value": "{{ email | default('unknown')}}"
              }
            ],
            "BlockDeviceMappings": [
              {
                "DeviceName": "/dev/sda1",
                "Ebs": {
                  "VolumeSize": 50
                }
              },
              {
                "DeviceName": "/dev/xvdb",
                "Ebs": {
                  "VolumeType": "gp2",
                  "VolumeSize": 30
                }
              }
            ]
          }
        },
      "frontend{{loop.index}}EIP" : {
          "Type" : "AWS::EC2::EIP",
          "DependsOn": [ "VpcGA" ],
          "Properties" : {
              "InstanceId" : { "Ref" : "frontend{{loop.index}}" }
          }
      },
        "frontend{{loop.index}}DNS": {
          "Type": "AWS::Route53::RecordSetGroup",
          "Properties": {
          "HostedZoneId" : { "Ref" : "zoneinternalidns" },
            "RecordSets": [
              {
                "Name": "frontend{{loop.index}}.{{zone_internal_dns}}",
                "Type": "A",
                "TTL": "10",
                "ResourceRecords": [
                  {
                    "Fn::GetAtt": [
                      "frontend{{loop.index}}",
                      "PrivateIp"
                    ]
                  }
                ]
              }
            ]
          }
        },
        {% endfor %}
        {% for c in range(1,(app_instance_count|int)+1) %}
        "app{{loop.index}}": {
          "Type": "AWS::EC2::Instance",
          "Properties": {
            "ImageId": {
              "Fn::FindInMap": [
                "RegionMapping",
                {
                  "Ref": "AWS::Region"
                },
                "RHELAMI"
              ]
            },
            "InstanceType": "{{app_instance_type}}",
            "KeyName": "{{key_name}}",
            "SecurityGroupIds": [
              {
                "Fn::GetAtt": [
                  "HostSG",
                  "GroupId"
                ]
              }
            ],
            "SubnetId": {
              "Ref": "PublicSubnet"
            },
            "Tags": [
              {
                "Key": "Name",
                "Value": "app{{loop.index}}"
              },
              {
                "Key": "AnsibleGroup",
                "Value": "apps"
              },
              {
                "Key": "Project",
                "Value": "{{project_tag}}"
              },
              {
                "Key": "{{ project_tag }}",
                "Value": "app"
              },
              {
                "Key": "internaldns",
                "Value": "app{{loop.index}}.{{chomped_zone_internal_dns}}"
              },
              {
                "Key": "owner",
                "Value": "{{ email | default('unknown')}}"
              }
            ],
            "BlockDeviceMappings": [
              {
                "DeviceName": "/dev/sda1",
                "Ebs": {
                  "VolumeSize": 50
                }
              },
              {
                "DeviceName": "/dev/xvdb",
                "Ebs": {
                  "VolumeType": "gp2",
                  "VolumeSize": 30
                }
              }
            ]
          }
        },
        "app{{loop.index}}DNS": {
          "Type": "AWS::Route53::RecordSetGroup",
          "Properties": {
          "HostedZoneId" : { "Ref" : "zoneinternalidns" },
            "RecordSets": [
              {
                "Name": "app{{loop.index}}.{{zone_internal_dns}}",
                "Type": "A",
                "TTL": "10",
                "ResourceRecords": [
                  {
                    "Fn::GetAtt": [
                      "app{{loop.index}}",
                      "PrivateIp"
                    ]
                  }
                ]
              }
            ]
          }
        },
        {% endfor %}
    {% for c in range(1,(appdb_instance_count|int)+1) %}
    "appdb{{loop.index}}": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "ImageId": {
          "Fn::FindInMap": [
            "RegionMapping",
            {
              "Ref": "AWS::Region"
            },
            "RHELAMI"
          ]
        },
        "InstanceType": "{{appdb_instance_type}}",
        "KeyName": "{{key_name}}",
        "SecurityGroupIds": [
          {
            "Fn::GetAtt": [
              "HostSG",
              "GroupId"
            ]
          }
        ],
        "SubnetId": {
          "Ref": "PublicSubnet"
        },
        "Tags": [
          {
            "Key": "Name",
            "Value": "appdb{{loop.index}}"
          },
          {
            "Key": "AnsibleGroup",
            "Value": "appdbs"
          },
          {
            "Key": "Project",
            "Value": "{{project_tag}}"
          },
          {
            "Key": "{{ project_tag }}",
            "Value": "appdb"
          },
          {
            "Key": "internaldns",
            "Value": "appdb{{loop.index}}.{{chomped_zone_internal_dns}}"
          },
          {
            "Key": "owner",
            "Value": "{{ email | default('unknown')}}"
          }
        ],
        "BlockDeviceMappings": [
          {
            "DeviceName": "/dev/sda1",
            "Ebs": {
              "VolumeSize": 50
            }
          },
          {
            "DeviceName": "/dev/xvdb",
            "Ebs": {
              "VolumeType": "gp2",
              "VolumeSize": 30
            }
          }
        ]
      }
    },
    "appdb{{loop.index}}DNS": {
      "Type": "AWS::Route53::RecordSetGroup",
      "Properties": {
      "HostedZoneId" : { "Ref" : "zoneinternalidns" },
        "RecordSets": [
          {
            "Name": "appdb{{loop.index}}.{{zone_internal_dns}}",
            "Type": "A",
            "TTL": "10",
            "ResourceRecords": [
              {
                "Fn::GetAtt": [
                  "appdb{{loop.index}}",
                  "PrivateIp"
                ]
              }
            ]
          }
        ]
      }
    },
    {% endfor %}
    {% for c in range(1,(support_instance_count|int)+1) %}
    "support{{loop.index}}": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "ImageId": {
          "Fn::FindInMap": [
            "RegionMapping",
            {
              "Ref": "AWS::Region"
            },
            "RHELAMI"
          ]
        },
        "InstanceType": "{{support_instance_type}}",
        "KeyName": "{{key_name}}",
        "SecurityGroupIds": [
          {
            "Fn::GetAtt": [
              "HostSG",
              "GroupId"
            ]
          }
        ],
        "SubnetId": {
          "Ref": "PublicSubnet"
        },
        "Tags": [
          {
            "Key": "Name",
            "Value": "support{{loop.index}}"
          },
          {
            "Key": "AnsibleGroup",
            "Value": "support"
          },
          {
            "Key": "Project",
            "Value": "{{project_tag}}"
          },
          {
            "Key": "{{ project_tag }}",
            "Value": "support"
          },
          {
            "Key": "internaldns",
            "Value": "support{{loop.index}}.{{chomped_zone_internal_dns}}"
          },
          {
            "Key": "owner",
            "Value": "{{ email | default('unknown')}}"
          }
        ],
        "BlockDeviceMappings": [
          {
            "DeviceName": "/dev/sda1",
            "Ebs": {
              "VolumeSize": 50
            }
          },
          {
            "DeviceName": "/dev/xvdb",
            "Ebs": {
              "VolumeType": "gp2",
              "VolumeSize": 50
            }
          }
        ]
      }
    },
    "support{{loop.index}}DNS": {
      "Type": "AWS::Route53::RecordSetGroup",
      "Properties": {
      "HostedZoneId" : { "Ref" : "zoneinternalidns" },
        "RecordSets": [
          {
            "Name": "support{{loop.index}}.{{zone_internal_dns}}",
            "Type": "A",
            "TTL": "10",
            "ResourceRecords": [
              {
                "Fn::GetAtt": [
                  "support{{loop.index}}",
                  "PrivateIp"
                ]
              }
            ]
          }
        ]
      }
    }{% if loop.index < support_instance_count %},{% endif %}
  {% endfor %}
  },
  "Outputs": {
    "Route53internalzoneOutput": {
      "Description": "The ID of the internal route 53 zone",
      "Value": {
        "Ref": "zoneinternalidns"
      }
    }
  }
}
ansible/configs/ansible-tower-implementation/files/cloud_providers/terraform_ec2_cloud_template.tf.j2
New file
@@ -0,0 +1,164 @@
#----------------------------------------------
# Terraform ec2 variables
#----------------------------------------------
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "security_group" { default = "{{ env_type }}-host_sg" }
variable "keypair" { default = "{{ env_type }}-{{ key_name }}" }
variable "aws_region" { default = "us-east-1" }
variable "aws_amis" {
    type = "map"
    default = {
        RHELAMI.ap-northeast-1 = "ami-5de0433c"
        RHELAMI.ap-northeast-2 = "ami-44db152a"
        RHELAMI.ap-southeast-1 = "ami-2c95344f"
        RHELAMI.ap-southeast-2 = "ami-39ac915a"
        RHELAMI.ap-south-1     = "ami-cdbdd7a2"
        RHELAMI.eu-central-1   = "ami-e4c63e8b"
        RHELAMI.eu-west-1      = "ami-02ace471"
        RHELAMI.sa-east-1      = "ami-7de77b11"
        RHELAMI.us-east-1      = "ami-b63769a1"
        RHELAMI.us-east-2      = "ami-0932686c"
        RHELAMI.us-gov-west-1  = "ami-91cfafb2"
        RHELAMI.us-west-1      = "ami-2cade64c"
        RHELAMI.us-west-2      = "ami-6f68cf0f"
        WIN2012R2AMI.us-east-1       = "ami-7da4ab6b"
        WIN2012R2AMI.us-east-2       = "ami-ffae8f9a"
        WIN2012R2AMI.us-west-1       = "ami-a11836c1"
        WIN2012R2AMI.us-west-2       = "ami-a1c1ddd8"
        WIN2012R2AMI.eu-west-1       = "ami-cc8e98a8"
        WIN2012R2AMI.eu-central-1    = "ami-da1ebdb5"
        WIN2012R2AMI.ap-northeast-1  = "ami-4312cc2d"
        WIN2012R2AMI.ap-northeast-2  = "ami-68756f0f"
        WIN2012R2AMI.ap-southeast-1  = "ami-7644d315"
        WIN2012R2AMI.ap-southeast-2  = "ami-468f9225"
        WIN2012R2AMI.sa-east-1       = "ami-c8285ca4"
        WIN2012R2AMI.ap-south-1      = "ami-8eafd6e1"
    }
}
variable "ebs_root_block_size" { default = "{{ rootfs_size_bastion }}" }
#----------------------------------------------
# Access credentials
#----------------------------------------------
provider "aws" {
    access_key = "${var.aws_access_key}"
    secret_key = "${var.aws_secret_key}"
    region     = "${var.aws_region}"
}
#----------------------------------------------
# VPC
#----------------------------------------------
# Main VPC that will contain everything.
resource "aws_vpc" "main" {
  cidr_block           = "192.168.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags {
    Name = "{{vpcid_name_tag}}"
  }
}
# The public subnet is where resources connected to the internet will go
resource "aws_subnet" "public" {
    vpc_id                  = "${aws_vpc.main.id}"
    cidr_block              = "192.168.0.0/24"
    map_public_ip_on_launch = true
    tags { Name = "{{project_tag}}-public-subnet" }
}
# Internet accessible route table + gateway for the public subnet
resource "aws_internet_gateway" "public" {
  vpc_id = "${aws_vpc.main.id}"
  tags { Name = "{{vpcid_name_tag}}-igw" }
}
resource "aws_route_table" "public" {
  vpc_id = "${aws_vpc.main.id}"
  route {
      cidr_block = "0.0.0.0/0"
      gateway_id = "${aws_internet_gateway.public.id}"
  }
  tags { Name = "{{vpcid_name_tag}}-public-rt" }
}
resource "aws_route_table_association" "public" {
  subnet_id      = "${aws_subnet.public.id}"
  route_table_id = "${aws_route_table.public.id}"
}
#----------------------------------------------
# Security Group
#----------------------------------------------
resource "aws_security_group" "host_sg" {
  name   = "{{ env_type }}-host_sg"
  vpc_id = "${aws_vpc.main.id}"
  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }
}
{% for instance in instances %}
variable "{{instance['name']}}_os_version" { default = "{{instance['image_id']}}" }
variable "{{instance['name']}}_instance_type" { default = "{{ instance['name'] + '_instance_type' }}" }  # c4.large
variable "{{instance['name']}}_instance_count" { default = "{{ instance['name'] + '_instance_count' }}" }  # c4.large
#----------------------------------------------
# Instance : {{instance['name']}}
#----------------------------------------------
resource "aws_instance" "{{instance['name']}}_host" {
  count             = "{{instance['count']}}"
  ami               = "${lookup(var.aws_amis, "${var.{{instance['name']}}_os_version}.${var.aws_region}")}"
  instance_type     = "${var.{{instance['name']}}_instance_type}"
  subnet_id         = "${aws_subnet.public.id}"
  key_name          = "${var.keypair}"
  security_groups   = [ "${aws_security_group.host_sg.id}" ]
  root_block_device = {
    volume_type     = "gp2"
    volume_size     = "${var.ebs_root_block_size}"
  }
  tags {
    Name              = "{{ env_type }}-{{instance['name']}}-${count.index}"
    Role              = "{{ env_type }}-{{instance['name']}}"
    Workshop          = "{{ env_type }}"
    sshUser           = "ec2-user"
    Index             = "${count.index}"
{% for tag in instance['tags'] %}
    {{tag['key']}}      = "{{tag['value']}}"
{% endfor %}
  }
}
#----------------------------------------------
# DNS
#----------------------------------------------
resource "aws_route53_record" "{{instance['name']}}_dns" {
  # same number of records as instances
  count = "{{instance['count']}}"
  zone_id = "{{ HostedZoneId }}"
  name = "{{instance['name']}}.{{subdomain_base}}."
  type = "A"
  ttl = "900"
  # matches up record N to instance N
  records = ["${element(aws_instance.{{instance['name']}}_host.*.public_ip, count.index)}"]
}
{% endfor %}
ansible/configs/ansible-tower-implementation/files/cloud_providers/terraform_ec2_cloud_template.tfvars.j2
New file
@@ -0,0 +1,2 @@
aws_access_key = "{{ aws_access_key_id }}"
aws_secret_key = "{{ aws_secret_access_key }}"
ansible/configs/ansible-tower-implementation/files/hosts_template.j2
New file
@@ -0,0 +1,45 @@
{% if cloud_provider == 'ec2' %}
[towers]
{% for host in groups['towers'] %}
{{host}} ansible_host={{host}}.{{guid}}.internal
{% endfor %}
[servers]
{% for host in groups['servers']  %}
{{host}} ansible_host={{host}}.{{guid}}.internal
{% endfor %}
[towerimp:children]
towers
servers
[towerimp:vars]
timeout=60
ansible_become=yes
ansible_user={{remote_user}}
ansible_ssh_private_key_file="~/.ssh/{{guid}}key.pem"
ansible_ssh_common_args="-o StrictHostKeyChecking=no"
{% endif %}
{% if cloud_provider == 'osp' %}
[towers]
{% for host in groups['towers'] %}
{{host}} ansible_host={{hostvars[host].private_ip_address}}
{% endfor %}
[servers]
{% for host in groups['servers']  %}
{{host}} ansible_host={{hostvars[host].private_ip_address}}
{% endfor %}
[towerimp:children]
towers
servers
[towerimp:vars]
timeout=60
ansible_become=yes
ansible_user={{remote_user}}
ansible_ssh_private_key_file="~/.ssh/{{guid}}key.pem"
ansible_ssh_common_args="-o StrictHostKeyChecking=no"
{% endif %}
ansible/configs/ansible-tower-implementation/files/repos_template.j2
New file
@@ -0,0 +1,32 @@
[rhel-7-server-rpms]
name=Red Hat Enterprise Linux 7
baseurl={{own_repo_path}}/rhel-7-server-rpms
enabled=1
gpgcheck=0
[rhel-7-server-rh-common-rpms]
name=Red Hat Enterprise Linux 7 Common
baseurl={{own_repo_path}}/rhel-7-server-rh-common-rpms
enabled=1
gpgcheck=0
[rhel-7-server-extras-rpms]
name=Red Hat Enterprise Linux 7 Extras
baseurl={{own_repo_path}}/rhel-7-server-extras-rpms
enabled=1
gpgcheck=0
[rhel-7-server-optional-rpms]
name=Red Hat Enterprise Linux 7 Optional
baseurl={{own_repo_path}}/rhel-7-server-optional-rpms
enabled=1
gpgcheck=0
[epel]
name=Extra Packages for Enterprise Linux 7 - $basearch
baseurl=http://download.fedoraproject.org/pub/epel/7/$basearch
mirrorlist=http://mirrors.fedoraproject.org/metalink?repo=epel-7&arch=$basearch
failovermethod=priority
enabled=1
gpgcheck=0
#gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
ansible/configs/ansible-tower-implementation/post_infra.yml
New file
@@ -0,0 +1,25 @@
- name: Step 002 Post Infrastructure
  hosts: localhost
  connection: local
  become: false
  tags:
    - step002
    - post_infrastructure
  tasks:
    - name: Job Template to launch a Job Template with update on launch inventory set
      uri:
        url: "https://{{ ansible_tower_ip }}/api/v1/job_templates/{{ job_template_id }}/launch/"
        method: POST
        user: "{{tower_admin}}"
        password: "{{tower_admin_password}}"
        body:
          extra_vars:
            guid: "{{guid}}"
            ipa_host_password: "{{ipa_host_password}}"
        body_format: json
        validate_certs: False
        HEADER_Content-Type: "application/json"
        status_code: 200, 201
      when: tower_run == 'true'
ansible/configs/ansible-tower-implementation/post_software.yml
New file
@@ -0,0 +1,36 @@
- name: Step 00xxxxx post software
  hosts: support
  gather_facts: False
  become: yes
  tasks:
    - debug:
        msg: "Post-Software tasks Started"
- name: Step lab post software deployment
  hosts: bastions
  gather_facts: False
  become: yes
  tags:
    - opentlc_bastion_tasks
  tasks:
    - import_role:
        name: "bastion-opentlc-ipa"
      when: install_ipa_client|bool
    # sssd bug, fixed by restart
    - name: restart sssd
      service:
        name: sssd
        state: restarted
      when: install_ipa_client
- name: PostSoftware flight-check
  hosts: localhost
  connection: local
  gather_facts: false
  become: false
  tags:
    - post_flight_check
  tasks:
    - debug:
        msg: "Post-Software checks completed successfully"
ansible/configs/ansible-tower-implementation/pre_infra.yml
New file
@@ -0,0 +1,30 @@
- name: Step 000 Pre Infrastructure
  hosts: localhost
  connection: local
  gather_facts: false
  become: false
  tags:
    - step001
    - pre_infrastructure
    - generate_env_keys
  tasks:
    - debug:
        msg: "Step 000 Pre Infrastructure"
    - name: Generate SSH keys
      shell: ssh-keygen -b 2048 -t rsa -f "{{output_dir}}/{{env_authorized_key}}" -q -N ""
      args:
        creates: "{{output_dir}}/{{env_authorized_key}}"
      when: set_env_authorized_key | bool
    - name: fix permission
      file:
        path: "{{output_dir}}/{{env_authorized_key}}"
        mode: 0400
      when: set_env_authorized_key | bool
    - name: Generate SSH pub key
      shell: ssh-keygen -y -f "{{output_dir}}/{{env_authorized_key}}" > "{{output_dir}}/{{env_authorized_key}}.pub"
      args:
        creates: "{{output_dir}}/{{env_authorized_key}}.pub"
      when: set_env_authorized_key | bool
ansible/configs/ansible-tower-implementation/pre_software.yml
New file
@@ -0,0 +1,47 @@
# Cloudformation template or equivalent should tag all hosts with Project:{{ env_type }}-{{ guid }}
- name: Configure all hosts with Repositories, Common Files and Set environment key
  hosts:
    - all:!windows
  become: true
  gather_facts: False
  roles:
    - { role: "set-repositories", when: 'repo_method is defined' }
    - { role: "common", when: 'install_common' }
    - { role: "set_env_authorized_key", when: 'set_env_authorized_key' }
  tags:
    - step004
    - common_tasks
- name: Configuring Bastion Hosts
  hosts: bastions
  become: true
  roles:
    -  { role: "bastion", when: 'install_bastion' }
    - role: bastion-student-user
      when: install_student_user | bool
  tags:
    - step004
    - bastion_tasks
- name: Inject and configure FTL on bastion as grader host
  hosts: bastions
  become: true
  tasks:
    - name: Setup FTL
      include_role:
        name: ftl-injector
  tags:
    - step004
    - ftl-injector
- name: PreSoftware flight-check
  hosts: localhost
  connection: local
  gather_facts: false
  become: false
  tags:
    - flight_check
  tasks:
    - debug:
        msg: "Pre-Software checks completed successfully"
ansible/configs/ansible-tower-implementation/requirements.yml
New file
@@ -0,0 +1,6 @@
---
# External role to setup grader host virtualenv and FTL grading infra
- src: https://github.com/redhat-gpte-devopsautomation/ftl-injector
  name: ftl-injector
  version: v0.16.0
ansible/configs/ansible-tower-implementation/sample_vars.yml
New file
@@ -0,0 +1,39 @@
---
# sample vars configuration file
#
# This file is passed to ansible-playbook to set key vars which need to be set
# and typically customized for a sucessful deployment.
#
# Usage: ansible-playbook main.yml -e @configs/ansible-tower-implementation/sample_vars.yml
#
# Ideally make and keep a copy OUTSIDE your repo, especially if using Cloud Credentials
# Credentials can also be set seperately i.e. ~/secrets.yml and passed in with
# a 2nd `-e` argument i.e. -e ~/secrets.yml
env_type: ansible-tower-implementation                # Name of config to deploy
output_dir: /tmp/workdir                # Writable working scratch directory
email: name@example.com                 # User info for notifications
guid: guid01                            # Your Global UNIQUE Identifier
subdomain_base_suffix: .example.opentlc.com   # Your domain used with guid in FQDN
# Path to your yum repos
own_repo_path: http://admin.example.com/repos/version
# Cloud specfic settings - example given here for AWS
cloud_provider: ec2                     # Which AgnosticD Cloud Provider to use
aws_region: us-east-1                   # AWS Region to deploy in
HostedZoneId: Z3IHLWJZOU9SRT            # You will need to change this
key_name: ocpkey                        # Keyname must exist in AWS
# AWS Credentials. These are required (don't sync them to your fork)
#
# Alternatively consider keeping these in an external file and including:
# $ ansible-playbook main.yml -e @configs/ansible-tower-implementation/sample.yml -e @~/secret.yml
#
# aws_access_key_id:
# aws_secret_access_key:
# ...
ansible/configs/ansible-tower-implementation/sample_vars_osp.yml
New file
@@ -0,0 +1,58 @@
---
# sample vars configuration file
#
# This file is passed to ansible-playbook to set key vars which need to be set
# and typically customized for a sucessful deployment.
#
# Usage: ansible-playbook main.yml -e @configs/ansible-tower-implementation/sample_vars.yml
#
# Ideally make and keep a copy OUTSIDE your repo, especially if using Cloud Credentials
# Credentials can also be set seperately i.e. ~/secrets.yml and passed in with
# a 2nd `-e` argument i.e. -e ~/secrets.yml
env_type: ansible-tower-implementation                # Name of config to deploy
output_dir: /tmp/output_dir                # Writable working scratch directory
email: name@example.com                 # User info for notifications
guid: guid01                            # Your Global UNIQUE Identifier
repo_method: file
own_repo_path: http://admin.example.com/repos/version
ansible_user: cloud-user
remote_user: cloud-user
# Cloud specfic settings - example given here for OSP
cloud_provider: osp                     # Which AgnosticD Cloud Provider to use
# The domain that you want to add DNS entries to
osp_cluster_dns_zone: red.osp.opentlc.com
# The dynamic DNS server you will add entries to.
# NOTE: This is only serverlicable when {{ use_dynamic_dns}} is true
osp_cluster_dns_server: ddns01.opentlc.com
use_dynamic_dns: true
# Instance type
bastion_instance_type: 2c2g30d
server_instance_type: 2c2g30d
tower_instance_type: 2c4g30d
#___image: rhel-guest-7.7u2    # blue
___image: rhel-server-7.7-update-2  # red
bastion_instance_image: "{{ ___image }}"
server_instance_image: "{{ ___image }}"
tower_instance_image: "{{ ___image }}"
student_name: student
admin_user: opentlc-mgr
#admin_user: gucore
update_all_packages: false
osp_project_create: true
ansible/configs/ansible-tower-implementation/software.yml
New file
@@ -0,0 +1,19 @@
---
- name: Step 00xxxxx software
  hosts: localhost
  gather_facts: False
  become: false
  tasks:
    - debug:
        msg: "Software tasks Started"
- name: Software flight-check
  hosts: localhost
  connection: local
  gather_facts: false
  become: false
  tags:
    - post_flight_check
  tasks:
    - debug:
        msg: "Software checks completed successfully"
ansible/configs/ansible-tower-implementation/start.yml
New file
@@ -0,0 +1,21 @@
---
- import_playbook: ../../include_vars.yml
- name: Stop instances
  hosts: localhost
  gather_facts: false
  become: false
  environment:
    AWS_ACCESS_KEY_ID: "{{aws_access_key_id}}"
    AWS_SECRET_ACCESS_KEY: "{{aws_secret_access_key}}"
  tasks:
    - debug:
        msg: "Step 002 Post Infrastructure"
    - name: Start instances
      ec2:
        instance_tags:
          "aws:cloudformation:stack-name": "{{ project_tag }}"
        state: running
        region: "{{ aws_region }}"
ansible/configs/ansible-tower-implementation/stop.yml
New file
@@ -0,0 +1,21 @@
---
- import_playbook: ../../include_vars.yml
- name: Stop instances
  hosts: localhost
  gather_facts: false
  become: false
  environment:
    AWS_ACCESS_KEY_ID: "{{aws_access_key_id}}"
    AWS_SECRET_ACCESS_KEY: "{{aws_secret_access_key}}"
  tasks:
    - debug:
        msg: "Step 002 Post Infrastructure"
    - name: Stop instances
      ec2:
        instance_tags:
          "aws:cloudformation:stack-name": "{{ project_tag }}"
        state: stopped
        region: "{{ aws_region }}"
ansible/configs/ansible-tower-implementation/topology.png