diff --git a/providers/gcp/resources/discovery.go b/providers/gcp/resources/discovery.go index f166e991a5..256f4d8aad 100644 --- a/providers/gcp/resources/discovery.go +++ b/providers/gcp/resources/discovery.go @@ -49,14 +49,45 @@ func Discover(runtime *plugin.Runtime) (*inventory.Inventory, error) { gcpOrg := res.(*mqlGcpOrganization) - for i := range conn.Conf.Discover.Targets { - target := conn.Conf.Discover.Targets[i] - list, err := discoverOrganization(conn, gcpOrg, target) - if err != nil { - return nil, err - } - in.Spec.Assets = append(in.Spec.Assets, list...) + list, err := discoverOrganization(conn, gcpOrg) + if err != nil { + return nil, err + } + in.Spec.Assets = append(in.Spec.Assets, list...) + } else if conn.ResourceType() == connection.Folder { + res, err := NewResource(runtime, "gcp.folder", nil) + if err != nil { + return nil, err + } + + gcpFolder := res.(*mqlGcpFolder) + if stringx.ContainsAnyOf(conn.Conf.Discover.Targets, DiscoveryAll, DiscoveryAuto, DiscoveryFolders) { + in.Spec.Assets = append(in.Spec.Assets, &inventory.Asset{ + PlatformIds: []string{ + connection.NewFolderPlatformID(gcpFolder.Id.Data), + }, + Name: "GCP Folder " + gcpFolder.Id.Data, + Platform: &inventory.Platform{ + Name: "gcp-folder", + Title: "GCP Folder", + Runtime: "gcp", + Kind: "gcp-object", + Family: []string{"google"}, + }, + Labels: map[string]string{}, + // NOTE: we explicitly do not exclude discovery here, as we want to discover the projects for the folder + Connections: []*inventory.Config{conn.Conf.Clone(inventory.WithParentConnectionId(conn.Conf.Id))}, + }) + } + + list, err := discoverFolder(conn, gcpFolder) + if err != nil { + return nil, err } + if len(in.Spec.Assets) > 0 { + in.Spec.Assets[0].RelatedAssets = list + } + in.Spec.Assets = append(in.Spec.Assets, list...) } else if conn.ResourceType() == connection.Project { res, err := NewResource(runtime, "gcp.project", nil) if err != nil { @@ -110,7 +141,7 @@ func Discover(runtime *plugin.Runtime) (*inventory.Inventory, error) { return in, nil } -func discoverOrganization(conn *connection.GcpConnection, gcpOrg *mqlGcpOrganization, target string) ([]*inventory.Asset, error) { +func discoverOrganization(conn *connection.GcpConnection, gcpOrg *mqlGcpOrganization) ([]*inventory.Asset, error) { assetList := []*inventory.Asset{} if stringx.ContainsAnyOf(conn.Conf.Discover.Targets, DiscoveryAll, DiscoveryAuto, DiscoveryProjects) { projects := gcpOrg.GetProjects() @@ -150,6 +181,88 @@ func discoverOrganization(conn *connection.GcpConnection, gcpOrg *mqlGcpOrganiza }) } } + if stringx.ContainsAnyOf(conn.Conf.Discover.Targets, DiscoveryAll, DiscoveryAuto, DiscoveryFolders) { + folders := gcpOrg.GetFolders() + if folders.Error != nil { + return nil, folders.Error + } + + folderList := folders.Data.GetList() // resolve all folders including nested + if folderList.Error != nil { + return nil, folderList.Error + } + + for i := range folderList.Data { + folder := folderList.Data[i].(*mqlGcpFolder) + + folderConf := conn.Conf.Clone(inventory.WithParentConnectionId(conn.Conf.Id)) + if folderConf.Options == nil { + folderConf.Options = map[string]string{} + } + folderConf.Options["folder-id"] = folder.Id.Data + + assetList = append(assetList, &inventory.Asset{ + PlatformIds: []string{ + connection.NewFolderPlatformID(folder.Id.Data), + }, + Name: "GCP Folder " + folder.Id.Data, + Platform: &inventory.Platform{ + Name: "gcp-folder", + Title: "GCP Folder", + Runtime: "gcp", + Kind: "gcp-object", + Family: []string{"google"}, + }, + Labels: map[string]string{}, + // NOTE: we explicitly do not exclude discovery here, as we want to discover the projects for the folder + Connections: []*inventory.Config{conn.Conf.Clone(inventory.WithParentConnectionId(conn.Conf.Id))}, + }) + } + } + return assetList, nil +} + +func discoverFolder(conn *connection.GcpConnection, gcpFolder *mqlGcpFolder) ([]*inventory.Asset, error) { + assetList := []*inventory.Asset{} + + if stringx.ContainsAnyOf(conn.Conf.Discover.Targets, DiscoveryAll, DiscoveryAuto, DiscoveryProjects) { + projects := gcpFolder.GetProjects() + if projects.Error != nil { + return nil, projects.Error + } + + projectList := projects.Data.GetList() // resolve all projects including nested + if projectList.Error != nil { + return nil, projectList.Error + } + + for i := range projectList.Data { + project := projectList.Data[i].(*mqlGcpProject) + + projectConf := conn.Conf.Clone(inventory.WithParentConnectionId(conn.Conf.Id)) + if projectConf.Options == nil { + projectConf.Options = map[string]string{} + } + delete(projectConf.Options, "folder-id") + projectConf.Options["project-id"] = project.Id.Data + + assetList = append(assetList, &inventory.Asset{ + PlatformIds: []string{ + connection.NewProjectPlatformID(project.Id.Data), + }, + Name: project.Name.Data, + Platform: &inventory.Platform{ + Name: "gcp-project", + Title: "GCP Project " + project.Name.Data, + Runtime: "gcp", + Kind: "gcp-object", + Family: []string{"google"}, + }, + Labels: map[string]string{}, + Connections: []*inventory.Config{projectConf}, // pass-in the parent connection config + }) + } + } return assetList, nil } diff --git a/providers/gcp/resources/folder.go b/providers/gcp/resources/folder.go index a14073fb3d..626a6f5761 100644 --- a/providers/gcp/resources/folder.go +++ b/providers/gcp/resources/folder.go @@ -6,6 +6,7 @@ package resources import ( "context" "fmt" + "strings" "go.mondoo.com/cnquery/v10/llx" "go.mondoo.com/cnquery/v10/providers-sdk/v1/plugin" @@ -37,6 +38,10 @@ func initGcpFolder(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[s return args, nil, nil } + if args == nil { + args = make(map[string]*llx.RawData) + } + conn := runtime.Connection.(*connection.GcpConnection) client, err := conn.Client(cloudresourcemanager.CloudPlatformReadOnlyScope, iam.CloudPlatformScope, compute.CloudPlatformScope) @@ -52,14 +57,17 @@ func initGcpFolder(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[s folderId := conn.ResourceID() if args["id"] != nil { - folderId = fmt.Sprintf("folders/%s", args["id"].Value.(string)) + folderId = args["id"].Value.(string) } - folder, err := svc.Folders.Get(folderId).Do() + + folderPath := fmt.Sprintf("folders/%s", folderId) + folder, err := svc.Folders.Get(folderPath).Do() if err != nil { return nil, nil, err } - args["id"] = llx.StringData(folder.Name) + retrievedFolderID := strings.TrimPrefix(folder.Name, "folders/") + args["id"] = llx.StringData(retrievedFolderID) args["name"] = llx.StringData(folder.DisplayName) args["created"] = llx.TimeDataPtr(parseTime(folder.CreateTime)) args["updated"] = llx.TimeDataPtr(parseTime(folder.CreateTime)) @@ -154,7 +162,7 @@ func (g *mqlGcpFolder) folders() (*mqlGcpFolders, error) { if g.Id.Error != nil { return nil, g.Id.Error } - folderId := g.Id.Data + folderId := "folders/" + g.Id.Data res, err := CreateResource(g.MqlRuntime, "gcp.folders", map[string]*llx.RawData{ "parentId": llx.StringData(folderId), }) @@ -168,7 +176,7 @@ func (g *mqlGcpFolder) projects() (*mqlGcpProjects, error) { if g.Id.Error != nil { return nil, g.Id.Error } - folderId := g.Id.Data + folderId := "folders/" + g.Id.Data res, err := CreateResource(g.MqlRuntime, "gcp.projects", map[string]*llx.RawData{ "parentId": llx.StringData(folderId), })