Amazon Inspector examples using SDK for Java 2.x - AWS SDK Code Examples

There are more AWS SDK examples available in the AWS Doc SDK Examples GitHub repo.

Amazon Inspector examples using SDK for Java 2.x

The following code examples show you how to perform actions and implement common scenarios by using the AWS SDK for Java 2.x with Amazon Inspector.

Basics are code examples that show you how to perform the essential operations within a service.

Actions are code excerpts from larger programs and must be run in context. While actions show you how to call individual service functions, you can see actions in context in their related scenarios.

Each example includes a link to the complete source code, where you can find instructions on how to set up and run the code in context.

Get started

The following code example shows how to get started using .

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * 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 HelloInspector { private static final Logger logger = LoggerFactory.getLogger(HelloInspector.class); public static void main(String[] args) { logger.info("Hello Amazon Inspector!"); try (Inspector2Client inspectorClient = Inspector2Client.builder().build()) { logger.info("Listing member accounts for this Inspector administrator account..."); listMembers(inspectorClient); logger.info("The Hello Inspector example completed successfully."); } catch (Inspector2Exception e) { logger.error("Error: {}", e.getMessage()); logger.info("Troubleshooting:"); logger.info("1. Verify AWS credentials are configured"); logger.info("2. Check IAM permissions for Inspector2"); logger.info("3. Ensure Inspector2 is enabled in your account"); logger.info("4. Verify you're using a supported region"); } } /** * Lists all member accounts associated with the current Inspector administrator account. * * @param inspectorClient The Inspector2Client used to interact with AWS Inspector. */ public static void listMembers(Inspector2Client inspectorClient) { try { ListMembersRequest request = ListMembersRequest.builder() .maxResults(50) // optional: limit results .build(); ListMembersResponse response = inspectorClient.listMembers(request); List<Member> members = response.members(); if (members == null || members.isEmpty()) { logger.info("No member accounts found for this Inspector administrator account."); return; } logger.info("Found {} member account(s):", members.size()); for (Member member : members) { logger.info(" - Account ID: {}, Status: {}", member.accountId(), member.relationshipStatusAsString()); } } catch (Inspector2Exception e) { logger.error("Failed to list members: {}", e.awsErrorDetails().errorMessage()); } } }
  • For API details, see ListMembers in AWS SDK for Java 2.x API Reference.

Basics

The following code example shows how to:

  • Check Inspector account status.

  • Ensure Inspector is enabled.

  • Analyze security findings.

  • Check scan coverage.

  • Create a findings filter.

  • List existing filters.

  • Check usage and costs.

  • Get coverage statistics.

  • Delete a filter.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

Run an interactive scenario demonstrating features.

/** * Before running this Java V2 code example, set up your development * environment, including your credentials. * <p> * For more information, see the following documentation topic: * <p> * 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<String> 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."); } } }

A wrapper class for SDK methods.

public class InspectorActions { private static Inspector2AsyncClient inspectorAsyncClient; private static final Logger logger = LoggerFactory.getLogger(InspectorActions.class); private static Inspector2AsyncClient getAsyncClient() { if (inspectorAsyncClient == null) { SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder() .maxConcurrency(100) .connectionTimeout(Duration.ofSeconds(60)) .readTimeout(Duration.ofSeconds(60)) .writeTimeout(Duration.ofSeconds(60)) .build(); ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder() .apiCallTimeout(Duration.ofMinutes(2)) .apiCallAttemptTimeout(Duration.ofSeconds(90)) .retryStrategy(RetryMode.STANDARD) .build(); inspectorAsyncClient = Inspector2AsyncClient.builder() .httpClient(httpClient) .overrideConfiguration(overrideConfig) .build(); } return inspectorAsyncClient; } /** * Enables AWS Inspector for the provided account(s) and default resource types. * * @param accountIds Optional list of AWS account IDs. */ public CompletableFuture<String> enableInspectorAsync(List<String> accountIds) { // The resource types to enable. List<ResourceScanType> resourceTypes = List.of( ResourceScanType.EC2, ResourceScanType.ECR, ResourceScanType.LAMBDA, ResourceScanType.LAMBDA_CODE ); // Build the request. EnableRequest.Builder requestBuilder = EnableRequest.builder() .resourceTypes(resourceTypes); if (accountIds != null && !accountIds.isEmpty()) { requestBuilder.accountIds(accountIds); } EnableRequest request = requestBuilder.build(); return getAsyncClient().enable(request) .whenComplete((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause(); if (cause instanceof ValidationException) { throw new CompletionException( "Inspector may already be enabled 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 enable Inspector: %s".formatted(exception.getMessage()), exception ); } }) .thenApply(response -> { StringBuilder summary = new StringBuilder("Enable results:\n"); if (response.accounts() == null || response.accounts().isEmpty()) { summary.append("Inspector may already be enabled 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(); }); } /** * Retrieves and prints the coverage statistics using a paginator. */ public CompletableFuture<String> listCoverageStatisticsAsync() { ListCoverageStatisticsRequest request = ListCoverageStatisticsRequest.builder() .build(); return getAsyncClient().listCoverageStatistics(request) .whenComplete((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause(); if (cause instanceof ValidationException) { throw new CompletionException( "Validation error listing coverage statistics: %s".formatted(cause.getMessage()), cause ); } if (cause instanceof Inspector2Exception) { Inspector2Exception e = (Inspector2Exception) cause; throw new CompletionException( "Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), e ); } throw new CompletionException( "Unexpected error listing coverage statistics: %s".formatted(exception.getMessage()), exception ); } }) .thenApply(response -> { List<Counts> countsList = response.countsByGroup(); StringBuilder sb = new StringBuilder(); if (countsList == null || countsList.isEmpty()) { sb.append("No coverage statistics available.\n"); return sb.toString(); } sb.append("Coverage Statistics:\n"); for (Counts c : countsList) { sb.append(" Group: ").append(c.groupKey()).append("\n") .append(" Total Count: ").append(c.count()).append("\n\n"); } return sb.toString(); }); } /** * Asynchronously lists Inspector2 usage totals using a paginator. * * @param accountIds optional list of account IDs * @param maxResults maximum results per page * @return CompletableFuture completed with formatted summary text */ public CompletableFuture<String> listUsageTotalsAsync( List<String> accountIds, int maxResults) { logger.info("Starting usage totals paginator…"); ListUsageTotalsRequest.Builder builder = ListUsageTotalsRequest.builder() .maxResults(maxResults); if (accountIds != null && !accountIds.isEmpty()) { builder.accountIds(accountIds); } ListUsageTotalsRequest request = builder.build(); ListUsageTotalsPublisher paginator = getAsyncClient().listUsageTotalsPaginator(request); StringBuilder summaryBuilder = new StringBuilder(); return paginator.subscribe(response -> { if (response.totals() != null && !response.totals().isEmpty()) { response.totals().forEach(total -> { if (total.usage() != null) { total.usage().forEach(usage -> { logger.info("Usage: {} = {}", usage.typeAsString(), usage.total()); summaryBuilder.append(usage.typeAsString()) .append(": ") .append(usage.total()) .append("\n"); }); } }); } else { logger.info("Page contained no usage totals."); } }).thenRun(() -> logger.info("Successfully listed usage totals.")) .thenApply(v -> { String summary = summaryBuilder.toString(); return summary.isEmpty() ? "No usage totals found." : summary; }).exceptionally(ex -> { Throwable cause = ex.getCause() != null ? ex.getCause() : ex; if (cause instanceof ValidationException ve) { throw new CompletionException( "Validation error listing usage totals: %s".formatted(ve.getMessage()), ve ); } throw new CompletionException("Failed to list usage totals", cause); }); } /** * Retrieves the account status using the Inspector2Client. */ public CompletableFuture<String> getAccountStatusAsync() { BatchGetAccountStatusRequest request = BatchGetAccountStatusRequest.builder() .accountIds(Collections.emptyList()) .build(); return getAsyncClient().batchGetAccountStatus(request) .whenComplete((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause(); if (cause instanceof AccessDeniedException) { throw new CompletionException( "You do not have sufficient access: %s".formatted(cause.getMessage()), cause ); } if (cause instanceof Inspector2Exception) { Inspector2Exception e = (Inspector2Exception) cause; throw new CompletionException( "Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), e ); } throw new CompletionException( "Unexpected error getting account status: %s".formatted(exception.getMessage()), exception ); } }) .thenApply(response -> { StringBuilder sb = new StringBuilder(); List<AccountState> accounts = response.accounts(); if (accounts == null || accounts.isEmpty()) { sb.append("No account status returned.\n"); return sb.toString(); } sb.append("Inspector Account Status:\n"); for (AccountState account : accounts) { String accountId = account.accountId() != null ? account.accountId() : "Unknown"; sb.append(" Account ID: ").append(accountId).append("\n"); // Overall account state if (account.state() != null && account.state().status() != null) { sb.append(" Overall State: ") .append(account.state().status()) .append("\n"); } else { sb.append(" Overall State: Unknown\n"); } // Resource state (only status available) ResourceState resources = account.resourceState(); if (resources != null) { sb.append(" Resource Status: available\n"); } sb.append("\n"); } return sb.toString(); }); } /** * Asynchronously lists Inspector2 filters using a paginator. * * @param maxResults maximum filters per page (nullable) * @return CompletableFuture completed with summary text */ public CompletableFuture<String> listFiltersAsync(Integer maxResults) { logger.info("Starting async filters paginator…"); ListFiltersRequest.Builder builder = ListFiltersRequest.builder(); if (maxResults != null) { builder.maxResults(maxResults); } ListFiltersRequest request = builder.build(); // Paginator from SDK ListFiltersPublisher paginator = getAsyncClient().listFiltersPaginator(request); StringBuilder collectedFilterIds = new StringBuilder(); return paginator.subscribe(response -> { response.filters().forEach(filter -> { logger.info("Filter: " + filter.arn()); collectedFilterIds.append(filter.arn()).append("\n"); }); }).thenApply(v -> { String result = collectedFilterIds.toString(); logger.info("Successfully listed all filters."); return result.isEmpty() ? "No filters found." : result; }).exceptionally(ex -> { Throwable cause = ex.getCause() != null ? ex.getCause() : ex; if (cause instanceof ValidationException ve) { throw new CompletionException( "Validation error listing filters: %s".formatted(ve.getMessage()), ve ); } throw new RuntimeException("Failed to list filters", ex); }); } /** * Creates a new LOW severity filter in AWS Inspector2 to suppress findings. * * @param filterName the name of the filter to create * @param description a descriptive string explaining the purpose of the filter * @return a CompletableFuture that completes with the ARN of the created filter * @throws CompletionException wraps any validation, Inspector2 service, or unexpected errors */ public CompletableFuture<String> createLowSeverityFilterAsync( String filterName, String description) { // Define a filter to match LOW severity findings. StringFilter severityFilter = StringFilter.builder() .value(Severity.LOW.toString()) .comparison(StringComparison.EQUALS) .build(); // Create filter criteria. FilterCriteria filterCriteria = FilterCriteria.builder() .severity(Collections.singletonList(severityFilter)) .build(); // Build the filter creation request. CreateFilterRequest request = CreateFilterRequest.builder() .name(filterName) .filterCriteria(filterCriteria) .action(FilterAction.SUPPRESS) .description(description) .build(); return getAsyncClient().createFilter(request) .whenComplete((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause() != null ? exception.getCause() : exception; if (cause instanceof ValidationException ve) { throw new CompletionException( "Validation error creating filter: %s".formatted(ve.getMessage()), ve ); } if (cause instanceof Inspector2Exception e) { throw new CompletionException( "Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), e ); } // Unexpected async error throw new CompletionException( "Unexpected error creating filter: %s".formatted(exception.getMessage()), exception ); } }) // Extract and return the ARN of the created filter. .thenApply(CreateFilterResponse::arn); } /** * Lists all AWS Inspector findings of LOW severity asynchronously. * * @return CompletableFuture containing a List of finding ARNs. * Returns an empty list if no LOW severity findings are found. */ public CompletableFuture<ArrayList<String>> listLowSeverityFindingsAsync() { logger.info("Starting async LOW severity findings paginator…"); // Build a filter criteria for LOW severity. StringFilter severityFilter = StringFilter.builder() .value(Severity.LOW.toString()) .comparison(StringComparison.EQUALS) .build(); FilterCriteria filterCriteria = FilterCriteria.builder() .severity(Collections.singletonList(severityFilter)) .build(); // Build the request. ListFindingsRequest request = ListFindingsRequest.builder() .filterCriteria(filterCriteria) .build(); ListFindingsPublisher paginator = getAsyncClient().listFindingsPaginator(request); List<String> allArns = Collections.synchronizedList(new ArrayList<>()); return paginator.subscribe(response -> { if (response.findings() != null && !response.findings().isEmpty()) { response.findings().forEach(finding -> { logger.info("Finding ARN: {}", finding.findingArn()); allArns.add(finding.findingArn()); }); } else { logger.info("Page contained no findings."); } }) .thenRun(() -> logger.info("Successfully listed all LOW severity findings.")) .thenApply(v -> new ArrayList<>(allArns)) // Return list instead of a formatted string .exceptionally(ex -> { Throwable cause = ex.getCause() != null ? ex.getCause() : ex; if (cause instanceof ValidationException ve) { throw new CompletionException( "Validation error listing LOW severity findings: %s".formatted(ve.getMessage()), ve ); } throw new RuntimeException("Failed to list LOW severity findings", ex); }); } /** * Lists AWS Inspector2 coverage details for scanned resources using a paginator. * * @param maxResults Maximum number of resources to return. */ public CompletableFuture<String> listCoverageAsync(int maxResults) { ListCoverageRequest initialRequest = ListCoverageRequest.builder() .maxResults(maxResults) .build(); ListCoveragePublisher paginator = getAsyncClient().listCoveragePaginator(initialRequest); StringBuilder summary = new StringBuilder(); return paginator.subscribe(response -> { List<CoveredResource> coveredResources = response.coveredResources(); if (coveredResources == null || coveredResources.isEmpty()) { summary.append("No coverage information available for this page.\n"); return; } Map<String, List<CoveredResource>> byType = coveredResources.stream() .collect(Collectors.groupingBy(CoveredResource::resourceTypeAsString)); byType.forEach((type, list) -> summary.append(" ").append(type) .append(": ").append(list.size()) .append(" resource(s)\n") ); // Include up to 3 sample resources per page for (int i = 0; i < Math.min(coveredResources.size(), 3); i++) { CoveredResource r = coveredResources.get(i); summary.append(" - ").append(r.resourceTypeAsString()) .append(": ").append(r.resourceId()).append("\n"); summary.append(" Scan Type: ").append(r.scanTypeAsString()).append("\n"); if (r.scanStatus() != null) { summary.append(" Status: ").append(r.scanStatus().statusCodeAsString()).append("\n"); } if (r.accountId() != null) { summary.append(" Account ID: ").append(r.accountId()).append("\n"); } summary.append("\n"); } }).thenApply(v -> { if (summary.length() == 0) { return "No coverage information found across all pages."; } else { return "Coverage Information:\n" + summary.toString(); } }).exceptionally(ex -> { Throwable cause = ex.getCause(); if (cause instanceof ValidationException) { throw new CompletionException( "Validation error listing coverage: " + cause.getMessage(), cause); } else if (cause instanceof Inspector2Exception e) { throw new CompletionException( "Inspector2 service error: " + e.awsErrorDetails().errorMessage(), e); } throw new CompletionException("Unexpected error listing coverage: " + ex.getMessage(), ex); }); } /** * Deletes an AWS Inspector2 filter. * * @param filterARN The ARN of the filter to delete. */ public CompletableFuture<Void> deleteFilterAsync(String filterARN) { return getAsyncClient().deleteFilter( DeleteFilterRequest.builder() .arn(filterARN) .build() ) .handle((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause() != null ? exception.getCause() : exception; if (cause instanceof ResourceNotFoundException rnfe) { String msg = "Filter not found for ARN: %s".formatted(filterARN); logger.warn(msg, rnfe); throw new CompletionException(msg, rnfe); } throw new RuntimeException("Failed to delete the filter: " + cause, cause); } return null; }); } /** * Retrieves detailed information about a specific AWS Inspector2 finding asynchronously. * * @param findingArn The ARN of the finding to look up. * @return A {@link CompletableFuture} that, when completed, provides a formatted string * containing all available details for the finding. * @throws RuntimeException if the async call to Inspector2 fails. */ public CompletableFuture<String> getFindingDetailsAsync(String findingArn) { BatchGetFindingDetailsRequest request = BatchGetFindingDetailsRequest.builder() .findingArns(findingArn) .build(); return getAsyncClient().batchGetFindingDetails(request) .thenApply(response -> { if (response.findingDetails() == null || response.findingDetails().isEmpty()) { return String.format("No details found for ARN: ", findingArn); } StringBuilder sb = new StringBuilder(); response.findingDetails().forEach(detail -> { sb.append("Finding ARN: ").append(detail.findingArn()).append("\n") .append("Risk Score: ").append(detail.riskScore()).append("\n"); // ExploitObserved timings if (detail.exploitObserved() != null) { sb.append("Exploit First Seen: ").append(detail.exploitObserved().firstSeen()).append("\n") .append("Exploit Last Seen: ").append(detail.exploitObserved().lastSeen()).append("\n"); } // Reference URLs if (detail.hasReferenceUrls()) { sb.append("Reference URLs:\n"); detail.referenceUrls().forEach(url -> sb.append(" • ").append(url).append("\n")); } // Tools if (detail.hasTools()) { sb.append("Tools:\n"); detail.tools().forEach(tool -> sb.append(" • ").append(tool).append("\n")); } // TTPs if (detail.hasTtps()) { sb.append("TTPs:\n"); detail.ttps().forEach(ttp -> sb.append(" • ").append(ttp).append("\n")); } // CWEs if (detail.hasCwes()) { sb.append("CWEs:\n"); detail.cwes().forEach(cwe -> sb.append(" • ").append(cwe).append("\n")); } // Evidence if (detail.hasEvidences()) { sb.append("Evidence:\n"); detail.evidences().forEach(ev -> { sb.append(" - Severity: ").append(ev.severity()).append("\n"); }); } sb.append("\n"); }); return sb.toString(); }) .exceptionally(ex -> { Throwable cause = ex.getCause() != null ? ex.getCause() : ex; if (cause instanceof ResourceNotFoundException rnfe) { return "Finding not found: %s".formatted(findingArn); } // Fallback for other exceptions throw new RuntimeException("Failed to get finding details for ARN: " + findingArn, cause); }); } /** * Asynchronously disables AWS Inspector for the specified accounts and resource types. * * @param accountIds a {@link List} of AWS account IDs for which to disable Inspector; * may be {@code null} or empty to target the current account * @return a {@link CompletableFuture} that, when completed, returns a {@link String} * summarizing the disable results for each account * @throws CompletionException if the disable operation fails due to validation errors, * service errors, or other exceptions * @see <a href="https://docs.aws.amazon.com/inspector/latest/APIReference/API_Disable.html"> * AWS Inspector2 Disable API</a> */ public CompletableFuture<String> disableInspectorAsync(List<String> accountIds) { // The resource types to disable. List<ResourceScanType> 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(); }); } }

Actions

The following code example shows how to use BatchGetAccountStatus.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Retrieves the account status using the Inspector2Client. */ public CompletableFuture<String> getAccountStatusAsync() { BatchGetAccountStatusRequest request = BatchGetAccountStatusRequest.builder() .accountIds(Collections.emptyList()) .build(); return getAsyncClient().batchGetAccountStatus(request) .whenComplete((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause(); if (cause instanceof AccessDeniedException) { throw new CompletionException( "You do not have sufficient access: %s".formatted(cause.getMessage()), cause ); } if (cause instanceof Inspector2Exception) { Inspector2Exception e = (Inspector2Exception) cause; throw new CompletionException( "Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), e ); } throw new CompletionException( "Unexpected error getting account status: %s".formatted(exception.getMessage()), exception ); } }) .thenApply(response -> { StringBuilder sb = new StringBuilder(); List<AccountState> accounts = response.accounts(); if (accounts == null || accounts.isEmpty()) { sb.append("No account status returned.\n"); return sb.toString(); } sb.append("Inspector Account Status:\n"); for (AccountState account : accounts) { String accountId = account.accountId() != null ? account.accountId() : "Unknown"; sb.append(" Account ID: ").append(accountId).append("\n"); // Overall account state if (account.state() != null && account.state().status() != null) { sb.append(" Overall State: ") .append(account.state().status()) .append("\n"); } else { sb.append(" Overall State: Unknown\n"); } // Resource state (only status available) ResourceState resources = account.resourceState(); if (resources != null) { sb.append(" Resource Status: available\n"); } sb.append("\n"); } return sb.toString(); }); }

The following code example shows how to use BatchGetFindingDetails.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Retrieves detailed information about a specific AWS Inspector2 finding asynchronously. * * @param findingArn The ARN of the finding to look up. * @return A {@link CompletableFuture} that, when completed, provides a formatted string * containing all available details for the finding. * @throws RuntimeException if the async call to Inspector2 fails. */ public CompletableFuture<String> getFindingDetailsAsync(String findingArn) { BatchGetFindingDetailsRequest request = BatchGetFindingDetailsRequest.builder() .findingArns(findingArn) .build(); return getAsyncClient().batchGetFindingDetails(request) .thenApply(response -> { if (response.findingDetails() == null || response.findingDetails().isEmpty()) { return String.format("No details found for ARN: ", findingArn); } StringBuilder sb = new StringBuilder(); response.findingDetails().forEach(detail -> { sb.append("Finding ARN: ").append(detail.findingArn()).append("\n") .append("Risk Score: ").append(detail.riskScore()).append("\n"); // ExploitObserved timings if (detail.exploitObserved() != null) { sb.append("Exploit First Seen: ").append(detail.exploitObserved().firstSeen()).append("\n") .append("Exploit Last Seen: ").append(detail.exploitObserved().lastSeen()).append("\n"); } // Reference URLs if (detail.hasReferenceUrls()) { sb.append("Reference URLs:\n"); detail.referenceUrls().forEach(url -> sb.append(" • ").append(url).append("\n")); } // Tools if (detail.hasTools()) { sb.append("Tools:\n"); detail.tools().forEach(tool -> sb.append(" • ").append(tool).append("\n")); } // TTPs if (detail.hasTtps()) { sb.append("TTPs:\n"); detail.ttps().forEach(ttp -> sb.append(" • ").append(ttp).append("\n")); } // CWEs if (detail.hasCwes()) { sb.append("CWEs:\n"); detail.cwes().forEach(cwe -> sb.append(" • ").append(cwe).append("\n")); } // Evidence if (detail.hasEvidences()) { sb.append("Evidence:\n"); detail.evidences().forEach(ev -> { sb.append(" - Severity: ").append(ev.severity()).append("\n"); }); } sb.append("\n"); }); return sb.toString(); }) .exceptionally(ex -> { Throwable cause = ex.getCause() != null ? ex.getCause() : ex; if (cause instanceof ResourceNotFoundException rnfe) { return "Finding not found: %s".formatted(findingArn); } // Fallback for other exceptions throw new RuntimeException("Failed to get finding details for ARN: " + findingArn, cause); }); }

The following code example shows how to use CreateFilter.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Creates a new LOW severity filter in AWS Inspector2 to suppress findings. * * @param filterName the name of the filter to create * @param description a descriptive string explaining the purpose of the filter * @return a CompletableFuture that completes with the ARN of the created filter * @throws CompletionException wraps any validation, Inspector2 service, or unexpected errors */ public CompletableFuture<String> createLowSeverityFilterAsync( String filterName, String description) { // Define a filter to match LOW severity findings. StringFilter severityFilter = StringFilter.builder() .value(Severity.LOW.toString()) .comparison(StringComparison.EQUALS) .build(); // Create filter criteria. FilterCriteria filterCriteria = FilterCriteria.builder() .severity(Collections.singletonList(severityFilter)) .build(); // Build the filter creation request. CreateFilterRequest request = CreateFilterRequest.builder() .name(filterName) .filterCriteria(filterCriteria) .action(FilterAction.SUPPRESS) .description(description) .build(); return getAsyncClient().createFilter(request) .whenComplete((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause() != null ? exception.getCause() : exception; if (cause instanceof ValidationException ve) { throw new CompletionException( "Validation error creating filter: %s".formatted(ve.getMessage()), ve ); } if (cause instanceof Inspector2Exception e) { throw new CompletionException( "Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), e ); } // Unexpected async error throw new CompletionException( "Unexpected error creating filter: %s".formatted(exception.getMessage()), exception ); } }) // Extract and return the ARN of the created filter. .thenApply(CreateFilterResponse::arn); }
  • For API details, see CreateFilter in AWS SDK for Java 2.x API Reference.

The following code example shows how to use DeleteFilter.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Deletes an AWS Inspector2 filter. * * @param filterARN The ARN of the filter to delete. */ public CompletableFuture<Void> deleteFilterAsync(String filterARN) { return getAsyncClient().deleteFilter( DeleteFilterRequest.builder() .arn(filterARN) .build() ) .handle((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause() != null ? exception.getCause() : exception; if (cause instanceof ResourceNotFoundException rnfe) { String msg = "Filter not found for ARN: %s".formatted(filterARN); logger.warn(msg, rnfe); throw new CompletionException(msg, rnfe); } throw new RuntimeException("Failed to delete the filter: " + cause, cause); } return null; }); }
  • For API details, see DeleteFilter in AWS SDK for Java 2.x API Reference.

The following code example shows how to use Disable.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Asynchronously disables AWS Inspector for the specified accounts and resource types. * * @param accountIds a {@link List} of AWS account IDs for which to disable Inspector; * may be {@code null} or empty to target the current account * @return a {@link CompletableFuture} that, when completed, returns a {@link String} * summarizing the disable results for each account * @throws CompletionException if the disable operation fails due to validation errors, * service errors, or other exceptions * @see <a href="https://docs.aws.amazon.com/inspector/latest/APIReference/API_Disable.html"> * AWS Inspector2 Disable API</a> */ public CompletableFuture<String> disableInspectorAsync(List<String> accountIds) { // The resource types to disable. List<ResourceScanType> 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(); }); }
  • For API details, see Disable in AWS SDK for Java 2.x API Reference.

The following code example shows how to use Enable.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Enables AWS Inspector for the provided account(s) and default resource types. * * @param accountIds Optional list of AWS account IDs. */ public CompletableFuture<String> enableInspectorAsync(List<String> accountIds) { // The resource types to enable. List<ResourceScanType> resourceTypes = List.of( ResourceScanType.EC2, ResourceScanType.ECR, ResourceScanType.LAMBDA, ResourceScanType.LAMBDA_CODE ); // Build the request. EnableRequest.Builder requestBuilder = EnableRequest.builder() .resourceTypes(resourceTypes); if (accountIds != null && !accountIds.isEmpty()) { requestBuilder.accountIds(accountIds); } EnableRequest request = requestBuilder.build(); return getAsyncClient().enable(request) .whenComplete((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause(); if (cause instanceof ValidationException) { throw new CompletionException( "Inspector may already be enabled 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 enable Inspector: %s".formatted(exception.getMessage()), exception ); } }) .thenApply(response -> { StringBuilder summary = new StringBuilder("Enable results:\n"); if (response.accounts() == null || response.accounts().isEmpty()) { summary.append("Inspector may already be enabled 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(); }); }
  • For API details, see Enable in AWS SDK for Java 2.x API Reference.

The following code example shows how to use ListCoverage.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Lists AWS Inspector2 coverage details for scanned resources using a paginator. * * @param maxResults Maximum number of resources to return. */ public CompletableFuture<String> listCoverageAsync(int maxResults) { ListCoverageRequest initialRequest = ListCoverageRequest.builder() .maxResults(maxResults) .build(); ListCoveragePublisher paginator = getAsyncClient().listCoveragePaginator(initialRequest); StringBuilder summary = new StringBuilder(); return paginator.subscribe(response -> { List<CoveredResource> coveredResources = response.coveredResources(); if (coveredResources == null || coveredResources.isEmpty()) { summary.append("No coverage information available for this page.\n"); return; } Map<String, List<CoveredResource>> byType = coveredResources.stream() .collect(Collectors.groupingBy(CoveredResource::resourceTypeAsString)); byType.forEach((type, list) -> summary.append(" ").append(type) .append(": ").append(list.size()) .append(" resource(s)\n") ); // Include up to 3 sample resources per page for (int i = 0; i < Math.min(coveredResources.size(), 3); i++) { CoveredResource r = coveredResources.get(i); summary.append(" - ").append(r.resourceTypeAsString()) .append(": ").append(r.resourceId()).append("\n"); summary.append(" Scan Type: ").append(r.scanTypeAsString()).append("\n"); if (r.scanStatus() != null) { summary.append(" Status: ").append(r.scanStatus().statusCodeAsString()).append("\n"); } if (r.accountId() != null) { summary.append(" Account ID: ").append(r.accountId()).append("\n"); } summary.append("\n"); } }).thenApply(v -> { if (summary.length() == 0) { return "No coverage information found across all pages."; } else { return "Coverage Information:\n" + summary.toString(); } }).exceptionally(ex -> { Throwable cause = ex.getCause(); if (cause instanceof ValidationException) { throw new CompletionException( "Validation error listing coverage: " + cause.getMessage(), cause); } else if (cause instanceof Inspector2Exception e) { throw new CompletionException( "Inspector2 service error: " + e.awsErrorDetails().errorMessage(), e); } throw new CompletionException("Unexpected error listing coverage: " + ex.getMessage(), ex); }); }
  • For API details, see ListCoverage in AWS SDK for Java 2.x API Reference.

The following code example shows how to use ListCoverageStatistics.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Retrieves and prints the coverage statistics using a paginator. */ public CompletableFuture<String> listCoverageStatisticsAsync() { ListCoverageStatisticsRequest request = ListCoverageStatisticsRequest.builder() .build(); return getAsyncClient().listCoverageStatistics(request) .whenComplete((response, exception) -> { if (exception != null) { Throwable cause = exception.getCause(); if (cause instanceof ValidationException) { throw new CompletionException( "Validation error listing coverage statistics: %s".formatted(cause.getMessage()), cause ); } if (cause instanceof Inspector2Exception) { Inspector2Exception e = (Inspector2Exception) cause; throw new CompletionException( "Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), e ); } throw new CompletionException( "Unexpected error listing coverage statistics: %s".formatted(exception.getMessage()), exception ); } }) .thenApply(response -> { List<Counts> countsList = response.countsByGroup(); StringBuilder sb = new StringBuilder(); if (countsList == null || countsList.isEmpty()) { sb.append("No coverage statistics available.\n"); return sb.toString(); } sb.append("Coverage Statistics:\n"); for (Counts c : countsList) { sb.append(" Group: ").append(c.groupKey()).append("\n") .append(" Total Count: ").append(c.count()).append("\n\n"); } return sb.toString(); }); }

The following code example shows how to use ListFilters.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Asynchronously lists Inspector2 filters using a paginator. * * @param maxResults maximum filters per page (nullable) * @return CompletableFuture completed with summary text */ public CompletableFuture<String> listFiltersAsync(Integer maxResults) { logger.info("Starting async filters paginator…"); ListFiltersRequest.Builder builder = ListFiltersRequest.builder(); if (maxResults != null) { builder.maxResults(maxResults); } ListFiltersRequest request = builder.build(); // Paginator from SDK ListFiltersPublisher paginator = getAsyncClient().listFiltersPaginator(request); StringBuilder collectedFilterIds = new StringBuilder(); return paginator.subscribe(response -> { response.filters().forEach(filter -> { logger.info("Filter: " + filter.arn()); collectedFilterIds.append(filter.arn()).append("\n"); }); }).thenApply(v -> { String result = collectedFilterIds.toString(); logger.info("Successfully listed all filters."); return result.isEmpty() ? "No filters found." : result; }).exceptionally(ex -> { Throwable cause = ex.getCause() != null ? ex.getCause() : ex; if (cause instanceof ValidationException ve) { throw new CompletionException( "Validation error listing filters: %s".formatted(ve.getMessage()), ve ); } throw new RuntimeException("Failed to list filters", ex); }); }
  • For API details, see ListFilters in AWS SDK for Java 2.x API Reference.

The following code example shows how to use ListFindings.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Lists all AWS Inspector findings of LOW severity asynchronously. * * @return CompletableFuture containing a List of finding ARNs. * Returns an empty list if no LOW severity findings are found. */ public CompletableFuture<ArrayList<String>> listLowSeverityFindingsAsync() { logger.info("Starting async LOW severity findings paginator…"); // Build a filter criteria for LOW severity. StringFilter severityFilter = StringFilter.builder() .value(Severity.LOW.toString()) .comparison(StringComparison.EQUALS) .build(); FilterCriteria filterCriteria = FilterCriteria.builder() .severity(Collections.singletonList(severityFilter)) .build(); // Build the request. ListFindingsRequest request = ListFindingsRequest.builder() .filterCriteria(filterCriteria) .build(); ListFindingsPublisher paginator = getAsyncClient().listFindingsPaginator(request); List<String> allArns = Collections.synchronizedList(new ArrayList<>()); return paginator.subscribe(response -> { if (response.findings() != null && !response.findings().isEmpty()) { response.findings().forEach(finding -> { logger.info("Finding ARN: {}", finding.findingArn()); allArns.add(finding.findingArn()); }); } else { logger.info("Page contained no findings."); } }) .thenRun(() -> logger.info("Successfully listed all LOW severity findings.")) .thenApply(v -> new ArrayList<>(allArns)) // Return list instead of a formatted string .exceptionally(ex -> { Throwable cause = ex.getCause() != null ? ex.getCause() : ex; if (cause instanceof ValidationException ve) { throw new CompletionException( "Validation error listing LOW severity findings: %s".formatted(ve.getMessage()), ve ); } throw new RuntimeException("Failed to list LOW severity findings", ex); }); }
  • For API details, see ListFindings in AWS SDK for Java 2.x API Reference.

The following code example shows how to use ListUsageTotals.

SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

/** * Asynchronously lists Inspector2 usage totals using a paginator. * * @param accountIds optional list of account IDs * @param maxResults maximum results per page * @return CompletableFuture completed with formatted summary text */ public CompletableFuture<String> listUsageTotalsAsync( List<String> accountIds, int maxResults) { logger.info("Starting usage totals paginator…"); ListUsageTotalsRequest.Builder builder = ListUsageTotalsRequest.builder() .maxResults(maxResults); if (accountIds != null && !accountIds.isEmpty()) { builder.accountIds(accountIds); } ListUsageTotalsRequest request = builder.build(); ListUsageTotalsPublisher paginator = getAsyncClient().listUsageTotalsPaginator(request); StringBuilder summaryBuilder = new StringBuilder(); return paginator.subscribe(response -> { if (response.totals() != null && !response.totals().isEmpty()) { response.totals().forEach(total -> { if (total.usage() != null) { total.usage().forEach(usage -> { logger.info("Usage: {} = {}", usage.typeAsString(), usage.total()); summaryBuilder.append(usage.typeAsString()) .append(": ") .append(usage.total()) .append("\n"); }); } }); } else { logger.info("Page contained no usage totals."); } }).thenRun(() -> logger.info("Successfully listed usage totals.")) .thenApply(v -> { String summary = summaryBuilder.toString(); return summary.isEmpty() ? "No usage totals found." : summary; }).exceptionally(ex -> { Throwable cause = ex.getCause() != null ? ex.getCause() : ex; if (cause instanceof ValidationException ve) { throw new CompletionException( "Validation error listing usage totals: %s".formatted(ve.getMessage()), ve ); } throw new CompletionException("Failed to list usage totals", cause); }); }
  • For API details, see ListUsageTotals in AWS SDK for Java 2.x API Reference.