docs: def main():
      """
      Create a ElastiCache Redis Node and EC2 Instance
      """
  
      template = Template()
  
      # Description
      template.add_description(
          'AWS CloudFormation Sample Template ElastiCache_Redis:'
          'Sample template showing how to create an Amazon'
          'ElastiCache Redis Cluster. **WARNING** This template'
          'creates an Amazon EC2 Instance and an Amazon ElastiCache'
          'Cluster. You will be billed for the AWS resources used'
          'if you create a stack from this template.')
  
      # Mappings
      template.add_mapping('AWSInstanceType2Arch', {
          't1.micro':     {'Arch': 'PV64'},
          't2.micro':     {'Arch': 'HVM64'},
          't2.small':     {'Arch': 'HVM64'},
          't2.medium':    {'Arch': 'HVM64'},
          'm1.small':     {'Arch': 'PV64'},
          'm1.medium':    {'Arch': 'PV64'},
          'm1.large':     {'Arch': 'PV64'},
          'm1.xlarge':    {'Arch': 'PV64'},
          'm2.xlarge':    {'Arch': 'PV64'},
          'm2.2xlarge':   {'Arch': 'PV64'},
          'm2.4xlarge':   {'Arch': 'PV64'},
          'm3.medium':    {'Arch': 'HVM64'},
          'm3.large':     {'Arch': 'HVM64'},
          'm3.xlarge':    {'Arch': 'HVM64'},
          'm3.2xlarge':   {'Arch': 'HVM64'},
          'c1.medium':    {'Arch': 'PV64'},
          'c1.xlarge':    {'Arch': 'PV64'},
          'c3.large':     {'Arch': 'HVM64'},
          'c3.xlarge':    {'Arch': 'HVM64'},
          'c3.2xlarge':   {'Arch': 'HVM64'},
          'c3.4xlarge':   {'Arch': 'HVM64'},
          'c3.8xlarge':   {'Arch': 'HVM64'},
          'c4.large':     {'Arch': 'HVM64'},
          'c4.xlarge':    {'Arch': 'HVM64'},
          'c4.2xlarge':   {'Arch': 'HVM64'},
          'c4.4xlarge':   {'Arch': 'HVM64'},
          'c4.8xlarge':   {'Arch': 'HVM64'},
          'g2.2xlarge':   {'Arch': 'HVMG2'},
          'r3.large':     {'Arch': 'HVM64'},
          'r3.xlarge':    {'Arch': 'HVM64'},
          'r3.2xlarge':   {'Arch': 'HVM64'},
          'r3.4xlarge':   {'Arch': 'HVM64'},
          'r3.8xlarge':   {'Arch': 'HVM64'},
          'i2.xlarge':    {'Arch': 'HVM64'},
          'i2.2xlarge':   {'Arch': 'HVM64'},
          'i2.4xlarge':   {'Arch': 'HVM64'},
          'i2.8xlarge':   {'Arch': 'HVM64'},
          'd2.xlarge':    {'Arch': 'HVM64'},
          'd2.2xlarge':   {'Arch': 'HVM64'},
          'd2.4xlarge':   {'Arch': 'HVM64'},
          'd2.8xlarge':   {'Arch': 'HVM64'},
          'hi1.4xlarge':  {'Arch': 'HVM64'},
          'hs1.8xlarge':  {'Arch': 'HVM64'},
          'cr1.8xlarge':  {'Arch': 'HVM64'},
          'cc2.8xlarge':  {'Arch': 'HVM64'}
          })
  
      template.add_mapping('AWSRegionArch2AMI', {
          'us-east-1': {'PV64': 'ami-0f4cfd64',
                        'HVM64': 'ami-0d4cfd66',
                        'HVMG2': 'ami-5b05ba30'},
          'us-west-2': {'PV64': 'ami-d3c5d1e3',
                        'HVM64': 'ami-d5c5d1e5',
                        'HVMG2': 'ami-a9d6c099'},
          'us-west-1': {'PV64': 'ami-85ea13c1',
                        'HVM64': 'ami-87ea13c3',
                        'HVMG2': 'ami-37827a73'},
          'eu-west-1': {'PV64': 'ami-d6d18ea1',
                        'HVM64': 'ami-e4d18e93',
                        'HVMG2': 'ami-72a9f105'},
          'eu-central-1': {'PV64': 'ami-a4b0b7b9',
                           'HVM64': 'ami-a6b0b7bb',
                           'HVMG2': 'ami-a6c9cfbb'},
          'ap-northeast-1': {'PV64': 'ami-1a1b9f1a',
                             'HVM64': 'ami-1c1b9f1c',
                             'HVMG2': 'ami-f644c4f6'},
          'ap-southeast-1': {'PV64': 'ami-d24b4280',
                             'HVM64': 'ami-d44b4286',
                             'HVMG2': 'ami-12b5bc40'},
          'ap-southeast-2': {'PV64': 'ami-ef7b39d5',
                             'HVM64': 'ami-db7b39e1',
                             'HVMG2': 'ami-b3337e89'},
          'sa-east-1': {'PV64': 'ami-5b098146',
                        'HVM64': 'ami-55098148',
                        'HVMG2': 'NOT_SUPPORTED'},
          'cn-north-1': {'PV64': 'ami-bec45887',
                         'HVM64': 'ami-bcc45885',
                         'HVMG2': 'NOT_SUPPORTED'}
          })
  
      template.add_mapping('Region2Principal', {
          'us-east-1': {'EC2Principal': 'ec2.amazonaws.com',
                        'OpsWorksPrincipal': 'opsworks.amazonaws.com'},
          'us-west-2': {'EC2Principal': 'ec2.amazonaws.com',
                        'OpsWorksPrincipal': 'opsworks.amazonaws.com'},
          'us-west-1': {'EC2Principal': 'ec2.amazonaws.com',
                        'OpsWorksPrincipal': 'opsworks.amazonaws.com'},
          'eu-west-1': {'EC2Principal': 'ec2.amazonaws.com',
                        'OpsWorksPrincipal': 'opsworks.amazonaws.com'},
          'ap-southeast-1': {'EC2Principal': 'ec2.amazonaws.com',
                             'OpsWorksPrincipal': 'opsworks.amazonaws.com'},
          'ap-northeast-1': {'EC2Principal': 'ec2.amazonaws.com',
                             'OpsWorksPrincipal': 'opsworks.amazonaws.com'},
          'ap-southeast-2': {'EC2Principal': 'ec2.amazonaws.com',
                             'OpsWorksPrincipal': 'opsworks.amazonaws.com'},
          'sa-east-1': {'EC2Principal': 'ec2.amazonaws.com',
                        'OpsWorksPrincipal': 'opsworks.amazonaws.com'},
          'cn-north-1': {'EC2Principal': 'ec2.amazonaws.com.cn',
                         'OpsWorksPrincipal': 'opsworks.amazonaws.com.cn'},
          'eu-central-1': {'EC2Principal': 'ec2.amazonaws.com',
                           'OpsWorksPrincipal': 'opsworks.amazonaws.com'}
          })
  
      # Parameters
      cachenodetype = template.add_parameter(Parameter(
          'ClusterNodeType',
          Description='The compute and memory capacity of the nodes in the Redis'
                      ' Cluster',
          Type='String',
          Default='cache.m1.small',
          AllowedValues=['cache.m1.small',
                         'cache.m1.large',
                         'cache.m1.xlarge',
                         'cache.m2.xlarge',
                         'cache.m2.2xlarge',
                         'cache.m2.4xlarge',
                         'cache.c1.xlarge'],
          ConstraintDescription='must select a valid Cache Node type.',
          ))
  
      instancetype = template.add_parameter(Parameter(
          'InstanceType',
          Description='WebServer EC2 instance type',
          Type='String',
          Default='t2.micro',
          AllowedValues=['t1.micro',
                         't2.micro',
                         't2.small',
                         't2.medium',
                         'm1.small',
                         'm1.medium',
                         'm1.large',
                         'm1.xlarge',
                         'm2.xlarge',
                         'm2.2xlarge',
                         'm2.4xlarge',
                         'm3.medium',
                         'm3.large',
                         'm3.xlarge',
                         'm3.2xlarge',
                         'c1.medium',
                         'c1.xlarge',
                         'c3.large',
                         'c3.xlarge',
                         'c3.2xlarge',
                         'c3.4xlarge',
                         'c3.8xlarge',
                         'c4.large',
                         'c4.xlarge',
                         'c4.2xlarge',
                         'c4.4xlarge',
                         'c4.8xlarge',
                         'g2.2xlarge',
                         'r3.large',
                         'r3.xlarge',
                         'r3.2xlarge',
                         'r3.4xlarge',
                         'r3.8xlarge',
                         'i2.xlarge',
                         'i2.2xlarge',
                         'i2.4xlarge',
                         'i2.8xlarge',
                         'd2.xlarge',
                         'd2.2xlarge',
                         'd2.4xlarge',
                         'd2.8xlarge',
                         'hi1.4xlarge',
                         'hs1.8xlarge',
                         'cr1.8xlarge',
                         'cc2.8xlarge',
                         'cg1.4xlarge'],
          ConstraintDescription='must be a valid EC2 instance type.',
          ))
  
      keyname = template.add_parameter(Parameter(
          'KeyName',
          Description='Name of an existing EC2 KeyPair to enable SSH access'
                      ' to the instance',
          Type='AWS::EC2::KeyPair::KeyName',
          ConstraintDescription='must be the name of an existing EC2 KeyPair.',
          ))
  
      sshlocation = template.add_parameter(Parameter(
          'SSHLocation',
          Description='The IP address range that can be used to SSH to'
                      ' the EC2 instances',
          Type='String',
          MinLength='9',
          MaxLength='18',
          Default='0.0.0.0/0',
          AllowedPattern='(\\d{1,3})\\.(\\d{1,3})\\.'
                         '(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})',
          ConstraintDescription='must be a valid IP CIDR range of the'
                                ' form x.x.x.x/x.'
          ))
  
      # Resources
      webserverrole = template.add_resource(iam.Role(
          'WebServerRole',
          AssumeRolePolicyDocument=Policy(
              Statement=[
                  Statement(
                      Effect=Allow,
                      Action=[AssumeRole],
                      Principal=Principal('Service',
                                          [FindInMap('Region2Principal',
                                                     Ref('AWS::Region'),
                                                     'EC2Principal')]),
                      )
                  ]
              ),
          Path='/',
      ))
  
      template.add_resource(iam.PolicyType(
          'WebServerRolePolicy',
          PolicyName='WebServerRole',
          PolicyDocument=awacs.aws.Policy(
              Statement=[awacs.aws.Statement(
                  Action=[awacs.aws.Action("elasticache",
                          "DescribeCacheClusters")],
                  Resource=["*"],
                  Effect=awacs.aws.Allow
              )]
          ),
          Roles=[Ref(webserverrole)],
      ))
  
      webserverinstanceprofile = template.add_resource(iam.InstanceProfile(
          'WebServerInstanceProfile',
          Path='/',
          Roles=[Ref(webserverrole)],
      ))
  
      webserversg = template.add_resource(ec2.SecurityGroup(
          'WebServerSecurityGroup',
          GroupDescription='Enable HTTP and SSH access',
          SecurityGroupIngress=[
              ec2.SecurityGroupRule(
                  IpProtocol='tcp',
                  FromPort='22',
                  ToPort='22',
                  CidrIp=Ref(sshlocation),
                  ),
              ec2.SecurityGroupRule(
                  IpProtocol='tcp',
                  FromPort='80',
                  ToPort='80',
                  CidrIp='0.0.0.0/0',
                  )
              ]
          ))
  
      webserverinstance = template.add_resource(ec2.Instance(
          'WebServerInstance',
          Metadata=cloudformation.Metadata(
              cloudformation.Init({
                  'config': cloudformation.InitConfig(
                      packages={
                          'yum': {
                              'httpd':     [],
                              'php':       [],
                              'php-devel': [],
                              'gcc':       [],
                              'make':      []
                              }
                          },
  
                      files=cloudformation.InitFiles({
                          '/var/www/html/index.php': cloudformation.InitFile(
                              content=Join('', [
                                  '<?php\n',
                                  'echo \"<h1>AWS CloudFormation sample'
                                  ' application for Amazon ElastiCache'
                                  ' Redis Cluster</h1>\";\n',
                                  '\n',
                                  '$cluster_config = json_decode('
                                  'file_get_contents(\'/tmp/cacheclusterconfig\''
                                  '), true);\n',
                                  '$endpoint = $cluster_config[\'CacheClusters'
                                  '\'][0][\'CacheNodes\'][0][\'Endpoint\'][\'Add'
                                  'ress\'];\n',
                                  '$port = $cluster_config[\'CacheClusters\'][0]'
                                  '[\'CacheNodes\'][0][\'Endpoint\'][\'Port\'];'
                                  '\n',
                                  '\n',
                                  'echo \"<p>Connecting to Redis Cache Cluster '
                                  'node \'{$endpoint}\' on port {$port}</p>\";'
                                  '\n',
                                  '\n',
                                  '$redis=new Redis();\n',
                                  '$redis->connect($endpoint, $port);\n',
                                  '$redis->set(\'testkey\', \'Hello World!\');'
                                  '\n',
                                  '$return = $redis->get(\'testkey\');\n',
                                  '\n',
                                  'echo \"<p>Retrieved value: $return</p>\";'
                                  '\n',
                                  '?>\n'
                                  ]),
                              mode='000644',
                              owner='apache',
                              group='apache'
                              ),
                          '/etc/cron.d/get_cluster_config':
                              cloudformation.InitFile(
                                  content='*/5 * * * * root'
                                          ' /usr/local/bin/get_cluster_config',
                                  mode='000644',
                                  owner='root',
                                  group='root'
                                  ),
                          '/usr/local/bin/get_cluster_config':
                              cloudformation.InitFile(
                                  content=Join('', [
                                      '#! /bin/bash\n',
                                      'aws elasticache describe-cache-clusters ',
                                      '         --cache-cluster-id ',
                                      Ref('RedisCluster'),
                                      '         --show-cache-node-info'
                                      ' --region ', Ref('AWS::Region'),
                                      ' > /tmp/cacheclusterconfig\n'
                                      ]),
                                  mode='000755',
                                  owner='root',
                                  group='root'
                                  ),
                          '/usr/local/bin/install_phpredis':
                              cloudformation.InitFile(
                                  content=Join('', [
                                      '#! /bin/bash\n',
                                      'cd /tmp\n',
                                      'wget https://github.com/nicolasff/'
                                      'phpredis/zipball/master -O phpredis.zip'
                                      '\n',
                                      'unzip phpredis.zip\n',
                                      'cd nicolasff-phpredis-*\n',
                                      'phpize\n',
                                      './configure\n',
                                      'make && make install\n',
                                      'touch /etc/php.d/redis.ini\n',
                                      'echo extension=redis.so > /etc/php.d/'
                                      'redis.ini\n'
                                      ]),
                                  mode='000755',
                                  owner='root',
                                  group='root'
                                  ),
                          '/etc/cfn/cfn-hup.conf': cloudformation.InitFile(
                              content=Join('', [
                                  '[main]\n',
                                  'stack=', Ref('AWS::StackId'), '\n',
                                  'region=', Ref('AWS::Region'), '\n'
                                  ]),
                              mode='000400',
                              owner='root',
                              group='root'
                              ),
                          '/etc/cfn/hooks.d/cfn-auto-reloader.conf':
                              cloudformation.InitFile(
                                  content=Join('', [
                                      '[cfn-auto-reloader-hook]\n',
                                      'triggers=post.update\n',
                                      'path=Resources.WebServerInstance.Metadata'
                                      '.AWS::CloudFormation::Init\n',
                                      'action=/opt/aws/bin/cfn-init -v ',
                                      '         --stack ', Ref('AWS::StackName'),
                                      '         --resource WebServerInstance ',
                                      '         --region ', Ref('AWS::Region'),
                                      '\n',
                                      'runas=root\n'
                                      ]),
                                  # Why doesn't the Amazon template have this?
                                  # mode='000400',
                                  # owner='root',
                                  # group='root'
                                  ),
                          }),
  
                      commands={
                          '01-install_phpredis': {
                              'command': '/usr/local/bin/install_phpredis'
                              },
                          '02-get-cluster-config': {
                              'command': '/usr/local/bin/get_cluster_config'
                              }
                          },
  
                      services={
                          "sysvinit": cloudformation.InitServices({
                              "httpd": cloudformation.InitService(
                                  enabled=True,
                                  ensureRunning=True,
                                  ),
                              "cfn-hup": cloudformation.InitService(
                                  enabled=True,
                                  ensureRunning=True,
                                  files=['/etc/cfn/cfn-hup.conf',
                                         '/etc/cfn/hooks.d/'
                                         'cfn-auto-reloader.conf']
                                  ),
                              }),
                          },
                      )
                  })
              ),
          ImageId=FindInMap('AWSRegionArch2AMI', Ref('AWS::Region'),
                            FindInMap('AWSInstanceType2Arch',
                                      Ref(instancetype), 'Arch')),
          InstanceType=Ref(instancetype),
          SecurityGroups=[Ref(webserversg)],
          KeyName=Ref(keyname),
          IamInstanceProfile=Ref(webserverinstanceprofile),
          UserData=Base64(Join('', [
              '#!/bin/bash -xe\n',
              'yum update -y aws-cfn-bootstrap\n',
  
              '# Setup the PHP sample application\n',
              '/opt/aws/bin/cfn-init -v ',
              '         --stack ', Ref('AWS::StackName'),
              '         --resource WebServerInstance ',
              '         --region ', Ref('AWS::Region'), '\n',
  
              '# Signal the status of cfn-init\n',
              '/opt/aws/bin/cfn-signal -e $? ',
              '         --stack ', Ref('AWS::StackName'),
              '         --resource WebServerInstance ',
              '         --region ', Ref('AWS::Region'), '\n'
              ])),
          CreationPolicy=CreationPolicy(
              ResourceSignal=ResourceSignal(Timeout='PT15M')
              ),
          Tags=Tags(Application=Ref('AWS::StackId'),
                    Details='Created using Troposhpere')
          ))
  
      redisclustersg = template.add_resource(elasticache.SecurityGroup(
          'RedisClusterSecurityGroup',
          Description='Lock the cluster down',
          ))
  
      template.add_resource(elasticache.SecurityGroupIngress(
          'RedisClusterSecurityGroupIngress',
          CacheSecurityGroupName=Ref(redisclustersg),
          EC2SecurityGroupName=Ref(webserversg),
          ))
  
      template.add_resource(elasticache.CacheCluster(
          'RedisCluster',
          Engine='redis',
          CacheNodeType=Ref(cachenodetype),
          NumCacheNodes='1',
          CacheSecurityGroupNames=[Ref(redisclustersg)],
          ))
  
      # Outputs
      template.add_output([
          Output(
              'WebsiteURL',
              Description='Application URL',
              Value=Join('', [
                  'http://',
                  GetAtt(webserverinstance, 'PublicDnsName'),
  
                  ])
              )
          ])
  
      # Print CloudFormation Template
      print(template.to_json())