def apply(ctx, profile, config_file, environment, region, change_set_name, change_set_id): """ Apply a CloudFormation ChangeSet to create or update a CloudFormation stack. If using --change-set-name then --config --environment and --region are required. If using --change-set-id no other values are required (although --profile and --region may be needed). """ if not change_set_name and not change_set_id: raise click.UsageError( "Option '--change-set-name' or '--change-set-id' required.") try: if change_set_id: runner = create_changeset_runner(profile, region, change_set_id) runner.apply_change_set() else: if not config_file: raise click.UsageError( "Missing option '-c' / '--config-file'.") if not environment: raise click.UsageError( "Missing option '-e' / '--environment'.") cfg = load_config(config_file, ctx.obj.config, environment, False, ChangeSetName=change_set_name) runner = create_runner(profile, cfg) runner.apply_change_set() except (ValidationError, StackError) as e: error(f'\nError: {e}') exit(1)
def reject(ctx, profile, config_file, environment, region, change_set_name, change_set_id): """ Reject a CloudFormation ChangeSet, deleting the stack if in REVIEW_IN_PROGRESS status and has no other ChangeSets. If using --change-set-name then --config --environment and --region are required. If using --change-set-id no other values are required (although --profile and --region may be needed). """ if not change_set_name and not change_set_id: raise click.UsageError( "Option '--change-set-name' or '--change-set-id' required.") try: if change_set_id: runner = create_changeset_runner(profile, region, change_set_id) runner.reject_change_set() else: if not config_file: raise click.UsageError( "Missing option '-c' / '--config-file'.") if not environment: raise click.UsageError( "Missing option '-e' / '--environment'.") cfg = load_config(config_file, ctx.obj.config, environment, False, ChangeSetName=change_set_name) runner = create_runner(profile, cfg) runner.reject_change_set() except (ValidationError, StackError) as e: error(f'\nError: {e}') exit(1)
def delete(ctx, profile, config, environment, region, retain_resources): try: cfg = load_config(config, environment, region, None, None) runner = create_runner(profile, cfg, None, False) runner.delete(retain_resources) except (ValidationError, StackError) as e: error(f'\nError: {e}')
def apply(ctx, profile, config, environment, region, change_set_name): try: cfg = load_config(config, environment, region, None, None) runner = create_runner(profile, cfg, change_set_name, False) runner.execute_change_set() except (ValidationError, StackError) as e: error(f'\nError: {e}')
def delete(self, retain_resources=[]): """ Delete a Stack, optionally retraining certain resources. Waits for Stack to delete and prints Events if deletion fails :param list retain_resources: List of LogicalIds to retain :raises StackError: if deletion fails """ if not self.stack: raise ValidationError(f'Stack {self.config.stack_name} not found') if not StackStatus.is_deletable(self.stack): raise ValidationError(f'Stack {self.config.stack_name} is not in a deletable status: ' f'{StackStatus.get_status(self.stack).name}') info(f'\nDeleting Stack {self.config.stack_name}') last_timestamp = self.get_last_timestamp() try: self.client.delete_stack(StackName=self.config.stack_name, RetainResources=retain_resources) self.client.get_waiter('stack_delete_complete').wait( StackName=self.config.stack_name, WaiterConfig={'Delay': 10, 'MaxAttempts': 360}) info(f'\nDeletion of Stack {self.config.stack_name} successfully completed') self.stack = None except ClientError as ce: raise StackError(ce) except WaiterError as we: error(f'\nDeletion of Stack {self.config.stack_name} failed:\n') self.print_events(last_timestamp) raise StackError(we)
def execute_change_set(self): """ Execute a ChangeSet, waiting for execution to complete and printing the details of Stack Events caused by this ChangeSet :raises StackError: If there is an error executing the ChangeSet """ last_timestamp = self.get_last_timestamp() try: info(f'\nExecuting ChangeSet {self.change_set_name} for {self.config.stack_name}') self.client.execute_change_set(ChangeSetName=self.change_set_name, StackName=self.config.stack_name) waiter_name = 'stack_create_complete' if StackStatus.is_creatable(self.stack) else 'stack_update_complete' self.client.get_waiter(waiter_name).wait(StackName=self.config.stack_name, WaiterConfig={'Delay': 10, 'MaxAttempts': 360}) info(f'\nChangeSet {self.change_set_name} for {self.config.stack_name} successfully completed:\n') self.print_events(last_timestamp) except ClientError as ce: raise StackError(ce) except WaiterError as we: error(f'\nChangeSet {self.change_set_name} for {self.config.stack_name} failed:\n') self.print_events(last_timestamp) raise StackError(we)
def deploy(ctx, profile, config, environment, region, template, parameter, change_set_name, auto_approve): try: cfg = load_config(config, environment, region, template, parameter) runner = create_runner(profile, cfg, change_set_name, auto_approve) runner.deploy() except (ValidationError, StackError) as e: error(f'\nError: {e}')
def failed_change_set(self, last_timestamp): """ Print Stack Events on failed change set. Subclasses can override this to output errors in a different format. """ error( f'\nChangeSet {self.change_set_name} for {self.config.stack_name} failed:\n' ) self.print_events(last_timestamp)
def build_lambda(ctx, source_dir, output_dir, runtime, archive_name): """ Build a Lambda function zip file. Can be chained into the upload command where it pre-populates the --filename option. """ try: ctx.obj.zip_file = stackmanager.packager.build_lambda( source_dir, output_dir, runtime, archive_name) except (PackagingError, ValidationError) as e: error(f'\nError: {e}') exit(1)
def delete(ctx, profile, config_file, environment, region, retain_resources): """ Delete a CloudFormation stack. """ try: cfg = load_config(config_file, ctx.obj.config, environment, False) runner = create_runner(profile, cfg) runner.delete(retain_resources) except (ValidationError, StackError) as e: error(f'\nError: {e}') exit(1)
def get_output(ctx, profile, config_file, environment, region, output_key): """ Returns matching Output value if it exists. """ try: cfg = load_config(config_file, ctx.obj.config, environment, False) runner = create_runner(profile, cfg) output_value = runner.get_output(output_key) echo(output_value) except (ValidationError, StackError) as e: error(f'\nError: {e}') exit(1)
def status(ctx, profile, config_file, environment, region, event_days): """ Print current status of Stack. Includes pending ChangeSets and recent events. """ try: cfg = load_config(config_file, ctx.obj.config, environment, False) runner = create_runner(profile, cfg) runner.status(event_days) except (ValidationError, StackError) as e: error(f'\nError: {e}') exit(1)
def upload(ctx, profile, region, filename, bucket, key, bucket_parameter, key_parameter): """ Uploads a File to S3. This might be a large CloudFormation template, or a Lambda zip file. Can be chained into the deploy command where it pre-populates parameters for the uploaded file. """ if not filename and not ctx.obj.zip_file: raise click.UsageError("Missing option '-f' / '--filename'.") try: uploader = create_uploader(profile, region) uploader.upload(filename or ctx.obj.zip_file, bucket, key) ctx.obj.config.add_parameters({ bucket_parameter: bucket, key_parameter: key }) except (TransferError, ValidationError) as e: error(f'\nError: {e}') exit(1)
def deploy(ctx, profile, config_file, environment, region, template, parameter, parameter_use_previous, change_set_name, existing_changes, auto_apply): """ Create or update a CloudFormation stack using ChangeSets. """ try: cfg = load_config(config_file, ctx.obj.config, environment, Template=template, Parameters=parameter, PreviousParameters=parameter_use_previous, ChangeSetName=change_set_name, ExistingChanges=existing_changes, AutoApply=auto_apply) runner = create_runner(profile, cfg) runner.deploy() except (ValidationError, StackError) as e: error(f'\nError: {e}') exit(1)