diff --git a/.doc_gen/metadata/controltower_metadata.yaml b/.doc_gen/metadata/controltower_metadata.yaml index 083079b4db1..01f3fcfc2ab 100644 --- a/.doc_gen/metadata/controltower_metadata.yaml +++ b/.doc_gen/metadata/controltower_metadata.yaml @@ -4,6 +4,15 @@ controltower_Hello: synopsis: get started using &CTower;. category: Hello languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.hello.main Python: versions: - sdk_version: 3 @@ -25,6 +34,15 @@ controltower_Hello: controltower_ListBaselines: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.list_baselines.main Python: versions: - sdk_version: 3 @@ -47,6 +65,15 @@ controltower_ListBaselines: controltower_ListEnabledBaselines: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.list_enabled_baselines.main Python: versions: - sdk_version: 3 @@ -69,6 +96,15 @@ controltower_ListEnabledBaselines: controltower_EnableBaseline: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.enable_baseline.main Python: versions: - sdk_version: 3 @@ -91,6 +127,15 @@ controltower_EnableBaseline: controltower_ResetEnabledBaseline: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.reset_enabled_baseline.main Python: versions: - sdk_version: 3 @@ -113,6 +158,15 @@ controltower_ResetEnabledBaseline: controltower_DisableBaseline: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.disable_baseline.main Python: versions: - sdk_version: 3 @@ -135,6 +189,15 @@ controltower_DisableBaseline: controltower_ListEnabledControls: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.list_enabled_controls.main Python: versions: - sdk_version: 3 @@ -157,6 +220,15 @@ controltower_ListEnabledControls: controltower_EnableControl: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.enable_control.main Python: versions: - sdk_version: 3 @@ -179,6 +251,15 @@ controltower_EnableControl: controltower_GetControlOperation: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.get_control_operation.main Python: versions: - sdk_version: 3 @@ -201,6 +282,15 @@ controltower_GetControlOperation: controltower_DisableControl: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.disable_control.main Python: versions: - sdk_version: 3 @@ -223,6 +313,15 @@ controltower_DisableControl: controltower_ListLandingZones: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.list_landing_zones.main Python: versions: - sdk_version: 3 @@ -245,6 +344,15 @@ controltower_ListLandingZones: controltower_GetBaselineOperation: languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: + snippet_tags: + - controltower.java2.list_landing_zones.main Python: versions: - sdk_version: 3 @@ -272,6 +380,16 @@ controltower_Scenario: - List, enable, get, and disable controls. category: Basics languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/controltower + sdkguide: + excerpts: + - description: Run an interactive scenario demonstrating &CTowerlong; features. + snippet_tags: + - controltower.java2.controltower_scenario.main + - controltower.java2.controltower_actions.main Python: versions: - sdk_version: 3 diff --git a/javav2/example_code/controltower/README.md b/javav2/example_code/controltower/README.md new file mode 100644 index 00000000000..09d4fcc4a63 --- /dev/null +++ b/javav2/example_code/controltower/README.md @@ -0,0 +1,119 @@ +# AWS Control Tower code examples for the SDK for Java 2.x + +## Overview + +Shows how to use the AWS SDK for Java 2.x to work with AWS Control Tower. + + + + +_AWS Control Tower enables you to enforce and manage governance rules for security, operations, and compliance at scale across all your organizations and accounts._ + +## ⚠ Important + +* Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/). +* Running the tests might result in charges to your AWS account. +* We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege). +* This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services). + + + + +## Code examples + +### Prerequisites + +For prerequisites, see the [README](../../README.md#Prerequisites) in the `javav2` folder. + + + + + +### Get started + +- [Hello AWS Control Tower](src/main/java/com/example/controltower/HelloControlTower.java#L28) (`ListBaselines`) + + +### Basics + +Code examples that show you how to perform the essential operations within a service. + +- [Learn the basics](src/main/java/com/example/controltower/ControlTowerActions.java) + + +### Single actions + +Code excerpts that show you how to call individual service functions. + +- [DisableBaseline](src/main/java/com/example/controltower/ControlTowerActions.java#L241) +- [DisableControl](src/main/java/com/example/controltower/ControlTowerActions.java#L431) +- [EnableBaseline](src/main/java/com/example/controltower/ControlTowerActions.java#L188) +- [EnableControl](src/main/java/com/example/controltower/ControlTowerActions.java#L377) +- [GetBaselineOperation](src/main/java/com/example/controltower/ControlTowerActions.java#L38) +- [GetControlOperation](src/main/java/com/example/controltower/ControlTowerActions.java#L474) +- [ListBaselines](src/main/java/com/example/controltower/ControlTowerActions.java#L88) +- [ListEnabledBaselines](src/main/java/com/example/controltower/ControlTowerActions.java#L138) +- [ListEnabledControls](src/main/java/com/example/controltower/ControlTowerActions.java#L324) +- [ListLandingZones](src/main/java/com/example/controltower/ControlTowerActions.java#L38) +- [ResetEnabledBaseline](src/main/java/com/example/controltower/ControlTowerActions.java#L548) + + + + + +## Run the examples + +### Instructions + + + + + +#### Hello AWS Control Tower + +This example shows you how to get started using AWS Control Tower. + + +#### Learn the basics + +This example shows you how to do the following: + +- List landing zones. +- List, enable, get, reset, and disable baselines. +- List, enable, get, and disable controls. + + + + + + + + + +### Tests + +⚠ Running tests might result in charges to your AWS account. + + +To find instructions for running these tests, see the [README](../../README.md#Tests) +in the `javav2` folder. + + + + + + +## Additional resources + +- [AWS Control Tower User Guide](https://docs.aws.amazon.com/controltower/latest/userguide/what-is-control-tower.html) +- [AWS Control Tower API Reference](https://docs.aws.amazon.com/controltower/latest/APIReference/Welcome.html) +- [SDK for Java 2.x AWS Control Tower reference](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/controltower/package-summary.html) + + + + +--- + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 diff --git a/javav2/example_code/controltower/pom.xml b/javav2/example_code/controltower/pom.xml new file mode 100644 index 00000000000..a7f0363c5a6 --- /dev/null +++ b/javav2/example_code/controltower/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + aws.example + controltower + 1.0-SNAPSHOT + + 17 + 17 + UTF-8 + + + + + software.amazon.awssdk + bom + 2.28.11 + pom + import + + + + + + software.amazon.awssdk + controltower + + + software.amazon.awssdk + controlcatalog + + + org.slf4j + slf4j-api + 2.0.7 + + + ch.qos.logback + logback-classic + 1.4.8 + + + software.amazon.awssdk + organizations + + + + software.amazon.awssdk + url-connection-client + 2.36.2 + compile + + + software.amazon.awssdk + sdk-core + + + org.junit.jupiter + junit-jupiter-engine + 5.10.0 + test + + + org.junit.jupiter + junit-jupiter-api + 5.10.0 + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 17 + 17 + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + com.example.controltower.ControlTowerScenario + us-east-1 + + + + + \ No newline at end of file diff --git a/javav2/example_code/controltower/src/main/java/com/example/controltower/ControlTowerActions.java b/javav2/example_code/controltower/src/main/java/com/example/controltower/ControlTowerActions.java new file mode 100644 index 00000000000..a41a9c6ba46 --- /dev/null +++ b/javav2/example_code/controltower/src/main/java/com/example/controltower/ControlTowerActions.java @@ -0,0 +1,586 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.controltower; + +import software.amazon.awssdk.services.controltower.ControlTowerClient; +import software.amazon.awssdk.services.controltower.model.*; +import software.amazon.awssdk.services.controltower.paginators.ListBaselinesIterable; +import software.amazon.awssdk.services.controltower.paginators.ListEnabledBaselinesIterable; +import software.amazon.awssdk.services.controltower.paginators.ListEnabledControlsIterable; +import software.amazon.awssdk.services.controltower.paginators.ListLandingZonesIterable; +import software.amazon.awssdk.services.controlcatalog.ControlCatalogClient; +import software.amazon.awssdk.services.controlcatalog.paginators.ListControlsIterable; +import software.amazon.awssdk.core.exception.SdkException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.ArrayList; + +/** + * Before running this Java V2 code example, set up your development + * environment, including your credentials. + * + * For more information, see the following documentation topic: + * + * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html + * + * This Java code example shows how to perform AWS Control Tower operations. + */ + +// snippet-start:[controltower.java2.controltower_actions.main] +public class ControlTowerActions { + + // snippet-start:[controltower.java2.list_landing_zones.main] + /** + * Lists all landing zones using pagination to retrieve complete results. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @return a list of all landing zones + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static List listLandingZones(ControlTowerClient controlTowerClient) { + try { + List landingZones = new ArrayList<>(); + + String nextToken = null; + + do { + ListLandingZonesRequest.Builder requestBuilder = ListLandingZonesRequest.builder(); + if (nextToken != null) { + requestBuilder.nextToken(nextToken); + } + + ListLandingZonesResponse response = controlTowerClient.listLandingZones(requestBuilder.build()); + + if (response.landingZones() != null) { + landingZones.addAll(response.landingZones()); + } + + nextToken = response.nextToken(); + } while (nextToken != null); + + System.out.format("Retrieved {} landing zones", landingZones.size()); + return landingZones; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "AccessDeniedException": + System.out.format("Access denied when listing landing zones: {}", e.getMessage()); + break; + default: + System.out.format("Error listing landing zones: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error listing landing zones: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.list_landing_zones.main] + + // snippet-start:[controltower.java2.list_baselines.main] + /** + * Lists all available baselines using pagination to retrieve complete results. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @return a list of all baselines + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static List listBaselines(ControlTowerClient controlTowerClient) { + try { + List baselines = new ArrayList<>(); + + String nextToken = null; + + do { + ListBaselinesRequest.Builder requestBuilder = ListBaselinesRequest.builder(); + if (nextToken != null) { + requestBuilder.nextToken(nextToken); + } + + ListBaselinesResponse response = controlTowerClient.listBaselines(requestBuilder.build()); + + if (response.baselines() != null) { + baselines.addAll(response.baselines()); + } + + nextToken = response.nextToken(); + } while (nextToken != null); + + System.out.format("Retrieved {} baselines", baselines.size()); + return baselines; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "AccessDeniedException": + System.out.format("Access denied when listing baselines: {}", e.getMessage()); + break; + default: + System.out.format("Error listing baselines: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error listing baselines: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.list_baselines.main] + + // snippet-start:[controltower.java2.list_enabled_baselines.main] + /** + * Lists all enabled baselines using pagination to retrieve complete results. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @return a list of all enabled baselines + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static List listEnabledBaselines(ControlTowerClient controlTowerClient) { + try { + List enabledBaselines = new ArrayList<>(); + + String nextToken = null; + + do { + ListEnabledBaselinesRequest.Builder requestBuilder = ListEnabledBaselinesRequest.builder(); + if (nextToken != null) { + requestBuilder.nextToken(nextToken); + } + + ListEnabledBaselinesResponse response = controlTowerClient.listEnabledBaselines(requestBuilder.build()); + + if (response.enabledBaselines() != null) { + enabledBaselines.addAll(response.enabledBaselines()); + } + + nextToken = response.nextToken(); + } while (nextToken != null); + + System.out.format("Retrieved {} enabled baselines", enabledBaselines.size()); + return enabledBaselines; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "ResourceNotFoundException": + System.out.format("Target not found when listing enabled baselines: {}", e.getMessage()); + break; + default: + System.out.format("Error listing enabled baselines: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error listing enabled baselines: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.list_enabled_baselines.main] + + // snippet-start:[controltower.java2.enable_baseline.main] + /** + * Enables a baseline for a specified target. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @param baselineIdentifier the identifier of the baseline to enable + * @param baselineVersion the version of the baseline to enable + * @param targetIdentifier the identifier of the target (e.g., OU ARN) + * @return the operation identifier + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static String enableBaseline(ControlTowerClient controlTowerClient, + String baselineIdentifier, + String baselineVersion, + String targetIdentifier) { + try { + EnableBaselineRequest request = EnableBaselineRequest.builder() + .baselineIdentifier(baselineIdentifier) + .baselineVersion(baselineVersion) + .targetIdentifier(targetIdentifier) + .build(); + + EnableBaselineResponse response = controlTowerClient.enableBaseline(request); + String operationId = response.operationIdentifier(); + + System.out.format("Enabled baseline with operation ID: {}", operationId); + return operationId; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "ValidationException": + if (e.getMessage().contains("already enabled")) { + System.out.format("Baseline is already enabled for this target"); + return null; + } + System.out.format("Validation error enabling baseline: {}", e.getMessage()); + break; + case "ConflictException": + System.out.format("Conflict enabling baseline: {}", e.getMessage()); + break; + default: + System.out.format("Error enabling baseline: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error enabling baseline: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.enable_baseline.main] + + // snippet-start:[controltower.java2.disable_baseline.main] + /** + * Disables a baseline for a specified target. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @param enabledBaselineIdentifier the identifier of the enabled baseline to disable + * @return the operation identifier + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static String disableBaseline(ControlTowerClient controlTowerClient, + String enabledBaselineIdentifier) { + try { + DisableBaselineRequest request = DisableBaselineRequest.builder() + .enabledBaselineIdentifier(enabledBaselineIdentifier) + .build(); + + DisableBaselineResponse response = controlTowerClient.disableBaseline(request); + String operationId = response.operationIdentifier(); + + System.out.format("Disabled baseline with operation ID: {}", operationId); + return operationId; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "ConflictException": + System.out.format("Conflict disabling baseline: {}", e.getMessage()); + break; + case "ResourceNotFoundException": + System.out.format("Baseline not found for disabling: {}", e.getMessage()); + break; + default: + System.out.format("Error disabling baseline: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error disabling baseline: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.disable_baseline.main] + + // snippet-start:[controltower.java2.get_baseline_operation.main] + /** + * Gets the status of a baseline operation. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @param operationIdentifier the identifier of the operation + * @return the operation status + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static BaselineOperationStatus getBaselineOperation(ControlTowerClient controlTowerClient, + String operationIdentifier) { + try { + GetBaselineOperationRequest request = GetBaselineOperationRequest.builder() + .operationIdentifier(operationIdentifier) + .build(); + + GetBaselineOperationResponse response = controlTowerClient.getBaselineOperation(request); + BaselineOperationStatus status = response.baselineOperation().status(); + + System.out.format("Baseline operation status: {}", status); + return status; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "ResourceNotFoundException": + System.out.format("Baseline operation not found: {}", e.getMessage()); + break; + default: + System.out.format("Error getting baseline operation status: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error getting baseline operation status: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.get_baseline_operation.main] + + // snippet-start:[controltower.java2.list_enabled_controls.main] + /** + * Lists all enabled controls for a specific target using pagination. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @param targetIdentifier the identifier of the target (e.g., OU ARN) + * @return a list of enabled controls + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static List listEnabledControls(ControlTowerClient controlTowerClient, + String targetIdentifier) { + try { + List enabledControls = new ArrayList<>(); + + // Use paginator to retrieve all results + ListEnabledControlsIterable listIterable = controlTowerClient.listEnabledControlsPaginator( + ListEnabledControlsRequest.builder() + .targetIdentifier(targetIdentifier) + .build() + ); + + listIterable.stream() + .flatMap(response -> response.enabledControls().stream()) + .forEach(enabledControls::add); + + System.out.format("Retrieved {} enabled controls for target {}", enabledControls.size(), targetIdentifier); + return enabledControls; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "AccessDeniedException": + System.out.format("Access denied when listing enabled controls: {}", e.getMessage()); + break; + case "ResourceNotFoundException": + if (e.getMessage().contains("not registered with AWS Control Tower")) { + System.out.format("Control Tower must be enabled to work with controls"); + } else { + System.out.format("Target not found when listing enabled controls: {}", e.getMessage()); + } + break; + default: + System.out.format("Error listing enabled controls: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error listing enabled controls: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.list_enabled_controls.main] + + // snippet-start:[controltower.java2.enable_control.main] + /** + * Enables a control for a specified target. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @param controlIdentifier the identifier of the control to enable + * @param targetIdentifier the identifier of the target (e.g., OU ARN) + * @return the operation identifier + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static String enableControl(ControlTowerClient controlTowerClient, + String controlIdentifier, + String targetIdentifier) { + try { + EnableControlRequest request = EnableControlRequest.builder() + .controlIdentifier(controlIdentifier) + .targetIdentifier(targetIdentifier) + .build(); + + EnableControlResponse response = controlTowerClient.enableControl(request); + String operationId = response.operationIdentifier(); + + System.out.format("Enabled control with operation ID: {}", operationId); + return operationId; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "ValidationException": + if (e.getMessage().contains("already enabled")) { + System.out.format("Control is already enabled for this target"); + return null; + } + System.out.format("Validation error enabling control: {}", e.getMessage()); + break; + case "ResourceNotFoundException": + if (e.getMessage().contains("not registered with AWS Control Tower")) { + System.out.format("Control Tower must be enabled to work with controls"); + } else { + System.out.format("Control not found: {}", e.getMessage()); + } + break; + default: + System.out.format("Error enabling control: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error enabling control: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.enable_control.main] + + // snippet-start:[controltower.java2.disable_control.main] + /** + * Disables a control for a specified target. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @param controlIdentifier the identifier of the control to disable + * @param targetIdentifier the identifier of the target (e.g., OU ARN) + * @return the operation identifier + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static String disableControl(ControlTowerClient controlTowerClient, + String controlIdentifier, + String targetIdentifier) { + try { + DisableControlRequest request = DisableControlRequest.builder() + .controlIdentifier(controlIdentifier) + .targetIdentifier(targetIdentifier) + .build(); + + DisableControlResponse response = controlTowerClient.disableControl(request); + String operationId = response.operationIdentifier(); + + System.out.format("Disabled control with operation ID: {}", operationId); + return operationId; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "ResourceNotFoundException": + System.out.format("Control not found for disabling: {}", e.getMessage()); + break; + default: + System.out.format("Error disabling control: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error disabling control: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.disable_control.main] + + // snippet-start:[controltower.java2.get_control_operation.main] + /** + * Gets the status of a control operation. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @param operationIdentifier the identifier of the operation + * @return the operation status + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static ControlOperationStatus getControlOperation(ControlTowerClient controlTowerClient, + String operationIdentifier) { + try { + GetControlOperationRequest request = GetControlOperationRequest.builder() + .operationIdentifier(operationIdentifier) + .build(); + + GetControlOperationResponse response = controlTowerClient.getControlOperation(request); + ControlOperationStatus status = response.controlOperation().status(); + + System.out.format("Control operation status: {}", status); + return status; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "ResourceNotFoundException": + System.out.format("Control operation not found: {}", e.getMessage()); + break; + default: + System.out.format("Error getting control operation status: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error getting control operation status: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.get_control_operation.main] + + // snippet-start:[controltower.java2.list_controls.main] + /** + * Lists all controls in the Control Tower control catalog. + * + * @param controlCatalogClient the Control Catalog client to use for the operation + * @return a list of controls + * @throws SdkException if a service-specific error occurs + */ + public static List listControls( + ControlCatalogClient controlCatalogClient) { + try { + List controls = new ArrayList<>(); + + ListControlsIterable paginator = controlCatalogClient.listControlsPaginator( + software.amazon.awssdk.services.controlcatalog.model.ListControlsRequest.builder().build()); + + paginator.stream() + .flatMap(response -> response.controls().stream()) + .forEach(controls::add); + + System.out.format("Retrieved {} controls", controls.size()); + return controls; + + } catch (SdkException e) { + if (e.getMessage().contains("AccessDeniedException")) { + System.out.format("Access denied. Please ensure you have the necessary permissions."); + } else { + System.out.format("Couldn't list controls. Here's why: {}", e.getMessage()); + } + throw e; + } + } + // snippet-end:[controltower.java2.list_controls.main] + + // snippet-start:[controltower.java2.reset_enabled_baseline.main] + /** + * Resets an enabled baseline for a specific target. + * + * @param controlTowerClient the Control Tower client to use for the operation + * @param enabledBaselineIdentifier the identifier of the enabled baseline to reset + * @return the operation identifier + * @throws ControlTowerException if a service-specific error occurs + * @throws SdkException if an SDK error occurs + */ + public static String resetEnabledBaseline(ControlTowerClient controlTowerClient, + String enabledBaselineIdentifier) { + try { + ResetEnabledBaselineRequest request = ResetEnabledBaselineRequest.builder() + .enabledBaselineIdentifier(enabledBaselineIdentifier) + .build(); + + ResetEnabledBaselineResponse response = controlTowerClient.resetEnabledBaseline(request); + String operationId = response.operationIdentifier(); + + System.out.format("Reset enabled baseline with operation ID: {}", operationId); + return operationId; + + } catch (ControlTowerException e) { + String errorCode = e.awsErrorDetails().errorCode(); + switch (errorCode) { + case "ResourceNotFoundException": + System.out.format("Target not found: {}", e.getMessage()); + break; + default: + System.out.format("Couldn't reset enabled baseline. Here's why: {}", e.getMessage()); + } + throw e; + } catch (SdkException e) { + System.out.format("SDK error resetting enabled baseline: {}", e.getMessage()); + throw e; + } + } + // snippet-end:[controltower.java2.reset_enabled_baseline.main] +} +// snippet-end:[controltower.java2.controltower_actions.main] \ No newline at end of file diff --git a/javav2/example_code/controltower/src/main/java/com/example/controltower/ControlTowerScenario.java b/javav2/example_code/controltower/src/main/java/com/example/controltower/ControlTowerScenario.java new file mode 100644 index 00000000000..c7b771d524c --- /dev/null +++ b/javav2/example_code/controltower/src/main/java/com/example/controltower/ControlTowerScenario.java @@ -0,0 +1,363 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.controltower; + +import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; +import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.controlcatalog.ControlCatalogClient; +import software.amazon.awssdk.services.controlcatalog.model.ControlSummary; +import software.amazon.awssdk.services.organizations.OrganizationsClient; +import software.amazon.awssdk.services.organizations.model.*; +import software.amazon.awssdk.services.controltower.ControlTowerClient; +import software.amazon.awssdk.services.controltower.model.*; +import java.util.List; +import java.util.Scanner; +import java.util.Set; +import java.util.stream.Collectors; +import static java.lang.System.*; + +/** + * Before running this Java V2 code example, set up your development + * environment, including your credentials. + * + * For more information, see the following documentation topic: + * + * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html + * + */ + +public class ControlTowerScenario { + + public static final String DASHES = new String(new char[80]).replace("\0", "-"); + static Scanner scanner = new Scanner(in); + + private OrganizationsClient orgClient; + private ControlCatalogClient catClient; + private String ouArn; + private String stack = null; + private String ouId = null; + private String accountId = null; + private String landingZoneArn = null; + private boolean useLandingZone = false; + + public static void main(String[] args) { + + out.println(DASHES); + out.println("Welcome to the AWS Control Tower basics scenario!"); + out.println(DASHES); + + try { + ControlTowerClient controlTowerClient = ControlTowerClient.builder() + .region(Region.US_EAST_1) + .credentialsProvider(ProfileCredentialsProvider.create("default")) + .build(); + + OrganizationsClient orgClient = OrganizationsClient.builder() + .region(Region.AWS_GLOBAL) + .credentialsProvider(ProfileCredentialsProvider.create("default")) + .build(); + + ControlCatalogClient catClient = ControlCatalogClient.builder() + .region(Region.US_EAST_1) + .credentialsProvider(ProfileCredentialsProvider.create("default")) + .build(); + + ControlTowerScenario scenario = new ControlTowerScenario(orgClient, catClient); + scenario.runScenario(controlTowerClient); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + // ---------------------------------------------------------- + // Constructor: store orgClient for setupOrganization() + // ---------------------------------------------------------- + public ControlTowerScenario(OrganizationsClient orgClient, ControlCatalogClient catClient) { + this.orgClient = orgClient; + this.catClient = catClient; + } + + // ---------------------------------------------------------- + // Main scenario flow + // ---------------------------------------------------------- + private void runScenario(ControlTowerClient controlTowerClient) { + try { + out.println(DASHES); + System.out.println(""" + Some demo operations require the use of a landing zone. + You can use an existing landing zone or opt out of these operations in the demo. + For instructions on how to set up a landing zone, + see https://docs.aws.amazon.com/controltower/latest/userguide/getting-started-from-console.html + """); + + out.println("Step 1: Listing landing zones..."); + waitForInputToContinue(scanner); + + // CALL: ControlTowerActions.listLandingZones() + List landingZones = + ControlTowerActions.listLandingZones(controlTowerClient); + + if (!landingZones.isEmpty()) { + System.out.println("\nAvailable Landing Zones:"); + + for (int i = 0; i < landingZones.size(); i++) { + LandingZoneSummary lz = landingZones.get(i); + System.out.printf("%d %s)%n", i + 1, lz.arn()); + } + + if (askYesNo( + "Do you want to use the first landing zone in the list (" + + landingZones.get(0).arn() + ")? (y/n): ")) { + + useLandingZone = true; + landingZoneArn = landingZones.get(0).arn(); + + System.out.println("Using landing zone ID: " + landingZoneArn); + + // CALL: setupOrganization() + String sandboxOuId = setupOrganization(); + ouId = sandboxOuId; + + } else if (askYesNo( + "Do you want to use a different existing Landing Zone for this demo? (y/n): ")) { + + useLandingZone = true; + + System.out.print("Enter landing zone id: "); + landingZoneArn = scanner.nextLine().trim(); + + // CALL: setupOrganization() + String sandboxOuId = setupOrganization(); + ouId = sandboxOuId; + } + } + waitForInputToContinue(scanner); + + // ---------------------------------------------------------- + // CALL: ControlTowerActions.listBaselines() + // ---------------------------------------------------------- + out.println(DASHES); + out.println("Step 2: Listing available baselines..."); + + List baselines = + ControlTowerActions.listBaselines(controlTowerClient); + + baselines.forEach(b -> { + out.println("Baseline: " + b.name()); + out.println(" ARN: " + b.arn()); + }); + waitForInputToContinue(scanner); + + // ---------------------------------------------------------- + // CALL: ControlTowerActions.listControls() + // ---------------------------------------------------------- + out.println(DASHES); + out.println("Managing Controls:"); + + List controls = + ControlTowerActions.listControls(catClient); + + out.println("\nListing first 5 available Controls:"); + + for (int i = 0; i < Math.min(5, controls.size()); i++) { + ControlSummary c = controls.get(i); + out.println(String.format("%d. %s - %s", + (i + 1), c.name(), c.arn())); + } + + if (useLandingZone) { + + String targetOu = ouArn; + waitForInputToContinue(scanner); + + // ---------------------------------------------------------- + // CALL: ControlTowerActions.listEnabledControls() + // ---------------------------------------------------------- + List enabledControls = + ControlTowerActions.listEnabledControls(controlTowerClient, targetOu); + + out.println("\nListing enabled controls:"); + + for (int i = 0; i < enabledControls.size(); i++) { + EnabledControlSummary ec = enabledControls.get(i); + out.println(String.format("%d. %s", + (i + 1), ec.controlIdentifier())); + } + + // Determine first non-enabled control + Set enabledControlArns = enabledControls.stream() + .map(EnabledControlSummary::arn) + .collect(Collectors.toSet()); + + String controlArnToEnable = controls.stream() + .map(ControlSummary::arn) + .filter(arn -> !enabledControlArns.contains(arn)) + .findFirst() + .orElse(null); + + waitForInputToContinue(scanner); + + // ---------------------------------------------------------- + // CALL: ControlTowerActions.enableControl() + // ---------------------------------------------------------- + if (controlArnToEnable != null && + askYesNo("Do you want to enable the control " + + controlArnToEnable + "? (y/n): ")) { + + out.println("\nEnabling control: " + controlArnToEnable); + + String operationId = + ControlTowerActions.enableControl( + controlTowerClient, controlArnToEnable, targetOu); + + if (operationId != null) { + out.println("Enabled control with operation id " + operationId); + } + } + waitForInputToContinue(scanner); + + // ---------------------------------------------------------- + // CALL: ControlTowerActions.disableControl() + // ---------------------------------------------------------- + if (controlArnToEnable != null && + askYesNo("Do you want to disable the control? (y/n): ")) { + + out.println("\nDisabling control..."); + + String operationId = + ControlTowerActions.disableControl( + controlTowerClient, controlArnToEnable, targetOu); + + out.println("Disable operation ID: " + operationId); + } + } + + // Final pause + waitForInputToContinue(scanner); + + out.println("\nThis concludes the example scenario."); + out.println("Thanks for watching!"); + out.println(DASHES); + out.println(DASHES); + out.println("Scenario completed Successfully!"); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public String setupOrganization() { + System.out.println("\nChecking organization status..."); + String orgId; + + // ------------------------------- + // 1. Describe or create organization + // ------------------------------- + try { + DescribeOrganizationResponse desc = orgClient.describeOrganization(); + orgId = desc.organization().id(); + System.out.println("Account is part of organization: " + orgId); + + } catch (AwsServiceException e) { + + if ("AWSOrganizationsNotInUseException".equals(e.awsErrorDetails().errorCode())) { + + System.out.println("No organization found. Creating one..."); + + CreateOrganizationResponse create = + orgClient.createOrganization( + CreateOrganizationRequest.builder() + .featureSet("ALL") + .build() + ); + + orgId = create.organization().id(); + System.out.println("Created organization: " + orgId); + + // ✅ NO WAITERS — we simply proceed + // Organizations may take time to stabilize, + // but this method returns immediately. + + } else { + throw e; + } + } + + // ------------------------------- + // 2. Locate or create Sandbox OU + // ------------------------------- + String sandboxOuId = null; + + ListRootsResponse roots = orgClient.listRoots(); + String rootId = roots.roots().get(0).id(); + + System.out.println("Checking Sandbox OU..."); + + for (ListOrganizationalUnitsForParentResponse page : + orgClient.listOrganizationalUnitsForParentPaginator( + ListOrganizationalUnitsForParentRequest.builder() + .parentId(rootId) + .build() + )) { + + for (OrganizationalUnit ou : page.organizationalUnits()) { + if ("Sandbox".equals(ou.name())) { + sandboxOuId = ou.id(); + this.ouArn = ou.arn(); + System.out.println("Found Sandbox OU: " + sandboxOuId); + break; + } + } + + if (sandboxOuId != null) { + break; + } + } + + // ------------------------------- + // Create OU if missing + // ------------------------------- + if (sandboxOuId == null) { + System.out.println("Creating Sandbox OU..."); + + CreateOrganizationalUnitResponse created = + orgClient.createOrganizationalUnit( + CreateOrganizationalUnitRequest.builder() + .parentId(rootId) + .name("Sandbox") + .build() + ); + + sandboxOuId = created.organizationalUnit().id(); + this.ouArn = created.organizationalUnit().arn(); + + System.out.println("Created Sandbox OU: " + sandboxOuId); + + // ✅ NO WAITER — return immediately + } + + return sandboxOuId; + } + + // ---------------------------------------------------------- + // Utility methods + // ---------------------------------------------------------- + private static boolean askYesNo(String msg) { + out.print(msg); + return scanner.nextLine().trim().toLowerCase().startsWith("y"); + } + + private static void waitForInputToContinue(Scanner sc) { + out.println("\nEnter 'c' then to continue:"); + while (true) { + String in = sc.nextLine(); + if ("c".equalsIgnoreCase(in.trim())) { + out.println("Continuing..."); + break; + } + } + } +} diff --git a/javav2/example_code/controltower/src/main/java/com/example/controltower/HelloControlTower.java b/javav2/example_code/controltower/src/main/java/com/example/controltower/HelloControlTower.java new file mode 100644 index 00000000000..b6478acf8a7 --- /dev/null +++ b/javav2/example_code/controltower/src/main/java/com/example/controltower/HelloControlTower.java @@ -0,0 +1,79 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.controltower; + +import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.controltower.ControlTowerClient; +import software.amazon.awssdk.services.controltower.model.ControlTowerException; +import software.amazon.awssdk.services.controltower.model.ListBaselinesRequest; +import software.amazon.awssdk.services.controltower.paginators.ListBaselinesIterable; +import java.util.ArrayList; +import java.util.List; + +/** + * Before running this Java V2 code example, set up your development + * environment, including your credentials. + * + * For more information, see the following documentation topic: + * + * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html + * + * Use the AWS SDK for Java (v2) to create an AWS Control Tower client + * and list all available baselines. + * This example uses the default settings specified in your shared credentials + * and config files. + */ + +// snippet-start:[controltower.java2.hello.main] +public class HelloControlTower { + + public static void main(String[] args) { + try { + ControlTowerClient controlTowerClient = ControlTowerClient.builder() + .region(Region.US_EAST_1) + .credentialsProvider(ProfileCredentialsProvider.create("default")) + .build() ; + helloControlTower(controlTowerClient); + } catch (ControlTowerException e) { + System.err.println("Control Tower error occurred: " + e.awsErrorDetails().errorMessage()); + } + } + + /** + * Use the AWS SDK for Java (v2) to create an AWS Control Tower client + * and list all available baselines. + * This example uses the default settings specified in your shared credentials + * and config files. + * + * @param controlTowerClient A ControlTowerClient object. This object wraps + * the low-level AWS Control Tower service API. + */ + public static void helloControlTower(ControlTowerClient controlTowerClient) { + System.out.println("Hello, AWS Control Tower! Let's list available baselines:\n"); + + ListBaselinesIterable paginator = controlTowerClient.listBaselinesPaginator( + ListBaselinesRequest.builder().build()); + List baselineNames = new ArrayList<>(); + + try { + paginator.stream() + .flatMap(response -> response.baselines().stream()) + .forEach(baseline -> baselineNames.add(baseline.name())); + + System.out.println(baselineNames.size() + " baseline(s) retrieved."); + for (String baselineName : baselineNames) { + System.out.println("\t" + baselineName); + } + + } catch (ControlTowerException e) { + if ("AccessDeniedException".equals(e.awsErrorDetails().errorCode())) { + System.out.println("Access denied. Please ensure you have the necessary permissions."); + } else { + System.out.println("An error occurred: " + e.getMessage()); + } + } + } +} +// snippet-end:[controltower.java2.hello.main] diff --git a/javav2/example_code/controltower/src/main/java/resources/config.properties b/javav2/example_code/controltower/src/main/java/resources/config.properties new file mode 100644 index 00000000000..c18495c2f4c --- /dev/null +++ b/javav2/example_code/controltower/src/main/java/resources/config.properties @@ -0,0 +1,3 @@ +dataAccessRoleArn = +s3Uri = +documentClassifier = diff --git a/javav2/example_code/controltower/src/main/java/resources/logback.xml b/javav2/example_code/controltower/src/main/java/resources/logback.xml new file mode 100644 index 00000000000..d705768de36 --- /dev/null +++ b/javav2/example_code/controltower/src/main/java/resources/logback.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + diff --git a/javav2/example_code/controltower/src/test/java/ControlTowerTest.java b/javav2/example_code/controltower/src/test/java/ControlTowerTest.java new file mode 100644 index 00000000000..3dfd0afcf98 --- /dev/null +++ b/javav2/example_code/controltower/src/test/java/ControlTowerTest.java @@ -0,0 +1,72 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import com.example.controltower.ControlTowerActions; +import com.example.controltower.HelloControlTower; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestMethodOrder; +import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.controlcatalog.ControlCatalogClient; +import software.amazon.awssdk.services.controltower.ControlTowerClient; +import software.amazon.awssdk.services.organizations.OrganizationsClient; +import software.amazon.awssdk.services.organizations.model.DescribeOrganizationResponse; +import software.amazon.awssdk.services.organizations.model.ListOrganizationalUnitsForParentRequest; +import software.amazon.awssdk.services.organizations.model.ListOrganizationalUnitsForParentResponse; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class ControlTowerTest { + private static ControlTowerClient controlTowerClient; + private static OrganizationsClient orgClient; + private static ControlCatalogClient catClient ; + + @BeforeAll + public static void setUp() { + controlTowerClient = ControlTowerClient.builder() + .region(Region.US_EAST_1) + .credentialsProvider(ProfileCredentialsProvider.create("default")) + .build(); + + orgClient = OrganizationsClient.builder() + .region(Region.AWS_GLOBAL) + .credentialsProvider(ProfileCredentialsProvider.create("default")) + .build(); + + catClient = ControlCatalogClient.builder() + .region(Region.US_EAST_1) + .credentialsProvider(ProfileCredentialsProvider.create("default")) + .build(); + + } + + @Test + @Order(1) + public void testHelloService() { + assertDoesNotThrow(() -> { + HelloControlTower.helloControlTower(controlTowerClient); + }); + System.out.println("Test 1 passed"); + } + + @Test + @Order(2) + public void testControlTowerActions() { + assertDoesNotThrow(() -> { + // SAFE: read-only, no admin role required + ControlTowerActions.listLandingZones(controlTowerClient); + ControlTowerActions.listBaselines(controlTowerClient); + ControlTowerActions.listControls(catClient); + + }); + + System.out.println("Test 2 passed"); + } + +} \ No newline at end of file