/// <summary>
        /// Creates the stack and returns the stack ID
        /// </summary>
        /// <param name="deployment">The running deployment</param>
        /// <returns>The stack id</returns>
        private Task <string> CreateCloudFormation(RunningDeployment deployment, ICloudFormationRequestBuilder template)
        {
            Guard.NotNull(template, "template can not be null");

            return(WithAmazonServiceExceptionHandling(async() =>
            {
                var stackId = await clientFactory.CreateStackAsync(template.BuildCreateStackRequest());
                Log.Info($"Created stack {stackId} in region {awsEnvironmentGeneration.AwsRegion.SystemName}");
                return stackId;
            }));
        }
        /// <summary>
        /// Updates the stack and returns the stack ID
        /// </summary>
        /// <param name="stack">The stack name or id</param>
        /// <param name="deployment">The current deployment</param>
        /// <param name="template">The CloudFormation template</param>
        /// <returns>stackId</returns>
        private async Task <string> UpdateCloudFormation(
            RunningDeployment deployment,
            StackArn stack,
            ICloudFormationRequestBuilder template)
        {
            Guard.NotNull(deployment, "deployment can not be null");
            var deploymentStartTime = DateTime.Now;

            try
            {
                var result = await ClientHelpers.CreateCloudFormationClient(awsEnvironmentGeneration)
                             .UpdateStackAsync(template.BuildUpdateStackRequest());

                Log.Info(
                    $"Updated stack with id {result.StackId} in region {awsEnvironmentGeneration.AwsRegion.SystemName}");

                return(result.StackId);
            }
            catch (AmazonCloudFormationException ex)
            {
                // Some stack states indicate that we can delete the stack and start again. Otherwise we have some other
                // exception that needs to be dealt with.
                if (!(await StackMustBeDeleted(stack)).SelectValueOrDefault(x => x))
                {
                    // Is this an unrecoverable state, or just a stack that has nothing to update?
                    if (DealWithUpdateException(ex))
                    {
                        // There was nothing to update, but we return the id for consistency anyway
                        var result = await QueryStackAsync(clientFactory, stack);

                        return(result.StackId);
                    }
                }

                // If the stack exists, is in a ROLLBACK_COMPLETE state, and was never successfully
                // created in the first place, we can end up here. In this case we try to create
                // the stack from scratch.
                await DeleteCloudFormation(stack);

                await clientFactory.WaitForStackToComplete(CloudFormationDefaults.StatusWaitPeriod, stack, LogAndThrowRollbacks(clientFactory, stack, false, filter : FilterStackEventsSince(deploymentStartTime)));

                return(await CreateCloudFormation(deployment, template));
            }
            catch (AmazonServiceException ex)
            {
                LogAmazonServiceException(ex);
                throw;
            }
        }
        /// <summary>
        /// Update or create the stack
        /// </summary>
        /// <param name="deployment">The current deployment</param>
        /// <param name="stack"></param>
        /// <param name="template"></param>
        private async Task DeployStack(RunningDeployment deployment, StackArn stack, ICloudFormationRequestBuilder template, DateTime deploymentStartTime)
        {
            Guard.NotNull(deployment, "deployment can not be null");

            var stackId = await template.Inputs
                          // Use the parameters to either create or update the stack
                          .Map(async parameters => await StackExists(stack, StackStatus.DoesNotExist) != StackStatus.DoesNotExist
                               ?await UpdateCloudFormation(deployment, stack, template)
                               : await CreateCloudFormation(deployment, template));

            if (waitForComplete)
            {
                await clientFactory.WaitForStackToComplete(CloudFormationDefaults.StatusWaitPeriod, stack, LogAndThrowRollbacks(clientFactory, stack, filter : FilterStackEventsSince(deploymentStartTime)));
            }

            // Take the stack ID returned by the create or update events, and save it as an output variable
            Log.SetOutputVariable("AwsOutputs[StackId]", stackId ?? "", deployment.Variables);
            Log.Info(
                $"Saving variable \"Octopus.Action[{deployment.Variables["Octopus.Action.Name"]}].Output.AwsOutputs[StackId]\"");
        }
        private async Task DeployCloudFormation(RunningDeployment deployment, StackArn stack, ICloudFormationRequestBuilder template)
        {
            Guard.NotNull(deployment, "deployment can not be null");

            var deploymentStartTime = DateTime.Now;

            await clientFactory.WaitForStackToComplete(CloudFormationDefaults.StatusWaitPeriod, stack, LogAndThrowRollbacks(clientFactory, stack, false, filter : FilterStackEventsSince(deploymentStartTime)));

            await DeployStack(deployment, stack, template, deploymentStartTime);
        }