resourceTypes = List.of(
+ ResourceScanType.EC2,
+ ResourceScanType.ECR,
+ ResourceScanType.LAMBDA,
+ ResourceScanType.LAMBDA_CODE
+ );
+
+ // Build the request.
+ DisableRequest.Builder requestBuilder = DisableRequest.builder()
+ .resourceTypes(resourceTypes);
+
+ if (accountIds != null && !accountIds.isEmpty()) {
+ requestBuilder.accountIds(accountIds);
+ }
+
+ DisableRequest request = requestBuilder.build();
+
+ return getAsyncClient().disable(request)
+ .whenComplete((response, exception) -> {
+ if (exception != null) {
+ Throwable cause = exception.getCause();
+ if (cause instanceof ValidationException) {
+ throw new CompletionException(
+ "Inspector may already be disabled for this account: %s".formatted(cause.getMessage()),
+ cause
+ );
+ }
+
+ if (cause instanceof Inspector2Exception) {
+ Inspector2Exception e = (Inspector2Exception) cause;
+ throw new CompletionException(
+ "AWS Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()),
+ cause
+ );
+ }
+
+ throw new CompletionException(
+ "Failed to disable Inspector: %s".formatted(exception.getMessage()),
+ exception
+ );
+ }
+ })
+ .thenApply(response -> {
+ StringBuilder summary = new StringBuilder("Disable results:\n");
+
+ if (response.accounts() == null || response.accounts().isEmpty()) {
+ summary.append("Inspector may already be disabled for all target accounts.");
+ return summary.toString();
+ }
+
+ for (Account account : response.accounts()) {
+ String accountId = account.accountId() != null ? account.accountId() : "Unknown";
+ String status = account.status() != null ? account.statusAsString() : "Unknown";
+ summary.append(" • Account: ").append(accountId)
+ .append(" → Status: ").append(status).append("\n");
+ }
+
+ return summary.toString();
+ });
+ }
+ // snippet-end:[inspector.java2.disable.main]
+}
+// snippet-end:[inspector.java2_actions.main]
\ No newline at end of file
diff --git a/javav2/example_code/inspector/src/main/java/com/java/inspector/InspectorScenario.java b/javav2/example_code/inspector/src/main/java/com/java/inspector/InspectorScenario.java
new file mode 100644
index 00000000000..dcbbad9c4a1
--- /dev/null
+++ b/javav2/example_code/inspector/src/main/java/com/java/inspector/InspectorScenario.java
@@ -0,0 +1,208 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package com.java.inspector;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Scanner;
+
+// snippet-start:[inspector.java2_scenario.main]
+
+/**
+ * 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 InspectorScenario {
+
+ public static final String DASHES = new String(new char[80]).replace("\0", "-");
+ private static final Logger logger = LoggerFactory.getLogger(InspectorScenario.class);
+ private static final Scanner scanner = new Scanner(System.in);
+
+ public static void main(String[] args) {
+ InspectorActions inspectorActions = new InspectorActions();
+ logger.info("Amazon Inspector Basics Scenario");
+
+ logger.info("""
+ Amazon Inspector is a security assessment service provided by Amazon Web Services (AWS) that helps
+ improve the security and compliance of applications deployed on AWS. It automatically assesses
+ applications for vulnerabilities or deviations from best practices. By leveraging Amazon Inspector,
+ users can gain insights into the overall security state of their application and identify potential
+ security risks.
+
+ This service operates by conducting both network and host-based assessments, allowing it to detect a
+ wide range of security issues, including those related to operating systems, network configurations,
+ and application dependencies.
+ """);
+
+ waitForInputToContinue();
+
+ try {
+ runScenario(inspectorActions);
+
+ logger.info("");
+ logger.info("Scenario completed successfully!");
+ logger.info("");
+ logger.info("What you learned:");
+ logger.info(" - How to check Inspector account status");
+ logger.info(" - How to enable Inspector");
+ logger.info(" - How to list and analyze findings");
+ logger.info(" - How to check coverage information");
+ logger.info(" - How to create and manage filters");
+ logger.info(" - How to track usage and costs");
+ logger.info(" - How to clean up resources");
+ logger.info("");
+
+ } catch (Exception ex) {
+ logger.error("Scenario failed due to unexpected error: {}", ex.getMessage(), ex);
+
+ } finally {
+ scanner.close();
+ logger.info("Exiting...");
+ }
+ }
+
+ /**
+ * Runs the Inspector scenario in a step-by-step sequence.
+ *
+ * All InspectorActions methods are asynchronous and return CompletableFutures.
+ * Each step ends with .join(). Any async exception thrown during .join() will bubble up
+ *
+ */
+ public static void runScenario(InspectorActions actions) {
+ String filterArn = null;
+ boolean inspectorEnabled = false;
+
+ try {
+ // Step 1
+ logger.info(DASHES);
+ logger.info("Step 1: Checking Inspector account status...");
+ String status = actions.getAccountStatusAsync().join();
+ logger.info(status);
+ waitForInputToContinue();
+
+ // Step 2
+ logger.info(DASHES);
+ logger.info("Step 2: Enabling Inspector...");
+ String message = actions.enableInspectorAsync(null).join();
+ logger.info(message);
+ inspectorEnabled = true; // track that Inspector was enabled
+ waitForInputToContinue();
+
+ // Step 3
+ logger.info(DASHES);
+ logger.info("Step 3: Listing LOW severity findings...");
+
+ // Call the service method
+ List allFindings = actions.listLowSeverityFindingsAsync().join();
+
+ if (!allFindings.isEmpty()) {
+ // Only proceed if there are findings
+ String lastArn = allFindings.get(allFindings.size() - 1);
+ logger.info("Look up details on: {}", lastArn);
+ waitForInputToContinue();
+ String details = actions.getFindingDetailsAsync(lastArn).join();
+ logger.info(details);
+ } else {
+ logger.info("No LOW severity findings found.");
+ }
+
+ waitForInputToContinue();
+
+ // Step 4
+ logger.info(DASHES);
+ logger.info("Step 4: Listing coverage...");
+ String coverage = actions.listCoverageAsync(5).join();
+ logger.info(coverage);
+ waitForInputToContinue();
+
+ // Step 5
+ logger.info(DASHES);
+ logger.info("Step 5: Creating filter...");
+ String filterName = "suppress-low-" + System.currentTimeMillis();
+ filterArn = actions.createLowSeverityFilterAsync(filterName, "Suppress low severity findings").join();
+ logger.info("Created filter: {}", filterArn);
+ waitForInputToContinue();
+
+ // Step 6
+ logger.info(DASHES);
+ logger.info("Step 6: Listing filters...");
+ String filters = actions.listFiltersAsync(10).join();
+ logger.info(filters);
+ waitForInputToContinue();
+
+ // Step 7
+ logger.info(DASHES);
+ logger.info("Step 7: Usage totals...");
+ String usage = actions.listUsageTotalsAsync(null, 10).join();
+ logger.info(usage);
+ waitForInputToContinue();
+
+ // Step 8
+ logger.info(DASHES);
+ logger.info("Step 8: Coverage statistics...");
+ String stats = actions.listCoverageStatisticsAsync().join();
+ logger.info(stats);
+ waitForInputToContinue();
+
+ // Step 9
+ logger.info(DASHES);
+ logger.info("Step 9: Delete filter?");
+ logger.info("Filter ARN: {}", filterArn);
+ logger.info("Delete the filter and disable Inspector? (y/n)");
+
+ if (scanner.nextLine().trim().equalsIgnoreCase("y")) {
+ actions.deleteFilterAsync(filterArn).join();
+ logger.info("Filter deleted.");
+ String disableMsg = actions.disableInspectorAsync(null).join();
+ logger.info(disableMsg);
+ inspectorEnabled = false; // track that Inspector was disabled
+ }
+
+ waitForInputToContinue();
+
+ } catch (Exception ex) {
+ logger.error("Scenario encountered an error: {}", ex.getMessage(), ex);
+ // Rethrow the exception
+ throw ex;
+
+ } finally {
+ // Cleanup in case of an exception
+ if (filterArn != null) {
+ try {
+ actions.deleteFilterAsync(filterArn).join();
+ logger.info("Cleanup: Filter deleted.");
+ } catch (Exception e) {
+ logger.warn("Failed to delete filter during cleanup: {}", e.getMessage(), e);
+ }
+ }
+
+ if (inspectorEnabled) {
+ try {
+ actions.disableInspectorAsync(null).join();
+ logger.info("Cleanup: Inspector disabled.");
+ } catch (Exception e) {
+ logger.warn("Failed to disable Inspector during cleanup: {}", e.getMessage(), e);
+ }
+ }
+ }
+ }
+
+ // Utility Method
+ private static void waitForInputToContinue() {
+ while (true) {
+ logger.info("");
+ logger.info("Enter 'c' to continue:");
+ String input = scanner.nextLine().trim();
+ if (input.equalsIgnoreCase("c")) break;
+ logger.info("Invalid input, try again.");
+ }
+ }
+}
+// snippet-end:[inspector.java2_scenario.main]
\ No newline at end of file
diff --git a/javav2/example_code/inspector/src/main/resources/log4j2.xml b/javav2/example_code/inspector/src/main/resources/log4j2.xml
new file mode 100644
index 00000000000..914470047e7
--- /dev/null
+++ b/javav2/example_code/inspector/src/main/resources/log4j2.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/javav2/example_code/inspector/src/test/java/InspectorTests.java b/javav2/example_code/inspector/src/test/java/InspectorTests.java
new file mode 100644
index 00000000000..e5ff85ca7cb
--- /dev/null
+++ b/javav2/example_code/inspector/src/test/java/InspectorTests.java
@@ -0,0 +1,148 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import com.java.inspector.InspectorScenario;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.java.inspector.HelloInspector;
+import com.java.inspector.InspectorActions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.inspector2.Inspector2Client;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@TestInstance(TestInstance.Lifecycle.PER_METHOD)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class InspectorTests {
+ private static Inspector2Client inspector;
+ private static InspectorActions inspectorActions;
+ private static final Logger logger = LoggerFactory.getLogger(InspectorTests.class);
+ @BeforeAll
+ public static void setUp() {
+ inspector = Inspector2Client.builder()
+ .region(Region.US_EAST_1)
+ .build() ;
+
+ inspectorActions = new InspectorActions();
+ }
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(1)
+ public void testHelloService() {
+ assertDoesNotThrow(() -> {
+ HelloInspector.listMembers(inspector);
+ });
+ logger.info("Test 1 passed");
+ }
+
+
+ /**
+ * Integration test for InspectorActions Async methods.
+ *
+ * This test validates that all async action methods complete successfully and
+ * return expected values (like filter ARN).
+ *
+ * Note that it will fail the test if any .join() throws a CompletionException.
+ */
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(2)
+ public void testInspectorActionsIntegration() {
+ assertDoesNotThrow(() -> {
+ int maxResults = 10;
+
+ String filterName = "suppress-low-severity-" + System.currentTimeMillis();
+
+ inspectorActions.getAccountStatusAsync().join();
+
+ inspectorActions.enableInspectorAsync(null).join();
+
+ List allFindings = inspectorActions.listLowSeverityFindingsAsync().join();
+ if (!allFindings.isEmpty()) {
+ // Only proceed if there are findings
+ String lastArn = allFindings.get(allFindings.size() - 1);
+ logger.info("Fetching details for last finding ARN: {}", lastArn);
+
+ String details = inspectorActions.getFindingDetailsAsync(lastArn).join();
+
+ // Check details are not empty/null
+ assertNotNull(details, "Finding details should not be null");
+ assertFalse(details.isEmpty(), "Finding details should not be empty");
+ } else {
+ logger.info("No LOW severity findings found.");
+ }
+
+ maxResults = 5;
+ inspectorActions.listCoverageAsync(maxResults).join();
+
+ String filterARN = inspectorActions.createLowSeverityFilterAsync(filterName,"Suppress low severity findings for demo purposes").join();
+
+ // Assert it returned a valid ARN
+ assertNotNull(filterARN, "Filter ARN should not be null");
+ assertFalse(filterARN.isBlank(), "Filter ARN should not be empty");
+
+ inspectorActions.listFiltersAsync(10).join();
+
+ inspectorActions.listUsageTotalsAsync(null, 10).join();
+
+ inspectorActions.listCoverageStatisticsAsync().join();
+
+ inspectorActions.deleteFilterAsync(filterARN).join();
+
+ inspectorActions.disableInspectorAsync(null).join();
+ });
+
+ logger.info("Test 2 passed");
+ }
+
+
+ @Test
+ @Tag("IntegrationTest")
+ @Order(3)
+ public void testInspectorScenarioEndToEnd() {
+ assertDoesNotThrow(() -> {
+ // The scenario calls scanner.nextLine() repeatedly.
+ // We simulate user input by providing many "c" lines.
+ String simulatedInput = String.join("\n",
+ Collections.nCopies(20, "c")) + "\n";
+
+ InputStream originalIn = System.in;
+ PrintStream originalOut = System.out;
+
+ try {
+ // Redirect System.in to simulated input
+ ByteArrayInputStream testIn = new ByteArrayInputStream(simulatedInput.getBytes());
+ System.setIn(testIn);
+
+ // Capture System.out so logs don’t spam the console
+ System.setOut(new PrintStream(new ByteArrayOutputStream()));
+
+ // Run the scenario
+ InspectorScenario.main(new String[]{});
+
+ } finally {
+ // Restore original I/O streams
+ System.setIn(originalIn);
+ System.setOut(originalOut);
+ }
+ });
+
+ logger.info("Test 3 (Scenario end-to-end) passed");
+ }
+}
\ No newline at end of file
diff --git a/scenarios/basics/inspector/SPECIFICATION.md b/scenarios/basics/inspector/SPECIFICATION.md
index 51eece5c95f..784cf2ba868 100644
--- a/scenarios/basics/inspector/SPECIFICATION.md
+++ b/scenarios/basics/inspector/SPECIFICATION.md
@@ -1,6 +1,9 @@
# Amazon Inspector Specification
-This document contains a draft proposal for an *Amazon Inspector Basics Scenario*, generated by the Code Examples SpecGen AI tool. The specifications describe a potential code example scenario based on research, usage data, service information, and AI-assistance. The following should be reviewed for accuracy and correctness before proceeding on to a final specification.
+This SDK Basics scenario demonstrates how to interact with Amazon Inspector, a basics scenario that showcases AWS services and SDKs. It is primarily intended for the AWS code examples team to use while developing this example in additional languages.
+
+## Resources
+This Basics scenario does not require any additional AWS resources.
### Relevant documentation
@@ -11,79 +14,122 @@ This document contains a draft proposal for an *Amazon Inspector Basics Scenario
### API Actions Used
+* [CreateFilter](https://docs.aws.amazon.com/inspector/v2/APIReference/API_CreateFilter.html)
+
* [Enable](https://docs.aws.amazon.com/inspector/v2/APIReference/API_Enable.html)
+
+* [ListCoverageStatistics](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListCoverageStatistics.html)
+
+* [ListUsageTotals](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListUsageTotals.html)
+
* [BatchGetAccountStatus](https://docs.aws.amazon.com/inspector/v2/APIReference/API_BatchGetAccountStatus.html)
+
+* [ListFilters](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListFilters.html)
+
* [ListFindings](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListFindings.html)
+
* [BatchGetFindingDetails](https://docs.aws.amazon.com/inspector/v2/APIReference/API_BatchGetFindingDetails.html)
+
* [ListCoverage](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListCoverage.html)
-* [Disable](https://docs.aws.amazon.com/inspector/v2/APIReference/API_Disable.html)
-## Proposed example structure
+* [DeleteFilter](https://docs.aws.amazon.com/inspector/v2/APIReference/API_DeleteFilter.html)
-The output below demonstrates how this example would run for the customer. It includes a Hello service example (included for all services), and the scenario description. The scenario code would also be presented as Action snippets, with a code snippet for each SDK action.
+- [Disable](https://docs.aws.amazon.com/inspector/v2/APIReference/API_Disable.html)
-### Hello
-The Hello example is a separate runnable example. - Set up the Inspector service client - Check the current account status for Inspector - Display available scan types and regions
+## Hello Amazon Inspector
+
+The Hello example is designed for users who are new to Amazon Inspector. It demonstrates how to set up the Inspector service client and retrieve a list of all member accounts associated with the current Inspector administrator account.
## Scenario
-#### Setup
+This scenario demonstrates the basic usage of **Amazon Inspector** using a Java program. It focuses on checking account status, enabling Inspector, listing findings, reviewing coverage, and managing filters.
+
+---
+
+### Setup
-* Enable Amazon Inspector for the account
-* Verify Inspector is successfully activated
-* Display account status and enabled scan types
+* Check Amazon Inspector account status
+* Enable Inspector for available resource types (if not already enabled)
+* Display account status summary
-#### Coverage Assessment
+---
-* List coverage statistics for EC2 instances, ECR repositories, and Lambda functions
-* Display resource coverage details
-* Show scanning status for different resource types
+### Coverage Assessment
-#### Findings Management
+* List coverage details for scanned resources
+* Display overall coverage statistics
+* Review scan status for resources (general overview)
+
+---
+
+### Findings Management
* List security findings across all resource types
-* Filter findings by severity level (CRITICAL, HIGH, MEDIUM, LOW)
-* Retrieve detailed information for specific findings
+* Create an example filter to suppress low-severity findings
+* List existing filters
+
+---
+
+### Usage and Costs
+
+* Check usage totals and metrics for Inspector
+* Review coverage statistics
+
+---
-#### Vulnerability Analysis
+### Cleanup
-* Display vulnerability details including CVE information
-* Show affected resources and remediation guidance
-* Filter findings by resource type (EC2, ECR, Lambda)
+* Delete the filter
+* Diable inspector
-#### Cleanup
+---
-* Optionally disable Inspector scanning (with user confirmation)
-* Display final account status
+### Outcome
+
+By following this scenario, users learn how to:
+
+* Check Inspector account status and configuration
+* Enable Inspector for different resource types
+* List and analyze security findings
+* Monitor scan coverage
+* Create and manage filters
+* Track usage and coverage statistics
+* Cleanup the resources
## Errors
-SDK Code examples include basic exception handling for each action used. The table below describes an appropriate exception which will be handled in the code for each service action.
-
-|Action |Error |Handling |
-|--- |--- |--- |
-|`Enable` |ValidationException |Validate resource types and account permissions. |
-|`Enable` |AccessDeniedException |Notify user of insufficient permissions and exit. |
-|`BatchGetAccountStatus` |ValidationException |Validate account IDs format. |
-|`BatchGetAccountStatus` |AccessDeniedException |Handle permission errors gracefully. |
-|`ListFindings` |ValidationException |Validate filter criteria and pagination parameters. |
-|`ListFindings` |InternalServerException |Retry operation with exponential backoff. |
-|`BatchGetFindingDetails` |ValidationException |Validate finding ARNs format. |
-|`BatchGetFindingDetails` |AccessDeniedException |Handle access denied for specific findings. |
-|`ListCoverage` |ValidationException |Validate filter and pagination parameters. |
-|`Disable` |ValidationException |Validate resource types for disabling. |
-|`Disable` |ConflictException |Handle cases where Inspector cannot be disabled. |
+The table below describes the exceptions handled in the program for each action.
-## Metadata
+| Action | Exception | Handling |
+|-------------------------------|---------------------------|--------------------------------------------------------------------------|
+| `Enable` | `ValidationException` | Prints a message indicating Inspector may already be enabled. |
+| `Disable` | `ValidationException` | Prints a message indicating Inspector may already be disabled. |
+| `listUsageTotals` | `ValidationException` | Validation error listing usage totals.
+| `BatchGetAccountStatus` | `AccessDeniedException` | Prints AWS service error details and rethrows the exception. |
+| `ListFindings` | `ValidationException` | Prints validation error details. |
+| `ListCoverage` | `ValidationException` | Prints validation error details. |
+| `ListCoverageStatistics` | `ValidationException` | Prints validation error details. |
+| `createFilter` | `ValidationException` | Prints validation error details. |
+| `ListFilters` | `ValidationException` | Prints AWS service error details and rethrows the exception. |
+| `deleteFilter` | `ResourceNotFoundException` | Prints AWS service error details and rethrows the exception. |
+| `batchGetFindingDetails` | `ResourceNotFoundException` | Prints AWS service error details and rethrows the exception. |
-|action / scenario |metadata file |metadata key |
-|--- |--- |--- |
-|`Enable` |inspector_metadata.yaml |inspector_Enable |
-|`BatchGetAccountStatus` |inspector_metadata.yaml |inspector_BatchGetAccountStatus |
-|`ListFindings` |inspector_metadata.yaml |inspector_ListFindings |
-|`BatchGetFindingDetails` |inspector_metadata.yaml |inspector_BatchGetFindingDetails |
-|`ListCoverage` |inspector_metadata.yaml |inspector_ListCoverage |
-|`Disable` |inspector_metadata.yaml |inspector_Disable |
-|`Amazon Inspector Basics Scenario` |inspector_metadata.yaml |inspector_Scenario |
+## Metadata
+
+| Action / Scenario | Metadata File | Metadata Key |
+|-----------------------------------------|------------------------|-------------------------------|
+| `Enable` | inspector_metadata.yaml | inspector_Enable |
+| `Disable` | inspector_metadata.yaml | inspector_Disable |
+| `BatchGetAccountStatus` | inspector_metadata.yaml | inspector_GetAccountStatus |
+| `ListFindings` | inspector_metadata.yaml | inspector_ListFindings |
+| `ListCoverage` | inspector_metadata.yaml | inspector_ListCoverage |
+| `ListCoverageStatistics` | inspector_metadata.yaml | inspector_ListCoverageStatistics |
+| `ListUsageTotals` | inspector_metadata.yaml | inspector_ListUsageTotals |
+| `CreateFilter` | inspector_metadata.yaml | inspector_CreateFilter |
+| `ListFilters` | inspector_metadata.yaml | inspector_ListFilters |
+| `DeleteFilter` | inspector_metadata.yaml | inspector_DeleteFilter |
+| `BatchGetFindingDetails` | inspector_metadata.yaml | inspector_BatchGetFindingDetails |
+| `Amazon Inspector Hello` | inspector_metadata.yaml | inspector_Hello |
+| `Amazon Inspector Basics Scenario` | inspector_metadata.yaml | inspector_Scenario
\ No newline at end of file