{
  "AWSTemplateFormatVersion" : "2010-09-09",
  
  "Description" : "AWS CloudFormation Sample Template VPC_ElastiCache_Cluster: Sample template showing how to create an Amazon ElastiCache Cache Cluster with Auto Discovery and access it from a very simple PHP application within an existing VPC. **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.",
  
  "Parameters" : {

    "VpcId" : {
      "Type" : "String",
      "Description" : "VpcId of your existing Virtual Private Cloud (VPC)"
    },

    "CacheSubnets" : {
      "Type" : "CommaDelimitedList",
      "Description" : "The list of SubnetIds for the CacheCluster, you need at least 2 subnets in different availability zones"
    },

    "InstanceSubnet" : {
      "Type" : "String",
      "Description" : "The SubnetId for the web server instance. It must have internet gateway access and access to the subnets containing the CacheCluster"
    },

    "KeyName": {
      "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the web server",
      "Type": "String",
      "MinLength": "1",
      "MaxLength": "255",
      "AllowedPattern" : "[\\x20-\\x7E]*",
      "ConstraintDescription" : "can contain only ASCII characters."
    },
          
    "InstanceType" : {
      "Description" : "WebServer EC2 instance type",
      "Type" : "String",
      "Default" : "m1.small",
      "AllowedValues" : [ "t1.micro","m1.small","m1.medium","m1.large","m1.xlarge", "m3.xlarge", "m3.2xlarge", "m2.xlarge","m2.2xlarge","m2.4xlarge","c1.medium","c1.xlarge","cc1.4xlarge","cc2.8xlarge","cg1.4xlarge", "hi1.4xlarge", "hs1.8xlarge"],
      "ConstraintDescription" : "must be a valid EC2 instance type."
    },
 
    "CacheNodeType" : {
      "Default" : "cache.t1.micro",
      "Description" : "The compute and memory capacity of the nodes in the Cache Cluster",
      "Type" : "String",
      "AllowedValues" : [ "cache.t1.micro", "cache.m1.small", "cache.m1.medium", "cache.m1.large", "cache.m1.xlarge", "cache.m2.xlarge", "cache.m2.2xlarge", "cache.m2.4xlarge", "cache.m3.xlarge", "cache.m3.2xlarge", "cache.c1.xlarge" ],
      "ConstraintDescription" : "must select a valid Cache Node type."
    },
        
    "NumberOfCacheNodes" : {
      "Default": "1",
      "Description" : "The number of Cache Nodes the Cache Cluster should have",
      "Type": "Number",
      "MinValue": "1",
      "MaxValue": "10",
      "ConstraintDescription" : "must be between 5 and 10."
    },

    "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."
    }   
  },
  
  "Mappings" : {
    "AWSInstanceType2Arch" : {
      "t1.micro"    : { "Arch" : "PV64" },
      "m1.small"    : { "Arch" : "PV64" },
      "m1.medium"   : { "Arch" : "PV64" },
      "m1.large"    : { "Arch" : "PV64" },
      "m1.xlarge"   : { "Arch" : "PV64" },
      "m3.xlarge"   : { "Arch" : "PV64" },
      "m3.2xlarge"  : { "Arch" : "PV64" },
      "m2.xlarge"   : { "Arch" : "PV64" },
      "m2.2xlarge"  : { "Arch" : "PV64" },
      "m2.4xlarge"  : { "Arch" : "PV64" },
      "c1.medium"   : { "Arch" : "PV64" },
      "c1.xlarge"   : { "Arch" : "PV64" },
      "cc1.4xlarge" : { "Arch" : "CLU64" },
      "cc2.8xlarge" : { "Arch" : "CLU64" },
      "cg1.4xlarge" : { "Arch" : "GPU64" },
      "hi1.4xlarge" : { "Arch" : "PV64" },
      "hs1.8xlarge" : { "Arch" : "PV64" }
    },

    "AWSRegionArch2AMI" : {
      "us-east-1"      : { "PV64" : "ami-05355a6c", "CLU64" : "ami-a73758ce", "GPU64" : "ami-cf3758a6" },
      "us-west-2"      : { "PV64" : "ami-0358ce33", "CLU64" : "ami-d75bcde7", "GPU64" : "NOT_YET_SUPPORTED" },
      "us-west-1"      : { "PV64" : "ami-3ffed17a", "CLU64" : "ami-47fed102", "GPU64" : "NOT_YET_SUPPORTED" },
      "eu-west-1"      : { "PV64" : "ami-c7c0d6b3", "CLU64" : "ami-d1c0d6a5", "GPU64" : "ami-45c0d631" },
      "ap-southeast-1" : { "PV64" : "ami-fade91a8", "CLU64" : "ami-18de914a", "GPU64" : "NOT_YET_SUPPORTED" },
      "ap-southeast-2" : { "PV64" : "ami-d16bfbeb", "CLU64" : "ami-876bfbbd", "GPU64" : "NOT_YET_SUPPORTED" },
      "ap-northeast-1" : { "PV64" : "ami-39b23d38", "CLU64" : "ami-2db33c2c", "GPU64" : "NOT_YET_SUPPORTED" },
      "sa-east-1"      : { "PV64" : "ami-5253894f", "CLU64" : "ami-38538925", "GPU64" : "NOT_YET_SUPPORTED" }
    }
  },
    
  "Resources" : {   

    "CacheSubnetGroup" : {
      "Type" : "AWS::ElastiCache::SubnetGroup",
      "Properties" : {
        "Description" : "Subnets available for the ElastiCache Cluster",
        "SubnetIds" : { "Ref" : "CacheSubnets" }
      }
    },

    "CacheParameters" : {
      "Type" : "AWS::ElastiCache::ParameterGroup",
      "Properties" : {
        "CacheParameterGroupFamily" : "memcached1.4",
        "Description" : "Parameter group",
        "Properties" : {
          "cas_disabled" : "1"
        }
      }
    },

    "CacheSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Allow access to the cache from the Web Server",
        "VpcId" : { "Ref" : "VpcId" },
        "SecurityGroupIngress" : [
          {"IpProtocol" : "tcp", "FromPort" : "0", "ToPort" : "65535", "SourceSecurityGroupId" : { "Ref" : "WebServerSecurityGroup"} }
        ]
      }      
    },  

    "CacheCluster" : {
      "Type": "AWS::ElastiCache::CacheCluster",
      "Properties": {
        "CacheSubnetGroupName" : { "Ref" : "CacheSubnetGroup" },
        "CacheNodeType"        : { "Ref" : "CacheNodeType" },
        "VpcSecurityGroupIds"  : [ { "Ref" : "CacheSecurityGroup" } ],
        "Engine"               : "memcached",
        "NumCacheNodes"        : { "Ref" : "NumberOfCacheNodes" }
      }
    },

    "WebServerSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable HTTP and SSH access",
        "VpcId" : { "Ref" : "VpcId" },
        "SecurityGroupIngress" : [
          {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"} },
          {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0"}
        ]
      }      
    },  

    "EIP" :  {
      "Type" : "AWS::EC2::EIP",
      "Properties" : {
        "Domain" : "vpc"
      }
    },

    "EIPAssoc" : {
      "Type" : "AWS::EC2::EIPAssociation",
      "Properties" : {
        "InstanceId" : { "Ref" : "WebServerHost" },
        "AllocationId" : { "Fn::GetAtt" : ["EIP", "AllocationId"] }
      }
    },
 
    "WebServerHost": {  
      "Type" : "AWS::EC2::Instance",
      "Metadata" : {
        "AWS::CloudFormation::Init" : {
          "config" : {
            "packages" : {
              "yum" : {
                "httpd"             : [],
                "gcc-c++"           : [],
                "php"               : [],
                "php-pear"          : []
              }
            },

            "files" : {
              "/var/www/html/index.php" : {
                "content" : { "Fn::Join" : ["", [
                  "<?php\n",
                  "echo '<h1>AWS CloudFormation sample application for Amazon ElastiCache</h1>';\n",
                  "\n",
                  "$server_endpoint = '", { "Fn::GetAtt" : [ "CacheCluster", "ConfigurationEndpoint.Address" ]}, "';\n",
                  "$server_port = ", { "Fn::GetAtt" : [ "CacheCluster", "ConfigurationEndpoint.Port" ]}, ";\n",
                  "\n",
                  "/**\n",
                  " * The following will initialize a Memcached client to utilize the Auto Discovery feature.\n",
                  " * \n",
                  " * By configuring the client with the Dynamic client mode with single endpoint, the\n",
                  " * client will periodically use the configuration endpoint to retrieve the current cache\n",
                  " * cluster configuration. This allows scaling the cache cluster up or down in number of nodes\n",
                  " * without requiring any changes to the PHP application. \n",
                  " */\n",
                  "\n",
                  "$dynamic_client = new Memcached();\n",
                  "$dynamic_client->setOption(Memcached::OPT_CLIENT_MODE, Memcached::DYNAMIC_CLIENT_MODE);\n",
                  "$dynamic_client->addServer($server_endpoint, $server_port);\n",
                  "\n",
                  "$tmp_object = new stdClass;\n",
                  "$tmp_object->str_attr = 'test';\n",
                  "$tmp_object->int_attr = 123;\n",
                  "\n",
                  "$dynamic_client->set('key', $tmp_object, 10) or die ('Failed to save data to the cache');\n",
                  "echo '<p>Store data in the cache (data will expire in 10 seconds)</p>';\n",
                  "\n",
                  "$get_result = $dynamic_client->get('key');\n",
                  "echo '<p>Data from the cache:<br/>';\n",
                  "\n",
                  "var_dump($get_result);\n",
                  "\n",
                  "echo '</p>';\n",
                  "?>\n"
                ]]},
                "mode"    : "000644",
                "owner"   : "apache",
                "group"   : "apache"
              }
            },

            "commands" : {
              "00_install_memcached_client" : {
                "command" : "pecl install https://s3.amazonaws.com/elasticache-downloads/ClusterClient/PHP/latest-64bit"
              },
              "01_enable_auto_discovery" : {
                "command" : "echo 'extension=amazon-elasticache-cluster-client.so' > /etc/php.d/memcached.ini"
              }
            },

            "services" : {
              "sysvinit" : {
                "httpd"    : { "enabled" : "true", "ensureRunning" : "true" },
                "sendmail" : { "enabled" : "false", "ensureRunning" : "false" }
              }
            }
          }
        }
      },
      "Properties": {
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" }, 
                       { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch"  ]}]},
        "InstanceType"    : { "Ref" : "InstanceType" },
        "SecurityGroupIds": [ {"Ref" : "WebServerSecurityGroup"} ],
        "SubnetId"        : { "Ref" : "InstanceSubnet" },
        "KeyName"         : { "Ref" : "KeyName" },
        "UserData"        : { "Fn::Base64" : { "Fn::Join" : ["", [
          "#!/bin/bash -v\n",
          "yum update -y aws-cfn-bootstrap\n",

          "# Setup the PHP sample application\n",
          "/opt/aws/bin/cfn-init ",
          "         --stack ", { "Ref" : "AWS::StackName" }, 
          "         --resource WebServerHost ",
          "         --region ", { "Ref" : "AWS::Region" }, "\n",

          "# Signal the status of cfn-init\n",
          "/opt/aws/bin/cfn-signal -e $? '", { "Ref" : "WebServerWaitHandle" }, "'\n"
        ]]}}        
      }
    },

    "WebServerWaitHandle" : {
      "Type" : "AWS::CloudFormation::WaitConditionHandle"
    },

    "WebServerWaitCondition" : {
      "Type" : "AWS::CloudFormation::WaitCondition",
      "DependsOn" : "WebServerHost",
      "Properties" : {
        "Handle" : {"Ref" : "WebServerWaitHandle"},
        "Timeout" : "300"
      }
    }
  },
  
  "Outputs" : {
    "WebsiteURL" : {
      "Value" : { "Fn::Join" : ["", ["http://", {"Ref" : "EIP"} ]]},
      "Description" : "Application URL"
    }
  }
}
