]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Adding puppet and puppet master instances
authorChris Alfonso <calfonso@redhat.com>
Wed, 16 May 2012 20:15:34 +0000 (16:15 -0400)
committerChris Alfonso <calfonso@redhat.com>
Fri, 18 May 2012 12:58:17 +0000 (08:58 -0400)
heat/engine/parser.py
heat/engine/security_group.py
heat/engine/user.py [new file with mode: 0644]
templates/PuppetMaster_Single_Instance.template [new file with mode: 0644]
templates/WordPress_Single_Instance_puppet.template [new file with mode: 0644]

index 1c2e3323d8a8bfa2b1d3b5d79d47be08567a9379..87a8bbdc8ece0d2e27d12afa51f46f553c30e639 100644 (file)
@@ -17,13 +17,13 @@ import eventlet
 import json
 import logging
 import sys
-
 from heat.common import exception
 from heat.engine import resources
 from heat.engine import instance
 from heat.engine import volume
 from heat.engine import eip
 from heat.engine import security_group
+from heat.engine import user 
 from heat.engine import wait_condition
 
 from heat.db import api as db_api
@@ -98,6 +98,12 @@ class Stack(object):
             elif type == 'AWS::CloudFormation::WaitCondition':
                 self.resources[r] = wait_condition.WaitCondition(r,
                                                 self.t['Resources'][r], self)
+            elif type == 'AWS::IAM::User':
+                self.resources[r] = user.User(r,
+                                                self.t['Resources'][r], self)
+            elif type == 'AWS::IAM::AccessKey':
+                self.resources[r] = user.AccessKey(r,
+                                                self.t['Resources'][r], self)
             else:
                 self.resources[r] = resources.GenericResource(r,
                                                 self.t['Resources'][r], self)
index 6d413a6c179f22c1d5528ecf15f9cc8cf67cf6bc..2b3f0f77dd92cec2073e273c1d18a2ba2c3a25d1 100644 (file)
@@ -16,7 +16,6 @@
 import eventlet
 import logging
 import os
-
 from novaclient.exceptions import BadRequest
 from heat.common import exception
 from heat.engine.resources import Resource
@@ -53,7 +52,6 @@ class SecurityGroup(Resource):
                                                      self.description)
 
         self.instance_id_set(sec.id)
-
         if 'SecurityGroupIngress' in self.t['Properties']:
             rules_client = self.nova().security_group_rules
             for i in self.t['Properties']['SecurityGroupIngress']:
diff --git a/heat/engine/user.py b/heat/engine/user.py
new file mode 100644 (file)
index 0000000..41893b3
--- /dev/null
@@ -0,0 +1,67 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import eventlet
+import logging
+import os
+from novaclient.exceptions import BadRequest
+from heat.common import exception
+from heat.engine.resources import Resource
+
+logger = logging.getLogger(__file__)
+
+
+class User(Resource):
+    def __init__(self, name, json_snippet, stack):
+        super(User, self).__init__(name, json_snippet, stack)
+        self.instance_id = ''
+
+    def create(self):
+        self.state_set(self.CREATE_COMPLETE)
+
+    def FnGetAtt(self, key):
+        res = None
+        if key == 'Policies':
+            res = self.t['Properties']['Policies']
+        else:
+            raise exception.InvalidTemplateAttribute(resource=self.name,
+                                                     key=key)
+
+        logger.info('%s.GetAtt(%s) == %s' % (self.name, key, res))
+        return unicode(res)
+
+class AccessKey(Resource):
+    def __init__(self, name, json_snippet, stack):
+        super(AccessKey, self).__init__(name, json_snippet, stack)
+
+    def create(self):
+        self.state_set(self.CREATE_COMPLETE)
+
+    def FnGetRefId(self):
+        return unicode(self.name)
+
+    def FnGetAtt(self, key):
+        res = None
+        if key == 'UserName':
+            res = self.t['Properties']['UserName']
+        if key == 'SecretAccessKey':
+            res = 'TODO-Add-Real-SecreateAccessKey'
+        else:
+            raise exception.InvalidTemplateAttribute(resource=self.name,
+                                                     key=key)
+
+        logger.info('%s.GetAtt(%s) == %s' % (self.name, key, res))
+        return unicode(res)
+
diff --git a/templates/PuppetMaster_Single_Instance.template b/templates/PuppetMaster_Single_Instance.template
new file mode 100644 (file)
index 0000000..63e3352
--- /dev/null
@@ -0,0 +1,240 @@
+{
+  "AWSTemplateFormatVersion" : "2010-09-09",
+  
+  "Description": "Sample template to bring up Puppet Master instance that can be used to bootstrap and manage Puppet Clients. The Puppet Master is populated from an embedded template that defines the set of applications to load. **WARNING** This template creates one or more Amazon EC2 instances. You will be billed for the AWS resources used if you create a stack from this template.",
+  
+  "Parameters" : {
+    "InstanceType" : {
+      "Description" : "WebServer EC2 instance type",
+      "Type" : "String",
+      "Default" : "m1.large",
+      "AllowedValues" : [ "t1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m2.xlarge","m2.2xlarge","m2.4xlarge","c1.medium","c1.xlarge","cc1.4xlarge","cc2.8xlarge","cg1.4xlarge"],
+      "ConstraintDescription" : "must be a valid EC2 instance type."
+    },
+    "KeyName" : {
+      "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the PuppetMaster",
+      "Type" : "String"
+    },    
+    "ContentManifest" : {
+      "Default" : "/wordpress/: { include wordpress }",
+      "Description" : "Manifest of roles to add to nodes.pp",
+      "Type" : "String"
+    },
+    "ContentLocation" : {
+      "Default" : "https://s3.amazonaws.com/cloudformation-examples/wordpress-puppet-config.tar.gz",
+      "Description" : "Location of package (Zip, GZIP or Git repository URL) that includes the PuppetMaster content",
+      "Type" : "String"
+    },
+    "LinuxDistribution": {
+      "Default": "F16",
+      "Description" : "Distribution of choice",
+      "Type": "String",
+      "AllowedValues" : [ "F16", "F17", "U10", "RHEL-6.1", "RHEL-6.2", "RHEL-6.3" ]
+    }
+  },
+  "Mappings" : {
+    "AWSInstanceType2Arch" : {
+      "t1.micro"    : { "Arch" : "32" },
+      "m1.small"    : { "Arch" : "32" },
+      "m1.large"    : { "Arch" : "64" },
+      "m1.xlarge"   : { "Arch" : "64" },
+      "m2.xlarge"   : { "Arch" : "64" },
+      "m2.2xlarge"  : { "Arch" : "64" },
+      "m2.4xlarge"  : { "Arch" : "64" },
+      "c1.medium"   : { "Arch" : "32" },
+      "c1.xlarge"   : { "Arch" : "64" },
+      "cc1.4xlarge" : { "Arch" : "64" }
+    },
+    "DistroArch2AMI": {
+      "F16"      : { "32" : "F16-i386-cfntools", "64" : "F16-x86_64-cfntools" },
+      "F17"      : { "32" : "F17-i386-cfntools", "64" : "F17-x86_64-cfntools" },
+      "U10"      : { "32" : "U10-i386-cfntools", "64" : "U10-x86_64-cfntools" },
+      "RHEL-6.1" : { "32" : "rhel61-i386-cfntools", "64" : "rhel61-x86_64-cfntools" },
+      "RHEL-6.2" : { "32" : "rhel62-i386-cfntools", "64" : "rhel62-x86_64-cfntools" },
+      "RHEL-6.3" : { "32" : "rhel63-i386-cfntools", "64" : "rhel63-x86_64-cfntools" }
+    }
+  }, 
+  "Resources" : {  
+    "CFNInitUser" : {
+      "Type" : "AWS::IAM::User",
+      "Properties" : {
+        "Policies": [{
+          "PolicyName": "AccessForCFNInit",
+          "PolicyDocument" : {
+            "Statement": [{
+              "Effect"   : "Allow",
+              "Action"   : "cloudformation:DescribeStackResource",
+              "Resource" : "*"
+            }]
+          }
+        }]
+      }
+    },
+
+    "CFNKeys" : {
+      "Type" : "AWS::IAM::AccessKey",
+      "Properties" : {
+        "UserName" : { "Ref": "CFNInitUser" }
+      }
+    },
+
+    "PuppetMasterInstance" : {
+      "Type" : "AWS::EC2::Instance",
+      "Metadata" : {
+        "AWS::CloudFormation::Init" : {
+          "config" : {
+            "packages" : {
+              "yum" : {
+                "puppet"        : [],
+                "puppet-server" : [],
+                "ruby-devel"    : [],
+                "gcc"           : [],
+                "make"          : [],
+                "rubygems"      : []
+              },
+              "rubygems" : {
+                "json"          : []
+              }
+            },
+            "sources" : {
+              "/etc/puppet" : { "Ref" : "ContentLocation" }
+            },
+            "files" : {
+              "/etc/yum.repos.d/epel.repo" : {
+                "source" : "https://s3.amazonaws.com/cloudformation-examples/enable-epel-on-amazon-linux-ami",
+                "mode"   : "000644",
+                "owner"  : "root",
+                "group"  : "root"
+              },
+              "/etc/puppet/autosign.conf" : {
+                "content" : "*.internal\n",
+                "mode"    : "100644",
+                "owner"   : "root",
+                "group"   : "wheel"
+              },
+              "/etc/puppet/fileserver.conf" : {
+                "content" : "[modules]\n   allow *.internal\n",
+                "mode"    : "100644",
+                "owner"   : "root",
+                "group"   : "wheel"
+              },              
+              "/etc/puppet/puppet.conf" : {
+                "content" : { "Fn::Join" : ["", [
+                  "[main]\n",
+                  "   logdir=/var/log/puppet\n",
+                  "   rundir=/var/run/puppet\n",
+                  "   ssldir=$vardir/ssl\n",
+                  "   pluginsync=true\n",
+                  "[agent]\n",
+                  "   classfile=$vardir/classes.txt\n",
+                  "   localconfig=$vardir/localconfig\n"]] },
+                "mode"    : "000644",
+                "owner"   : "root",
+                "group"   : "root"
+              },
+              "/etc/puppet/modules/cfn/manifests/init.pp" : {
+                "content" : "class cfn {}",
+                "mode"    : "100644",
+                "owner"   : "root",
+                "group"   : "wheel"
+              },
+              "/etc/puppet/modules/cfn/lib/facter/cfn.rb" : {
+                "source"  : "https://s3.amazonaws.com/cloudformation-examples/cfn-facter-plugin.rb",
+                "mode"    : "100644",
+                "owner"   : "root",
+                "group"   : "wheel"
+              },
+              "/etc/puppet/manifests/nodes.pp" : {
+                "content" : {"Fn::Join" : ["", [
+                  "node basenode {\n",
+                  "  include cfn\n",
+                  "}\n",            
+                  "node /^.*internal$/ inherits basenode {\n",
+                  "  case $cfn_roles {\n",
+                  "    ", { "Ref" : "ContentManifest" }, "\n", 
+                  "  }\n",
+                  "}\n"]]},
+                "mode"    : "100644",
+                "owner"   : "root",
+                "group"   : "wheel" 
+              },
+              "/etc/puppet/manifests/site.pp" : {
+                "content" : "import \"nodes\"\n",
+                "mode"    : "100644",
+                "owner"   : "root",
+                "group"   : "wheel" 
+              }
+            },
+            "services" : {
+              "sysvinit" : {  
+                "puppetmaster" : {
+                  "enabled" : "true",
+                  "ensureRunning" : "true"                
+                }
+              }
+            }
+          }
+        }
+      },
+      "Properties" : {
+        "InstanceType" : { "Ref" : "InstanceType" },
+        "SecurityGroups" : [ { "Ref" : "PuppetGroup" } ],
+        "ImageId" : { "Fn::FindInMap" : [ "DistroArch2AMI", { "Ref" : "LinuxDistribution" },
+                    { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
+        "KeyName" : { "Ref" : "KeyName" },
+        "UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
+          "#!/bin/bash\n",
+          "yum update -y aws-cfn-bootstrap\n",
+
+          "/opt/aws/bin/cfn-init --region ", { "Ref" : "AWS::Region" },
+          "    -s ", { "Ref" : "AWS::StackName" }, " -r PuppetMasterInstance ",
+          "    --access-key ", { "Ref" : "CFNKeys" },
+          "    --secret-key ", { "Fn::GetAtt" : ["CFNKeys", "SecretAccessKey"]}, "\n",
+          "/opt/aws/bin/cfn-signal -e $? '", { "Ref" : "PuppetMasterWaitHandle" }, "'\n"]]}}
+      }
+    },
+
+    "EC2SecurityGroup" : {
+      "Type" : "AWS::EC2::SecurityGroup",
+      "Properties" : {
+        "GroupDescription" : "Group for clients to communicate with Puppet Master"
+      }
+    },
+
+    "PuppetGroup" : {
+      "Type" : "AWS::EC2::SecurityGroup",
+      "Properties" : {
+        "GroupDescription" : "Group for puppet communication",
+        "SecurityGroupIngress" : [
+          { "IpProtocol" : "tcp", "FromPort" : "8140", "ToPort" : "8140", "CidrIp": "0.0.0.0/0"},
+          { "IpProtocol" : "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": "0.0.0.0/0" }
+        ]        
+      }
+    },
+
+    "PuppetMasterWaitHandle" : {
+      "Type" : "AWS::CloudFormation::WaitConditionHandle"
+    },
+
+    "PuppetMasterWaitCondition" : {
+      "Type" : "AWS::CloudFormation::WaitCondition",
+      "DependsOn" : "PuppetMasterInstance",
+      "Properties" : {
+        "Handle" : { "Ref" : "PuppetMasterWaitHandle" },
+        "Timeout" : "600"
+      }
+    }
+  },
+  
+  "Outputs" : {
+    "PuppetMasterDNSName" : {
+      "Value" : { "Fn::GetAtt" : [ "PuppetMasterInstance", "PrivateDnsName" ] },
+      "Description" : "DNS Name of PuppetMaster"
+    },
+    "PuppetClientSecurityGroup" : {
+      "Value" : { "Ref" : "EC2SecurityGroup" },
+      "Description" : "Clients of the Puppet Master should be part of this security group"
+    }
+  }
+}
+
diff --git a/templates/WordPress_Single_Instance_puppet.template b/templates/WordPress_Single_Instance_puppet.template
new file mode 100644 (file)
index 0000000..a326e5e
--- /dev/null
@@ -0,0 +1,245 @@
+TemplateFormatVersion" : "2010-09-09",
+  
+  "Description": "Sample template to bring up WordPress using the Puppet client to install server roles. A WaitCondition is used to hold up the stack creation until the application is deployed. **WARNING** This template creates one or more Amazon EC2 instances and CloudWatch alarms. You will be billed for the AWS resources used if you create a stack from this template.",
+  
+  "Parameters" : {
+    "KeyName": {
+      "Type": "String",
+      "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the web server"
+    },
+    "PuppetClientSecurityGroup": {
+      "Description" : "The EC2 security group for the instances",
+      "Type": "String"
+    },
+    "PuppetMasterDNSName": {
+      "Description" : "The PuppetMaster DNS name",
+      "Type": "String"
+    },
+    "InstanceType" : {
+      "Description" : "WebServer EC2 instance type",
+      "Type" : "String",
+      "Default" : "m1.small",
+      "AllowedValues" : [ "t1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m2.xlarge","m2.2xlarge","m2.4xlarge","c1.medium","c1.xlarge","cc1.4xlarge","cc2.8xlarge","cg1.4xlarge"],
+      "ConstraintDescription" : "must be a valid EC2 instance type."
+    },
+    "DatabaseType": {
+      "Default": "db.m1.small",
+      "Description" : "The database instance type",
+      "Type": "String",
+      "AllowedValues" : [ "db.m1.small", "db.m1.large", "db.m1.xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge" ],
+      "ConstraintDescription" : "must contain only alphanumeric characters."
+    },
+    "DatabaseUser": {
+      "Default" : "admin",
+      "NoEcho": "true",
+      "Type": "String",
+      "Description" : "Test database admin account name",
+      "MinLength": "1",
+      "MaxLength": "16",
+      "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*",
+      "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters."
+    },
+    "DatabasePassword": {
+      "Default" : "admin",
+      "NoEcho": "true",
+      "Type": "String",
+      "Description" : "Test database admin account password",
+      "MinLength": "1",
+      "MaxLength": "41",
+      "AllowedPattern" : "[a-zA-Z0-9]*",
+      "ConstraintDescription" : "must contain only alphanumeric characters."
+    }
+  },
+  
+  "Mappings" : {
+    "AWSInstanceType2Arch" : {
+      "t1.micro"    : { "Arch" : "64" },
+      "m1.small"    : { "Arch" : "64" },
+      "m1.medium"   : { "Arch" : "64" },
+      "m1.large"    : { "Arch" : "64" },
+      "m1.xlarge"   : { "Arch" : "64" },
+      "m2.xlarge"   : { "Arch" : "64" },
+      "m2.2xlarge"  : { "Arch" : "64" },
+      "m2.4xlarge"  : { "Arch" : "64" },
+      "c1.medium"   : { "Arch" : "64" },
+      "c1.xlarge"   : { "Arch" : "64" },
+      "cc1.4xlarge" : { "Arch" : "64HVM" },
+      "cc2.8xlarge" : { "Arch" : "64HVM" },
+      "cg1.4xlarge" : { "Arch" : "64HVM" }
+    },
+
+    "AWSRegionArch2AMI" : {
+      "us-east-1"      : { "32" : "ami-31814f58", "64" : "ami-1b814f72", "64HVM" : "ami-0da96764" },
+      "us-west-2"      : { "32" : "ami-38fe7308", "64" : "ami-30fe7300", "64HVM" : "NOT_YET_SUPPORTED" },
+      "us-west-1"      : { "32" : "ami-11d68a54", "64" : "ami-1bd68a5e", "64HVM" : "NOT_YET_SUPPORTED" },
+      "eu-west-1"      : { "32" : "ami-973b06e3", "64" : "ami-953b06e1", "64HVM" : "NOT_YET_SUPPORTED" },
+      "ap-southeast-1" : { "32" : "ami-b4b0cae6", "64" : "ami-beb0caec", "64HVM" : "NOT_YET_SUPPORTED" },
+      "ap-northeast-1" : { "32" : "ami-0644f007", "64" : "ami-0a44f00b", "64HVM" : "NOT_YET_SUPPORTED" },
+      "sa-east-1"      : { "32" : "ami-3e3be423", "64" : "ami-3c3be421", "64HVM" : "NOT_YET_SUPPORTED" }
+    }
+  },
+    
+  "Resources" : {  
+
+    "CFNInitUser" : {
+      "Type" : "AWS::IAM::User",
+      "Properties" : {
+        "Policies": [{
+          "PolicyName": "AccessForCFNInit",
+          "PolicyDocument" : {
+            "Statement": [{
+              "Effect"   : "Allow",
+              "Action"   : "cloudformation:DescribeStackResource",
+              "Resource" : "*"
+            }]
+          }
+        }]
+      }
+    },
+
+    "CFNKeys" : {
+      "Type" : "AWS::IAM::AccessKey",
+      "Properties" : {
+        "UserName" : { "Ref": "CFNInitUser" }
+      }
+    },
+
+    "WebServer": {  
+      "Type": "AWS::EC2::Instance",
+      "Metadata" : {
+        "AWS::CloudFormation::Init" : {
+          "config" : {
+            "packages" : {
+              "yum" : {
+                "puppet"     : [],
+                "ruby-devel" : [],
+                "gcc"        : [],
+                "make"       : [],
+                "rubygems"   : []
+              },
+              "rubygems" : {
+                "json"       : []
+              }
+            },
+            "files" : {
+              "/etc/yum.repos.d/epel.repo" : {
+                "source" : "https://s3.amazonaws.com/cloudformation-examples/enable-epel-on-amazon-linux-ami",
+                "mode"   : "000644",
+                "owner"  : "root",
+                "group"  : "root"
+              },
+              "/etc/puppet/puppet.conf" : {
+                "content" : { "Fn::Join" : ["", [
+                  "[main]\n",
+                  "   logdir=/var/log/puppet\n",
+                  "   rundir=/var/run/puppet\n",
+                  "   ssldir=$vardir/ssl\n",
+                  "   pluginsync=true\n",
+                  "[agent]\n",
+                  "   classfile=$vardir/classes.txt\n",
+                  "   localconfig=$vardir/localconfig\n",
+                  "   server=",{ "Ref" : "PuppetMasterDNSName" },"\n"
+                ]] },
+                "mode" : "000644",
+                "owner" : "root",
+                "group" : "root"
+              }
+            },
+            "services" : {
+              "sysvinit" : {  
+                "puppet" : {
+                  "enabled" : "true",
+                  "ensureRunning" : "true"                
+                }
+              }
+            }
+          }
+        },
+        "Puppet" : {
+          "roles"    : [ "wordpress" ],
+          "host"     : {"Fn::GetAtt" : ["WordPressDatabase", "Endpoint.Address"]},
+          "database" : "WordPressDB",
+          "user"     : {"Ref" : "DatabaseUser"},
+          "password" : {"Ref" : "DatabasePassword" }
+        }
+      },
+      "Properties": {
+        "SecurityGroups": [ { "Ref": "PuppetClientSecurityGroup" }, { "Ref" : "EC2SecurityGroup" } ],
+        "ImageId": { "Fn::FindInMap": [ "AWSRegionArch2AMI", { "Ref": "AWS::Region" }, { "Fn::FindInMap": [ "AWSInstanceType2Arch", { "Ref": "InstanceType" }, "Arch" ] } ]
+        },
+        "UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
+          "#!/bin/bash\n",
+          "yum update -y aws-cfn-bootstrap\n",
+
+          "/opt/aws/bin/cfn-init --region ", { "Ref" : "AWS::Region" },
+          "    -s ", { "Ref" : "AWS::StackName" }, " -r WebServer ",
+          "    --access-key ", { "Ref" : "CFNKeys" },
+          "    --secret-key ", { "Fn::GetAtt" : ["CFNKeys", "SecretAccessKey"]}, "\n",
+          "/opt/aws/bin/cfn-signal -e $? '", { "Ref" : "ApplicationWaitHandle" }, "'\n"
+        ]]}},
+        "KeyName": { "Ref": "KeyName" },
+        "InstanceType": { "Ref": "InstanceType" }
+      }
+    },
+
+
+    "EC2SecurityGroup" : {
+      "Type" : "AWS::EC2::SecurityGroup",
+      "Properties" : {
+        "GroupDescription" : "Enable HTTP access for Wordpress plus SSH access via port 22",
+        "SecurityGroupIngress" : [
+          {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : "0.0.0.0/0" },
+          {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0" }
+        ]
+      }
+    },
+
+    "ApplicationWaitHandle" : {
+      "Type" : "AWS::CloudFormation::WaitConditionHandle"
+    },
+
+    "ApplicationWaitCondition" : {
+      "Type" : "AWS::CloudFormation::WaitCondition",
+      "DependsOn"  : "WebServer",
+      "Properties" : {
+        "Handle"   : { "Ref" : "ApplicationWaitHandle" },
+        "Timeout"  : "600"
+      }
+    },
+
+    "WordPressDatabase" : {
+      "Type" : "AWS::RDS::DBInstance",
+      "Properties" : {
+        "AllocatedStorage"   : "5",
+        "DBName"             : "WordPressDB",
+        "Engine"             : "MySQL",
+        "DBInstanceClass"    : { "Ref" : "DatabaseType" },
+        "DBSecurityGroups"   : [ { "Ref": "DBSecurityGroup" } ],
+        "MasterUsername"     : { "Ref" : "DatabaseUser" },
+        "MasterUserPassword" : { "Ref" : "DatabasePassword" }
+      }
+    },
+
+    "DBSecurityGroup": {
+      "Type": "AWS::RDS::DBSecurityGroup",
+      "Properties": {
+        "DBSecurityGroupIngress": {
+          "EC2SecurityGroupName": { "Ref": "EC2SecurityGroup" }
+        },
+        "GroupDescription": "database access"
+      }
+    }
+  },
+  
+  "Outputs": {
+    "WebsiteURL": {
+      "Value": { "Fn::Join": [ "", [ "http://", { "Fn::GetAtt": [ "WebServer", "PublicDnsName" ] }, "/wordpress" ] ] },
+      "Description" : "URL of the WordPress website"
+    },
+    "InstallURL": {
+      "Value": { "Fn::Join": [ "", [ "http://", { "Fn::GetAtt": [ "WebServer", "PublicDnsName" ] }, "/wordpress/wp-admin/install.php" ] ] },
+      "Description" : "URL to install WordPress"
+    }
+  }
+}
+