private async Task <string> CreateChangeSet(DeployContext context, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var parameters = context.Parameters .Select((entry) => new Parameter { ParameterKey = entry.Key, ParameterValue = entry.Value }) .ToList(); var tags = context.Tags .Select((entry) => new Tag { Key = entry.Key, Value = entry.Value }) .ToList(); var request = new CreateChangeSetRequest { StackName = context.StackName, ChangeSetName = context.ChangeSetName, ChangeSetType = await GetChangeSetType(context, cancellationToken), Capabilities = context.Capabilities, TemplateURL = context.TemplateURL, Parameters = parameters, Tags = tags, }; var response = await cloudformation.CreateChangeSetAsync(request, cancellationToken); return(response.StackId); }
private Amazon.CloudFormation.Model.CreateChangeSetResponse CallAWSServiceOperation(IAmazonCloudFormation client, Amazon.CloudFormation.Model.CreateChangeSetRequest request) { Utils.Common.WriteVerboseEndpointMessage(this, client.Config, "AWS CloudFormation", "CreateChangeSet"); try { #if DESKTOP return(client.CreateChangeSet(request)); #elif CORECLR return(client.CreateChangeSetAsync(request).GetAwaiter().GetResult()); #else #error "Unknown build edition" #endif } catch (AmazonServiceException exc) { var webException = exc.InnerException as System.Net.WebException; if (webException != null) { throw new Exception(Utils.Common.FormatNameResolutionFailureMessage(client.Config, webException.Message), webException); } throw; } }
public static async Task <DeployStackResult> DeployStackAsync(ILogger log, IAmazonCloudFormation cloudformation, long ticks, string stackName, List <Tag> tags, Dictionary <string, string> parameters, string templateBody = null, Uri templateUrl = null, string roleArn = null) { log.Information("Checking stack: {stackName}", stackName); Stack existingStack = await GetExistingStackAsync(cloudformation, stackName).ConfigureAwait(false); log.Information("Found existing stack: " + (existingStack != null)); var changeSetName = stackName + "-" + ticks; var param = parameters.Select(x => new Parameter { ParameterKey = x.Key, ParameterValue = x.Value }).ToList(); // Determine if the stack is in a good state to be updated. ChangeSetType changeSetType; if (existingStack == null || existingStack.StackStatus == StackStatus.REVIEW_IN_PROGRESS || existingStack.StackStatus == StackStatus.DELETE_COMPLETE) { changeSetType = ChangeSetType.CREATE; } // If the state was ROLLBACK_COMPLETE that means the stack failed on initial creation and the resources where cleaned up. // It is safe to delete the stack so we can recreate it. else if (existingStack.StackStatus == StackStatus.ROLLBACK_COMPLETE) { await DeleteRollbackCompleteStackAsync(log, cloudformation, existingStack).ConfigureAwait(false); changeSetType = ChangeSetType.CREATE; } // If the status was ROLLBACK_IN_PROGRESS that means the initial creation is failing. // Wait to see if it goes into ROLLBACK_COMPLETE status meaning everything got cleaned up and then delete it. else if (existingStack.StackStatus == StackStatus.ROLLBACK_IN_PROGRESS) { existingStack = await WaitForNoLongerInProgress(log, cloudformation, existingStack.StackName).ConfigureAwait(false); if (existingStack != null && existingStack.StackStatus == StackStatus.ROLLBACK_COMPLETE) { await DeleteRollbackCompleteStackAsync(log, cloudformation, existingStack).ConfigureAwait(false); } changeSetType = ChangeSetType.CREATE; } // If the status was DELETE_IN_PROGRESS then just wait for delete to complete else if (existingStack.StackStatus == StackStatus.DELETE_IN_PROGRESS) { await WaitForNoLongerInProgress(log, cloudformation, existingStack.StackName).ConfigureAwait(false); changeSetType = ChangeSetType.CREATE; } // The Stack state is in a normal state and ready to be updated. else if (existingStack.StackStatus == StackStatus.CREATE_COMPLETE || existingStack.StackStatus == StackStatus.UPDATE_COMPLETE || existingStack.StackStatus == StackStatus.UPDATE_ROLLBACK_COMPLETE) { changeSetType = ChangeSetType.UPDATE; } // All other states means the Stack is in an inconsistent state. else { log.Error($"The stack's current state of {existingStack.StackStatus} is invalid for updating"); return(DeployStackResult.Failed); } CreateChangeSetResponse changeSetResponse; try { var changeSetRequest = new CreateChangeSetRequest { StackName = stackName, ChangeSetName = changeSetName, ChangeSetType = changeSetType, Capabilities = new List <string> { CAPABILITY_NAMED_IAM }, Tags = tags, Parameters = param, RoleARN = roleArn }; if (templateUrl != null) { changeSetRequest.TemplateURL = templateUrl.ToString(); } else { changeSetRequest.TemplateBody = templateBody; } // Create the change set which performs the transformation on the Serverless resources in the template. changeSetResponse = await cloudformation.CreateChangeSetAsync(changeSetRequest).ConfigureAwait(false); log.Information("CloudFormation change set created. StackName: {stackName}, ChangeSetName: {changeSetName}", stackName, changeSetName); } catch (Exception e) { log.Error(e, "Error creating cloudformation change set"); throw; } // The change set can take a few seconds to be reviewed and be ready to be executed. DeployStackResult waitResult = await WaitForChangeSetBeingAvailableAsync(log, cloudformation, changeSetResponse.Id).ConfigureAwait(false); if (waitResult == DeployStackResult.NoChanges || waitResult == DeployStackResult.Failed) { return(waitResult); } var executeChangeSetRequest = new ExecuteChangeSetRequest { StackName = stackName, ChangeSetName = changeSetResponse.Id }; // Execute the change set. var timeChangeSetExecuted = DateTime.Now; try { await cloudformation.ExecuteChangeSetAsync(executeChangeSetRequest).ConfigureAwait(false); if (changeSetType == ChangeSetType.CREATE) { log.Information($"Created CloudFormation stack {stackName}"); } else { log.Information($"Initiated CloudFormation stack update on {stackName}"); } } catch (Exception e) { log.Error(e, "Error executing cloudformation change set"); throw; } // Wait for the stack to finish var updatedStack = await WaitStackToCompleteAsync(log, cloudformation, stackName, timeChangeSetExecuted).ConfigureAwait(false); if (updatedStack.StackStatus == StackStatus.CREATE_COMPLETE || updatedStack.StackStatus == StackStatus.UPDATE_COMPLETE) { log.Information($"Stack finished updating with status: {updatedStack.StackStatus}"); // Display the output parameters. DisplayOutputs(log, updatedStack); } else { log.Error($"Stack update failed with status: {updatedStack.StackStatus} ({updatedStack.StackStatusReason})"); return(DeployStackResult.Failed); } return(DeployStackResult.Ok); }