使用適用於 Java 的 SDK 2.x 的 Amazon Inspector 範例 - AWS SDK 程式碼範例

文件 AWS 開發套件範例 GitHub 儲存庫中有更多可用的 AWS SDK 範例

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用適用於 Java 的 SDK 2.x 的 Amazon Inspector 範例

下列程式碼範例示範如何使用 AWS SDK for Java 2.x 搭配 Amazon Inspector 執行動作和實作常見案例。

基本概念是程式碼範例,這些範例說明如何在服務內執行基本操作。

Actions 是大型程式的程式碼摘錄,必須在內容中執行。雖然動作會告訴您如何呼叫個別服務函數,但您可以在其相關情境中查看內容中的動作。

每個範例均包含完整原始碼的連結,您可在連結中找到如何設定和執行內容中程式碼的相關指示。

開始使用

下列程式碼範例示範如何開始使用 。

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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()); } } }
  • 如需 API 詳細資訊,請參閱AWS SDK for Java 2.x 《 API 參考》中的 ListMembers

基本概念

以下程式碼範例顯示做法:

  • 檢查 Inspector 帳戶狀態。

  • 確保 Inspector 已啟用。

  • 分析安全性問題清單。

  • 檢查掃描涵蓋範圍。

  • 建立問題清單篩選條件。

  • 列出現有的篩選條件。

  • 檢查用量和成本。

  • 取得涵蓋範圍統計資料。

  • 刪除篩選條件。

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

執行展示 功能的互動式情境。

/** * 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."); } } }

SDK 方法的包裝函式類別。

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(); }); } }

動作

以下程式碼範例顯示如何使用 BatchGetAccountStatus

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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(); }); }
  • 如需 API 詳細資訊,請參閱AWS SDK for Java 2.x 《 API 參考》中的 BatchGetAccountStatus

以下程式碼範例顯示如何使用 BatchGetFindingDetails

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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); }); }

以下程式碼範例顯示如何使用 CreateFilter

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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); }
  • 如需 API 詳細資訊,請參閱《AWS SDK for Java 2.x API 參考》中的 CreateFilter

以下程式碼範例顯示如何使用 DeleteFilter

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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; }); }
  • 如需 API 詳細資訊,請參閱AWS SDK for Java 2.x 《 API 參考》中的 DeleteFilter

以下程式碼範例顯示如何使用 Disable

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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(); }); }
  • 如需 API 詳細資訊,請參閱AWS SDK for Java 2.x 《 API 參考》中的停用

以下程式碼範例顯示如何使用 Enable

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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(); }); }
  • 如需 API 詳細資訊,請參閱《 AWS SDK for Java 2.x API 參考》中的啟用

以下程式碼範例顯示如何使用 ListCoverage

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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); }); }
  • 如需 API 詳細資訊,請參閱AWS SDK for Java 2.x 《 API 參考》中的 ListCoverage

以下程式碼範例顯示如何使用 ListCoverageStatistics

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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(); }); }

以下程式碼範例顯示如何使用 ListFilters

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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); }); }
  • 如需 API 詳細資訊,請參閱AWS SDK for Java 2.x 《 API 參考》中的 ListFilters

以下程式碼範例顯示如何使用 ListFindings

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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); }); }
  • 如需 API 詳細資訊,請參閱AWS SDK for Java 2.x 《 API 參考》中的 ListFindings

以下程式碼範例顯示如何使用 ListUsageTotals

SDK for Java 2.x
注意

GitHub 上提供更多範例。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/** * 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); }); }
  • 如需 API 詳細資訊,請參閱AWS SDK for Java 2.x 《 API 參考》中的 ListUsageTotals