diff --git a/examples/OpsWorksSnippet.py b/examples/OpsWorksSnippet.py new file mode 100644 index 000000000..a5a83deef --- /dev/null +++ b/examples/OpsWorksSnippet.py @@ -0,0 +1,185 @@ +# Converted from AWS OpsWorks Snippets located at: +# http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/quickref-opsworks.html + +from troposphere import GetAZs, Join +from troposphere import Parameter, Ref, Template +from troposphere.elasticloadbalancing import LoadBalancer, HealthCheck +from troposphere.opsworks import App, ElasticLoadBalancerAttachment, Instance +from troposphere.opsworks import Layer, Stack +from troposphere.opsworks import Source, Recipes, VolumeConfiguration + + +template = Template() + +template.add_version("2010-09-09") + +ServiceRole = template.add_parameter(Parameter( + "ServiceRole", + Default="aws-opsworks-service-role", + Description="The OpsWorks service role", + Type="String", + MinLength="1", + MaxLength="64", + AllowedPattern="[a-zA-Z][a-zA-Z0-9-]*", + ConstraintDescription="must begin with a letter and contain only " + + "alphanumeric characters.", +)) + +InstanceRole = template.add_parameter(Parameter( + "InstanceRole", + Default="aws-opsworks-ec2-role", + Description="The OpsWorks instance role", + Type="String", + MinLength="1", + MaxLength="64", + AllowedPattern="[a-zA-Z][a-zA-Z0-9-]*", + ConstraintDescription="must begin with a letter and contain only " + + "alphanumeric characters.", +)) + +AppName = template.add_parameter(Parameter( + "AppName", + Default="myapp", + Description="The app name", + Type="String", + MinLength="1", + MaxLength="64", + AllowedPattern="[a-zA-Z][a-zA-Z0-9]*", + ConstraintDescription="must begin with a letter and contain only " + + "alphanumeric characters.", +)) + +MysqlRootPassword = template.add_parameter(Parameter( + "MysqlRootPassword", + Description="MysqlRootPassword", + NoEcho=True, + Type="String", +)) + +myStack = template.add_resource(Stack( + "myStack", + Name=Ref("AWS::StackName"), + ServiceRoleArn=Join( + "", + [ + "arn:aws:iam::", + Ref("AWS::AccountId"), + ":role/", Ref(ServiceRole) + ]), + DefaultInstanceProfileArn=Join( + "", + [ + "arn:aws:iam::", + Ref("AWS::AccountId"), + ":instance-profile/", + Ref(InstanceRole) + ]), + UseCustomCookbooks=True, + CustomCookbooksSource=Source( + Type="git", + Url="git://github.com/amazonwebservices/" + + "opsworks-example-cookbooks.git", + ), +)) + +myLayer = template.add_resource(Layer( + "myLayer", + StackId=Ref(myStack), + Type="php-app", + Shortname="php-app", + EnableAutoHealing=True, + AutoAssignElasticIps=False, + AutoAssignPublicIps=True, + Name="MyPHPApp", + CustomRecipes=Recipes( + Configure=["phpapp::appsetup"], + ), +)) + +DBLayer = template.add_resource(Layer( + "DBLayer", + StackId=Ref(myStack), + Type="db-master", + Shortname="db-layer", + EnableAutoHealing=True, + AutoAssignElasticIps=False, + AutoAssignPublicIps=True, + Name="MyMySQL", + CustomRecipes=Recipes( + Setup=["phpapp::dbsetup"] + ), + Attributes={ + "MysqlRootPassword": Ref(MysqlRootPassword), + "MysqlRootPasswordUbiquitous": "true", + }, + VolumeConfigurations=[ + VolumeConfiguration( + MountPoint="/vol/mysql", + NumberOfDisks=1, + Size=10, + ) + ], +)) + +ELB = template.add_resource(LoadBalancer( + "ELB", + AvailabilityZones=GetAZs(""), + Listeners=[{ + "LoadBalancerPort": "80", + "InstancePort": "80", + "Protocol": "HTTP", + "InstanceProtocol": "HTTP", + }], + HealthCheck=HealthCheck( + Target="HTTP:80/", + HealthyThreshold="2", + UnhealthyThreshold="10", + Interval="30", + Timeout="5", + ), +)) + +ELBAttachment = template.add_resource(ElasticLoadBalancerAttachment( + "ELBAttachment", + ElasticLoadBalancerName=Ref(ELB), + LayerId=Ref(myLayer), +)) + +myAppInstance1 = template.add_resource(Instance( + "myAppInstance1", + StackId=Ref(myStack), + LayerIds=[Ref(myLayer)], + InstanceType="m1.small", +)) + +myAppInstance2 = template.add_resource(Instance( + "myAppInstance2", + StackId=Ref(myStack), + LayerIds=[Ref(myLayer)], + InstanceType="m1.small", +)) + +myDBInstance = template.add_resource(Instance( + "myDBInstance", + StackId=Ref(myStack), + LayerIds=[Ref(DBLayer)], + InstanceType="m1.small", +)) + +myApp = template.add_resource(App( + "myApp", + StackId=Ref(myStack), + Type="php", + Name=Ref(AppName), + AppSource=Source( + Type="git", + Url="git://github.com/amazonwebservices/" + + "opsworks-demo-php-simple-app.git", + Revision="version2", + ), + Attributes={ + "DocumentRoot": "web", + }, +)) + +print(template.to_json()) diff --git a/tests/examples_output/OpsWorksSnippet.template b/tests/examples_output/OpsWorksSnippet.template new file mode 100644 index 000000000..6841237f0 --- /dev/null +++ b/tests/examples_output/OpsWorksSnippet.template @@ -0,0 +1,229 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": { + "AppName": { + "AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*", + "ConstraintDescription": "must begin with a letter and contain only alphanumeric characters.", + "Default": "myapp", + "Description": "The app name", + "MaxLength": "64", + "MinLength": "1", + "Type": "String" + }, + "InstanceRole": { + "AllowedPattern": "[a-zA-Z][a-zA-Z0-9-]*", + "ConstraintDescription": "must begin with a letter and contain only alphanumeric characters.", + "Default": "aws-opsworks-ec2-role", + "Description": "The OpsWorks instance role", + "MaxLength": "64", + "MinLength": "1", + "Type": "String" + }, + "MysqlRootPassword": { + "Description": "MysqlRootPassword", + "NoEcho": true, + "Type": "String" + }, + "ServiceRole": { + "AllowedPattern": "[a-zA-Z][a-zA-Z0-9-]*", + "ConstraintDescription": "must begin with a letter and contain only alphanumeric characters.", + "Default": "aws-opsworks-service-role", + "Description": "The OpsWorks service role", + "MaxLength": "64", + "MinLength": "1", + "Type": "String" + } + }, + "Resources": { + "DBLayer": { + "Properties": { + "Attributes": { + "MysqlRootPassword": { + "Ref": "MysqlRootPassword" + }, + "MysqlRootPasswordUbiquitous": "true" + }, + "AutoAssignElasticIps": "false", + "AutoAssignPublicIps": "true", + "CustomRecipes": { + "Setup": [ + "phpapp::dbsetup" + ] + }, + "EnableAutoHealing": "true", + "Name": "MyMySQL", + "Shortname": "db-layer", + "StackId": { + "Ref": "myStack" + }, + "Type": "db-master", + "VolumeConfigurations": [ + { + "MountPoint": "/vol/mysql", + "NumberOfDisks": 1, + "Size": 10 + } + ] + }, + "Type": "AWS::OpsWorks::Layer" + }, + "ELB": { + "Properties": { + "AvailabilityZones": { + "Fn::GetAZs": "" + }, + "HealthCheck": { + "HealthyThreshold": "2", + "Interval": "30", + "Target": "HTTP:80/", + "Timeout": "5", + "UnhealthyThreshold": "10" + }, + "Listeners": [ + { + "InstancePort": "80", + "InstanceProtocol": "HTTP", + "LoadBalancerPort": "80", + "Protocol": "HTTP" + } + ] + }, + "Type": "AWS::ElasticLoadBalancing::LoadBalancer" + }, + "ELBAttachment": { + "Properties": { + "ElasticLoadBalancerName": { + "Ref": "ELB" + }, + "LayerId": { + "Ref": "myLayer" + } + }, + "Type": "AWS::OpsWorks::ElasticLoadBalancerAttachment" + }, + "myApp": { + "Properties": { + "AppSource": { + "Revision": "version2", + "Type": "git", + "Url": "git://github.com/amazonwebservices/opsworks-demo-php-simple-app.git" + }, + "Attributes": { + "DocumentRoot": "web" + }, + "Name": { + "Ref": "AppName" + }, + "StackId": { + "Ref": "myStack" + }, + "Type": "php" + }, + "Type": "AWS::OpsWorks::App" + }, + "myAppInstance1": { + "Properties": { + "InstanceType": "m1.small", + "LayerIds": [ + { + "Ref": "myLayer" + } + ], + "StackId": { + "Ref": "myStack" + } + }, + "Type": "AWS::OpsWorks::Instance" + }, + "myAppInstance2": { + "Properties": { + "InstanceType": "m1.small", + "LayerIds": [ + { + "Ref": "myLayer" + } + ], + "StackId": { + "Ref": "myStack" + } + }, + "Type": "AWS::OpsWorks::Instance" + }, + "myDBInstance": { + "Properties": { + "InstanceType": "m1.small", + "LayerIds": [ + { + "Ref": "DBLayer" + } + ], + "StackId": { + "Ref": "myStack" + } + }, + "Type": "AWS::OpsWorks::Instance" + }, + "myLayer": { + "Properties": { + "AutoAssignElasticIps": "false", + "AutoAssignPublicIps": "true", + "CustomRecipes": { + "Configure": [ + "phpapp::appsetup" + ] + }, + "EnableAutoHealing": "true", + "Name": "MyPHPApp", + "Shortname": "php-app", + "StackId": { + "Ref": "myStack" + }, + "Type": "php-app" + }, + "Type": "AWS::OpsWorks::Layer" + }, + "myStack": { + "Properties": { + "CustomCookbooksSource": { + "Type": "git", + "Url": "git://github.com/amazonwebservices/opsworks-example-cookbooks.git" + }, + "DefaultInstanceProfileArn": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":instance-profile/", + { + "Ref": "InstanceRole" + } + ] + ] + }, + "Name": { + "Ref": "AWS::StackName" + }, + "ServiceRoleArn": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/", + { + "Ref": "ServiceRole" + } + ] + ] + }, + "UseCustomCookbooks": "true" + }, + "Type": "AWS::OpsWorks::Stack" + } + } +} diff --git a/troposphere/opsworks.py b/troposphere/opsworks.py new file mode 100644 index 000000000..c603db260 --- /dev/null +++ b/troposphere/opsworks.py @@ -0,0 +1,139 @@ +# Copyright (c) 2014, Yuta Okamoto +# All rights reserved. +# +# See LICENSE file for full license. + +from . import AWSObject, AWSProperty, Ref +from .validators import boolean, integer + + +class Source(AWSProperty): + props = { + 'Password': (basestring, False), + 'Revision': (basestring, False), + 'SshKey': (basestring, False), + 'Type': (basestring, False), + 'Url': (basestring, False), + 'Username': (basestring, False), + } + + +class SslConfiguration(AWSProperty): + props = { + 'Certificate': (basestring, True), + 'Chain': (basestring, False), + 'PrivateKey': (basestring, True), + } + + +class Recipes(AWSProperty): + props = { + 'Configure': ([basestring], False), + 'Deploy': ([basestring], False), + 'Setup': ([basestring], False), + 'Shutdown': ([basestring], False), + 'Undeploy': ([basestring], False), + } + + +class VolumeConfiguration(AWSProperty): + props = { + 'MountPoint': (basestring, True), + 'NumberOfDisks': (integer, True), + 'RaidLevel': (integer, False), + 'Size': (integer, True), + } + + +class StackConfigurationManager(AWSProperty): + props = { + 'Name': (basestring, False), + 'Version': (basestring, False), + } + + +class App(AWSObject): + type = "AWS::OpsWorks::App" + + props = { + 'AppSource': (Source, False), + 'Attributes': (dict, False), + 'Description': (basestring, False), + 'Domains': ([basestring], False), + 'EnableSsl': (boolean, False), + 'Name': (basestring, True), + 'Shortname': (basestring, False), + 'SslConfiguration': (SslConfiguration, False), + 'StackId': (basestring, True), + 'Type': (basestring, True), + } + + +class ElasticLoadBalancerAttachment(AWSObject): + type = "AWS::OpsWorks::ElasticLoadBalancerAttachment" + + props = { + 'ElasticLoadBalancerName': (basestring, True), + 'LayerId': (basestring, True), + } + + +class Instance(AWSObject): + type = "AWS::OpsWorks::Instance" + + props = { + 'AmiId': (basestring, False), + 'Architecture': (basestring, False), + 'AvailabilityZone': (basestring, False), + 'InstallUpdatesOnBoot': (boolean, False), + 'InstanceType': (basestring, True), + 'LayerIds': ([basestring, Ref], True), + 'Os': (basestring, False), + 'RootDeviceType': (basestring, False), + 'SshKeyName': (basestring, False), + 'StackId': (basestring, True), + 'SubnetId': (basestring, False), + } + + +class Layer(AWSObject): + type = "AWS::OpsWorks::Layer" + + props = { + 'Attributes': (dict, False), + 'AutoAssignElasticIps': (boolean, True), + 'AutoAssignPublicIps': (boolean, True), + 'CustomInstanceProfileArn': (basestring, False), + 'CustomRecipes': (Recipes, False), + 'CustomSecurityGroupIds': ([basestring, Ref], False), + 'EnableAutoHealing': (boolean, True), + 'InstallUpdatesOnBoot': (boolean, False), + 'Name': (basestring, True), + 'Packages': ([basestring], False), + 'Shortname': (basestring, True), + 'StackId': (basestring, True), + 'Type': (basestring, True), + 'VolumeConfigurations': ([VolumeConfiguration], False), + } + + +class Stack(AWSObject): + type = "AWS::OpsWorks::Stack" + + props = { + 'Attributes': (dict, False), + 'ConfigurationManager': (StackConfigurationManager, False), + 'CustomCookbooksSource': (Source, False), + 'CustomJson': (basestring, False), # TODO: JSON object + 'DefaultAvailabilityZone': (basestring, False), + 'DefaultInstanceProfileArn': (basestring, True), + 'DefaultOs': (basestring, False), + 'DefaultRootDeviceType': (basestring, False), + 'DefaultSshKeyName': (basestring, False), + 'DefaultSubnetId': (basestring, False), + 'HostnameTheme': (basestring, False), + 'Name': (basestring, True), + 'ServiceRoleArn': (basestring, True), + 'UseCustomCookbooks': (boolean, False), + 'VpcId': (basestring, False), + }