使用 Security Command Center API 列出资源

资产是组织的 Google Cloud 资源,例如 Compute Engine 实例或 Cloud Storage 存储桶。

本指南介绍了如何使用 Security Command Center 客户端库访问 Security Command Center 为项目或组织中的资产维护的已弃用记录。

Security Command Center 仅会保留 Cloud Asset Inventory 中部分资产的记录。如需查看您的环境中的资产的完整列表,请使用 Cloud Asset Inventory 列出资产。

详情请参阅以下内容:

IAM 角色的授予级别

Security Command Center 的 IAM 角色可以在组织、文件夹或项目级层授予。您能否查看、修改、创建或更新发现结果、资产和安全来源,取决于您获授予的访问权限级别。要详细了解 Security Command Center 角色的相关信息,请参阅访问权限控制

准备工作

在设置来源之前,您需要完成以下操作:

页面大小

所有 Security Command Center list API 都是分页的。每个响应都会返回一个结果页面和一个返回下一页的令牌。页面大小可配置。默认的 pageSize 为 10,可设置为最小值 1 和最大值 1000。

资源类型

Security Command Center 中的 resourceType 特性使用与 Cloud Asset Inventory 不同的命名惯例。如需查看资源类型格式的列表,请参阅 Security Command Center 中支持的资源类型

列出所有素材资源

以下示例展示了如何列出所有资源:

gcloud

如需列出项目、文件夹或组织中的所有资产,请运行以下命令 命令:

gcloud scc assets list PARENT_ID

PARENT_ID 替换为以下某个值:

  • 组织 ID,格式如下:ORGANIZATION_ID(仅限数字 ID)
  • 文件夹 ID,格式如下:folders/FOLDER_ID
  • 项目 ID,格式如下:projects/PROJECT_ID

如需查看更多示例,请运行以下命令:

 gcloud scc assets list --help

如需查看文档中的示例,请参阅 gcloud scc assets list

Python

from google.cloud import securitycenter

client = securitycenter.SecurityCenterClient()
# 'parent' must be in one of the following formats:
#   "organizations/{organization_id}"
#   "projects/{project_id}"
#   "folders/{folder_id}"
parent = f"organizations/{organization_id}"

# Call the API and print results.
asset_iterator = client.list_assets(request={"parent": parent})
for i, asset_result in enumerate(asset_iterator):
    print(i, asset_result)

Java

static ImmutableList<ListAssetsResult> listAssets(OrganizationName organizationName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Start setting up a request to search for all assets in an organization, project, or folder.
    //
    // Parent must be in one of the following formats:
    //    OrganizationName organizationName = OrganizationName.of("organization-id");
    //    ProjectName projectName = ProjectName.of("project-id");
    //    FolderName folderName = FolderName.of("folder-id");
    ListAssetsRequest.Builder request =
        ListAssetsRequest.newBuilder().setParent(organizationName.toString());

    // Call the API.
    ListAssetsPagedResponse response = client.listAssets(request.build());

    // This creates one list for all assets.  If your organization has a large number of assets
    // this can cause out of memory issues.  You can process them incrementally by returning
    // the Iterable returned response.iterateAll() directly.
    ImmutableList<ListAssetsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("All assets:");
    System.out.println(results);
    return results;
  } catch (IOException e) {
    throw new RuntimeException("Couldn't create client.", e);
  }
}

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"cloud.google.com/go/securitycenter/apiv1/securitycenterpb"
	"google.golang.org/api/iterator"
)

// listAllAssets prints every asset to w for orgID. orgID is the numeric
// Organization ID.
func listAllAssets(w io.Writer, orgID string) error {
	// orgID := "12321311"
	// Instantiate a context and a security service client to make API calls.
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %w", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.

	req := &securitycenterpb.ListAssetsRequest{
		// Parent must be in one of the following formats:
		//		"organizations/{orgId}"
		//		"projects/{projectId}"
		//		"folders/{folderId}"
		Parent: fmt.Sprintf("organizations/%s", orgID),
	}

	assetsFound := 0
	it := client.ListAssets(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("ListAssets: %w", err)
		}
		asset := result.Asset
		properties := asset.SecurityCenterProperties
		fmt.Fprintf(w, "Asset Name: %s,", asset.Name)
		fmt.Fprintf(w, "Resource Name %s,", properties.ResourceName)
		fmt.Fprintf(w, "Resource Type %s\n", properties.ResourceType)
		assetsFound++
	}
	return nil
}

Node.js

// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center');

// Creates a new client.
const client = new SecurityCenterClient();
//  organizationId is the numeric ID of the organization.
/*
 * TODO(developer): Uncomment the following lines
 */
// parent: must be in one of the following formats:
//    `organizations/${organization_id}`
//    `projects/${project_id}`
//    `folders/${folder_id}`
const parent = `organizations/${organizationId}`;
// Call the API with automatic pagination.
async function listAssets() {
  const [response] = await client.listAssets({parent: parent});
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.asset.name} ${
        result.asset.securityCenterProperties.resourceName
      }`
    )
  );
}

listAssets();

每个资源的输出都是一个 JSON 对象,类似如下:

asset:
  createTime: '2020-10-05T17:55:14.823Z'
  iamPolicy:
    policyBlob: '{"bindings":[{"role":"roles/owner","members":["serviceAccount:SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com","user:USER_EMAIL@gmail.com"]}]}'
  name: organizations/ORGANIZATION_ID/assets/ASSET_ID
  resourceProperties:
    createTime: '2020-10-05T17:36:17.915Z'
    lifecycleState: ACTIVE
    name: PROJECT_ID
    parent: '{"id":"ORGANIZATION_ID","type":"organization"}'
    projectId: PROJECT_ID
    projectNumber: 'PROJECT_NUMBER'
  securityCenterProperties:
    resourceDisplayName: PROJECT_ID
    resourceName: //cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER
    resourceOwners:
    - serviceAccount:SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com
    - user:USER_EMAIL@gmail.com
    resourceParent: //cloudresourcemanager.googleapis.com/organizations/ORGANIZATION_ID
    resourceParentDisplayName: ORGANIZATION_NAME
    resourceProject: //cloudresourcemanager.googleapis.com/projects/PROJECT_NUMBER
    resourceProjectDisplayName: PROJECT_ID
    resourceType: google.cloud.resourcemanager.Project
  securityMarks:
    name: organizations/ORGANIZATION_ID/assets/ASSET_ID/securityMarks
  updateTime: '2020-10-05T17:55:14.823Z'

过滤资源

项目、文件夹或组织可能包含多个素材资源。以上示例不使用任何过滤条件,因此会返回所有资源。Security Command Center 可让您使用资源过滤条件来获取特定资源的相关信息。过滤条件与 SQL 语句中的“where”子句类似,但列除外,它们会应用于 API 返回的对象。

上述示例中的示例输出显示了可用于资产过滤条件的一些字段、子字段及其属性。Security Command Center 支持完整的 JSON 数组和对象作为潜在属性类型。您可以按以下条件进行过滤:

  • 数组元素
  • 对象中部分字符串匹配的完整 JSON 对象
  • JSON 对象子字段

子字段必须是数字、字符串或布尔值,过滤条件表达式必须使用以下比较运算符:

  • 字符串
    • 完全相等运算符 =
    • 部分字符串与 : 匹配
  • 数字
    • 不等式 <><=>=
    • 相等 =
  • 布尔值
    • 相等 =

以下示例过滤资源:

gcloud

使用以下命令过滤资源:

gcloud scc assets list PARENT_ID --filter="FILTER"

请替换以下内容:

  • FILTER 替换为您需要使用的过滤条件。例如: 以下过滤条件仅返回项目资源:
    --filter="security_center_properties.resource_type=\"google.cloud.resourcemanager.Project\""
  • PARENT_ID 替换为以下某个值:
    • 组织 ID,格式如下:ORGANIZATION_ID(仅限数字 ID)
    • 文件夹 ID,格式如下:folders/FOLDER_ID
    • 项目 ID,格式如下:projects/PROJECT_ID

如需查看更多示例,请运行以下命令:

gcloud scc assets list --help

如需查看文档中的示例,请参阅 gcloud scc 资产列表

Python

from google.cloud import securitycenter

client = securitycenter.SecurityCenterClient()

# 'parent' must be in one of the following formats:
#   "organizations/{organization_id}"
#   "projects/{project_id}"
#   "folders/{folder_id}"
parent = f"organizations/{organization_id}"

project_filter = (
    "security_center_properties.resource_type="
    + '"google.cloud.resourcemanager.Project"'
)
# Call the API and print results.
asset_iterator = client.list_assets(
    request={"parent": parent, "filter": project_filter}
)
for i, asset_result in enumerate(asset_iterator):
    print(i, asset_result)

Java

static ImmutableList<ListAssetsResult> listAssetsWithFilter(OrganizationName organizationName) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Start setting up a request to search for all assets in an organization, project, or folder.
    //
    // Parent must be in one of the following formats:
    //    OrganizationName organizationName = OrganizationName.of("organization-id");
    //    ProjectName projectName = ProjectName.of("project-id");
    //    FolderName folderName = FolderName.of("folder-id");
    ListAssetsRequest.Builder request =
        ListAssetsRequest.newBuilder()
            .setParent(organizationName.toString())
            .setFilter(
                "security_center_properties.resource_type=\"google.cloud.resourcemanager.Project\"");

    // Call the API.
    ListAssetsPagedResponse response = client.listAssets(request.build());

    // This creates one list for all assets.  If your organization has a large number of assets
    // this can cause out of memory issues.  You can process them incrementally by returning
    // the Iterable returned response.iterateAll() directly.
    ImmutableList<ListAssetsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("Project assets:");
    System.out.println(results);
    return results;
  } catch (IOException e) {
    throw new RuntimeException("Couldn't create client.", e);
  }
}

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"cloud.google.com/go/securitycenter/apiv1/securitycenterpb"
	"google.golang.org/api/iterator"
)

// listAllProjectAssets lists all current GCP project assets in orgID and
// prints out results to w. orgID is the numeric organization ID of interest.
func listAllProjectAssets(w io.Writer, orgID string) error {
	// orgID := "12321311"
	// Instantiate a context and a security service client to make API calls.
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %w", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.
	req := &securitycenterpb.ListAssetsRequest{
		// Parent must be in one of the following formats:
		//		"organizations/{orgId}"
		//		"projects/{projectId}"
		//		"folders/{folderId}"
		Parent: fmt.Sprintf("organizations/%s", orgID),
		Filter: `security_center_properties.resource_type="google.cloud.resourcemanager.Project"`,
	}

	assetsFound := 0
	it := client.ListAssets(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("ListAssets: %w", err)
		}
		asset := result.Asset
		properties := asset.SecurityCenterProperties
		fmt.Fprintf(w, "Asset Name: %s,", asset.Name)
		fmt.Fprintf(w, "Resource Name %s,", properties.ResourceName)
		fmt.Fprintf(w, "Resource Type %s\n", properties.ResourceType)
		assetsFound++
	}
	return nil
}

Node.js

// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center');

// Creates a new client.
const client = new SecurityCenterClient();
//  organizationId is the numeric ID of the organization.
/*
 * TODO(developer): Uncomment the following lines
 */
// const organizationId = "1234567777";
const orgName = client.organizationPath(organizationId);

// Call the API with automatic pagination.
// You can also list assets in a project/ folder. To do so, modify the parent
// value and filter condition.
async function listFilteredAssets() {
  const [response] = await client.listAssets({
    parent: orgName,
    filter:
      'security_center_properties.resource_type="google.cloud.resourcemanager.Project"',
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.asset.name} ${
        result.asset.securityCenterProperties.resourceName
      } ${result.stateChange}`
    )
  );
}

listFilteredAssets();

在某个时间点列出

前面的示例展示了如何列出当前的资源组。您还可以通过 Security Command Center 查看资产的历史快照。 以下示例返回特定时间点的所有资源的状态。Security Command Center 支持毫秒级时间分辨率。

gcloud

使用以下命令列出特定时间点的资源:

gcloud scc assets list PARENT_ID --read-time="READ_TIME"

请替换以下内容:

  • READ_TIME 替换为要列出资源的时间。请使用以下格式:YYYY-MM-DDThh:mm:ss.ffffffZ。 例如:
    --read-time="2022-12-21T07:00:06.861Z"
  • PARENT_ID 替换为以下某个值:
    • 组织 ID,格式如下:ORGANIZATION_ID(仅限数字 ID)
    • 项目 ID,格式如下:projects/PROJECT_ID
    • 文件夹 ID,格式如下:folders/FOLDER_ID

如需查看更多示例,请运行以下命令:

gcloud scc assets list --help

如需查看文档中的示例,请参阅 gcloud scc assets list

Python

from datetime import datetime, timedelta, timezone

from google.cloud import securitycenter

client = securitycenter.SecurityCenterClient()

# 'parent' must be in one of the following formats:
#   "organizations/{organization_id}"
#   "projects/{project_id}"
#   "folders/{folder_id}"
parent = f"organizations/{organization_id}"

project_filter = (
    "security_center_properties.resource_type="
    + '"google.cloud.resourcemanager.Project"'
)

# Lists assets as of yesterday.
read_time = datetime.now(tz=timezone.utc) - timedelta(days=1)

# Call the API and print results.
asset_iterator = client.list_assets(
    request={"parent": parent, "filter": project_filter, "read_time": read_time}
)
for i, asset_result in enumerate(asset_iterator):
    print(i, asset_result)

Java

static ImmutableList<ListAssetsResult> listAssetsAsOfYesterday(
    OrganizationName organizationName, Instant asOf) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {
    // Start setting up a request to search for all assets in an organization, project, or folder.
    //
    // Parent must be in one of the following formats:
    //    OrganizationName organizationName = OrganizationName.of("organization-id");
    //    ProjectName projectName = ProjectName.of("project-id");
    //    FolderName folderName = FolderName.of("folder-id");
    // Initialize the builder with the parent and filter
    ListAssetsRequest.Builder request =
        ListAssetsRequest.newBuilder()
            .setParent(organizationName.toString())
            .setFilter(
                "security_center_properties.resource_type=\"google.cloud.resourcemanager.Project\"");

    // Set read time to either the instant passed in or one day ago.
    asOf = MoreObjects.firstNonNull(asOf, Instant.now().minus(Duration.ofDays(1)));
    request.getReadTimeBuilder().setSeconds(asOf.getEpochSecond()).setNanos(asOf.getNano());

    // Call the API.
    ListAssetsPagedResponse response = client.listAssets(request.build());

    // This creates one list for all assets.  If your organization has a large number of assets
    // this can cause out of memory issues.  You can process them incrementally by returning
    // the Iterable returned response.iterateAll() directly.
    ImmutableList<ListAssetsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("Projects:");
    System.out.println(results);
    return results;
  } catch (IOException e) {
    throw new RuntimeException("Couldn't create client.", e);
  }
}

Go

import (
	"context"
	"fmt"
	"io"
	"time"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"cloud.google.com/go/securitycenter/apiv1/securitycenterpb"
	"github.com/golang/protobuf/ptypes"
	"google.golang.org/api/iterator"
)

// listAllProjectAssets lists all GCP Projects in orgID at asOf time and prints
// out results to w. orgID is the numeric organization ID of interest.
func listAllProjectAssetsAtTime(w io.Writer, orgID string, asOf time.Time) error {
	// orgID := "12321311"
	// Instantiate a context and a security service client to make API calls.
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %w", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.

	// Convert the time to a Timestamp protobuf
	readTime, err := ptypes.TimestampProto(asOf)
	if err != nil {
		return fmt.Errorf("TimestampProto(%v): %w", asOf, err)
	}

	// You can also list assets in a project/ folder. To do so, modify the parent and
	// filter condition.
	req := &securitycenterpb.ListAssetsRequest{
		// Parent must be in one of the following formats:
		//		"organizations/{orgId}"
		//		"projects/{projectId}"
		//		"folders/{folderId}"
		Parent:   fmt.Sprintf("organizations/%s", orgID),
		Filter:   `security_center_properties.resource_type="google.cloud.resourcemanager.Project"`,
		ReadTime: readTime,
	}

	assetsFound := 0
	it := client.ListAssets(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("ListAssets: %w", err)
		}
		asset := result.Asset
		properties := asset.SecurityCenterProperties
		fmt.Fprintf(w, "Asset Name: %s,", asset.Name)
		fmt.Fprintf(w, "Resource Name %s,", properties.ResourceName)
		fmt.Fprintf(w, "Resource Type %s\n", properties.ResourceType)
		assetsFound++
	}
	return nil
}

Node.js

// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center');

// Creates a new client.
const client = new SecurityCenterClient();
//  organizationId is the numeric ID of the organization.
/*
 * TODO(developer): Uncomment the following lines
 */
// parent: must be in one of the following formats:
//    `organizations/${organization_id}`
//    `projects/${project_id}`
//    `folders/${folder_id}`
const parent = `organizations/${organizationId}`;

const oneDayAgo = new Date();
oneDayAgo.setDate(oneDayAgo.getDate() - 1);

// Call the API with automatic pagination.
async function listAssetsAtTime() {
  const [response] = await client.listAssets({
    parent: parent,
    filter:
      'security_center_properties.resource_type="google.cloud.resourcemanager.Project"',
    // readTime must be in the form of a google.protobuf.Timestamp object
    // which takes seconds and nanoseconds.
    readTime: {
      seconds: Math.floor(oneDayAgo.getTime() / 1000),
      nanos: (oneDayAgo.getTime() % 1000) * 1e6,
    },
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.asset.name} ${
        result.asset.securityCenterProperties.resourceName
      }`
    )
  );
}

listAssetsAtTime();

列出状态变化的资源

Security Command Center 可让您比较两个时间点的资源,以确定该资源在指定时间段内是添加、移除还是存在。以下示例将 READ_TIME 时存在的项目与 COMPARE_DURATION 指定的前一个时间点进行比较。COMPARE_DURATION 以秒为单位。

设置 COMPARE_DURATION 后,列出的资源结果的 stateChange 特性使用以下值之一更新:

  • ADDED:该资源在 compareDuration 的开始不存在,但在 readTime 时存在。
  • REMOVED:资源在 compareDuration 的开始存在,但在 readTime 时不存在。
  • ACTIVE:资源在 compareDuration 定义的时间段的开始和结束以及 readTime 时都存在。

gcloud

使用以下命令比较两个时间点的资源状态:

gcloud scc assets list PARENT_ID \
    --filter="FILTER" \
    --read-time=READ_TIME \
    --compare-duration=COMPARE_DURATION

请替换以下内容:

  • COMPARE_DURATION,其中包含一个秒数,用于定义 --read-time 标志上指定时间之前的时间点。例如:
    --compare-duration=84600s
  • FILTER 替换为您需要使用的过滤条件。例如: 以下过滤条件仅返回项目资源:
    --filter="security_center_properties.resource_type=\"google.cloud.resourcemanager.Project\""
  • PARENT_ID 替换为以下某个值:
    • 组织 ID,格式如下:ORGANIZATION_ID(仅限数字 ID)
    • 项目 ID,格式如下:projects/PROJECT_ID
    • 文件夹 ID,格式如下:folders/FOLDER_ID
  • READ_TIME 替换为要列出资源的时间。请使用以下格式:YYYY-MM-DDThh:mm:ss.ffffffZ。例如:
    --read-time="2022-12-21T07:00:06.861Z"
    如需查看更多示例,请运行以下命令:
gcloud scc assets list --help

如需查看文档中的示例,请参阅 gcloud scc assets list

Python

from datetime import timedelta

from google.cloud import securitycenter

client = securitycenter.SecurityCenterClient()

# 'parent' must be in one of the following formats:
#   "organizations/{organization_id}"
#   "projects/{project_id}"
#   "folders/{folder_id}"
parent = f"organizations/{organization_id}"
project_filter = (
    "security_center_properties.resource_type="
    + '"google.cloud.resourcemanager.Project"'
)

# List assets and their state change the last 30 days
compare_delta = timedelta(days=30)

# Call the API and print results.
asset_iterator = client.list_assets(
    request={
        "parent": parent,
        "filter": project_filter,
        "compare_duration": compare_delta,
    }
)
for i, asset in enumerate(asset_iterator):
    print(i, asset)

Java

static ImmutableList<ListAssetsResult> listAssetAndStatusChanges(
    OrganizationName organizationName, Duration timeSpan, Instant asOf) {
  try (SecurityCenterClient client = SecurityCenterClient.create()) {

    // Start setting up a request to search for all assets in an organization, project, or folder.
    //
    // Parent must be in one of the following formats:
    //    OrganizationName organizationName = OrganizationName.of("organization-id");
    //    ProjectName projectName = ProjectName.of("project-id");
    //    FolderName folderName = FolderName.of("folder-id");
    ListAssetsRequest.Builder request =
        ListAssetsRequest.newBuilder()
            .setParent(organizationName.toString())
            .setFilter(
                "security_center_properties.resource_type=\"google.cloud.resourcemanager.Project\"");
    request
        .getCompareDurationBuilder()
        .setSeconds(timeSpan.getSeconds())
        .setNanos(timeSpan.getNano());

    // Set read time to either the instant passed in or now.
    asOf = MoreObjects.firstNonNull(asOf, Instant.now());
    request.getReadTimeBuilder().setSeconds(asOf.getEpochSecond()).setNanos(asOf.getNano());

    // Call the API.
    ListAssetsPagedResponse response = client.listAssets(request.build());

    // This creates one list for all assets.  If your organization has a large number of assets
    // this can cause out of memory issues.  You can process them incrementally by returning
    // the Iterable returned response.iterateAll() directly.
    ImmutableList<ListAssetsResult> results = ImmutableList.copyOf(response.iterateAll());
    System.out.println("Projects:");
    System.out.println(results);
    return results;
  } catch (IOException e) {
    throw new RuntimeException("Couldn't create client.", e);
  }
}

Go

import (
	"context"
	"fmt"
	"io"
	"time"

	securitycenter "cloud.google.com/go/securitycenter/apiv1"
	"cloud.google.com/go/securitycenter/apiv1/securitycenterpb"
	"github.com/golang/protobuf/ptypes"
	"google.golang.org/api/iterator"
)

// listAllProjectAssetsAndStateChange lists all current GCP project assets in
// orgID and prints the projects and there change from a day ago out to w.
// orgID is the numeric // organization ID of interest.
func listAllProjectAssetsAndStateChanges(w io.Writer, orgID string) error {
	// orgID := "12321311"
	// Instantiate a context and a security service client to make API calls.
	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %w", err)
	}
	defer client.Close() // Closing the client safely cleans up background resources.

	req := &securitycenterpb.ListAssetsRequest{
		// Parent must be in one of the following formats:
		//		"organizations/{orgId}"
		//		"projects/{projectId}"
		//		"folders/{folderId}"
		Parent:          fmt.Sprintf("organizations/%s", orgID),
		Filter:          `security_center_properties.resource_type="google.cloud.resourcemanager.Project"`,
		CompareDuration: ptypes.DurationProto(24 * time.Hour),
	}

	assetsFound := 0
	it := client.ListAssets(ctx, req)
	for {
		result, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return fmt.Errorf("ListAssets: %w", err)
		}
		asset := result.Asset
		properties := asset.SecurityCenterProperties
		fmt.Fprintf(w, "Asset Name: %s,", asset.Name)
		fmt.Fprintf(w, "Resource Name %s,", properties.ResourceName)
		fmt.Fprintf(w, "Resource Type %s", properties.ResourceType)
		fmt.Fprintf(w, "State Change %s\n", result.StateChange)
		assetsFound++
	}
	return nil
}

Node.js

// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center');

// Creates a new client.
const client = new SecurityCenterClient();
//  organizationId is the numeric ID of the organization.
/*
 * TODO(developer): Uncomment the following lines
 */
// parent: must be in one of the following formats:
//    `organizations/${organization_id}`
//    `projects/${project_id}`
//    `folders/${folder_id}`
const parent = `organizations/${organizationId}`;
// Call the API with automatic pagination.
async function listAssetsAndChanges() {
  const [response] = await client.listAssets({
    parent: parent,
    compareDuration: {seconds: 30 * /*Second in Day=*/ 86400, nanos: 0},
    filter:
      'security_center_properties.resource_type="google.cloud.resourcemanager.Project"',
  });
  let count = 0;
  Array.from(response).forEach(result =>
    console.log(
      `${++count} ${result.asset.name} ${
        result.asset.securityCenterProperties.resourceName
      } ${result.stateChange}`
    )
  );
}

listAssetsAndChanges();

FilterExamples

以下是一些其他有用的资源过滤条件。您可以在过滤器条件中使用 ANDOR 来组合参数以及扩展或优化结果。

查找具有特定所有者的项目资源

"security_center_properties.resource_type = \"google.cloud.resourcemanager.Project\" AND security_center_properties.resource_owners : \"$USER\""

$USER 通常采用 user:someone@domain.com 格式。比较 user 时使用子字符串运算符 :,不需要完全匹配。

包含开放 HTTP 端口的防火墙规则

"security_center_properties.resource_type = \"google.compute.Firewall\" AND resource_properties.name =\"default-allow-http\""

属于特定项目的资源

"security_center_properties.resource_parent = \"$PROJECT_1_NAME\" OR security_center_properties.resource_parent = \"$PROJECT_2_NAME\""

$PROJECT_1_NAME$PROJECT_2_NAME//cloudresourcemanager.googleapis.com/projects/$PROJECT_ID 形式的资源标识符,其中 $PROJECT_ID 是项目编号。完整示例如下所示://cloudresourcemanager.googleapis.com/projects/100090906

查找名称包含特定字符串的 Compute Engine 映像

以下过滤条件会返回包含子字符串“Debia”的 Compute Engine 映像:

"security_center_properties.resource_type = \"google.compute.Image\" AND resource_properties.name : \"Debia\""

属性包含键值对的资源

此过滤条件返回停用 bucketPolicyOnly 的 Cloud Storage 存储分区。resourceProperties.iamConfiguration 的值编码为字符串。您可以使用 \ 字符来转义字符串中的特殊字符,包括键名称和值之间的运算符 :

"resourceProperties.iamConfiguration:"\"bucketPolicyOnly\"\:{\"enabled\"\:false""

查找在特定时间之前创建的项目资源

这些示例过滤器与 2019 年 7 月 18 日下午 8:26:21(格林尼治标准时间)创建的资源相匹配。借助 create_time 过滤器,您可以使用以下格式和类型表示时间:

  • 以整数字面量表示的 Unix 时间(以毫秒为单位)

    "create_time <= 1563481581000"
    
  • 作为字符串字面量的 RFC 3339

    "create_time <= \"2019-07-18T20:26:21+00:00\""
    

从结果中排除资源

要从结果中排除某个资源,请在参数前添加 - 字符。该运算类似于在 SQL 语句中使用 NOT 运算符。

以下过滤条件会返回除 Debia 以外的所有项目资源:

"security_center_properties.resource_type = \"google.cloud.resourcemanager.Project\" AND -resource_properties.projectId = \"Debia\""

后续步骤

详细了解如何使用客户端库访问 Security Command Center