|
60 | 60 | "arn:aws:iam::aws:policy/AmazonRoute53FullAccess",
|
61 | 61 | "arn:aws:iam::aws:policy/AmazonS3FullAccess",
|
62 | 62 | "arn:aws:iam::aws:policy/IAMFullAccess",
|
63 |
| - "arn:aws:iam::aws:policy/AmazonVPCFullAccess" |
| 63 | + "arn:aws:iam::aws:policy/AmazonVPCFullAccess", |
| 64 | + "arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess" |
64 | 65 | ],
|
65 | 66 | "Path": "/"
|
66 | 67 | }
|
|
79 | 80 | "AddRoleToInstance": {
|
80 | 81 | "Description": "Add LabIdeRole to Cloud9 IDE Instance",
|
81 | 82 | "Type": "Custom::AddRoleToInstance",
|
82 |
| - "DependsOn": ["AddRoleToInstanceFunction"], |
| 83 | + "DependsOn": [ |
| 84 | + "AddRoleToInstanceFunction", |
| 85 | + "KopsStateStore" |
| 86 | + ], |
83 | 87 | "Properties": {
|
84 | 88 | "ServiceToken": {
|
85 | 89 | "Fn::GetAtt": [
|
|
104 | 108 | "LabIdeInstanceProfile",
|
105 | 109 | "Arn"
|
106 | 110 | ]
|
| 111 | + }, |
| 112 | + "BucketName": { |
| 113 | + "Ref": "KopsStateStore" |
107 | 114 | }
|
108 | 115 | }
|
109 | 116 | },
|
|
115 | 122 | "Fn::Join": [
|
116 | 123 | "\n",
|
117 | 124 | [
|
| 125 | + "from __future__ import print_function", |
118 | 126 | "import boto3",
|
| 127 | + "import logging", |
| 128 | + "import json", |
119 | 129 | "import time",
|
120 | 130 | "import traceback",
|
121 | 131 | "import cfnresponse",
|
122 | 132 | "",
|
| 133 | + "logger = logging.getLogger()", |
| 134 | + "logger.setLevel(logging.INFO)", |
| 135 | + "", |
123 | 136 | "def handler(event, context):",
|
| 137 | + " logger.debug('Event: {}'.format(event))", |
| 138 | + " logger.debug('Context: {}'.format(context))", |
124 | 139 | " responseData = {}",
|
125 | 140 | " ",
|
126 |
| - " # Immediately respond on Delete (no work to be done)", |
| 141 | + " # Immediately respond on Delete", |
127 | 142 | " if event['RequestType'] == 'Delete':",
|
128 |
| - " cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID')", |
| 143 | + " # Empty Bucket before CloudFormation deletes it", |
| 144 | + " session = boto3.Session()", |
| 145 | + " s3 = session.resource(service_name='s3')", |
| 146 | + " try:", |
| 147 | + " bucket = s3.Bucket(event['ResourceProperties']['BucketName'])", |
| 148 | + " bucket.object_versions.delete()", |
| 149 | + " ", |
| 150 | + " logger.info('Bucket '+event['ResourceProperties']['BucketName']+' objects/versions deleted.')", |
| 151 | + " cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID')", |
| 152 | + " except Exception as e:", |
| 153 | + " logger.error(e, exc_info=True)", |
| 154 | + " responseData = {'Error': traceback.format_exc(e)}", |
| 155 | + " cfnresponse.send(event, context, cfnresponse.FAILED, responseData, 'CustomResourcePhysicalID')", |
129 | 156 | " ",
|
130 |
| - " try:", |
131 |
| - " # Open AWS clients", |
132 |
| - " ec2 = boto3.client('ec2')", |
133 |
| - " ", |
134 |
| - " # Get the InstanceId of the Cloud9 IDE", |
135 |
| - " instance = ec2.describe_instances(Filters=[{'Name': 'tag:Name','Values': ['aws-cloud9-'+event['ResourceProperties']['StackName']+'-'+event['ResourceProperties']['EnvironmentId']]}])['Reservations'][0]['Instances'][0]", |
136 |
| - " ", |
137 |
| - " # Create the IamInstanceProfile request object", |
138 |
| - " iam_instance_profile = {", |
139 |
| - " 'Arn': event['ResourceProperties']['LabIdeInstanceProfileArn'],", |
140 |
| - " 'Name': event['ResourceProperties']['LabIdeInstanceProfileName']", |
141 |
| - " }", |
142 |
| - " ", |
143 |
| - " # Wait for Instance to become ready before adding Role", |
144 |
| - " instance_state = instance['State']['Name']", |
145 |
| - " while instance_state != 'running':", |
146 |
| - " time.sleep(5)", |
147 |
| - " instance_state = ec2.describe_instances(InstanceIds=[instance['InstanceId']])", |
148 |
| - " ec2.associate_iam_instance_profile(IamInstanceProfile=iam_instance_profile, InstanceId=instance['InstanceId'])", |
149 |
| - " ", |
150 |
| - " responseData = {'Success': 'Role added to instance'+instance['InstanceId']+'.'}", |
151 |
| - " cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID')", |
152 |
| - " except Exception as e:", |
153 |
| - " responseData = {'Error': traceback.format_exc(e)}", |
154 |
| - " cfnresponse.send(event, context, cfnresponse.FAILED, responseData, 'CustomResourcePhysicalID')" |
| 157 | + " if event['RequestType'] == 'Create':", |
| 158 | + " try:", |
| 159 | + " # Open AWS clients", |
| 160 | + " ec2 = boto3.client('ec2')", |
| 161 | + " ", |
| 162 | + " # Get the InstanceId of the Cloud9 IDE", |
| 163 | + " instance = ec2.describe_instances(Filters=[{'Name': 'tag:Name','Values': ['aws-cloud9-'+event['ResourceProperties']['StackName']+'-'+event['ResourceProperties']['EnvironmentId']]}])['Reservations'][0]['Instances'][0]", |
| 164 | + " ", |
| 165 | + " # Create the IamInstanceProfile request object", |
| 166 | + " iam_instance_profile = {", |
| 167 | + " 'Arn': event['ResourceProperties']['LabIdeInstanceProfileArn'],", |
| 168 | + " 'Name': event['ResourceProperties']['LabIdeInstanceProfileName']", |
| 169 | + " }", |
| 170 | + " ", |
| 171 | + " # Wait for Instance to become ready before adding Role", |
| 172 | + " instance_state = instance['State']['Name']", |
| 173 | + " while instance_state != 'running':", |
| 174 | + " time.sleep(5)", |
| 175 | + " instance_state = ec2.describe_instances(InstanceIds=[instance['InstanceId']])", |
| 176 | + " ec2.associate_iam_instance_profile(IamInstanceProfile=iam_instance_profile, InstanceId=instance['InstanceId'])", |
| 177 | + " ", |
| 178 | + " responseData = {'Success': 'Role added to instance'+instance['InstanceId']+'.'}", |
| 179 | + " cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID')", |
| 180 | + " except Exception as e:", |
| 181 | + " logger.error(e, exc_info=True)", |
| 182 | + " responseData = {'Error': traceback.format_exc(e)}", |
| 183 | + " cfnresponse.send(event, context, cfnresponse.FAILED, responseData, 'CustomResourcePhysicalID')" |
155 | 184 | ]
|
156 | 185 | ]
|
157 | 186 | }
|
|
216 | 245 | "iam:PassRole"
|
217 | 246 | ],
|
218 | 247 | "Resource": "*"
|
| 248 | + }, |
| 249 | + { |
| 250 | + "Effect": "Allow", |
| 251 | + "Action": [ |
| 252 | + "s3:*" |
| 253 | + ], |
| 254 | + "Resource": [ |
| 255 | + { |
| 256 | + "Fn::GetAtt": [ |
| 257 | + "KopsStateStore", |
| 258 | + "Arn" |
| 259 | + ] |
| 260 | + }, |
| 261 | + { |
| 262 | + "Fn::Join": [ |
| 263 | + "", |
| 264 | + [ |
| 265 | + { |
| 266 | + "Fn::GetAtt": [ |
| 267 | + "KopsStateStore", |
| 268 | + "Arn" |
| 269 | + ] |
| 270 | + }, |
| 271 | + "/*" |
| 272 | + ] |
| 273 | + ] |
| 274 | + } |
| 275 | + ] |
219 | 276 | }
|
220 | 277 | ]
|
221 | 278 | }
|
|
225 | 282 | }
|
226 | 283 | },
|
227 | 284 | "Outputs": {
|
228 |
| - "LabIdeUrl": { |
| 285 | + "Cloud9IDE": { |
229 | 286 | "Value": {
|
230 | 287 | "Fn::Join": [
|
231 | 288 | "",
|
|
237 | 294 | ]
|
238 | 295 | ]
|
239 | 296 | }
|
240 |
| - }, |
241 |
| - "KOPSSTATESTORE": { |
242 |
| - "Value": { "Ref": "KopsStateStore" } |
243 |
| - } |
| 297 | + } |
244 | 298 | }
|
245 | 299 | }
|
0 commit comments