OrganizationManager.java
/*
* This file is part of the pl.wrzasq.lambda.
*
* @license http://mit-license.org/ The MIT license
* @copyright 2019 © by Rafał Wrzeszcz - Wrzasq.pl.
*/
package pl.wrzasq.lambda.cform.organization.service;
import com.amazonaws.SdkBaseException;
import com.amazonaws.services.organizations.AWSOrganizations;
import com.amazonaws.services.organizations.model.CreateOrganizationRequest;
import com.amazonaws.services.organizations.model.DeleteOrganizationRequest;
import com.amazonaws.services.organizations.model.DescribeOrganizationRequest;
import com.amazonaws.services.organizations.model.ListRootsRequest;
import com.amazonaws.services.organizations.model.Organization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.wrzasq.commons.aws.cloudformation.CustomResourceResponse;
import pl.wrzasq.lambda.cform.organization.model.OrganizationRequest;
import pl.wrzasq.lambda.cform.organization.model.OrganizationResponse;
/**
* Organizations API implementation.
*/
public class OrganizationManager {
/**
* Message pattern for drift case.
*/
private static final String DRIFT_LOG_MESSAGE_PATTERN
= "Organization ID {} differs from CloudFormation-provided physical resource ID {}.";
/**
* Logger.
*/
private Logger logger = LoggerFactory.getLogger(OrganizationManager.class);
/**
* AWS Organizations API client.
*/
private AWSOrganizations organizations;
/**
* Initializes object with given Organizations client.
*
* @param organizations AWS Organizations client.
*/
public OrganizationManager(AWSOrganizations organizations) {
this.organizations = organizations;
}
/**
* Handles organization creation.
*
* @param input Resource creation request.
* @param physicalResourceId Physical ID of existing resource (if present).
* @return Data about published version.
*/
public CustomResourceResponse<OrganizationResponse> sync(OrganizationRequest input, String physicalResourceId) {
Organization organization;
try {
organization = this.organizations.describeOrganization(
new DescribeOrganizationRequest()
)
.getOrganization();
// don't do anything here - in worst case it will fail in Delete call in UPDATE_COMPLETE_CLEANUP phase
// it will not hurt us (even if fails, as it's post-update phase) and allows to simplify logic of this
// method to avoid handling all combinations, which could lead to unhandled edge cases
if (!organization.getId().equals(physicalResourceId)) {
this.logger.warn(
OrganizationManager.DRIFT_LOG_MESSAGE_PATTERN,
organization.getId(),
physicalResourceId
);
}
this.logger.info("Organization already exists (ARN {}).", organization.getArn());
} catch (SdkBaseException error) {
this.logger.info("Exception occurred during organization data fetching, probably doesn't exist.", error);
organization = this.organizations.createOrganization(
new CreateOrganizationRequest()
.withFeatureSet(input.getFeatureSet())
)
.getOrganization();
this.logger.info("Created new organization, ARN {}.", organization.getArn());
}
var root = this.organizations.listRoots(new ListRootsRequest()).getRoots().get(0);
OrganizationResponse organizationResponse = new OrganizationResponse();
organizationResponse.setId(organization.getId());
organizationResponse.setArn(organization.getArn());
organizationResponse.setRootId(root.getId());
return new CustomResourceResponse<>(organizationResponse, organization.getId());
}
/**
* Handles organization deletion.
*
* @param input Resource delete request.
* @param physicalResourceId Physical ID of existing resource (if present).
* @return Empty response.
*/
public CustomResourceResponse<OrganizationResponse> delete(OrganizationRequest input, String physicalResourceId) {
var organization = this.organizations.describeOrganization(
new DescribeOrganizationRequest()
)
.getOrganization();
// avoid removing unknown data
if (!organization.getId().equals(physicalResourceId)) {
this.logger.error(
OrganizationManager.DRIFT_LOG_MESSAGE_PATTERN,
organization.getId(),
physicalResourceId
);
throw new IllegalStateException(
String.format(
"Can not delete Organization - ID %s doesn't match CloudFormation-provided resource ID %s.",
organization.getId(),
physicalResourceId
)
);
}
this.organizations.deleteOrganization(new DeleteOrganizationRequest());
this.logger.info("Organization deleted.");
return new CustomResourceResponse<>(null, physicalResourceId);
}
}