From 077e2cc5edfffd2d7bdfbbf7022552d214022af0 Mon Sep 17 00:00:00 2001 From: Christoph Hartmann Date: Tue, 20 Aug 2024 11:30:58 +0200 Subject: [PATCH] =?UTF-8?q?=E2=AD=90=EF=B8=8F=20improve=20microsoft.tenant?= =?UTF-8?q?=20resource=20(#4573)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ⭐️ improve microsoft.tenant resource * 🧹 PR feedback --- providers/ms365/resources/microsoft.go | 30 ---- providers/ms365/resources/ms365.lr | 26 ++- providers/ms365/resources/ms365.lr.go | 154 ++++++++++++----- .../ms365/resources/ms365.lr.manifest.yaml | 16 +- providers/ms365/resources/organizations.go | 62 ------- providers/ms365/resources/structs.go | 67 ++++++++ providers/ms365/resources/tenant.go | 160 ++++++++++++++++++ 7 files changed, 369 insertions(+), 146 deletions(-) delete mode 100644 providers/ms365/resources/organizations.go create mode 100644 providers/ms365/resources/tenant.go diff --git a/providers/ms365/resources/microsoft.go b/providers/ms365/resources/microsoft.go index c54b19fb7f..a95ee26368 100644 --- a/providers/ms365/resources/microsoft.go +++ b/providers/ms365/resources/microsoft.go @@ -3,13 +3,6 @@ package resources -import ( - "context" - - "github.com/microsoftgraph/msgraph-sdk-go/organization" - "go.mondoo.com/cnquery/v11/providers/ms365/connection" -) - type mqlMicrosoftInternal struct { // index users by id idxUsersById map[string]*mqlMicrosoftUser @@ -38,26 +31,3 @@ func (a *mqlMicrosoft) userById(id string) (*mqlMicrosoftUser, bool) { res, ok := a.idxUsersById[id] return res, ok } - -func (a *mqlMicrosoft) tenantDomainName() (string, error) { - conn := a.MqlRuntime.Connection.(*connection.Ms365Connection) - graphClient, err := conn.GraphClient() - if err != nil { - return "", err - } - ctx := context.Background() - resp, err := graphClient.Organization().Get(ctx, &organization.OrganizationRequestBuilderGetRequestConfiguration{}) - if err != nil { - return "", transformError(err) - } - tenantDomainName := "" - for _, org := range resp.GetValue() { - for _, d := range org.GetVerifiedDomains() { - if *d.GetIsInitial() { - tenantDomainName = *d.GetName() - } - } - } - - return tenantDomainName, nil -} diff --git a/providers/ms365/resources/ms365.lr b/providers/ms365/resources/ms365.lr index 2b86ea3f09..f3781a412c 100644 --- a/providers/ms365/resources/ms365.lr +++ b/providers/ms365/resources/ms365.lr @@ -4,10 +4,12 @@ option provider = "go.mondoo.com/cnquery/v9/providers/ms365" option go_package = "go.mondoo.com/cnquery/v11/providers/ms365/resources" +alias microsoft.organization = microsoft.tenant + // Microsoft microsoft { - // List of organizations - organizations() []microsoft.organization + // Deprecated: use `microsoft.tenant` instead + organizations() []microsoft.tenant // List of users users() []microsoft.user // List of groups @@ -28,20 +30,30 @@ microsoft { tenantDomainName() string } -// Microsoft organization -private microsoft.organization @defaults("displayName") { +// Microsoft Entra tenant +microsoft.tenant @defaults("name") { // Organization ID id string - // Organization assigned plans + // Service plans associated with the tenant assignedPlans []dict - // Organization creation date + // Provisioned plan + provisionedPlans []dict + // Deprecated: use `createdAt` instead createdDateTime time - // Organization display name + // Deprecated: use `name` instead displayName string + // Tenant display name + name string // Organization verified domains verifiedDomains []dict // Whether password hash sync is enabled for hybrid deployments onPremisesSyncEnabled bool + // Tenant creation date + createdAt time + // Tenant type + type string + // Commercial subscription + subscriptions() []dict } // Microsoft Entra ID user diff --git a/providers/ms365/resources/ms365.lr.go b/providers/ms365/resources/ms365.lr.go index 6bed5886ce..6ea65f9aa9 100644 --- a/providers/ms365/resources/ms365.lr.go +++ b/providers/ms365/resources/ms365.lr.go @@ -22,9 +22,9 @@ func init() { // to override args, implement: initMicrosoft(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createMicrosoft, }, - "microsoft.organization": { - // to override args, implement: initMicrosoftOrganization(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) - Create: createMicrosoftOrganization, + "microsoft.tenant": { + Init: initMicrosoftTenant, + Create: createMicrosoftTenant, }, "microsoft.user": { Init: initMicrosoftUser, @@ -223,7 +223,7 @@ func CreateResource(runtime *plugin.Runtime, name string, args map[string]*llx.R var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "microsoft.organizations": func(r plugin.Resource) *plugin.DataRes { - return (r.(*mqlMicrosoft).GetOrganizations()).ToDataRes(types.Array(types.Resource("microsoft.organization"))) + return (r.(*mqlMicrosoft).GetOrganizations()).ToDataRes(types.Array(types.Resource("microsoft.tenant"))) }, "microsoft.users": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMicrosoft).GetUsers()).ToDataRes(types.Array(types.Resource("microsoft.user"))) @@ -252,23 +252,38 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "microsoft.tenantDomainName": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMicrosoft).GetTenantDomainName()).ToDataRes(types.String) }, - "microsoft.organization.id": func(r plugin.Resource) *plugin.DataRes { - return (r.(*mqlMicrosoftOrganization).GetId()).ToDataRes(types.String) + "microsoft.tenant.id": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetId()).ToDataRes(types.String) }, - "microsoft.organization.assignedPlans": func(r plugin.Resource) *plugin.DataRes { - return (r.(*mqlMicrosoftOrganization).GetAssignedPlans()).ToDataRes(types.Array(types.Dict)) + "microsoft.tenant.assignedPlans": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetAssignedPlans()).ToDataRes(types.Array(types.Dict)) }, - "microsoft.organization.createdDateTime": func(r plugin.Resource) *plugin.DataRes { - return (r.(*mqlMicrosoftOrganization).GetCreatedDateTime()).ToDataRes(types.Time) + "microsoft.tenant.provisionedPlans": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetProvisionedPlans()).ToDataRes(types.Array(types.Dict)) }, - "microsoft.organization.displayName": func(r plugin.Resource) *plugin.DataRes { - return (r.(*mqlMicrosoftOrganization).GetDisplayName()).ToDataRes(types.String) + "microsoft.tenant.createdDateTime": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetCreatedDateTime()).ToDataRes(types.Time) }, - "microsoft.organization.verifiedDomains": func(r plugin.Resource) *plugin.DataRes { - return (r.(*mqlMicrosoftOrganization).GetVerifiedDomains()).ToDataRes(types.Array(types.Dict)) + "microsoft.tenant.displayName": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetDisplayName()).ToDataRes(types.String) }, - "microsoft.organization.onPremisesSyncEnabled": func(r plugin.Resource) *plugin.DataRes { - return (r.(*mqlMicrosoftOrganization).GetOnPremisesSyncEnabled()).ToDataRes(types.Bool) + "microsoft.tenant.name": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetName()).ToDataRes(types.String) + }, + "microsoft.tenant.verifiedDomains": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetVerifiedDomains()).ToDataRes(types.Array(types.Dict)) + }, + "microsoft.tenant.onPremisesSyncEnabled": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetOnPremisesSyncEnabled()).ToDataRes(types.Bool) + }, + "microsoft.tenant.createdAt": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetCreatedAt()).ToDataRes(types.Time) + }, + "microsoft.tenant.type": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetType()).ToDataRes(types.String) + }, + "microsoft.tenant.subscriptions": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftTenant).GetSubscriptions()).ToDataRes(types.Array(types.Dict)) }, "microsoft.user.id": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMicrosoftUser).GetId()).ToDataRes(types.String) @@ -1142,32 +1157,52 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlMicrosoft).TenantDomainName, ok = plugin.RawToTValue[string](v.Value, v.Error) return }, - "microsoft.organization.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { - r.(*mqlMicrosoftOrganization).__id, ok = v.Value.(string) + "microsoft.tenant.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).__id, ok = v.Value.(string) return }, - "microsoft.organization.id": func(r plugin.Resource, v *llx.RawData) (ok bool) { - r.(*mqlMicrosoftOrganization).Id, ok = plugin.RawToTValue[string](v.Value, v.Error) + "microsoft.tenant.id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).Id, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.tenant.assignedPlans": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).AssignedPlans, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, + "microsoft.tenant.provisionedPlans": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).ProvisionedPlans, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, + "microsoft.tenant.createdDateTime": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).CreatedDateTime, ok = plugin.RawToTValue[*time.Time](v.Value, v.Error) + return + }, + "microsoft.tenant.displayName": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).DisplayName, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.tenant.name": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).Name, ok = plugin.RawToTValue[string](v.Value, v.Error) return }, - "microsoft.organization.assignedPlans": func(r plugin.Resource, v *llx.RawData) (ok bool) { - r.(*mqlMicrosoftOrganization).AssignedPlans, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + "microsoft.tenant.verifiedDomains": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).VerifiedDomains, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) return }, - "microsoft.organization.createdDateTime": func(r plugin.Resource, v *llx.RawData) (ok bool) { - r.(*mqlMicrosoftOrganization).CreatedDateTime, ok = plugin.RawToTValue[*time.Time](v.Value, v.Error) + "microsoft.tenant.onPremisesSyncEnabled": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).OnPremisesSyncEnabled, ok = plugin.RawToTValue[bool](v.Value, v.Error) return }, - "microsoft.organization.displayName": func(r plugin.Resource, v *llx.RawData) (ok bool) { - r.(*mqlMicrosoftOrganization).DisplayName, ok = plugin.RawToTValue[string](v.Value, v.Error) + "microsoft.tenant.createdAt": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).CreatedAt, ok = plugin.RawToTValue[*time.Time](v.Value, v.Error) return }, - "microsoft.organization.verifiedDomains": func(r plugin.Resource, v *llx.RawData) (ok bool) { - r.(*mqlMicrosoftOrganization).VerifiedDomains, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + "microsoft.tenant.type": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).Type, ok = plugin.RawToTValue[string](v.Value, v.Error) return }, - "microsoft.organization.onPremisesSyncEnabled": func(r plugin.Resource, v *llx.RawData) (ok bool) { - r.(*mqlMicrosoftOrganization).OnPremisesSyncEnabled, ok = plugin.RawToTValue[bool](v.Value, v.Error) + "microsoft.tenant.subscriptions": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftTenant).Subscriptions, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) return }, "microsoft.user.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { @@ -2599,22 +2634,27 @@ func (c *mqlMicrosoft) GetTenantDomainName() *plugin.TValue[string] { }) } -// mqlMicrosoftOrganization for the microsoft.organization resource -type mqlMicrosoftOrganization struct { +// mqlMicrosoftTenant for the microsoft.tenant resource +type mqlMicrosoftTenant struct { MqlRuntime *plugin.Runtime __id string - // optional: if you define mqlMicrosoftOrganizationInternal it will be used here + // optional: if you define mqlMicrosoftTenantInternal it will be used here Id plugin.TValue[string] AssignedPlans plugin.TValue[[]interface{}] + ProvisionedPlans plugin.TValue[[]interface{}] CreatedDateTime plugin.TValue[*time.Time] DisplayName plugin.TValue[string] + Name plugin.TValue[string] VerifiedDomains plugin.TValue[[]interface{}] OnPremisesSyncEnabled plugin.TValue[bool] + CreatedAt plugin.TValue[*time.Time] + Type plugin.TValue[string] + Subscriptions plugin.TValue[[]interface{}] } -// createMicrosoftOrganization creates a new instance of this resource -func createMicrosoftOrganization(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { - res := &mqlMicrosoftOrganization{ +// createMicrosoftTenant creates a new instance of this resource +func createMicrosoftTenant(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlMicrosoftTenant{ MqlRuntime: runtime, } @@ -2631,7 +2671,7 @@ func createMicrosoftOrganization(runtime *plugin.Runtime, args map[string]*llx.R } if runtime.HasRecording { - args, err = runtime.ResourceFromRecording("microsoft.organization", res.__id) + args, err = runtime.ResourceFromRecording("microsoft.tenant", res.__id) if err != nil || args == nil { return res, err } @@ -2641,38 +2681,60 @@ func createMicrosoftOrganization(runtime *plugin.Runtime, args map[string]*llx.R return res, nil } -func (c *mqlMicrosoftOrganization) MqlName() string { - return "microsoft.organization" +func (c *mqlMicrosoftTenant) MqlName() string { + return "microsoft.tenant" } -func (c *mqlMicrosoftOrganization) MqlID() string { +func (c *mqlMicrosoftTenant) MqlID() string { return c.__id } -func (c *mqlMicrosoftOrganization) GetId() *plugin.TValue[string] { +func (c *mqlMicrosoftTenant) GetId() *plugin.TValue[string] { return &c.Id } -func (c *mqlMicrosoftOrganization) GetAssignedPlans() *plugin.TValue[[]interface{}] { +func (c *mqlMicrosoftTenant) GetAssignedPlans() *plugin.TValue[[]interface{}] { return &c.AssignedPlans } -func (c *mqlMicrosoftOrganization) GetCreatedDateTime() *plugin.TValue[*time.Time] { +func (c *mqlMicrosoftTenant) GetProvisionedPlans() *plugin.TValue[[]interface{}] { + return &c.ProvisionedPlans +} + +func (c *mqlMicrosoftTenant) GetCreatedDateTime() *plugin.TValue[*time.Time] { return &c.CreatedDateTime } -func (c *mqlMicrosoftOrganization) GetDisplayName() *plugin.TValue[string] { +func (c *mqlMicrosoftTenant) GetDisplayName() *plugin.TValue[string] { return &c.DisplayName } -func (c *mqlMicrosoftOrganization) GetVerifiedDomains() *plugin.TValue[[]interface{}] { +func (c *mqlMicrosoftTenant) GetName() *plugin.TValue[string] { + return &c.Name +} + +func (c *mqlMicrosoftTenant) GetVerifiedDomains() *plugin.TValue[[]interface{}] { return &c.VerifiedDomains } -func (c *mqlMicrosoftOrganization) GetOnPremisesSyncEnabled() *plugin.TValue[bool] { +func (c *mqlMicrosoftTenant) GetOnPremisesSyncEnabled() *plugin.TValue[bool] { return &c.OnPremisesSyncEnabled } +func (c *mqlMicrosoftTenant) GetCreatedAt() *plugin.TValue[*time.Time] { + return &c.CreatedAt +} + +func (c *mqlMicrosoftTenant) GetType() *plugin.TValue[string] { + return &c.Type +} + +func (c *mqlMicrosoftTenant) GetSubscriptions() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.Subscriptions, func() ([]interface{}, error) { + return c.subscriptions() + }) +} + // mqlMicrosoftUser for the microsoft.user resource type mqlMicrosoftUser struct { MqlRuntime *plugin.Runtime diff --git a/providers/ms365/resources/ms365.lr.manifest.yaml b/providers/ms365/resources/ms365.lr.manifest.yaml index c4c397305d..e616ec83c0 100755 --- a/providers/ms365/resources/ms365.lr.manifest.yaml +++ b/providers/ms365/resources/ms365.lr.manifest.yaml @@ -207,7 +207,6 @@ resources: onPremisesSyncEnabled: min_mondoo_version: 9.0.0 verifiedDomains: {} - is_private: true min_mondoo_version: 5.15.0 microsoft.passwordCredential: fields: @@ -362,6 +361,21 @@ resources: type: {} is_private: true min_mondoo_version: latest + microsoft.tenant: + fields: + assignedPlans: {} + createdAt: {} + createdDateTime: {} + displayName: {} + id: {} + name: {} + onPremisesSyncEnabled: {} + provisionedPlans: {} + subscription: {} + subscriptions: {} + type: {} + verifiedDomains: {} + min_mondoo_version: 9.0.0 microsoft.user: fields: accountEnabled: {} diff --git a/providers/ms365/resources/organizations.go b/providers/ms365/resources/organizations.go deleted file mode 100644 index 4c5e50350f..0000000000 --- a/providers/ms365/resources/organizations.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Mondoo, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package resources - -import ( - "context" - - "github.com/microsoftgraph/msgraph-sdk-go/organization" - "go.mondoo.com/cnquery/v11/llx" - "go.mondoo.com/cnquery/v11/providers-sdk/v1/util/convert" - "go.mondoo.com/cnquery/v11/providers/ms365/connection" - "go.mondoo.com/cnquery/v11/types" -) - -func (m *mqlMicrosoftOrganization) id() (string, error) { - return m.Id.Data, nil -} - -func (a *mqlMicrosoft) organizations() ([]interface{}, error) { - conn := a.MqlRuntime.Connection.(*connection.Ms365Connection) - graphClient, err := conn.GraphClient() - if err != nil { - return nil, err - } - - ctx := context.Background() - resp, err := graphClient.Organization().Get(ctx, &organization.OrganizationRequestBuilderGetRequestConfiguration{}) - if err != nil { - return nil, transformError(err) - } - - res := []interface{}{} - orgs := resp.GetValue() - for i := range orgs { - org := orgs[i] - - assignedPlans, err := convert.JsonToDictSlice(newAssignedPlans(org.GetAssignedPlans())) - if err != nil { - return nil, err - } - verifiedDomains, err := convert.JsonToDictSlice(newVerifiedDomains(org.GetVerifiedDomains())) - if err != nil { - return nil, err - } - mqlResource, err := CreateResource(a.MqlRuntime, "microsoft.organization", - map[string]*llx.RawData{ - "id": llx.StringDataPtr(org.GetId()), - "assignedPlans": llx.ArrayData(assignedPlans, types.Any), - "createdDateTime": llx.TimeDataPtr(org.GetCreatedDateTime()), - "displayName": llx.StringDataPtr(org.GetDisplayName()), - "verifiedDomains": llx.ArrayData(verifiedDomains, types.Any), - "onPremisesSyncEnabled": llx.BoolDataPtr(org.GetOnPremisesSyncEnabled()), - }) - if err != nil { - return nil, err - } - res = append(res, mqlResource) - } - - return res, nil -} diff --git a/providers/ms365/resources/structs.go b/providers/ms365/resources/structs.go index 8fb7b61856..f12690a013 100644 --- a/providers/ms365/resources/structs.go +++ b/providers/ms365/resources/structs.go @@ -39,6 +39,50 @@ func newAssignedPlan(p models.AssignedPlanable) AssignedPlan { } } +type ProvisionedPlan struct { + CapabilityStatus string `json:"capabilityStatus"` + Service string `json:"service"` +} + +func newProvisionedPlans(p []models.ProvisionedPlanable) []ProvisionedPlan { + res := []ProvisionedPlan{} + for i := range p { + res = append(res, newProvisionedPlan(p[i])) + } + return res +} + +func newProvisionedPlan(p models.ProvisionedPlanable) ProvisionedPlan { + return ProvisionedPlan{ + CapabilityStatus: convert.ToString(p.GetCapabilityStatus()), + Service: convert.ToString(p.GetService()), + } +} + +type ServicePlanInfo struct { + ServicePlanId *string `json:"servicePlanId"` + ServicePlanName *string `json:"servicePlanName"` + ProvisioningStatus *string `json:"provisioningStatus"` + AppliesTo *string `json:"appliesTo"` +} + +func newServicePlanInfos(p []models.ServicePlanInfoable) []ServicePlanInfo { + res := []ServicePlanInfo{} + for i := range p { + res = append(res, newServicePlanInfo(p[i])) + } + return res +} + +func newServicePlanInfo(p models.ServicePlanInfoable) ServicePlanInfo { + return ServicePlanInfo{ + ServicePlanId: newUuidString(p.GetServicePlanId()), + ServicePlanName: p.GetServicePlanName(), + ProvisioningStatus: p.GetProvisioningStatus(), + AppliesTo: p.GetAppliesTo(), + } +} + type VerifiedDomain struct { Capabilities string `json:"capabilities"` IsDefault bool `json:"isDefault"` @@ -947,3 +991,26 @@ func newVerifiedPublisher(p models.VerifiedPublisherable) VerifiedPublisher { CreatedAt: p.GetAddedDateTime(), } } + +type companySubscription struct { + Id *string `json:"id"` + CreatedAt *time.Time `json:"createdAt"` + IsTrial *bool `json:"isTrial"` + SkuPartNumber *string `json:"skuPartNumber"` + Status *string `json:"status"` + TotalLicenses *int32 `json:"totalLicenses"` + ServicePlans []ServicePlanInfo `json:"servicePlans"` +} + +func newCompanySubscription(p models.CompanySubscriptionable) companySubscription { + sub := companySubscription{ + Id: p.GetId(), + CreatedAt: p.GetCreatedDateTime(), + IsTrial: p.GetIsTrial(), + SkuPartNumber: p.GetSkuPartNumber(), + Status: p.GetStatus(), + TotalLicenses: p.GetTotalLicenses(), + ServicePlans: newServicePlanInfos(p.GetServiceStatus()), + } + return sub +} diff --git a/providers/ms365/resources/tenant.go b/providers/ms365/resources/tenant.go new file mode 100644 index 0000000000..f3043794e2 --- /dev/null +++ b/providers/ms365/resources/tenant.go @@ -0,0 +1,160 @@ +// Copyright (c) Mondoo, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package resources + +import ( + "context" + "github.com/microsoftgraph/msgraph-sdk-go/directory" + "github.com/microsoftgraph/msgraph-sdk-go/models" + "github.com/microsoftgraph/msgraph-sdk-go/organization" + "go.mondoo.com/cnquery/v11/llx" + "go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin" + "go.mondoo.com/cnquery/v11/providers-sdk/v1/util/convert" + "go.mondoo.com/cnquery/v11/providers/ms365/connection" +) + +func (m *mqlMicrosoftTenant) id() (string, error) { + return m.Id.Data, nil +} + +// Deprecated: use `microsoft.tenant` instead +func (a *mqlMicrosoft) organizations() ([]interface{}, error) { + conn := a.MqlRuntime.Connection.(*connection.Ms365Connection) + graphClient, err := conn.GraphClient() + if err != nil { + return nil, err + } + + ctx := context.Background() + resp, err := graphClient.Organization().Get(ctx, &organization.OrganizationRequestBuilderGetRequestConfiguration{}) + if err != nil { + return nil, transformError(err) + } + + res := []interface{}{} + orgs := resp.GetValue() + for i := range orgs { + org := orgs[i] + mqlResource, err := newMicrosoftTenant(a.MqlRuntime, org) + if err != nil { + return nil, err + } + res = append(res, mqlResource) + } + + return res, nil +} + +var tenantFields = []string{ + "id", + "assignedPlans", + "createdDateTime", + "displayName", + "verifiedDomains", + "onPremisesSyncEnabled", + "tenantType", + "provisionedPlans", +} + +func initMicrosoftTenant(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) { + conn := runtime.Connection.(*connection.Ms365Connection) + graphClient, err := conn.GraphClient() + if err != nil { + return nil, nil, err + } + + ctx := context.Background() + resp, err := graphClient.Organization().ByOrganizationId(conn.TenantId()).Get(ctx, &organization.OrganizationItemRequestBuilderGetRequestConfiguration{ + QueryParameters: &organization.OrganizationItemRequestBuilderGetQueryParameters{ + Select: tenantFields, + }, + }) + if err != nil { + return nil, nil, transformError(err) + } + + tenant, err := newMicrosoftTenant(runtime, resp) + if err != nil { + return nil, nil, err + } + return nil, tenant, nil +} + +func newMicrosoftTenant(runtime *plugin.Runtime, org models.Organizationable) (*mqlMicrosoftTenant, error) { + assignedPlans, err := convert.JsonToDictSlice(newAssignedPlans(org.GetAssignedPlans())) + if err != nil { + return nil, err + } + verifiedDomains, err := convert.JsonToDictSlice(newVerifiedDomains(org.GetVerifiedDomains())) + if err != nil { + return nil, err + } + + provisionedPlans, err := convert.JsonToDictSlice(newProvisionedPlans(org.GetProvisionedPlans())) + if err != nil { + return nil, err + } + + mqlResource, err := CreateResource(runtime, "microsoft.tenant", + map[string]*llx.RawData{ + "id": llx.StringDataPtr(org.GetId()), + "assignedPlans": llx.DictData(assignedPlans), + "createdDateTime": llx.TimeDataPtr(org.GetCreatedDateTime()), // deprecated + "name": llx.StringDataPtr(org.GetDisplayName()), + "displayName": llx.StringDataPtr(org.GetDisplayName()), // deprecated + "verifiedDomains": llx.DictData(verifiedDomains), + "onPremisesSyncEnabled": llx.BoolDataPtr(org.GetOnPremisesSyncEnabled()), + "createdAt": llx.TimeDataPtr(org.GetCreatedDateTime()), + "type": llx.StringDataPtr(org.GetTenantType()), + "provisionedPlans": llx.DictData(provisionedPlans), + }) + if err != nil { + return nil, err + } + return mqlResource.(*mqlMicrosoftTenant), nil +} + +// https://learn.microsoft.com/en-us/entra/identity/users/licensing-service-plan-reference +func (a *mqlMicrosoftTenant) subscriptions() ([]interface{}, error) { + conn := a.MqlRuntime.Connection.(*connection.Ms365Connection) + graphClient, err := conn.GraphClient() + if err != nil { + return nil, err + } + resp, err := graphClient.Directory().Subscriptions().Get(context.Background(), &directory.SubscriptionsRequestBuilderGetRequestConfiguration{}) + if err != nil { + return nil, transformError(err) + } + + res := []interface{}{} + for _, sub := range resp.GetValue() { + res = append(res, newCompanySubscription(sub)) + } + + return convert.JsonToDictSlice(res) +} + +func (a *mqlMicrosoft) tenantDomainName() (string, error) { + conn := a.MqlRuntime.Connection.(*connection.Ms365Connection) + graphClient, err := conn.GraphClient() + if err != nil { + return "", err + } + ctx := context.Background() + resp, err := graphClient.Organization().Get(ctx, &organization.OrganizationRequestBuilderGetRequestConfiguration{}) + if err != nil { + return "", transformError(err) + } + tenantDomainName := "" + + for _, org := range resp.GetValue() { + for _, d := range org.GetVerifiedDomains() { + if *d.GetIsInitial() { + tenantDomainName = *d.GetName() + } + } + } + + return tenantDomainName, nil +}