Merge pull request #2180 from jsmartin/cloudformation
CloudFormation support.
This commit is contained in:
commit
69a199727c
3 changed files with 687 additions and 0 deletions
45
examples/playbooks/cloudformation.yaml
Normal file
45
examples/playbooks/cloudformation.yaml
Normal file
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
# This playbook demonstrates how to use the ansible cloudformation module to launch an AWS CloudFormation stack.
|
||||
#
|
||||
# This module requires that the boto python library is installed, and that you have your AWS credentials
|
||||
# in $HOME/.boto
|
||||
|
||||
#The thought here is to bring up a bare infrastructure with CloudFormation, but use ansible to configure it.
|
||||
#I generally do this in 2 different playbook runs as to allow the ec2.py inventory to be updated.
|
||||
|
||||
#This module also uses "complex arguments" which were introduced in ansible 1.1 allowing you to specify the
|
||||
#Cloudformation template parameters
|
||||
|
||||
#This example launches a 3 node AutoScale group, with a security group, and an InstanceProfile with root permissions.
|
||||
|
||||
#If a stack does not exist, it will be created. If it does exist and the template file has changed, the stack will be updated.
|
||||
#If the parameters are different, the stack will also be updated.
|
||||
|
||||
#CloudFormation stacks can take awhile to provision, if you are curious about its status, use the AWS
|
||||
#web console or one of the CloudFormation CLI's.
|
||||
|
||||
#Example update -- try first launching the stack with 3 as the ClusterSize. After it is launched, change it to 4
|
||||
#and run the playbook again.
|
||||
|
||||
- name: provision stack
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
|
||||
# Launch the cloudformation-example.json template. Register the output.
|
||||
|
||||
tasks:
|
||||
- name: launch ansible cloudformation example
|
||||
cloudformation: >
|
||||
stack_name="ansible-cloudformation" state=present
|
||||
region=us-east-1 disable_rollback=true
|
||||
template=files/cloudformation-example.json
|
||||
args:
|
||||
template_parameters:
|
||||
KeyName: jmartin
|
||||
DiskType: ephemeral
|
||||
InstanceType: m1.small
|
||||
ClusterSize: 3
|
||||
register: stack
|
||||
- name: show stack outputs
|
||||
debug: msg="My stack outputs are ${stack.stack_outputs}"
|
399
examples/playbooks/files/cloudformation-example.json
Normal file
399
examples/playbooks/files/cloudformation-example.json
Normal file
|
@ -0,0 +1,399 @@
|
|||
{
|
||||
"Outputs" : {
|
||||
"ClusterSecGroup" : {
|
||||
"Description" : "Name of RegionalManagerSecGroup",
|
||||
"Value" : {
|
||||
"Ref" : "InstanceSecurityGroup"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Launches an example cluster",
|
||||
"Mappings" : {
|
||||
"ebs" : {
|
||||
"ap-northeast-1" : {
|
||||
"AMI" : "ami-4e6cd34f"
|
||||
},
|
||||
"ap-southeast-1" : {
|
||||
"AMI" : "ami-a6a7e7f4"
|
||||
},
|
||||
"eu-west-1" : {
|
||||
"AMI" : "ami-c37474b7"
|
||||
},
|
||||
"sa-east-1" : {
|
||||
"AMI" : "ami-1e08d103"
|
||||
},
|
||||
"us-east-1" : {
|
||||
"AMI" : "ami-1624987f"
|
||||
},
|
||||
"us-west-1" : {
|
||||
"AMI" : "ami-1bf9de5e"
|
||||
},
|
||||
"us-west-2" : {
|
||||
"AMI" : "ami-2a31bf1a"
|
||||
}
|
||||
},
|
||||
"ephemeral" : {
|
||||
"ap-northeast-1" : {
|
||||
"AMI" : "ami-5a6cd35b"
|
||||
},
|
||||
"ap-southeast-1" : {
|
||||
"AMI" : "ami-a8a7e7fa"
|
||||
},
|
||||
"eu-west-1" : {
|
||||
"AMI" : "ami-b57474c1"
|
||||
},
|
||||
"sa-east-1" : {
|
||||
"AMI" : "ami-1608d10b"
|
||||
},
|
||||
"us-east-1" : {
|
||||
"AMI" : "ami-e8249881"
|
||||
},
|
||||
"us-west-1" : {
|
||||
"AMI" : "ami-21f9de64"
|
||||
},
|
||||
"us-west-2" : {
|
||||
"AMI" : "ami-2e31bf1e"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Parameters" : {
|
||||
"ClusterSize" : {
|
||||
"Description" : "Number of nodes in the cluster",
|
||||
"Type" : "String"
|
||||
},
|
||||
"DiskType" : {
|
||||
"AllowedValues" : [
|
||||
"ephemeral",
|
||||
"ebs"
|
||||
],
|
||||
"Default" : "ephemeral",
|
||||
"Description" : "Type of Disk to use ( ephemeral/ebs )",
|
||||
"Type" : "String"
|
||||
},
|
||||
"InstanceType" : {
|
||||
"AllowedValues" : [
|
||||
"t1.micro",
|
||||
"m1.small",
|
||||
"m1.medium",
|
||||
"m1.large",
|
||||
"m1.xlarge",
|
||||
"m2.xlarge",
|
||||
"m2.2xlarge",
|
||||
"m2.4xlarge",
|
||||
"c1.medium",
|
||||
"c1.xlarge",
|
||||
"cc1.4xlarge"
|
||||
],
|
||||
"ConstraintDescription" : "must be valid instance type. ",
|
||||
"Default" : "m1.large",
|
||||
"Description" : "Type of EC2 instance for cluster",
|
||||
"Type" : "String"
|
||||
},
|
||||
"KeyName" : {
|
||||
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to the cluster",
|
||||
"Type" : "String"
|
||||
}
|
||||
},
|
||||
"Resources" : {
|
||||
"ApplicationWaitCondition" : {
|
||||
"DependsOn" : "ClusterServerGroup",
|
||||
"Properties" : {
|
||||
"Handle" : {
|
||||
"Ref" : "ApplicationWaitHandle"
|
||||
},
|
||||
"Timeout" : "4500"
|
||||
},
|
||||
"Type" : "AWS::CloudFormation::WaitCondition"
|
||||
},
|
||||
"ApplicationWaitHandle" : {
|
||||
"Type" : "AWS::CloudFormation::WaitConditionHandle"
|
||||
},
|
||||
"CFNInitUser" : {
|
||||
"Properties" : {
|
||||
"Path" : "/",
|
||||
"Policies" : [
|
||||
{
|
||||
"PolicyDocument" : {
|
||||
"Statement" : [
|
||||
{
|
||||
"Action" : [
|
||||
"cloudformation:DescribeStackResource",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Effect" : "Allow",
|
||||
"Resource" : "*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"PolicyName" : "AccessForCFNInit"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Type" : "AWS::IAM::User"
|
||||
},
|
||||
"CFNKeys" : {
|
||||
"Properties" : {
|
||||
"UserName" : {
|
||||
"Ref" : "CFNInitUser"
|
||||
}
|
||||
},
|
||||
"Type" : "AWS::IAM::AccessKey"
|
||||
},
|
||||
"ClusterCommunication1" : {
|
||||
"Properties" : {
|
||||
"FromPort" : "-1",
|
||||
"GroupName" : {
|
||||
"Ref" : "InstanceSecurityGroup"
|
||||
},
|
||||
"IpProtocol" : "icmp",
|
||||
"SourceSecurityGroupName" : {
|
||||
"Ref" : "InstanceSecurityGroup"
|
||||
},
|
||||
"ToPort" : "-1"
|
||||
},
|
||||
"Type" : "AWS::EC2::SecurityGroupIngress"
|
||||
},
|
||||
"ClusterCommunication2" : {
|
||||
"Properties" : {
|
||||
"FromPort" : "1",
|
||||
"GroupName" : {
|
||||
"Ref" : "InstanceSecurityGroup"
|
||||
},
|
||||
"IpProtocol" : "tcp",
|
||||
"SourceSecurityGroupName" : {
|
||||
"Ref" : "InstanceSecurityGroup"
|
||||
},
|
||||
"ToPort" : "65356"
|
||||
},
|
||||
"Type" : "AWS::EC2::SecurityGroupIngress"
|
||||
},
|
||||
"ClusterCommunication3" : {
|
||||
"Properties" : {
|
||||
"FromPort" : "1",
|
||||
"GroupName" : {
|
||||
"Ref" : "InstanceSecurityGroup"
|
||||
},
|
||||
"IpProtocol" : "udp",
|
||||
"SourceSecurityGroupName" : {
|
||||
"Ref" : "InstanceSecurityGroup"
|
||||
},
|
||||
"ToPort" : "65356"
|
||||
},
|
||||
"Type" : "AWS::EC2::SecurityGroupIngress"
|
||||
},
|
||||
"InstanceSecurityGroup" : {
|
||||
"Properties" : {
|
||||
"GroupDescription" : "Enable SSH access via port 22",
|
||||
"SecurityGroupIngress" : [
|
||||
{
|
||||
"CidrIp" : "0.0.0.0/0",
|
||||
"FromPort" : "22",
|
||||
"IpProtocol" : "tcp",
|
||||
"ToPort" : "22"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Type" : "AWS::EC2::SecurityGroup"
|
||||
},
|
||||
"LaunchConfig" : {
|
||||
"Properties" : {
|
||||
"IamInstanceProfile" : {
|
||||
"Ref" : "RootInstanceProfile"
|
||||
},
|
||||
"ImageId" : {
|
||||
"Fn::FindInMap" : [
|
||||
{
|
||||
"Ref" : "DiskType"
|
||||
},
|
||||
{
|
||||
"Ref" : "AWS::Region"
|
||||
},
|
||||
"AMI"
|
||||
]
|
||||
},
|
||||
"InstanceType" : {
|
||||
"Ref" : "InstanceType"
|
||||
},
|
||||
"KeyName" : {
|
||||
"Ref" : "KeyName"
|
||||
},
|
||||
"SecurityGroups" : [
|
||||
{
|
||||
"Ref" : "InstanceSecurityGroup"
|
||||
}
|
||||
],
|
||||
"UserData" : {
|
||||
"Fn::Base64" : {
|
||||
"Fn::Join" : [
|
||||
"\n",
|
||||
[
|
||||
"#!/bin/bash -v",
|
||||
"exec > >(tee /var/log/cfn-data.log|logger -t user-data -s 2>/dev/console) 2>&1",
|
||||
"",
|
||||
"sleep 10",
|
||||
"",
|
||||
"function retry {",
|
||||
" nTrys=0",
|
||||
" maxTrys=5",
|
||||
" status=256",
|
||||
" until [ $status == 0 ] ; do",
|
||||
" $1",
|
||||
" status=$?",
|
||||
" nTrys=$(($nTrys + 1))",
|
||||
" if [ $nTrys -gt $maxTrys ] ; then",
|
||||
" echo \"Number of re-trys exceeded. Exit code: $status\"",
|
||||
" exit $status",
|
||||
" fi",
|
||||
" if [ $status != 0 ] ; then",
|
||||
" echo \"Failed (exit code $status)... retry $nTrys\"",
|
||||
" sleep 10",
|
||||
" fi",
|
||||
" done",
|
||||
"}",
|
||||
"",
|
||||
"yum update -y aws-cfn-bootstrap",
|
||||
"",
|
||||
"#for all the stuff that complains about sudo and tty",
|
||||
"sed -i 's,Defaults requiretty,#Defaults requiretty,g' /etc/sudoers",
|
||||
"",
|
||||
"function error_exit",
|
||||
"{",
|
||||
{
|
||||
"Fn::Join" : [
|
||||
"",
|
||||
[
|
||||
" /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '",
|
||||
{
|
||||
"Ref" : "ApplicationWaitHandle"
|
||||
},
|
||||
"'"
|
||||
]
|
||||
]
|
||||
},
|
||||
"}",
|
||||
"yum update -y aws-cfn-bootstrap",
|
||||
"#this runs the first stage of cfinit",
|
||||
{
|
||||
"Fn::Join" : [
|
||||
"",
|
||||
[
|
||||
"#/opt/aws/bin/cfn-init -c ascending -v --region ",
|
||||
{
|
||||
"Ref" : "AWS::Region"
|
||||
},
|
||||
" -s ",
|
||||
{
|
||||
"Ref" : "AWS::StackName"
|
||||
},
|
||||
" -r ",
|
||||
"LaunchConfig",
|
||||
" --access-key ",
|
||||
{
|
||||
"Ref" : "CFNKeys"
|
||||
},
|
||||
" --secret-key ",
|
||||
{
|
||||
"Fn::GetAtt" : [
|
||||
"CFNKeys",
|
||||
"SecretAccessKey"
|
||||
]
|
||||
},
|
||||
" || error_exit 'Failed to initialize client using cfn-init'"
|
||||
]
|
||||
]
|
||||
},
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"result_code=$?",
|
||||
{
|
||||
"Fn::Join" : [
|
||||
"",
|
||||
[
|
||||
"/opt/aws/bin/cfn-signal -e $result_code '",
|
||||
{
|
||||
"Ref" : "ApplicationWaitHandle"
|
||||
},
|
||||
"'"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Type" : "AWS::AutoScaling::LaunchConfiguration"
|
||||
},
|
||||
"ClusterServerGroup" : {
|
||||
"Properties" : {
|
||||
"AvailabilityZones" : {
|
||||
"Fn::GetAZs" : ""
|
||||
},
|
||||
"LaunchConfigurationName" : {
|
||||
"Ref" : "LaunchConfig"
|
||||
},
|
||||
"MaxSize" : {
|
||||
"Ref" : "ClusterSize"
|
||||
},
|
||||
"MinSize" : {
|
||||
"Ref" : "ClusterSize"
|
||||
}
|
||||
},
|
||||
"Type" : "AWS::AutoScaling::AutoScalingGroup"
|
||||
},
|
||||
"RolePolicies" : {
|
||||
"Properties" : {
|
||||
"PolicyDocument" : {
|
||||
"Statement" : [
|
||||
{
|
||||
"Action" : "*",
|
||||
"Effect" : "Allow",
|
||||
"Resource" : "*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"PolicyName" : "root",
|
||||
"Roles" : [
|
||||
{
|
||||
"Ref" : "RootRole"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Type" : "AWS::IAM::Policy"
|
||||
},
|
||||
"RootInstanceProfile" : {
|
||||
"Properties" : {
|
||||
"Path" : "/",
|
||||
"Roles" : [
|
||||
{
|
||||
"Ref" : "RootRole"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Type" : "AWS::IAM::InstanceProfile"
|
||||
},
|
||||
"RootRole" : {
|
||||
"Properties" : {
|
||||
"AssumeRolePolicyDocument" : {
|
||||
"Statement" : [
|
||||
{
|
||||
"Action" : [
|
||||
"sts:AssumeRole"
|
||||
],
|
||||
"Effect" : "Allow",
|
||||
"Principal" : {
|
||||
"Service" : [
|
||||
"ec2.amazonaws.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Path" : "/"
|
||||
},
|
||||
"Type" : "AWS::IAM::Role"
|
||||
}
|
||||
}
|
||||
}
|
243
library/cloudformation
Normal file
243
library/cloudformation
Normal file
|
@ -0,0 +1,243 @@
|
|||
#!/usr/bin/python -tt
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: cloudformation
|
||||
short_description: create a AWS CloudFormation stack
|
||||
description:
|
||||
- Launches an AWS CloudFormation stack and waits for it complete.
|
||||
version_added: "1.1"
|
||||
options:
|
||||
stack_name:
|
||||
description:
|
||||
- name of the cloudformation stack
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
disable_rollback:
|
||||
description:
|
||||
- If a stacks fails to form, rollback will remove the stack
|
||||
required: false
|
||||
default: false
|
||||
aliases: []
|
||||
template_parameters:
|
||||
description:
|
||||
- a list of hashes of all the template variables for the stack
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
region:
|
||||
description:
|
||||
- The AWS region the stack will be launched in
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
state:
|
||||
description:
|
||||
- If state is "present", stack will be created. If state is "present" and if stack exists and template has changed, it will be updated.
|
||||
If state is present, stack will be removed.
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
template:
|
||||
description:
|
||||
- the path of the cloudformation template
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
wait_for:
|
||||
description:
|
||||
- Wait while the stack is being created/updated/deleted.
|
||||
required: false
|
||||
default: true
|
||||
aliases: []
|
||||
|
||||
examples:
|
||||
|
||||
tasks:
|
||||
- name: launch ansible cloudformation example
|
||||
cloudformation: >
|
||||
stack_name="ansible-cloudformation" state=present
|
||||
region=us-east-1 disable_rollback=true
|
||||
template=files/cloudformation-example.json
|
||||
args:
|
||||
template_parameters:
|
||||
KeyName: jmartin
|
||||
DiskType: ephemeral
|
||||
InstanceType: m1.small
|
||||
ClusterSize: 3
|
||||
|
||||
requirements: [ "boto" ]
|
||||
author: James S. Martin
|
||||
'''
|
||||
|
||||
import boto.cloudformation.connection
|
||||
import json
|
||||
|
||||
|
||||
class Region:
|
||||
def __init__(self, region):
|
||||
self.name = region
|
||||
self.endpoint = 'cloudformation.%s.amazonaws.com' % region
|
||||
|
||||
|
||||
def boto_exception(err):
|
||||
|
||||
if hasattr(err, 'error_message'):
|
||||
error = err.error_message
|
||||
elif hasattr(err, 'message'):
|
||||
error = err.message
|
||||
else:
|
||||
error = '%s: %s' % (Exception, err)
|
||||
try:
|
||||
error_msg = json.loads(error)
|
||||
except:
|
||||
error_msg = {'Error': error}
|
||||
return error_msg
|
||||
|
||||
|
||||
def stack_operation(cfn, stack_name, operation):
|
||||
existed = []
|
||||
result = {}
|
||||
operation_complete = False
|
||||
while operation_complete == False:
|
||||
try:
|
||||
stack = cfn.describe_stacks(stack_name)[0]
|
||||
existed.append('yes')
|
||||
except:
|
||||
if 'yes' in existed:
|
||||
result = {'changed': True, 'output': 'Stack Deleted'}
|
||||
result['events'] = map(str, list(stack.describe_events()))
|
||||
else:
|
||||
result = {'changed': True, 'output': 'Stack Not Found'}
|
||||
break
|
||||
if '%s_COMPLETE' % operation == stack.stack_status:
|
||||
result['changed'] = True
|
||||
result['events'] = map(str, list(stack.describe_events()))
|
||||
result['output'] = 'Stack %s complete' % operation
|
||||
break
|
||||
elif '%s_FAILED' % operation == stack.stack_status:
|
||||
result['changed'] = False
|
||||
result['events'] = map(str, list(stack.describe_events()))
|
||||
result['output'] = 'Stack %s failed' % operation
|
||||
break
|
||||
else:
|
||||
time.sleep(5)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
stack_name=dict(required=True),
|
||||
template_parameters=dict(required=False),
|
||||
region=dict(required=True,
|
||||
choices=['ap-northeast-1', 'ap-southeast-1',
|
||||
'ap-southeast-2', 'eu-west-1',
|
||||
'sa-east-1', 'us-east-1', 'us-west-1',
|
||||
'us-west-2']),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
template=dict(default=None, required=True),
|
||||
disable_rollback=dict(default=False),
|
||||
wait_for=dict(default=True)
|
||||
)
|
||||
)
|
||||
|
||||
wait_for = module.params['wait_for']
|
||||
state = module.params['state']
|
||||
stack_name = module.params['stack_name']
|
||||
region = Region(module.params['region'])
|
||||
template_body = open(module.params['template'], 'r').read()
|
||||
disable_rollback = module.params['disable_rollback']
|
||||
template_parameters = module.params['template_parameters']
|
||||
|
||||
template_parameters_tup = [(k, v) for k, v in template_parameters.items()]
|
||||
stack_outputs = {}
|
||||
stack_outputs[module.params['region']] = {}
|
||||
stack_outputs[module.params['region']][stack_name] = {}
|
||||
|
||||
try:
|
||||
cfn = boto.cloudformation.connection.CloudFormationConnection(
|
||||
region=region)
|
||||
except boto.exception.NoAuthHandlerFound, e:
|
||||
module.fail_json(msg=str(e))
|
||||
update = False
|
||||
stack_events = []
|
||||
result = {}
|
||||
operation = None
|
||||
output = ''
|
||||
|
||||
if state == 'present':
|
||||
try:
|
||||
cfn.create_stack(stack_name, parameters=template_parameters_tup,
|
||||
template_body=template_body,
|
||||
disable_rollback=disable_rollback,
|
||||
capabilities=['CAPABILITY_IAM'])
|
||||
operation = 'CREATE'
|
||||
except Exception, err:
|
||||
error_msg = boto_exception(err)
|
||||
if error_msg['Error']['Code'] == 'AlreadyExistsException':
|
||||
update = True
|
||||
else:
|
||||
result = {'changed': False, 'output': error_msg}
|
||||
module.fail_json(**result)
|
||||
if not update:
|
||||
result = stack_operation(cfn, stack_name, operation)
|
||||
if update:
|
||||
try:
|
||||
cfn.update_stack(stack_name, parameters=template_parameters_tup,
|
||||
template_body=template_body,
|
||||
disable_rollback=disable_rollback,
|
||||
capabilities=['CAPABILITY_IAM'])
|
||||
operation = 'UPDATE'
|
||||
except Exception, err:
|
||||
error_msg = boto_exception(err)
|
||||
if error_msg['Error']['Message'] == 'No updates are to be performed.':
|
||||
output = error_msg['Error']['Message']
|
||||
result = {'changed': False, 'output': output}
|
||||
if operation == 'UPDATE':
|
||||
result = stack_operation(cfn, stack_name, operation)
|
||||
|
||||
if state == 'present' or update:
|
||||
stack = cfn.describe_stacks(stack_name)[0]
|
||||
for output in stack.outputs:
|
||||
stack_outputs[module.params['region']][stack_name][
|
||||
output.key] = output.value
|
||||
result['stack_outputs'] = stack_outputs
|
||||
|
||||
# absent state is different because of the way delete_stack works.
|
||||
# problem is it it doesn't give an error if stack isn't found
|
||||
# so must describe the stack first
|
||||
|
||||
if state == 'absent':
|
||||
try:
|
||||
cfn.describe_stacks(stack_name)
|
||||
operation = 'DELETE'
|
||||
except Exception, err:
|
||||
error_msg = boto_exception(err)
|
||||
result = {'changed': False, 'output': error_msg}
|
||||
module.fail_json(result)
|
||||
if operation == 'DELETE':
|
||||
cfn.delete_stack(stack_name)
|
||||
result = stack_operation(cfn, stack_name, operation)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# this is magic, see lib/ansible/module_common.py
|
||||
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
||||
|
||||
main()
|
Loading…
Reference in a new issue