src/main/java/com/digitalglobe/util/maven/plugin/CloudFormationDeployMavenPlugin.java
/**
     * Use this function to determine if an update to an existing stack will produce any changes.  If it will produce
     * changes, execute the change set.  If it doesn't produce any changes, don't execute the stack and inform the
     * user and audit of the the fact that there are no changes to be made.
     *
     * @param templateUrl is the URL to the template to deploy.
     * @param cfAsyncClient is the CloudFormation Client to use when inquiring about stacks and deploying changes.
     * @param stackName is the name of the stack to update.
     * @param parameters are the parameters to update the stack with.
     * @throws MojoExecutionException when a validation or logic error occurs.
     * @throws IOException when an error occurs trying to connect to the AWS API.
     */
Use this function to determine if an update to an existing stack will produce any changes.  If it will produce
changes, execute the change set.  If it doesn't produce any changes, don't execute the stack and inform the
user and audit of the the fact that there are no changes to be made.

@param templateUrl is the URL to the template to deploy.
@param cfAsyncClient is the CloudFormation Client to use when inquiring about stacks and deploying changes.
@param stackName is the name of the stack to update.
@param parameters are the parameters to update the stack with.
@throws MojoExecutionException when a validation or logic error occurs.
@throws IOException when an error occurs trying to connect to the AWS API.
Use this function to determine if an update to an existing stack will produce any changes . If it will produce changes execute the change set . If it doesn ' t produce any changes don ' t execute the stack and inform the user and audit of the the fact that there are no changes to be made . @param templateUrl is the URL to the template to deploy . @param cfAsyncClient is the CloudFormation Client to use when inquiring about stacks and deploying changes . @param stackName is the name of the stack to update . @param parameters are the parameters to update the stack with . @throws MojoExecutionException when a validation or logic error occurs . @throws IOException when an error occurs trying to connect to the AWS API .
private void DetectAndProcessStackChanges(String templateUrl, CloudFormationAsyncClient cfAsyncClient, String stackName,
                                              Parameter[] parameters) throws MojoExecutionException, IOException {
        boolean retry;
        String changeSetName = "N-" + UUID.randomUUID().toString();
        String changeSetToken = UUID.randomUUID().toString();
        CreateChangeSetRequest changeSetRequest;
        if(requiresIAM) {
            changeSetRequest = CreateChangeSetRequest.builder()
                    .parameters(parameters)
                    .stackName(stackName)
                    .changeSetType("UPDATE")
                    .templateURL(templateUrl)
                    .changeSetName(changeSetName)
                    .usePreviousTemplate(false)
                    .clientToken(changeSetToken)
                    .capabilities(Capability.CAPABILITY_NAMED_IAM)
                    .build();
        } else {
            changeSetRequest = CreateChangeSetRequest.builder()
                    .parameters(parameters)
                    .stackName(stackName)
                    .changeSetType("UPDATE")
                    .templateURL(templateUrl)
                    .changeSetName(changeSetName)
                    .usePreviousTemplate(false)
                    .clientToken(changeSetToken)
                    .build();
        }
        DescribeChangeSetResponse describeStacksResult = null;
        do {
            try {
                cfAsyncClient.createChangeSet(changeSetRequest).thenRunAsync(() ->
                    WaitChangeSet(cfAsyncClient, stackName, changeSetName)).join();
                changeSetToken = UUID.randomUUID().toString();
                DescribeChangeSetRequest describeChangeSetRequest = DescribeChangeSetRequest.builder()
                        .changeSetName(changeSetName)
                        .stackName(stackName)
                        .nextToken(changeSetToken)
                        .build();
                describeStacksResult = cfAsyncClient.describeChangeSet(describeChangeSetRequest).get();
                if((describeStacksResult.status() == ChangeSetStatus.FAILED)) {
                    if ((describeStacksResult.changes().size() <= 0) &&
                            describeStacksResult.statusReason()
                                    .startsWith("The submitted information didn't contain changes.")) {
                        DeleteChangeSet(cfAsyncClient, describeStacksResult.stackName(), describeStacksResult.changeSetName());
                        retry = false;
                    } else if (describeStacksResult.statusReason().contains("Rate exceeded")) {
                        retry = true;
                    } else {
                        throw new MojoExecutionException("ChangeSet Error: " + describeStacksResult.statusReason());
                    }
                } else retry = false;
            } catch (MojoExecutionException mojo) {
                throw mojo;
            } catch (Exception ex) {
                retry = isRetry(ex);
                if(!retry) throw new MojoExecutionException(ex.getMessage(), ex);
            }
        } while(retry);
        if ((describeStacksResult != null) && (describeStacksResult.changes().size() > 0)) {
            int retry_count = 0;
            do {
                try {
                    ExecuteChangeSetRequest executeChangeSetRequest = ExecuteChangeSetRequest.builder()
                            .changeSetName(changeSetName)
                            .stackName(stackName)
                            .clientRequestToken(changeSetToken)
                            .build();
                    cfAsyncClient.executeChangeSet(executeChangeSetRequest).get();
                    WaitStackInProgress(stackName, cfAsyncClient);
                    DescribeStacksResponse describeResponse = cfAsyncClient.describeStacks(DescribeStacksRequest.builder()
                            .stackName(stackName).build()).get();
                    if(describeResponse.stacks().size() == 1) {
                        switch(describeResponse.stacks().get(0).stackStatus()) {
                            case UPDATE_ROLLBACK_COMPLETE:
                            case UPDATE_ROLLBACK_FAILED:
                                String reason = (describeResponse.stacks() != null) && (describeResponse.stacks().size() == 1) &&
                                        (describeResponse.stacks().get(0).stackStatusReason() != null) ?
                                        "CloudFormation Error: " + describeResponse.stacks().get(0).stackStatusReason() :
                                        "See CloudFormation Console for errors.";
                                throw new MojoExecutionException("CloudFormation Error: " + reason);
                        }
                        retry = false;
                    } else {
                        if (retry_count == 2) throw new MojoExecutionException("Invalid response from stack update.");
                        retry = true;
                    }
                } catch (Exception ex) {
                    retry = isRetry(ex);
                }
                retry_count++;
            } while(retry);
            audit.write("Updated " + stackName + " with id: " +
                    describeStacksResult.stackId() + ".\n");
            audit.write("Stack Finished.\n");
            System.out.println("Stack Finished.");
        } else {
            audit.write("No changes to the Stack required.\n");
            System.out.println("No changes to the Stack required.");
        }
    }
private void DetectAndProcessStackChanges ( String templateUrl , CloudFormationAsyncClient cfAsyncClient , String stackName , Parameter [ ] parameters ) throws MojoExecutionException , IOException { boolean retry ; String changeSetName = "N-" + UUID . randomUUID ( ) . toString ( ) ; String changeSetToken = UUID . randomUUID ( ) . toString ( ) ; CreateChangeSetRequest changeSetRequest ; if ( requiresIAM ) { changeSetRequest = CreateChangeSetRequest . builder ( ) . parameters ( parameters ) . stackName ( stackName ) . changeSetType ( "UPDATE" ) . templateURL ( templateUrl ) . changeSetName ( changeSetName ) . usePreviousTemplate ( false ) . clientToken ( changeSetToken ) . capabilities ( Capability . CAPABILITY_NAMED_IAM ) . build ( ) ; } else { changeSetRequest = CreateChangeSetRequest . builder ( ) . parameters ( parameters ) . stackName ( stackName ) . changeSetType ( "UPDATE" ) . templateURL ( templateUrl ) . changeSetName ( changeSetName ) . usePreviousTemplate ( false ) . clientToken ( changeSetToken ) . build ( ) ; } DescribeChangeSetResponse describeStacksResult = null ; do { try { cfAsyncClient . createChangeSet ( changeSetRequest ) . thenRunAsync ( ( ) -> WaitChangeSet ( cfAsyncClient , stackName , changeSetName ) ) . join ( ) ; changeSetToken = UUID . randomUUID ( ) . toString ( ) ; DescribeChangeSetRequest describeChangeSetRequest = DescribeChangeSetRequest . builder ( ) . changeSetName ( changeSetName ) . stackName ( stackName ) . nextToken ( changeSetToken ) . build ( ) ; describeStacksResult = cfAsyncClient . describeChangeSet ( describeChangeSetRequest ) . get ( ) ; if ( ( describeStacksResult . status ( ) == ChangeSetStatus . FAILED ) ) { if ( ( describeStacksResult . changes ( ) . size ( ) <= 0 ) && describeStacksResult . statusReason ( ) . startsWith ( "The submitted information didn't contain changes." ) ) { DeleteChangeSet ( cfAsyncClient , describeStacksResult . stackName ( ) , describeStacksResult . changeSetName ( ) ) ; retry = false ; } else if ( describeStacksResult . statusReason ( ) . contains ( "Rate exceeded" ) ) { retry = true ; } else { throw new MojoExecutionException ( "ChangeSet Error: " + describeStacksResult . statusReason ( ) ) ; } } else retry = false ; } catch ( MojoExecutionException mojo ) { throw mojo ; } catch ( Exception ex ) { retry = isRetry ( ex ) ; if ( ! retry ) throw new MojoExecutionException ( ex . getMessage ( ) , ex ) ; } } while ( retry ) ; if ( ( describeStacksResult != null ) && ( describeStacksResult . changes ( ) . size ( ) > 0 ) ) { int retry_count = 0 ; do { try { ExecuteChangeSetRequest executeChangeSetRequest = ExecuteChangeSetRequest . builder ( ) . changeSetName ( changeSetName ) . stackName ( stackName ) . clientRequestToken ( changeSetToken ) . build ( ) ; cfAsyncClient . executeChangeSet ( executeChangeSetRequest ) . get ( ) ; WaitStackInProgress ( stackName , cfAsyncClient ) ; DescribeStacksResponse describeResponse = cfAsyncClient . describeStacks ( DescribeStacksRequest . builder ( ) . stackName ( stackName ) . build ( ) ) . get ( ) ; if ( describeResponse . stacks ( ) . size ( ) == 1 ) { switch ( describeResponse . stacks ( ) . get ( 0 ) . stackStatus ( ) ) { case UPDATE_ROLLBACK_COMPLETE : case UPDATE_ROLLBACK_FAILED : String reason = ( describeResponse . stacks ( ) != null ) && ( describeResponse . stacks ( ) . size ( ) == 1 ) && ( describeResponse . stacks ( ) . get ( 0 ) . stackStatusReason ( ) != null ) ? "CloudFormation Error: " + describeResponse . stacks ( ) . get ( 0 ) . stackStatusReason ( ) : "See CloudFormation Console for errors." ; throw new MojoExecutionException ( "CloudFormation Error: " + reason ) ; } retry = false ; } else { if ( retry_count == 2 ) throw new MojoExecutionException ( "Invalid response from stack update." ) ; retry = true ; } } catch ( Exception ex ) { retry = isRetry ( ex ) ; } retry_count ++ ; } while ( retry ) ; audit . write ( "Updated " + stackName + " with id: " + describeStacksResult . stackId ( ) + ".\n" ) ; audit . write ( "Stack Finished.\n" ) ; System . out . println ( "Stack Finished." ) ; } else { audit . write ( "No changes to the Stack required.\n" ) ; System . out . println ( "No changes to the Stack required." ) ; } }
Use this function to determine if an update to an existing stack will produce any changes.
Use this function to determine if an update to an existing stack will produce any changes .
// Process any changes