diff --git a/client/app_role_assignments.go b/client/app_role_assignments.go index 09659f0..c497724 100644 --- a/client/app_role_assignments.go +++ b/client/app_role_assignments.go @@ -20,100 +20,24 @@ package client import ( "context" "fmt" - "net/url" - "strings" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureADAppRoleAssignments(ctx context.Context, servicePrincipalId string, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.AppRoleAssignmentList, error) { +// GetAzureADAppRoleAssignments https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list-approleassignedto?view=graph-rest-1.0 +func (s *azureClient) ListAzureADAppRoleAssignments(ctx context.Context, servicePrincipalId string, params query.GraphParams) <-chan AzureResult[azure.AppRoleAssignment] { var ( - path = fmt.Sprintf("/%s/servicePrincipals/%s/appRoleAssignedTo", constants.GraphApiVersion, servicePrincipalId) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count, Expand: expand} - headers map[string]string - response azure.AppRoleAssignmentList + out = make(chan AzureResult[azure.AppRoleAssignment]) + path = fmt.Sprintf("/%s/servicePrincipals/%s/appRoleAssignedTo", constants.GraphApiVersion, servicePrincipalId) ) - count = count || search != "" || (filter != "" && orderBy != "") || strings.Contains(filter, "endsWith") - if count { - headers = make(map[string]string) - headers["ConsistencyLevel"] = "eventual" + if params.Top == 0 { + params.Top = 999 } - if res, err := s.msgraph.Get(ctx, path, params.AsMap(), headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureADAppRoleAssignments(ctx context.Context, servicePrincipal, filter, search, orderBy, expand string, selectCols []string) <-chan azure.AppRoleAssignmentResult { - out := make(chan azure.AppRoleAssignmentResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.AppRoleAssignmentResult{} - nextLink string - ) - if list, err := s.GetAzureADAppRoleAssignments(ctx, servicePrincipal, filter, search, orderBy, expand, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.AppRoleAssignmentResult{Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.AppRoleAssignment](s.msgraph, ctx, path, params, out) - nextLink = list.NextLink - for nextLink != "" { - var list azure.AppRoleAssignmentList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.AppRoleAssignmentResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/apps.go b/client/apps.go index fb60ed5..80ce4fa 100644 --- a/client/apps.go +++ b/client/apps.go @@ -19,296 +19,43 @@ package client import ( "context" + "encoding/json" "fmt" - "net/url" - "strings" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/constants" - "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureADApp(ctx context.Context, objectId string, selectCols []string) (*azure.Application, error) { +// ListAzureADApps https://learn.microsoft.com/en-us/graph/api/application-list?view=graph-rest-beta +func (s *azureClient) ListAzureADApps(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.Application] { var ( - path = fmt.Sprintf("/%s/applications/%s", constants.GraphApiVersion, objectId) - params = query.Params{Select: selectCols}.AsMap() - response azure.ApplicationList + out = make(chan AzureResult[azure.Application]) + path = fmt.Sprintf("/%s/applications", constants.GraphApiVersion) ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response.Value[0], nil - } -} -func (s *azureClient) GetAzureADAppOwners(ctx context.Context, objectId string, filter string, search string, orderBy string, selectCols []string, top int32, count bool) (azure.DirectoryObjectList, error) { - var ( - path = fmt.Sprintf("/%s/applications/%s/owners", constants.GraphApiBetaVersion, objectId) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count}.AsMap() - response azure.DirectoryObjectList - ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil + if params.Top == 0 { + params.Top = 99 } -} -func (s *azureClient) GetAzureADAppMemberObjects(ctx context.Context, objectId string, securityEnabledOnly bool) (azure.MemberObjectList, error) { - var ( - path = fmt.Sprintf("/%s/directoryObjects/%s/getMemberObjects", constants.GraphApiVersion, objectId) - response azure.MemberObjectList - body = map[string]bool{ - "securityEnabledOnly": securityEnabledOnly, - } - ) - if res, err := s.msgraph.Post(ctx, path, body, nil, nil); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } + go getAzureObjectList[azure.Application](s.msgraph, ctx, path, params, out) + return out } -func (s *azureClient) GetAzureADApps(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.ApplicationList, error) { +// ListAzureADAppOwners https://learn.microsoft.com/en-us/graph/api/application-list-owners?view=graph-rest-beta +func (s *azureClient) ListAzureADAppOwners(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] { + var ( - path = fmt.Sprintf("/%s/applications", constants.GraphApiVersion) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count, Expand: expand} - headers map[string]string - response azure.ApplicationList + out = make(chan AzureResult[json.RawMessage]) + path = fmt.Sprintf("/%s/applications/%s/owners", constants.GraphApiBetaVersion, objectId) ) - count = count || search != "" || (filter != "" && orderBy != "") || strings.Contains(filter, "endsWith") - if count { - headers = make(map[string]string) - headers["ConsistencyLevel"] = "eventual" - } - if res, err := s.msgraph.Get(ctx, path, params.AsMap(), headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil + if params.Top == 0 { + params.Top = 99 } -} - -func (s *azureClient) ListAzureADApps(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.ApplicationResult { - out := make(chan azure.ApplicationResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.ApplicationResult{} - nextLink string - ) - - if list, err := s.GetAzureADApps(ctx, filter, search, orderBy, expand, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ApplicationResult{Ok: u}); !ok { - return - } - } - - nextLink = list.NextLink - for nextLink != "" { - var list azure.ApplicationList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - return - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - return - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - return - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - return - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ApplicationResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() - return out -} - -func (s *azureClient) ListAzureADAppOwners(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.AppOwnerResult { - out := make(chan azure.AppOwnerResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.AppOwnerResult{} - nextLink string - ) - - if list, err := s.GetAzureADAppOwners(ctx, objectId, filter, search, orderBy, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.AppOwnerResult{ - AppId: objectId, - Ok: u, - }); !ok { - return - } - } - - nextLink = list.NextLink - for nextLink != "" { - var list azure.DirectoryObjectList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - return - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - return - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - return - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - return - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.AppOwnerResult{ - AppId: objectId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() - return out -} - -func (s *azureClient) ListAzureADAppMemberObjects(ctx context.Context, objectId string, securityEnabledOnly bool) <-chan azure.MemberObjectResult { - out := make(chan azure.MemberObjectResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - var ( - errResult = azure.MemberObjectResult{ - ParentId: objectId, - ParentType: string(enums.EntityApplication), - } - nextLink string - ) - if list, err := s.GetAzureADAppMemberObjects(ctx, objectId, securityEnabledOnly); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.MemberObjectResult{ - ParentId: objectId, - ParentType: string(enums.EntityApplication), - Ok: u, - }); !ok { - return - } - } + go getAzureObjectList[json.RawMessage](s.msgraph, ctx, path, params, out) - nextLink = list.NextLink - for nextLink != "" { - var list azure.MemberObjectList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.MemberObjectResult{ - ParentId: objectId, - ParentType: string(enums.EntityApplication), - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/automation_accounts.go b/client/automation_accounts.go index 512271b..67e8a23 100644 --- a/client/automation_accounts.go +++ b/client/automation_accounts.go @@ -20,114 +20,20 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureAutomationAccount(ctx context.Context, subscriptionId, groupName, aaName, expand string) (*azure.AutomationAccount, error) { +// ListAzureAutomationAccounts https://learn.microsoft.com/en-us/rest/api/automation/automation-account/list?view=rest-automation-2021-06-22 +func (s *azureClient) ListAzureAutomationAccounts(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.AutomationAccount] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Automation/automationAccounts/%s", subscriptionId, groupName, aaName) - params = query.Params{ApiVersion: "2021-07-01", Expand: expand}.AsMap() - headers map[string]string - response azure.AutomationAccount + out = make(chan AzureResult[azure.AutomationAccount]) + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Automation/automationAccounts", subscriptionId) + params = query.RMParams{ApiVersion: "2021-06-22"} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureAutomationAccounts(ctx context.Context, subscriptionId string) (azure.AutomationAccountList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Automation/automationAccounts", subscriptionId) - params = query.Params{ApiVersion: "2021-06-22"}.AsMap() - headers map[string]string - response azure.AutomationAccountList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureAutomationAccounts(ctx context.Context, subscriptionId string) <-chan azure.AutomationAccountResult { - out := make(chan azure.AutomationAccountResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.AutomationAccountResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureAutomationAccounts(ctx, subscriptionId); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.AutomationAccountResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.AutomationAccount](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.AutomationAccountList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.AutomationAccountResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/client.go b/client/client.go index 03e5a76..80e6401 100644 --- a/client/client.go +++ b/client/client.go @@ -23,10 +23,15 @@ import ( "context" "encoding/json" "fmt" + "net/http" + "net/url" "github.com/bloodhoundad/azurehound/v2/client/config" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" + "github.com/bloodhoundad/azurehound/v2/panicrecovery" + "github.com/bloodhoundad/azurehound/v2/pipeline" ) func NewClient(config config.Config) (AzureClient, error) { @@ -86,87 +91,142 @@ func initClientViaGraph(msgraph, resourceManager rest.RestClient) (AzureClient, } } +type AzureResult[T any] struct { + Error error + Ok T +} + +func getAzureObjectList[T any](client rest.RestClient, ctx context.Context, path string, params query.Params, out chan AzureResult[T]) { + defer panicrecovery.PanicRecovery() + defer close(out) + + var ( + errResult AzureResult[T] + nextLink string + ) + + for { + var ( + list struct { + CountGraph int `json:"@odata.count,omitempty"` // The total count of all graph results + NextLinkGraph string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of graph values. + ContextGraph string `json:"@odata.context,omitempty"` + NextLinkRM string `json:"nextLink,omitempty"` // The URL to use for getting the next set of rm values. + Value []T `json:"value"` // A list of azure values + } + res *http.Response + err error + ) + + if nextLink != "" { + if nextUrl, err := url.Parse(nextLink); err != nil { + errResult.Error = err + _ = pipeline.Send(ctx.Done(), out, errResult) + return + } else { + paramsMap := make(map[string]string) + if params != nil { + paramsMap = params.AsMap() + } + if req, err := rest.NewRequest(ctx, "GET", nextUrl, nil, paramsMap, nil); err != nil { + errResult.Error = err + _ = pipeline.Send(ctx.Done(), out, errResult) + return + } else if res, err = client.Send(req); err != nil { + errResult.Error = err + _ = pipeline.Send(ctx.Done(), out, errResult) + return + } + } + } else { + if res, err = client.Get(ctx, path, params, nil); err != nil { + errResult.Error = err + _ = pipeline.Send(ctx.Done(), out, errResult) + return + } + } + + if err := rest.Decode(res.Body, &list); err != nil { + errResult.Error = err + _ = pipeline.Send(ctx.Done(), out, errResult) + return + } else { + for _, u := range list.Value { + if ok := pipeline.Send(ctx.Done(), out, AzureResult[T]{Ok: u}); !ok { + return + } + } + } + + if list.NextLinkRM == "" && list.NextLinkGraph == "" { + break + } else if list.NextLinkGraph != "" { + nextLink = list.NextLinkGraph + } else if list.NextLinkRM != "" { + nextLink = list.NextLinkRM + } + } +} + type azureClient struct { msgraph rest.RestClient resourceManager rest.RestClient tenant azure.Tenant } -func (s azureClient) TenantInfo() azure.Tenant { - return s.tenant +type AzureGraphClient interface { + GetAzureADOrganization(ctx context.Context, selectCols []string) (*azure.Organization, error) + + ListAzureADGroups(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.Group] + ListAzureADGroupMembers(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] + ListAzureADGroupOwners(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] + ListAzureADAppOwners(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] + ListAzureADApps(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.Application] + ListAzureADUsers(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.User] + ListAzureADRoleAssignments(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.UnifiedRoleAssignment] + ListAzureADRoles(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.Role] + ListAzureADServicePrincipalOwners(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] + ListAzureADServicePrincipals(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.ServicePrincipal] + ListAzureDeviceRegisteredOwners(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] + ListAzureDevices(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.Device] + ListAzureADAppRoleAssignments(ctx context.Context, servicePrincipalId string, params query.GraphParams) <-chan AzureResult[azure.AppRoleAssignment] } -func (s azureClient) CloseIdleConnections() { - s.msgraph.CloseIdleConnections() - s.resourceManager.CloseIdleConnections() +type AzureResourceManagerClient interface { + GetAzureADTenants(ctx context.Context, includeAllTenantCategories bool) (azure.TenantList, error) + + ListRoleAssignmentsForResource(ctx context.Context, resourceId string, filter, tenantId string) <-chan AzureResult[azure.RoleAssignment] + ListAzureADTenants(ctx context.Context, includeAllTenantCategories bool) <-chan AzureResult[azure.Tenant] + ListAzureContainerRegistries(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.ContainerRegistry] + ListAzureWebApps(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.WebApp] + ListAzureManagedClusters(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.ManagedCluster] + ListAzureVMScaleSets(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.VMScaleSet] + ListAzureKeyVaults(ctx context.Context, subscriptionId string, params query.RMParams) <-chan AzureResult[azure.KeyVault] + ListAzureManagementGroups(ctx context.Context, skipToken string) <-chan AzureResult[azure.ManagementGroup] + ListAzureManagementGroupDescendants(ctx context.Context, groupId string, top int32) <-chan AzureResult[azure.DescendantInfo] + ListAzureResourceGroups(ctx context.Context, subscriptionId string, params query.RMParams) <-chan AzureResult[azure.ResourceGroup] + ListAzureSubscriptions(ctx context.Context) <-chan AzureResult[azure.Subscription] + ListAzureVirtualMachines(ctx context.Context, subscriptionId string, params query.RMParams) <-chan AzureResult[azure.VirtualMachine] + ListAzureStorageAccounts(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.StorageAccount] + ListAzureStorageContainers(ctx context.Context, subscriptionId string, resourceGroupName string, saName string, filter string, includeDeleted string, maxPageSize string) <-chan AzureResult[azure.StorageContainer] + ListAzureAutomationAccounts(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.AutomationAccount] + ListAzureLogicApps(ctx context.Context, subscriptionId string, filter string, top int32) <-chan AzureResult[azure.LogicApp] + ListAzureFunctionApps(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.FunctionApp] } type AzureClient interface { - GetAzureADApp(ctx context.Context, objectId string, selectCols []string) (*azure.Application, error) - GetAzureADApps(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.ApplicationList, error) - GetAzureADDirectoryObject(ctx context.Context, objectId string) (json.RawMessage, error) - GetAzureADGroup(ctx context.Context, objectId string, selectCols []string) (*azure.Group, error) - GetAzureADGroupOwners(ctx context.Context, objectId string, filter string, search string, orderBy string, selectCols []string, top int32, count bool) (azure.DirectoryObjectList, error) - GetAzureADGroups(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.GroupList, error) - GetAzureADOrganization(ctx context.Context, selectCols []string) (*azure.Organization, error) - GetAzureADRole(ctx context.Context, roleId string, selectCols []string) (*azure.Role, error) - GetAzureADRoleAssignment(ctx context.Context, objectId string, selectCols []string) (*azure.UnifiedRoleAssignment, error) - GetAzureADRoleAssignments(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.UnifiedRoleAssignmentList, error) - GetAzureADRoles(ctx context.Context, filter, expand string) (azure.RoleList, error) - GetAzureADServicePrincipal(ctx context.Context, objectId string, selectCols []string) (*azure.ServicePrincipal, error) - GetAzureADServicePrincipalOwners(ctx context.Context, objectId string, filter string, search string, orderBy string, selectCols []string, top int32, count bool) (azure.DirectoryObjectList, error) - GetAzureADServicePrincipals(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.ServicePrincipalList, error) - GetAzureADTenants(ctx context.Context, includeAllTenantCategories bool) (azure.TenantList, error) - GetAzureADUser(ctx context.Context, objectId string, selectCols []string) (*azure.User, error) - GetAzureADUsers(ctx context.Context, filter string, search string, orderBy string, selectCols []string, top int32, count bool) (azure.UserList, error) - GetAzureDevice(ctx context.Context, objectId string, selectCols []string) (*azure.Device, error) - GetAzureDevices(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.DeviceList, error) - GetAzureKeyVault(ctx context.Context, subscriptionId, groupName, vaultName string) (*azure.KeyVault, error) - GetAzureKeyVaults(ctx context.Context, subscriptionId string, top int32) (azure.KeyVaultList, error) - GetAzureManagementGroup(ctx context.Context, groupId, filter, expand string, recurse bool) (*azure.ManagementGroup, error) - GetAzureManagementGroups(ctx context.Context) (azure.ManagementGroupList, error) - GetAzureResourceGroup(ctx context.Context, subscriptionId, groupName string) (*azure.ResourceGroup, error) - GetAzureResourceGroups(ctx context.Context, subscriptionId string, filter string, top int32) (azure.ResourceGroupList, error) - GetAzureSubscription(ctx context.Context, objectId string) (*azure.Subscription, error) - GetAzureSubscriptions(ctx context.Context) (azure.SubscriptionList, error) - GetAzureVirtualMachine(ctx context.Context, subscriptionId, groupName, vmName, expand string) (*azure.VirtualMachine, error) - GetAzureVirtualMachines(ctx context.Context, subscriptionId string, statusOnly bool) (azure.VirtualMachineList, error) - GetAzureStorageAccount(ctx context.Context, subscriptionId, groupName, saName, expand string) (*azure.StorageAccount, error) - GetAzureStorageAccounts(ctx context.Context, subscriptionId string) (azure.StorageAccountList, error) - GetResourceRoleAssignments(ctx context.Context, subscriptionId string, filter string, expand string) (azure.RoleAssignmentList, error) - GetRoleAssignmentsForResource(ctx context.Context, resourceId string, filter string) (azure.RoleAssignmentList, error) - ListAzureADAppMemberObjects(ctx context.Context, objectId string, securityEnabledOnly bool) <-chan azure.MemberObjectResult - ListAzureADAppOwners(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.AppOwnerResult - ListAzureADApps(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.ApplicationResult - ListAzureADGroupMembers(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.MemberObjectResult - ListAzureADGroupOwners(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.GroupOwnerResult - ListAzureADGroups(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.GroupResult - ListAzureADRoleAssignments(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.UnifiedRoleAssignmentResult - ListAzureADRoles(ctx context.Context, filter, expand string) <-chan azure.RoleResult - ListAzureADServicePrincipalOwners(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.ServicePrincipalOwnerResult - ListAzureADServicePrincipals(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.ServicePrincipalResult - ListAzureADTenants(ctx context.Context, includeAllTenantCategories bool) <-chan azure.TenantResult - ListAzureADUsers(ctx context.Context, filter string, search string, orderBy string, selectCols []string) <-chan azure.UserResult - ListAzureContainerRegistries(ctx context.Context, subscriptionId string) <-chan azure.ContainerRegistryResult - ListAzureWebApps(ctx context.Context, subscriptionId string) <-chan azure.WebAppResult - ListAzureManagedClusters(ctx context.Context, subscriptionId string, statusOnly bool) <-chan azure.ManagedClusterResult - ListAzureVMScaleSets(ctx context.Context, subscriptionId string, statusOnly bool) <-chan azure.VMScaleSetResult - ListAzureDeviceRegisteredOwners(ctx context.Context, objectId string, securityEnabledOnly bool) <-chan azure.DeviceRegisteredOwnerResult - ListAzureDevices(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.DeviceResult - ListAzureKeyVaults(ctx context.Context, subscriptionId string, top int32) <-chan azure.KeyVaultResult - ListAzureManagementGroupDescendants(ctx context.Context, groupId string) <-chan azure.DescendantInfoResult - ListAzureManagementGroups(ctx context.Context) <-chan azure.ManagementGroupResult - ListAzureResourceGroups(ctx context.Context, subscriptionId, filter string) <-chan azure.ResourceGroupResult - ListAzureSubscriptions(ctx context.Context) <-chan azure.SubscriptionResult - ListAzureVirtualMachines(ctx context.Context, subscriptionId string, statusOnly bool) <-chan azure.VirtualMachineResult - ListAzureStorageAccounts(ctx context.Context, subscriptionId string) <-chan azure.StorageAccountResult - ListAzureStorageContainers(ctx context.Context, subscriptionId string, resourceGroupName string, saName string, filter string, includeDeleted string, maxPageSize string) <-chan azure.StorageContainerResult - ListAzureAutomationAccounts(ctx context.Context, subscriptionId string) <-chan azure.AutomationAccountResult - ListAzureLogicApps(ctx context.Context, subscriptionId string, filter string, top int32) <-chan azure.LogicAppResult - ListAzureFunctionApps(ctx context.Context, subscriptionId string) <-chan azure.FunctionAppResult - ListResourceRoleAssignments(ctx context.Context, subscriptionId string, filter string, expand string) <-chan azure.RoleAssignmentResult - ListRoleAssignmentsForResource(ctx context.Context, resourceId string, filter string) <-chan azure.RoleAssignmentResult - ListAzureADAppRoleAssignments(ctx context.Context, servicePrincipal, filter, search, orderBy, expand string, selectCols []string) <-chan azure.AppRoleAssignmentResult + AzureGraphClient + AzureResourceManagerClient + TenantInfo() azure.Tenant CloseIdleConnections() } + +func (s azureClient) TenantInfo() azure.Tenant { + return s.tenant +} + +func (s azureClient) CloseIdleConnections() { + s.msgraph.CloseIdleConnections() + s.resourceManager.CloseIdleConnections() +} diff --git a/client/container_registries.go b/client/container_registries.go index 9e7eebb..cc7d1ed 100644 --- a/client/container_registries.go +++ b/client/container_registries.go @@ -20,114 +20,20 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureContainerRegistry(ctx context.Context, subscriptionId, groupName, crName, expand string) (*azure.ContainerRegistry, error) { +// ListAzureContainerRegistries https://learn.microsoft.com/en-us/rest/api/containerregistry/registries/list?view=rest-containerregistry-2023-01-01-preview +func (s *azureClient) ListAzureContainerRegistries(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.ContainerRegistry] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ContainerRegistry/registries/%s", subscriptionId, groupName, crName) - params = query.Params{ApiVersion: "2023-01-01-preview", Expand: expand}.AsMap() - headers map[string]string - response azure.ContainerRegistry + out = make(chan AzureResult[azure.ContainerRegistry]) + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.ContainerRegistry/registries", subscriptionId) + params = query.RMParams{ApiVersion: "2023-01-01-preview"} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureContainerRegistries(ctx context.Context, subscriptionId string) (azure.ContainerRegistryList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.ContainerRegistry/registries", subscriptionId) - params = query.Params{ApiVersion: "2023-01-01-preview"}.AsMap() - headers map[string]string - response azure.ContainerRegistryList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureContainerRegistries(ctx context.Context, subscriptionId string) <-chan azure.ContainerRegistryResult { - out := make(chan azure.ContainerRegistryResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.ContainerRegistryResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureContainerRegistries(ctx, subscriptionId); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ContainerRegistryResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.ContainerRegistry](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.ContainerRegistryList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ContainerRegistryResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/devices.go b/client/devices.go index 823ab71..d4935a2 100644 --- a/client/devices.go +++ b/client/devices.go @@ -19,204 +19,38 @@ package client import ( "context" + "encoding/json" "fmt" - "net/url" - "strings" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureDevice(ctx context.Context, objectId string, selectCols []string) (*azure.Device, error) { +// ListAzureDevices https://learn.microsoft.com/en-us/graph/api/device-list?view=graph-rest-1.0 +func (s *azureClient) ListAzureDevices(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.Device] { var ( - path = fmt.Sprintf("/%s/devices/%s", constants.GraphApiVersion, objectId) - params = query.Params{Select: selectCols}.AsMap() - response azure.DeviceList + out = make(chan AzureResult[azure.Device]) + path = fmt.Sprintf("/%s/devices", constants.GraphApiVersion) ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response.Value[0], nil - } -} -func (s *azureClient) GetAzureDeviceRegisteredOwners(ctx context.Context, objectId string, filter, search string, count bool) (azure.DirectoryObjectList, error) { - var ( - path = fmt.Sprintf("/%s/devices/%s/registeredOwners", constants.GraphApiBetaVersion, objectId) - params = query.Params{Filter: filter, Search: search, Count: count}.AsMap() - response azure.DirectoryObjectList - ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil + if params.Top == 0 { + params.Top = 999 } -} -func (s *azureClient) GetAzureDevices(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.DeviceList, error) { - var ( - path = fmt.Sprintf("/%s/devices", constants.GraphApiVersion) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count, Expand: expand} - headers map[string]string - response azure.DeviceList - ) + go getAzureObjectList[azure.Device](s.msgraph, ctx, path, params, out) - count = count || search != "" || (filter != "" && orderBy != "") || strings.Contains(filter, "endsWith") - if count { - headers = make(map[string]string) - headers["ConsistencyLevel"] = "eventual" - } - if res, err := s.msgraph.Get(ctx, path, params.AsMap(), headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureDevices(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.DeviceResult { - out := make(chan azure.DeviceResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.DeviceResult{} - nextLink string - ) - - if list, err := s.GetAzureDevices(ctx, filter, search, orderBy, expand, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.DeviceResult{Ok: u}); !ok { - return - } - } - - nextLink = list.NextLink - for nextLink != "" { - var list azure.DeviceList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.DeviceResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } -func (s *azureClient) ListAzureDeviceRegisteredOwners(ctx context.Context, objectId string, securityEnabledOnly bool) <-chan azure.DeviceRegisteredOwnerResult { - out := make(chan azure.DeviceRegisteredOwnerResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.DeviceRegisteredOwnerResult{ - DeviceId: objectId, - } - nextLink string - ) +// ListAzureDeviceRegisteredOwners https://learn.microsoft.com/en-us/graph/api/device-list-registeredowners?view=graph-rest-beta +func (s *azureClient) ListAzureDeviceRegisteredOwners(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] { + var ( + out = make(chan AzureResult[json.RawMessage]) + path = fmt.Sprintf("/%s/devices/%s/registeredOwners", constants.GraphApiBetaVersion, objectId) + ) - if list, err := s.GetAzureDeviceRegisteredOwners(ctx, objectId, "", "", false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.DeviceRegisteredOwnerResult{ - DeviceId: objectId, - Ok: u, - }); !ok { - return - } - } + go getAzureObjectList[json.RawMessage](s.msgraph, ctx, path, params, out) - nextLink = list.NextLink - for nextLink != "" { - var list azure.DirectoryObjectList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.DeviceRegisteredOwnerResult{ - DeviceId: objectId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/directory_objects.go b/client/directory_objects.go deleted file mode 100644 index 84ad165..0000000 --- a/client/directory_objects.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2022 Specter Ops, Inc. -// -// This file is part of AzureHound. -// -// AzureHound is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// AzureHound is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package client - -import ( - "context" - "encoding/json" - "fmt" - "io" - - "github.com/bloodhoundad/azurehound/v2/constants" -) - -func (s *azureClient) GetAzureADDirectoryObject(ctx context.Context, objectId string) (json.RawMessage, error) { - var ( - path = fmt.Sprintf("/%s/directoryObjects/%s", constants.GraphApiVersion, objectId) - ) - if res, err := s.msgraph.Get(ctx, path, nil, nil); err != nil { - return nil, err - } else if body, err := io.ReadAll(res.Body); err != nil { - res.Body.Close() - return nil, err - } else { - res.Body.Close() - return json.RawMessage(body), nil - } -} diff --git a/client/function_apps.go b/client/function_apps.go index 3e2e113..9c924d0 100644 --- a/client/function_apps.go +++ b/client/function_apps.go @@ -20,114 +20,20 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureFunctionApp(ctx context.Context, subscriptionId, groupName, functionAppName, expand string) (*azure.FunctionApp, error) { +// ListAzureFunctionApps +func (s *azureClient) ListAzureFunctionApps(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.FunctionApp] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Web/sites/%s", subscriptionId, groupName, functionAppName) - params = query.Params{ApiVersion: "2022-03-01", Expand: expand}.AsMap() - headers map[string]string - response azure.FunctionApp + out = make(chan AzureResult[azure.FunctionApp]) + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Web/sites", subscriptionId) + params = query.RMParams{ApiVersion: "2022-03-01"} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureFunctionApps(ctx context.Context, subscriptionId string) (azure.FunctionAppList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Web/sites", subscriptionId) - params = query.Params{ApiVersion: "2022-03-01"}.AsMap() - headers map[string]string - response azure.FunctionAppList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureFunctionApps(ctx context.Context, subscriptionId string) <-chan azure.FunctionAppResult { - out := make(chan azure.FunctionAppResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.FunctionAppResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureFunctionApps(ctx, subscriptionId); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.FunctionAppResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.FunctionApp](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.FunctionAppList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.FunctionAppResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/groups.go b/client/groups.go index 5db0474..824c8fe 100644 --- a/client/groups.go +++ b/client/groups.go @@ -19,294 +19,54 @@ package client import ( "context" + "encoding/json" "fmt" - "net/url" - "strings" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/constants" - "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureADGroup(ctx context.Context, objectId string, selectCols []string) (*azure.Group, error) { +// ListAzureADGroups https://learn.microsoft.com/en-us/graph/api/group-list?view=graph-rest-beta +func (s *azureClient) ListAzureADGroups(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.Group] { var ( - path = fmt.Sprintf("/%s/groups/%s", constants.GraphApiVersion, objectId) - params = query.Params{Select: selectCols}.AsMap() - response azure.GroupList + out = make(chan AzureResult[azure.Group]) + path = fmt.Sprintf("/%s/groups", constants.GraphApiVersion) ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response.Value[0], nil - } -} -func (s *azureClient) GetAzureADGroupOwners(ctx context.Context, objectId string, filter string, search string, orderBy string, selectCols []string, top int32, count bool) (azure.DirectoryObjectList, error) { - var ( - path = fmt.Sprintf("/%s/groups/%s/owners", constants.GraphApiBetaVersion, objectId) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count}.AsMap() - response azure.DirectoryObjectList - ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil + if params.Top == 0 { + params.Top = 99 } -} -func (s *azureClient) GetAzureADGroupMembers(ctx context.Context, objectId string, filter string, search string, count bool) (azure.MemberObjectList, error) { - var ( - path = fmt.Sprintf("/%s/groups/%s/members", constants.GraphApiBetaVersion, objectId) - params = query.Params{Filter: filter, Search: search, Count: count}.AsMap() - response azure.MemberObjectList - ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } + go getAzureObjectList[azure.Group](s.msgraph, ctx, path, params, out) + + return out } -func (s *azureClient) GetAzureADGroups(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.GroupList, error) { +// ListAzureADGroupOwners https://learn.microsoft.com/en-us/graph/api/group-list-owners?view=graph-rest-beta +func (s *azureClient) ListAzureADGroupOwners(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] { var ( - path = fmt.Sprintf("/%s/groups", constants.GraphApiVersion) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count, Expand: expand} - headers map[string]string - response azure.GroupList + out = make(chan AzureResult[json.RawMessage]) + path = fmt.Sprintf("/%s/groups/%s/owners", constants.GraphApiBetaVersion, objectId) ) - count = count || search != "" || (filter != "" && orderBy != "") || strings.Contains(filter, "endsWith") - if count { - headers = make(map[string]string) - headers["ConsistencyLevel"] = "eventual" + if params.Top == 0 { + params.Top = 99 } - if res, err := s.msgraph.Get(ctx, path, params.AsMap(), headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureADGroups(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.GroupResult { - out := make(chan azure.GroupResult) - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) + go getAzureObjectList[json.RawMessage](s.msgraph, ctx, path, params, out) - var ( - errResult = azure.GroupResult{} - nextLink string - ) - - if list, err := s.GetAzureADGroups(ctx, filter, search, orderBy, expand, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.GroupResult{Ok: u}); !ok { - return - } - } - - nextLink = list.NextLink - for nextLink != "" { - var list azure.GroupList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.GroupResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } -func (s *azureClient) ListAzureADGroupOwners(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.GroupOwnerResult { - out := make(chan azure.GroupOwnerResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.GroupOwnerResult{} - nextLink string - ) - - if list, err := s.GetAzureADGroupOwners(ctx, objectId, filter, search, orderBy, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.GroupOwnerResult{ - GroupId: objectId, - Ok: u, - }); !ok { - return - } - } - - nextLink = list.NextLink - for nextLink != "" { - var list azure.DirectoryObjectList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.GroupOwnerResult{ - GroupId: objectId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() - return out -} - -func (s *azureClient) ListAzureADGroupMembers(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.MemberObjectResult { - out := make(chan azure.MemberObjectResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.MemberObjectResult{ - ParentId: objectId, - ParentType: string(enums.EntityGroup), - } - nextLink string - ) +// ListAzureADGroupMembers https://learn.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-beta +func (s *azureClient) ListAzureADGroupMembers(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] { + var ( + out = make(chan AzureResult[json.RawMessage]) + path = fmt.Sprintf("/%s/groups/%s/members", constants.GraphApiBetaVersion, objectId) + ) - if list, err := s.GetAzureADGroupMembers(ctx, objectId, filter, search, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.MemberObjectResult{ - ParentId: objectId, - ParentType: string(enums.EntityGroup), - Ok: u, - }); !ok { - return - } - } + go getAzureObjectList[json.RawMessage](s.msgraph, ctx, path, params, out) - nextLink = list.NextLink - for nextLink != "" { - var list azure.MemberObjectList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.MemberObjectResult{ - ParentId: objectId, - ParentType: string(enums.EntityGroup), - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/keyvaults.go b/client/keyvaults.go index 56e33f7..c7735c7 100644 --- a/client/keyvaults.go +++ b/client/keyvaults.go @@ -20,117 +20,23 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureKeyVault(ctx context.Context, subscriptionId, groupName, vaultName string) (*azure.KeyVault, error) { +// ListAzureKeyVaults https://learn.microsoft.com/en-us/rest/api/keyvault/keyvault/vaults/list-by-subscription?view=rest-keyvault-keyvault-2019-09-01 +func (s *azureClient) ListAzureKeyVaults(ctx context.Context, subscriptionId string, params query.RMParams) <-chan AzureResult[azure.KeyVault] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.KeyVault/vaults/%s", subscriptionId, groupName, vaultName) - params = query.Params{ApiVersion: "2019-09-01"}.AsMap() - headers map[string]string - response azure.KeyVault - ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureKeyVaults(ctx context.Context, subscriptionId string, top int32) (azure.KeyVaultList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.KeyVault/vaults", subscriptionId) - params = query.Params{ApiVersion: "2019-09-01", Top: top}.AsMap() - headers map[string]string - response azure.KeyVaultList + out = make(chan AzureResult[azure.KeyVault]) + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.KeyVault/vaults", subscriptionId) ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil + if params.ApiVersion == "" { + params.ApiVersion = "2019-09-01" } -} - -func (s *azureClient) ListAzureKeyVaults(ctx context.Context, subscriptionId string, top int32) <-chan azure.KeyVaultResult { - out := make(chan azure.KeyVaultResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.KeyVaultResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureKeyVaults(ctx, subscriptionId, top); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.KeyVaultResult{ - SubscriptionId: subscriptionId, - Ok: u, - }); !ok { - return - } - } + go getAzureObjectList[azure.KeyVault](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.KeyVaultList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.KeyVaultResult{ - SubscriptionId: subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/logic_apps.go b/client/logic_apps.go index 54006a5..711bfe2 100644 --- a/client/logic_apps.go +++ b/client/logic_apps.go @@ -20,114 +20,20 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureLogicApp(ctx context.Context, subscriptionId, groupName, logicappName, expand string) (*azure.LogicApp, error) { +// ListAzureLogicApps https://learn.microsoft.com/en-us/rest/api/logic/workflows/list-by-subscription?view=rest-logic-2016-06-01 +func (s *azureClient) ListAzureLogicApps(ctx context.Context, subscriptionId string, filter string, top int32) <-chan AzureResult[azure.LogicApp] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Logic/workflows/%s", subscriptionId, groupName, logicappName) - params = query.Params{ApiVersion: "2016-06-01", Expand: expand}.AsMap() - headers map[string]string - response azure.LogicApp + out = make(chan AzureResult[azure.LogicApp]) + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Logic/workflows", subscriptionId) + params = query.RMParams{ApiVersion: "2016-06-01", Filter: filter, Top: top} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureLogicApps(ctx context.Context, subscriptionId string, filter string, top int32) (azure.LogicAppList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Logic/workflows", subscriptionId) - params = query.Params{ApiVersion: "2016-06-01", Filter: filter, Top: top}.AsMap() - headers map[string]string - response azure.LogicAppList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureLogicApps(ctx context.Context, subscriptionId string, filter string, top int32) <-chan azure.LogicAppResult { - out := make(chan azure.LogicAppResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.LogicAppResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureLogicApps(ctx, subscriptionId, filter, top); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.LogicAppResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.LogicApp](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.LogicAppList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.LogicAppResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/managed_clusters.go b/client/managed_clusters.go index b1db393..05c6b19 100644 --- a/client/managed_clusters.go +++ b/client/managed_clusters.go @@ -20,114 +20,20 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureManagedCluster(ctx context.Context, subscriptionId, groupName, mcName, expand string) (*azure.ManagedCluster, error) { +// ListAzureManagedClusters https://learn.microsoft.com/en-us/rest/api/servicefabric/managedclusters/managed-clusters/list-by-subscription?view=rest-servicefabric-managedclusters-2021-07-01 +func (s *azureClient) ListAzureManagedClusters(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.ManagedCluster] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ContainerService/managedClusters/%s", subscriptionId, groupName, mcName) - params = query.Params{ApiVersion: "2021-07-01", Expand: expand}.AsMap() - headers map[string]string - response azure.ManagedCluster + out = make(chan AzureResult[azure.ManagedCluster]) + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.ContainerService/managedClusters", subscriptionId) + params = query.RMParams{ApiVersion: "2021-07-01"} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureManagedClusters(ctx context.Context, subscriptionId string, statusOnly bool) (azure.ManagedClusterList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.ContainerService/managedClusters", subscriptionId) - params = query.Params{ApiVersion: "2021-07-01", StatusOnly: statusOnly}.AsMap() - headers map[string]string - response azure.ManagedClusterList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureManagedClusters(ctx context.Context, subscriptionId string, statusOnly bool) <-chan azure.ManagedClusterResult { - out := make(chan azure.ManagedClusterResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.ManagedClusterResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureManagedClusters(ctx, subscriptionId, statusOnly); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ManagedClusterResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.ManagedCluster](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.ManagedClusterList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ManagedClusterResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/management_groups.go b/client/management_groups.go index f68d866..a269b92 100644 --- a/client/management_groups.go +++ b/client/management_groups.go @@ -20,191 +20,33 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureManagementGroup(ctx context.Context, groupId, filter, expand string, recurse bool) (*azure.ManagementGroup, error) { +// ListAzureManagementGroups https://learn.microsoft.com/en-us/rest/api/managementgroups/management-groups/list?view=rest-managementgroups-2020-05-01 +func (s *azureClient) ListAzureManagementGroups(ctx context.Context, skipToken string) <-chan AzureResult[azure.ManagementGroup] { var ( - path = fmt.Sprintf("/providers/Microsoft.Management/managementGroups/%s", groupId) - params = query.Params{ApiVersion: "2020-05-01", Filter: filter, Expand: expand, Recurse: recurse}.AsMap() - headers map[string]string - response azure.ManagementGroup + out = make(chan AzureResult[azure.ManagementGroup]) + path = "/providers/Microsoft.Management/managementGroups" + params = query.RMParams{ApiVersion: "2020-05-01", SkipToken: skipToken} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} -func (s *azureClient) GetAzureManagementGroups(ctx context.Context) (azure.ManagementGroupList, error) { - var ( - path = "/providers/Microsoft.Management/managementGroups" - params = query.Params{ApiVersion: "2020-05-01"}.AsMap() - headers map[string]string - response azure.ManagementGroupList - ) + go getAzureObjectList[azure.ManagementGroup](s.resourceManager, ctx, path, params, out) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } + return out } -func (s *azureClient) GetAzureManagementGroupDescendants(ctx context.Context, groupId string, top int32) (azure.DescendantInfoList, error) { +// ListAzureManagementGroupDescendants https://learn.microsoft.com/en-us/rest/api/managementgroups/management-groups/get-descendants?view=rest-managementgroups-2020-05-01 +func (s *azureClient) ListAzureManagementGroupDescendants(ctx context.Context, groupId string, top int32) <-chan AzureResult[azure.DescendantInfo] { var ( - path = fmt.Sprintf("/providers/Microsoft.Management/managementGroups/%s/descendants", groupId) - params = query.Params{ApiVersion: "2020-05-01", Top: top}.AsMap() - headers map[string]string - response azure.DescendantInfoList + out = make(chan AzureResult[azure.DescendantInfo]) + path = fmt.Sprintf("/providers/Microsoft.Management/managementGroups/%s/descendants", groupId) + params = query.RMParams{ApiVersion: "2020-05-01", Top: top} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureManagementGroups(ctx context.Context) <-chan azure.ManagementGroupResult { - out := make(chan azure.ManagementGroupResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.ManagementGroupResult{} - nextLink string - ) - - if result, err := s.GetAzureManagementGroups(ctx); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ManagementGroupResult{Ok: u}); !ok { - return - } - } - - nextLink = result.NextLink - for nextLink != "" { - var list azure.ManagementGroupList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ManagementGroupResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() - return out -} - -func (s *azureClient) ListAzureManagementGroupDescendants(ctx context.Context, groupId string) <-chan azure.DescendantInfoResult { - out := make(chan azure.DescendantInfoResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.DescendantInfoResult{} - nextLink string - ) - - if result, err := s.GetAzureManagementGroupDescendants(ctx, groupId, 3000); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.DescendantInfoResult{Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.DescendantInfo](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.DescendantInfoList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.DescendantInfoResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/mocks/client.go b/client/mocks/client.go index fa2ca60..97dfeb3 100644 --- a/client/mocks/client.go +++ b/client/mocks/client.go @@ -5,12 +5,14 @@ package mocks import ( - context "context" - json "encoding/json" - reflect "reflect" - - azure "github.com/bloodhoundad/azurehound/v2/models/azure" - gomock "go.uber.org/mock/gomock" + "context" + "encoding/json" + "reflect" + + "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" + "github.com/bloodhoundad/azurehound/v2/models/azure" + "go.uber.org/mock/gomock" ) // MockAzureClient is a mock of AzureClient interface. @@ -48,96 +50,6 @@ func (mr *MockAzureClientMockRecorder) CloseIdleConnections() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseIdleConnections", reflect.TypeOf((*MockAzureClient)(nil).CloseIdleConnections)) } -// GetAzureADApp mocks base method. -func (m *MockAzureClient) GetAzureADApp(arg0 context.Context, arg1 string, arg2 []string) (*azure.Application, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADApp", arg0, arg1, arg2) - ret0, _ := ret[0].(*azure.Application) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADApp indicates an expected call of GetAzureADApp. -func (mr *MockAzureClientMockRecorder) GetAzureADApp(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADApp", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADApp), arg0, arg1, arg2) -} - -// GetAzureADApps mocks base method. -func (m *MockAzureClient) GetAzureADApps(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string, arg6 int32, arg7 bool) (azure.ApplicationList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADApps", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - ret0, _ := ret[0].(azure.ApplicationList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADApps indicates an expected call of GetAzureADApps. -func (mr *MockAzureClientMockRecorder) GetAzureADApps(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADApps", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADApps), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) -} - -// GetAzureADDirectoryObject mocks base method. -func (m *MockAzureClient) GetAzureADDirectoryObject(arg0 context.Context, arg1 string) (json.RawMessage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADDirectoryObject", arg0, arg1) - ret0, _ := ret[0].(json.RawMessage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADDirectoryObject indicates an expected call of GetAzureADDirectoryObject. -func (mr *MockAzureClientMockRecorder) GetAzureADDirectoryObject(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADDirectoryObject", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADDirectoryObject), arg0, arg1) -} - -// GetAzureADGroup mocks base method. -func (m *MockAzureClient) GetAzureADGroup(arg0 context.Context, arg1 string, arg2 []string) (*azure.Group, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADGroup", arg0, arg1, arg2) - ret0, _ := ret[0].(*azure.Group) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADGroup indicates an expected call of GetAzureADGroup. -func (mr *MockAzureClientMockRecorder) GetAzureADGroup(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADGroup", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADGroup), arg0, arg1, arg2) -} - -// GetAzureADGroupOwners mocks base method. -func (m *MockAzureClient) GetAzureADGroupOwners(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string, arg6 int32, arg7 bool) (azure.DirectoryObjectList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADGroupOwners", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - ret0, _ := ret[0].(azure.DirectoryObjectList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADGroupOwners indicates an expected call of GetAzureADGroupOwners. -func (mr *MockAzureClientMockRecorder) GetAzureADGroupOwners(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADGroupOwners", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADGroupOwners), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) -} - -// GetAzureADGroups mocks base method. -func (m *MockAzureClient) GetAzureADGroups(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string, arg6 int32, arg7 bool) (azure.GroupList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADGroups", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - ret0, _ := ret[0].(azure.GroupList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADGroups indicates an expected call of GetAzureADGroups. -func (mr *MockAzureClientMockRecorder) GetAzureADGroups(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADGroups", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADGroups), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) -} - // GetAzureADOrganization mocks base method. func (m *MockAzureClient) GetAzureADOrganization(arg0 context.Context, arg1 []string) (*azure.Organization, error) { m.ctrl.T.Helper() @@ -153,111 +65,6 @@ func (mr *MockAzureClientMockRecorder) GetAzureADOrganization(arg0, arg1 interfa return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADOrganization", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADOrganization), arg0, arg1) } -// GetAzureADRole mocks base method. -func (m *MockAzureClient) GetAzureADRole(arg0 context.Context, arg1 string, arg2 []string) (*azure.Role, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADRole", arg0, arg1, arg2) - ret0, _ := ret[0].(*azure.Role) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADRole indicates an expected call of GetAzureADRole. -func (mr *MockAzureClientMockRecorder) GetAzureADRole(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADRole", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADRole), arg0, arg1, arg2) -} - -// GetAzureADRoleAssignment mocks base method. -func (m *MockAzureClient) GetAzureADRoleAssignment(arg0 context.Context, arg1 string, arg2 []string) (*azure.UnifiedRoleAssignment, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADRoleAssignment", arg0, arg1, arg2) - ret0, _ := ret[0].(*azure.UnifiedRoleAssignment) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADRoleAssignment indicates an expected call of GetAzureADRoleAssignment. -func (mr *MockAzureClientMockRecorder) GetAzureADRoleAssignment(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADRoleAssignment", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADRoleAssignment), arg0, arg1, arg2) -} - -// GetAzureADRoleAssignments mocks base method. -func (m *MockAzureClient) GetAzureADRoleAssignments(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string, arg6 int32, arg7 bool) (azure.UnifiedRoleAssignmentList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADRoleAssignments", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - ret0, _ := ret[0].(azure.UnifiedRoleAssignmentList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADRoleAssignments indicates an expected call of GetAzureADRoleAssignments. -func (mr *MockAzureClientMockRecorder) GetAzureADRoleAssignments(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADRoleAssignments", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADRoleAssignments), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) -} - -// GetAzureADRoles mocks base method. -func (m *MockAzureClient) GetAzureADRoles(arg0 context.Context, arg1, arg2 string) (azure.RoleList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADRoles", arg0, arg1, arg2) - ret0, _ := ret[0].(azure.RoleList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADRoles indicates an expected call of GetAzureADRoles. -func (mr *MockAzureClientMockRecorder) GetAzureADRoles(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADRoles", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADRoles), arg0, arg1, arg2) -} - -// GetAzureADServicePrincipal mocks base method. -func (m *MockAzureClient) GetAzureADServicePrincipal(arg0 context.Context, arg1 string, arg2 []string) (*azure.ServicePrincipal, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADServicePrincipal", arg0, arg1, arg2) - ret0, _ := ret[0].(*azure.ServicePrincipal) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADServicePrincipal indicates an expected call of GetAzureADServicePrincipal. -func (mr *MockAzureClientMockRecorder) GetAzureADServicePrincipal(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADServicePrincipal", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADServicePrincipal), arg0, arg1, arg2) -} - -// GetAzureADServicePrincipalOwners mocks base method. -func (m *MockAzureClient) GetAzureADServicePrincipalOwners(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string, arg6 int32, arg7 bool) (azure.DirectoryObjectList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADServicePrincipalOwners", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - ret0, _ := ret[0].(azure.DirectoryObjectList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADServicePrincipalOwners indicates an expected call of GetAzureADServicePrincipalOwners. -func (mr *MockAzureClientMockRecorder) GetAzureADServicePrincipalOwners(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADServicePrincipalOwners", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADServicePrincipalOwners), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) -} - -// GetAzureADServicePrincipals mocks base method. -func (m *MockAzureClient) GetAzureADServicePrincipals(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string, arg6 int32, arg7 bool) (azure.ServicePrincipalList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADServicePrincipals", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - ret0, _ := ret[0].(azure.ServicePrincipalList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADServicePrincipals indicates an expected call of GetAzureADServicePrincipals. -func (mr *MockAzureClientMockRecorder) GetAzureADServicePrincipals(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADServicePrincipals", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADServicePrincipals), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) -} - // GetAzureADTenants mocks base method. func (m *MockAzureClient) GetAzureADTenants(arg0 context.Context, arg1 bool) (azure.TenantList, error) { m.ctrl.T.Helper() @@ -273,435 +80,151 @@ func (mr *MockAzureClientMockRecorder) GetAzureADTenants(arg0, arg1 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADTenants", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADTenants), arg0, arg1) } -// GetAzureADUser mocks base method. -func (m *MockAzureClient) GetAzureADUser(arg0 context.Context, arg1 string, arg2 []string) (*azure.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADUser", arg0, arg1, arg2) - ret0, _ := ret[0].(*azure.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADUser indicates an expected call of GetAzureADUser. -func (mr *MockAzureClientMockRecorder) GetAzureADUser(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADUser", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADUser), arg0, arg1, arg2) -} - -// GetAzureADUsers mocks base method. -func (m *MockAzureClient) GetAzureADUsers(arg0 context.Context, arg1, arg2, arg3 string, arg4 []string, arg5 int32, arg6 bool) (azure.UserList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureADUsers", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(azure.UserList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureADUsers indicates an expected call of GetAzureADUsers. -func (mr *MockAzureClientMockRecorder) GetAzureADUsers(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureADUsers", reflect.TypeOf((*MockAzureClient)(nil).GetAzureADUsers), arg0, arg1, arg2, arg3, arg4, arg5, arg6) -} - -// GetAzureDevice mocks base method. -func (m *MockAzureClient) GetAzureDevice(arg0 context.Context, arg1 string, arg2 []string) (*azure.Device, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureDevice", arg0, arg1, arg2) - ret0, _ := ret[0].(*azure.Device) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureDevice indicates an expected call of GetAzureDevice. -func (mr *MockAzureClientMockRecorder) GetAzureDevice(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureDevice", reflect.TypeOf((*MockAzureClient)(nil).GetAzureDevice), arg0, arg1, arg2) -} - -// GetAzureDevices mocks base method. -func (m *MockAzureClient) GetAzureDevices(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string, arg6 int32, arg7 bool) (azure.DeviceList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureDevices", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - ret0, _ := ret[0].(azure.DeviceList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureDevices indicates an expected call of GetAzureDevices. -func (mr *MockAzureClientMockRecorder) GetAzureDevices(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureDevices", reflect.TypeOf((*MockAzureClient)(nil).GetAzureDevices), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) -} - -// GetAzureKeyVault mocks base method. -func (m *MockAzureClient) GetAzureKeyVault(arg0 context.Context, arg1, arg2, arg3 string) (*azure.KeyVault, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureKeyVault", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(*azure.KeyVault) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureKeyVault indicates an expected call of GetAzureKeyVault. -func (mr *MockAzureClientMockRecorder) GetAzureKeyVault(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureKeyVault", reflect.TypeOf((*MockAzureClient)(nil).GetAzureKeyVault), arg0, arg1, arg2, arg3) -} - -// GetAzureKeyVaults mocks base method. -func (m *MockAzureClient) GetAzureKeyVaults(arg0 context.Context, arg1 string, arg2 int32) (azure.KeyVaultList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureKeyVaults", arg0, arg1, arg2) - ret0, _ := ret[0].(azure.KeyVaultList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureKeyVaults indicates an expected call of GetAzureKeyVaults. -func (mr *MockAzureClientMockRecorder) GetAzureKeyVaults(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureKeyVaults", reflect.TypeOf((*MockAzureClient)(nil).GetAzureKeyVaults), arg0, arg1, arg2) -} - -// GetAzureManagementGroup mocks base method. -func (m *MockAzureClient) GetAzureManagementGroup(arg0 context.Context, arg1, arg2, arg3 string, arg4 bool) (*azure.ManagementGroup, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureManagementGroup", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(*azure.ManagementGroup) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureManagementGroup indicates an expected call of GetAzureManagementGroup. -func (mr *MockAzureClientMockRecorder) GetAzureManagementGroup(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureManagementGroup", reflect.TypeOf((*MockAzureClient)(nil).GetAzureManagementGroup), arg0, arg1, arg2, arg3, arg4) -} - -// GetAzureManagementGroups mocks base method. -func (m *MockAzureClient) GetAzureManagementGroups(arg0 context.Context) (azure.ManagementGroupList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureManagementGroups", arg0) - ret0, _ := ret[0].(azure.ManagementGroupList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureManagementGroups indicates an expected call of GetAzureManagementGroups. -func (mr *MockAzureClientMockRecorder) GetAzureManagementGroups(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureManagementGroups", reflect.TypeOf((*MockAzureClient)(nil).GetAzureManagementGroups), arg0) -} - -// GetAzureResourceGroup mocks base method. -func (m *MockAzureClient) GetAzureResourceGroup(arg0 context.Context, arg1, arg2 string) (*azure.ResourceGroup, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureResourceGroup", arg0, arg1, arg2) - ret0, _ := ret[0].(*azure.ResourceGroup) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureResourceGroup indicates an expected call of GetAzureResourceGroup. -func (mr *MockAzureClientMockRecorder) GetAzureResourceGroup(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureResourceGroup", reflect.TypeOf((*MockAzureClient)(nil).GetAzureResourceGroup), arg0, arg1, arg2) -} - -// GetAzureResourceGroups mocks base method. -func (m *MockAzureClient) GetAzureResourceGroups(arg0 context.Context, arg1, arg2 string, arg3 int32) (azure.ResourceGroupList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureResourceGroups", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(azure.ResourceGroupList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureResourceGroups indicates an expected call of GetAzureResourceGroups. -func (mr *MockAzureClientMockRecorder) GetAzureResourceGroups(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureResourceGroups", reflect.TypeOf((*MockAzureClient)(nil).GetAzureResourceGroups), arg0, arg1, arg2, arg3) -} - -// GetAzureStorageAccount mocks base method. -func (m *MockAzureClient) GetAzureStorageAccount(arg0 context.Context, arg1, arg2, arg3, arg4 string) (*azure.StorageAccount, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureStorageAccount", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(*azure.StorageAccount) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureStorageAccount indicates an expected call of GetAzureStorageAccount. -func (mr *MockAzureClientMockRecorder) GetAzureStorageAccount(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureStorageAccount", reflect.TypeOf((*MockAzureClient)(nil).GetAzureStorageAccount), arg0, arg1, arg2, arg3, arg4) -} - -// GetAzureStorageAccounts mocks base method. -func (m *MockAzureClient) GetAzureStorageAccounts(arg0 context.Context, arg1 string) (azure.StorageAccountList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureStorageAccounts", arg0, arg1) - ret0, _ := ret[0].(azure.StorageAccountList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureStorageAccounts indicates an expected call of GetAzureStorageAccounts. -func (mr *MockAzureClientMockRecorder) GetAzureStorageAccounts(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureStorageAccounts", reflect.TypeOf((*MockAzureClient)(nil).GetAzureStorageAccounts), arg0, arg1) -} - -// GetAzureSubscription mocks base method. -func (m *MockAzureClient) GetAzureSubscription(arg0 context.Context, arg1 string) (*azure.Subscription, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureSubscription", arg0, arg1) - ret0, _ := ret[0].(*azure.Subscription) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureSubscription indicates an expected call of GetAzureSubscription. -func (mr *MockAzureClientMockRecorder) GetAzureSubscription(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureSubscription", reflect.TypeOf((*MockAzureClient)(nil).GetAzureSubscription), arg0, arg1) -} - -// GetAzureSubscriptions mocks base method. -func (m *MockAzureClient) GetAzureSubscriptions(arg0 context.Context) (azure.SubscriptionList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureSubscriptions", arg0) - ret0, _ := ret[0].(azure.SubscriptionList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureSubscriptions indicates an expected call of GetAzureSubscriptions. -func (mr *MockAzureClientMockRecorder) GetAzureSubscriptions(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureSubscriptions", reflect.TypeOf((*MockAzureClient)(nil).GetAzureSubscriptions), arg0) -} - -// GetAzureVirtualMachine mocks base method. -func (m *MockAzureClient) GetAzureVirtualMachine(arg0 context.Context, arg1, arg2, arg3, arg4 string) (*azure.VirtualMachine, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureVirtualMachine", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(*azure.VirtualMachine) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureVirtualMachine indicates an expected call of GetAzureVirtualMachine. -func (mr *MockAzureClientMockRecorder) GetAzureVirtualMachine(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureVirtualMachine", reflect.TypeOf((*MockAzureClient)(nil).GetAzureVirtualMachine), arg0, arg1, arg2, arg3, arg4) -} - -// GetAzureVirtualMachines mocks base method. -func (m *MockAzureClient) GetAzureVirtualMachines(arg0 context.Context, arg1 string, arg2 bool) (azure.VirtualMachineList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAzureVirtualMachines", arg0, arg1, arg2) - ret0, _ := ret[0].(azure.VirtualMachineList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAzureVirtualMachines indicates an expected call of GetAzureVirtualMachines. -func (mr *MockAzureClientMockRecorder) GetAzureVirtualMachines(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAzureVirtualMachines", reflect.TypeOf((*MockAzureClient)(nil).GetAzureVirtualMachines), arg0, arg1, arg2) -} - -// GetResourceRoleAssignments mocks base method. -func (m *MockAzureClient) GetResourceRoleAssignments(arg0 context.Context, arg1, arg2, arg3 string) (azure.RoleAssignmentList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetResourceRoleAssignments", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(azure.RoleAssignmentList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetResourceRoleAssignments indicates an expected call of GetResourceRoleAssignments. -func (mr *MockAzureClientMockRecorder) GetResourceRoleAssignments(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResourceRoleAssignments", reflect.TypeOf((*MockAzureClient)(nil).GetResourceRoleAssignments), arg0, arg1, arg2, arg3) -} - -// GetRoleAssignmentsForResource mocks base method. -func (m *MockAzureClient) GetRoleAssignmentsForResource(arg0 context.Context, arg1, arg2 string) (azure.RoleAssignmentList, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetRoleAssignmentsForResource", arg0, arg1, arg2) - ret0, _ := ret[0].(azure.RoleAssignmentList) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetRoleAssignmentsForResource indicates an expected call of GetRoleAssignmentsForResource. -func (mr *MockAzureClientMockRecorder) GetRoleAssignmentsForResource(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRoleAssignmentsForResource", reflect.TypeOf((*MockAzureClient)(nil).GetRoleAssignmentsForResource), arg0, arg1, arg2) -} - -// ListAzureADAppMemberObjects mocks base method. -func (m *MockAzureClient) ListAzureADAppMemberObjects(arg0 context.Context, arg1 string, arg2 bool) <-chan azure.MemberObjectResult { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADAppMemberObjects", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan azure.MemberObjectResult) - return ret0 -} - -// ListAzureADAppMemberObjects indicates an expected call of ListAzureADAppMemberObjects. -func (mr *MockAzureClientMockRecorder) ListAzureADAppMemberObjects(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADAppMemberObjects", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADAppMemberObjects), arg0, arg1, arg2) -} - // ListAzureADAppOwners mocks base method. -func (m *MockAzureClient) ListAzureADAppOwners(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string) <-chan azure.AppOwnerResult { +func (m *MockAzureClient) ListAzureADAppOwners(arg0 context.Context, arg1 string, arg2 query.GraphParams) <-chan client.AzureResult[json.RawMessage] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADAppOwners", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(<-chan azure.AppOwnerResult) + ret := m.ctrl.Call(m, "ListAzureADAppOwners", arg0, arg1, arg2) + ret0, _ := ret[0].(<-chan client.AzureResult[json.RawMessage]) return ret0 } // ListAzureADAppOwners indicates an expected call of ListAzureADAppOwners. -func (mr *MockAzureClientMockRecorder) ListAzureADAppOwners(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADAppOwners(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADAppOwners", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADAppOwners), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADAppOwners", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADAppOwners), arg0, arg1, arg2) } // ListAzureADAppRoleAssignments mocks base method. -func (m *MockAzureClient) ListAzureADAppRoleAssignments(arg0 context.Context, arg1, arg2, arg3, arg4, arg5 string, arg6 []string) <-chan azure.AppRoleAssignmentResult { +func (m *MockAzureClient) ListAzureADAppRoleAssignments(arg0 context.Context, arg1 string, arg2 query.GraphParams) <-chan client.AzureResult[azure.AppRoleAssignment] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADAppRoleAssignments", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(<-chan azure.AppRoleAssignmentResult) + ret := m.ctrl.Call(m, "ListAzureADAppRoleAssignments", arg0, arg1, arg2) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.AppRoleAssignment]) return ret0 } // ListAzureADAppRoleAssignments indicates an expected call of ListAzureADAppRoleAssignments. -func (mr *MockAzureClientMockRecorder) ListAzureADAppRoleAssignments(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADAppRoleAssignments(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADAppRoleAssignments", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADAppRoleAssignments), arg0, arg1, arg2, arg3, arg4, arg5, arg6) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADAppRoleAssignments", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADAppRoleAssignments), arg0, arg1, arg2) } // ListAzureADApps mocks base method. -func (m *MockAzureClient) ListAzureADApps(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string) <-chan azure.ApplicationResult { +func (m *MockAzureClient) ListAzureADApps(arg0 context.Context, arg1 query.GraphParams) <-chan client.AzureResult[azure.Application] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADApps", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(<-chan azure.ApplicationResult) + ret := m.ctrl.Call(m, "ListAzureADApps", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.Application]) return ret0 } // ListAzureADApps indicates an expected call of ListAzureADApps. -func (mr *MockAzureClientMockRecorder) ListAzureADApps(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADApps(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADApps", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADApps), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADApps", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADApps), arg0, arg1) } // ListAzureADGroupMembers mocks base method. -func (m *MockAzureClient) ListAzureADGroupMembers(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string) <-chan azure.MemberObjectResult { +func (m *MockAzureClient) ListAzureADGroupMembers(arg0 context.Context, arg1 string, arg2 query.GraphParams) <-chan client.AzureResult[json.RawMessage] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADGroupMembers", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(<-chan azure.MemberObjectResult) + ret := m.ctrl.Call(m, "ListAzureADGroupMembers", arg0, arg1, arg2) + ret0, _ := ret[0].(<-chan client.AzureResult[json.RawMessage]) return ret0 } // ListAzureADGroupMembers indicates an expected call of ListAzureADGroupMembers. -func (mr *MockAzureClientMockRecorder) ListAzureADGroupMembers(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADGroupMembers(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADGroupMembers", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADGroupMembers), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADGroupMembers", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADGroupMembers), arg0, arg1, arg2) } // ListAzureADGroupOwners mocks base method. -func (m *MockAzureClient) ListAzureADGroupOwners(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string) <-chan azure.GroupOwnerResult { +func (m *MockAzureClient) ListAzureADGroupOwners(arg0 context.Context, arg1 string, arg2 query.GraphParams) <-chan client.AzureResult[json.RawMessage] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADGroupOwners", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(<-chan azure.GroupOwnerResult) + ret := m.ctrl.Call(m, "ListAzureADGroupOwners", arg0, arg1, arg2) + ret0, _ := ret[0].(<-chan client.AzureResult[json.RawMessage]) return ret0 } // ListAzureADGroupOwners indicates an expected call of ListAzureADGroupOwners. -func (mr *MockAzureClientMockRecorder) ListAzureADGroupOwners(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADGroupOwners(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADGroupOwners", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADGroupOwners), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADGroupOwners", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADGroupOwners), arg0, arg1, arg2) } // ListAzureADGroups mocks base method. -func (m *MockAzureClient) ListAzureADGroups(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string) <-chan azure.GroupResult { +func (m *MockAzureClient) ListAzureADGroups(arg0 context.Context, arg1 query.GraphParams) <-chan client.AzureResult[azure.Group] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADGroups", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(<-chan azure.GroupResult) + ret := m.ctrl.Call(m, "ListAzureADGroups", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.Group]) return ret0 } // ListAzureADGroups indicates an expected call of ListAzureADGroups. -func (mr *MockAzureClientMockRecorder) ListAzureADGroups(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADGroups(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADGroups", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADGroups), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADGroups", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADGroups), arg0, arg1) } // ListAzureADRoleAssignments mocks base method. -func (m *MockAzureClient) ListAzureADRoleAssignments(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string) <-chan azure.UnifiedRoleAssignmentResult { +func (m *MockAzureClient) ListAzureADRoleAssignments(arg0 context.Context, arg1 query.GraphParams) <-chan client.AzureResult[azure.UnifiedRoleAssignment] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADRoleAssignments", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(<-chan azure.UnifiedRoleAssignmentResult) + ret := m.ctrl.Call(m, "ListAzureADRoleAssignments", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.UnifiedRoleAssignment]) return ret0 } // ListAzureADRoleAssignments indicates an expected call of ListAzureADRoleAssignments. -func (mr *MockAzureClientMockRecorder) ListAzureADRoleAssignments(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADRoleAssignments(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADRoleAssignments", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADRoleAssignments), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADRoleAssignments", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADRoleAssignments), arg0, arg1) } // ListAzureADRoles mocks base method. -func (m *MockAzureClient) ListAzureADRoles(arg0 context.Context, arg1, arg2 string) <-chan azure.RoleResult { +func (m *MockAzureClient) ListAzureADRoles(arg0 context.Context, arg1 query.GraphParams) <-chan client.AzureResult[azure.Role] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADRoles", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan azure.RoleResult) + ret := m.ctrl.Call(m, "ListAzureADRoles", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.Role]) return ret0 } // ListAzureADRoles indicates an expected call of ListAzureADRoles. -func (mr *MockAzureClientMockRecorder) ListAzureADRoles(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADRoles(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADRoles", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADRoles), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADRoles", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADRoles), arg0, arg1) } // ListAzureADServicePrincipalOwners mocks base method. -func (m *MockAzureClient) ListAzureADServicePrincipalOwners(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string) <-chan azure.ServicePrincipalOwnerResult { +func (m *MockAzureClient) ListAzureADServicePrincipalOwners(arg0 context.Context, arg1 string, arg2 query.GraphParams) <-chan client.AzureResult[json.RawMessage] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADServicePrincipalOwners", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(<-chan azure.ServicePrincipalOwnerResult) + ret := m.ctrl.Call(m, "ListAzureADServicePrincipalOwners", arg0, arg1, arg2) + ret0, _ := ret[0].(<-chan client.AzureResult[json.RawMessage]) return ret0 } // ListAzureADServicePrincipalOwners indicates an expected call of ListAzureADServicePrincipalOwners. -func (mr *MockAzureClientMockRecorder) ListAzureADServicePrincipalOwners(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADServicePrincipalOwners(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADServicePrincipalOwners", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADServicePrincipalOwners), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADServicePrincipalOwners", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADServicePrincipalOwners), arg0, arg1, arg2) } // ListAzureADServicePrincipals mocks base method. -func (m *MockAzureClient) ListAzureADServicePrincipals(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string) <-chan azure.ServicePrincipalResult { +func (m *MockAzureClient) ListAzureADServicePrincipals(arg0 context.Context, arg1 query.GraphParams) <-chan client.AzureResult[azure.ServicePrincipal] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADServicePrincipals", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(<-chan azure.ServicePrincipalResult) + ret := m.ctrl.Call(m, "ListAzureADServicePrincipals", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.ServicePrincipal]) return ret0 } // ListAzureADServicePrincipals indicates an expected call of ListAzureADServicePrincipals. -func (mr *MockAzureClientMockRecorder) ListAzureADServicePrincipals(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADServicePrincipals(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADServicePrincipals", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADServicePrincipals), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADServicePrincipals", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADServicePrincipals), arg0, arg1) } // ListAzureADTenants mocks base method. -func (m *MockAzureClient) ListAzureADTenants(arg0 context.Context, arg1 bool) <-chan azure.TenantResult { +func (m *MockAzureClient) ListAzureADTenants(arg0 context.Context, arg1 bool) <-chan client.AzureResult[azure.Tenant] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureADTenants", arg0, arg1) - ret0, _ := ret[0].(<-chan azure.TenantResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.Tenant]) return ret0 } @@ -712,24 +235,24 @@ func (mr *MockAzureClientMockRecorder) ListAzureADTenants(arg0, arg1 interface{} } // ListAzureADUsers mocks base method. -func (m *MockAzureClient) ListAzureADUsers(arg0 context.Context, arg1, arg2, arg3 string, arg4 []string) <-chan azure.UserResult { +func (m *MockAzureClient) ListAzureADUsers(arg0 context.Context, arg1 query.GraphParams) <-chan client.AzureResult[azure.User] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureADUsers", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(<-chan azure.UserResult) + ret := m.ctrl.Call(m, "ListAzureADUsers", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.User]) return ret0 } // ListAzureADUsers indicates an expected call of ListAzureADUsers. -func (mr *MockAzureClientMockRecorder) ListAzureADUsers(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureADUsers(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADUsers", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADUsers), arg0, arg1, arg2, arg3, arg4) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureADUsers", reflect.TypeOf((*MockAzureClient)(nil).ListAzureADUsers), arg0, arg1) } // ListAzureAutomationAccounts mocks base method. -func (m *MockAzureClient) ListAzureAutomationAccounts(arg0 context.Context, arg1 string) <-chan azure.AutomationAccountResult { +func (m *MockAzureClient) ListAzureAutomationAccounts(arg0 context.Context, arg1 string) <-chan client.AzureResult[azure.AutomationAccount] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureAutomationAccounts", arg0, arg1) - ret0, _ := ret[0].(<-chan azure.AutomationAccountResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.AutomationAccount]) return ret0 } @@ -740,10 +263,10 @@ func (mr *MockAzureClientMockRecorder) ListAzureAutomationAccounts(arg0, arg1 in } // ListAzureContainerRegistries mocks base method. -func (m *MockAzureClient) ListAzureContainerRegistries(arg0 context.Context, arg1 string) <-chan azure.ContainerRegistryResult { +func (m *MockAzureClient) ListAzureContainerRegistries(arg0 context.Context, arg1 string) <-chan client.AzureResult[azure.ContainerRegistry] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureContainerRegistries", arg0, arg1) - ret0, _ := ret[0].(<-chan azure.ContainerRegistryResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.ContainerRegistry]) return ret0 } @@ -754,10 +277,10 @@ func (mr *MockAzureClientMockRecorder) ListAzureContainerRegistries(arg0, arg1 i } // ListAzureDeviceRegisteredOwners mocks base method. -func (m *MockAzureClient) ListAzureDeviceRegisteredOwners(arg0 context.Context, arg1 string, arg2 bool) <-chan azure.DeviceRegisteredOwnerResult { +func (m *MockAzureClient) ListAzureDeviceRegisteredOwners(arg0 context.Context, arg1 string, arg2 query.GraphParams) <-chan client.AzureResult[json.RawMessage] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureDeviceRegisteredOwners", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan azure.DeviceRegisteredOwnerResult) + ret0, _ := ret[0].(<-chan client.AzureResult[json.RawMessage]) return ret0 } @@ -768,24 +291,24 @@ func (mr *MockAzureClientMockRecorder) ListAzureDeviceRegisteredOwners(arg0, arg } // ListAzureDevices mocks base method. -func (m *MockAzureClient) ListAzureDevices(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 []string) <-chan azure.DeviceResult { +func (m *MockAzureClient) ListAzureDevices(arg0 context.Context, arg1 query.GraphParams) <-chan client.AzureResult[azure.Device] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureDevices", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(<-chan azure.DeviceResult) + ret := m.ctrl.Call(m, "ListAzureDevices", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.Device]) return ret0 } // ListAzureDevices indicates an expected call of ListAzureDevices. -func (mr *MockAzureClientMockRecorder) ListAzureDevices(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureDevices(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureDevices", reflect.TypeOf((*MockAzureClient)(nil).ListAzureDevices), arg0, arg1, arg2, arg3, arg4, arg5) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureDevices", reflect.TypeOf((*MockAzureClient)(nil).ListAzureDevices), arg0, arg1) } // ListAzureFunctionApps mocks base method. -func (m *MockAzureClient) ListAzureFunctionApps(arg0 context.Context, arg1 string) <-chan azure.FunctionAppResult { +func (m *MockAzureClient) ListAzureFunctionApps(arg0 context.Context, arg1 string) <-chan client.AzureResult[azure.FunctionApp] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureFunctionApps", arg0, arg1) - ret0, _ := ret[0].(<-chan azure.FunctionAppResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.FunctionApp]) return ret0 } @@ -796,10 +319,10 @@ func (mr *MockAzureClientMockRecorder) ListAzureFunctionApps(arg0, arg1 interfac } // ListAzureKeyVaults mocks base method. -func (m *MockAzureClient) ListAzureKeyVaults(arg0 context.Context, arg1 string, arg2 int32) <-chan azure.KeyVaultResult { +func (m *MockAzureClient) ListAzureKeyVaults(arg0 context.Context, arg1 string, arg2 query.RMParams) <-chan client.AzureResult[azure.KeyVault] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureKeyVaults", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan azure.KeyVaultResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.KeyVault]) return ret0 } @@ -810,10 +333,10 @@ func (mr *MockAzureClientMockRecorder) ListAzureKeyVaults(arg0, arg1, arg2 inter } // ListAzureLogicApps mocks base method. -func (m *MockAzureClient) ListAzureLogicApps(arg0 context.Context, arg1, arg2 string, arg3 int32) <-chan azure.LogicAppResult { +func (m *MockAzureClient) ListAzureLogicApps(arg0 context.Context, arg1, arg2 string, arg3 int32) <-chan client.AzureResult[azure.LogicApp] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureLogicApps", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(<-chan azure.LogicAppResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.LogicApp]) return ret0 } @@ -824,52 +347,52 @@ func (mr *MockAzureClientMockRecorder) ListAzureLogicApps(arg0, arg1, arg2, arg3 } // ListAzureManagedClusters mocks base method. -func (m *MockAzureClient) ListAzureManagedClusters(arg0 context.Context, arg1 string, arg2 bool) <-chan azure.ManagedClusterResult { +func (m *MockAzureClient) ListAzureManagedClusters(arg0 context.Context, arg1 string) <-chan client.AzureResult[azure.ManagedCluster] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureManagedClusters", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan azure.ManagedClusterResult) + ret := m.ctrl.Call(m, "ListAzureManagedClusters", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.ManagedCluster]) return ret0 } // ListAzureManagedClusters indicates an expected call of ListAzureManagedClusters. -func (mr *MockAzureClientMockRecorder) ListAzureManagedClusters(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureManagedClusters(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureManagedClusters", reflect.TypeOf((*MockAzureClient)(nil).ListAzureManagedClusters), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureManagedClusters", reflect.TypeOf((*MockAzureClient)(nil).ListAzureManagedClusters), arg0, arg1) } // ListAzureManagementGroupDescendants mocks base method. -func (m *MockAzureClient) ListAzureManagementGroupDescendants(arg0 context.Context, arg1 string) <-chan azure.DescendantInfoResult { +func (m *MockAzureClient) ListAzureManagementGroupDescendants(arg0 context.Context, arg1 string, arg2 int32) <-chan client.AzureResult[azure.DescendantInfo] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureManagementGroupDescendants", arg0, arg1) - ret0, _ := ret[0].(<-chan azure.DescendantInfoResult) + ret := m.ctrl.Call(m, "ListAzureManagementGroupDescendants", arg0, arg1, arg2) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.DescendantInfo]) return ret0 } // ListAzureManagementGroupDescendants indicates an expected call of ListAzureManagementGroupDescendants. -func (mr *MockAzureClientMockRecorder) ListAzureManagementGroupDescendants(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureManagementGroupDescendants(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureManagementGroupDescendants", reflect.TypeOf((*MockAzureClient)(nil).ListAzureManagementGroupDescendants), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureManagementGroupDescendants", reflect.TypeOf((*MockAzureClient)(nil).ListAzureManagementGroupDescendants), arg0, arg1, arg2) } // ListAzureManagementGroups mocks base method. -func (m *MockAzureClient) ListAzureManagementGroups(arg0 context.Context) <-chan azure.ManagementGroupResult { +func (m *MockAzureClient) ListAzureManagementGroups(arg0 context.Context, arg1 string) <-chan client.AzureResult[azure.ManagementGroup] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureManagementGroups", arg0) - ret0, _ := ret[0].(<-chan azure.ManagementGroupResult) + ret := m.ctrl.Call(m, "ListAzureManagementGroups", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.ManagementGroup]) return ret0 } // ListAzureManagementGroups indicates an expected call of ListAzureManagementGroups. -func (mr *MockAzureClientMockRecorder) ListAzureManagementGroups(arg0 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureManagementGroups(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureManagementGroups", reflect.TypeOf((*MockAzureClient)(nil).ListAzureManagementGroups), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureManagementGroups", reflect.TypeOf((*MockAzureClient)(nil).ListAzureManagementGroups), arg0, arg1) } // ListAzureResourceGroups mocks base method. -func (m *MockAzureClient) ListAzureResourceGroups(arg0 context.Context, arg1, arg2 string) <-chan azure.ResourceGroupResult { +func (m *MockAzureClient) ListAzureResourceGroups(arg0 context.Context, arg1 string, arg2 query.RMParams) <-chan client.AzureResult[azure.ResourceGroup] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureResourceGroups", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan azure.ResourceGroupResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.ResourceGroup]) return ret0 } @@ -880,10 +403,10 @@ func (mr *MockAzureClientMockRecorder) ListAzureResourceGroups(arg0, arg1, arg2 } // ListAzureStorageAccounts mocks base method. -func (m *MockAzureClient) ListAzureStorageAccounts(arg0 context.Context, arg1 string) <-chan azure.StorageAccountResult { +func (m *MockAzureClient) ListAzureStorageAccounts(arg0 context.Context, arg1 string) <-chan client.AzureResult[azure.StorageAccount] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureStorageAccounts", arg0, arg1) - ret0, _ := ret[0].(<-chan azure.StorageAccountResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.StorageAccount]) return ret0 } @@ -894,10 +417,10 @@ func (mr *MockAzureClientMockRecorder) ListAzureStorageAccounts(arg0, arg1 inter } // ListAzureStorageContainers mocks base method. -func (m *MockAzureClient) ListAzureStorageContainers(arg0 context.Context, arg1, arg2, arg3, arg4, arg5, arg6 string) <-chan azure.StorageContainerResult { +func (m *MockAzureClient) ListAzureStorageContainers(arg0 context.Context, arg1, arg2, arg3, arg4, arg5, arg6 string) <-chan client.AzureResult[azure.StorageContainer] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureStorageContainers", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(<-chan azure.StorageContainerResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.StorageContainer]) return ret0 } @@ -908,10 +431,10 @@ func (mr *MockAzureClientMockRecorder) ListAzureStorageContainers(arg0, arg1, ar } // ListAzureSubscriptions mocks base method. -func (m *MockAzureClient) ListAzureSubscriptions(arg0 context.Context) <-chan azure.SubscriptionResult { +func (m *MockAzureClient) ListAzureSubscriptions(arg0 context.Context) <-chan client.AzureResult[azure.Subscription] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureSubscriptions", arg0) - ret0, _ := ret[0].(<-chan azure.SubscriptionResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.Subscription]) return ret0 } @@ -922,24 +445,24 @@ func (mr *MockAzureClientMockRecorder) ListAzureSubscriptions(arg0 interface{}) } // ListAzureVMScaleSets mocks base method. -func (m *MockAzureClient) ListAzureVMScaleSets(arg0 context.Context, arg1 string, arg2 bool) <-chan azure.VMScaleSetResult { +func (m *MockAzureClient) ListAzureVMScaleSets(arg0 context.Context, arg1 string) <-chan client.AzureResult[azure.VMScaleSet] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListAzureVMScaleSets", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan azure.VMScaleSetResult) + ret := m.ctrl.Call(m, "ListAzureVMScaleSets", arg0, arg1) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.VMScaleSet]) return ret0 } // ListAzureVMScaleSets indicates an expected call of ListAzureVMScaleSets. -func (mr *MockAzureClientMockRecorder) ListAzureVMScaleSets(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListAzureVMScaleSets(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureVMScaleSets", reflect.TypeOf((*MockAzureClient)(nil).ListAzureVMScaleSets), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureVMScaleSets", reflect.TypeOf((*MockAzureClient)(nil).ListAzureVMScaleSets), arg0, arg1) } // ListAzureVirtualMachines mocks base method. -func (m *MockAzureClient) ListAzureVirtualMachines(arg0 context.Context, arg1 string, arg2 bool) <-chan azure.VirtualMachineResult { +func (m *MockAzureClient) ListAzureVirtualMachines(arg0 context.Context, arg1 string, arg2 query.RMParams) <-chan client.AzureResult[azure.VirtualMachine] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureVirtualMachines", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan azure.VirtualMachineResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.VirtualMachine]) return ret0 } @@ -950,10 +473,10 @@ func (mr *MockAzureClientMockRecorder) ListAzureVirtualMachines(arg0, arg1, arg2 } // ListAzureWebApps mocks base method. -func (m *MockAzureClient) ListAzureWebApps(arg0 context.Context, arg1 string) <-chan azure.WebAppResult { +func (m *MockAzureClient) ListAzureWebApps(arg0 context.Context, arg1 string) <-chan client.AzureResult[azure.WebApp] { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListAzureWebApps", arg0, arg1) - ret0, _ := ret[0].(<-chan azure.WebAppResult) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.WebApp]) return ret0 } @@ -963,32 +486,18 @@ func (mr *MockAzureClientMockRecorder) ListAzureWebApps(arg0, arg1 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAzureWebApps", reflect.TypeOf((*MockAzureClient)(nil).ListAzureWebApps), arg0, arg1) } -// ListResourceRoleAssignments mocks base method. -func (m *MockAzureClient) ListResourceRoleAssignments(arg0 context.Context, arg1, arg2, arg3 string) <-chan azure.RoleAssignmentResult { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListResourceRoleAssignments", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(<-chan azure.RoleAssignmentResult) - return ret0 -} - -// ListResourceRoleAssignments indicates an expected call of ListResourceRoleAssignments. -func (mr *MockAzureClientMockRecorder) ListResourceRoleAssignments(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListResourceRoleAssignments", reflect.TypeOf((*MockAzureClient)(nil).ListResourceRoleAssignments), arg0, arg1, arg2, arg3) -} - // ListRoleAssignmentsForResource mocks base method. -func (m *MockAzureClient) ListRoleAssignmentsForResource(arg0 context.Context, arg1, arg2 string) <-chan azure.RoleAssignmentResult { +func (m *MockAzureClient) ListRoleAssignmentsForResource(arg0 context.Context, arg1, arg2, arg3 string) <-chan client.AzureResult[azure.RoleAssignment] { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListRoleAssignmentsForResource", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan azure.RoleAssignmentResult) + ret := m.ctrl.Call(m, "ListRoleAssignmentsForResource", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(<-chan client.AzureResult[azure.RoleAssignment]) return ret0 } // ListRoleAssignmentsForResource indicates an expected call of ListRoleAssignmentsForResource. -func (mr *MockAzureClientMockRecorder) ListRoleAssignmentsForResource(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAzureClientMockRecorder) ListRoleAssignmentsForResource(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRoleAssignmentsForResource", reflect.TypeOf((*MockAzureClient)(nil).ListRoleAssignmentsForResource), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRoleAssignmentsForResource", reflect.TypeOf((*MockAzureClient)(nil).ListRoleAssignmentsForResource), arg0, arg1, arg2, arg3) } // TenantInfo mocks base method. diff --git a/client/query/params.go b/client/query/params.go index f203ecb..3378ab5 100644 --- a/client/query/params.go +++ b/client/query/params.go @@ -38,38 +38,40 @@ const ( Skip string = "$skip" SkipToken string = "$skipToken" StatusOnly string = "StatusOnly" + TenantId string = "tenantId" Top string = "$top" ) -type Params struct { +type Params interface { + AsMap() map[string]string + NeedsEventualConsistencyHeaderFlag() bool +} + +type RMParams struct { ApiVersion string - Count bool Expand string Filter string IncludeDeleted string IncludeAllTenantCategories bool MaxPageSize string - OrderBy string Recurse bool - Search string - Select []string - Skip int SkipToken string StatusOnly bool + TenantId string // For cross-tenant request Top int32 } -func (s Params) AsMap() map[string]string { +func (s RMParams) NeedsEventualConsistencyHeaderFlag() bool { + return false +} + +func (s RMParams) AsMap() map[string]string { params := make(map[string]string) if s.ApiVersion != "" { params[ApiVersion] = s.ApiVersion } - if s.Count { - params[Count] = "true" - } - if s.Expand != "" { params[Expand] = s.Expand } @@ -82,14 +84,68 @@ func (s Params) AsMap() map[string]string { params[IncludeAllTenantCategories] = "true" } - if s.OrderBy != "" { - params[OrderBy] = s.OrderBy - } - if s.Recurse { params[Recurse] = "true" } + if s.SkipToken != "" { + params[SkipToken] = s.SkipToken + } + + if s.StatusOnly { + params[StatusOnly] = "true" + } + + if s.TenantId != "" { + params[TenantId] = s.TenantId + } + if s.Top > 0 { + params[Top] = strconv.FormatInt(int64(s.Top), 10) + } + + return params +} + +type GraphParams struct { + Count bool + Expand string + Format string + Filter string + OrderBy string + Search string + Select []string + Skip int + Top int32 + SkipToken string +} + +func (s GraphParams) NeedsEventualConsistencyHeaderFlag() bool { + return s.Count || s.Search != "" || s.OrderBy != "" || (s.Filter != "" && s.OrderBy != "") || strings.Contains(s.Filter, "endsWith") +} + +func (s GraphParams) AsMap() map[string]string { + params := make(map[string]string) + + if s.Count { + params[Count] = "true" + } + + if s.Expand != "" { + params[Expand] = s.Expand + } + + if s.Format != "" { + params[Format] = s.Format + } + + if s.Filter != "" { + params[Filter] = s.Filter + } + + if s.OrderBy != "" { + params[OrderBy] = s.OrderBy + } + if s.Search != "" { params[Search] = s.Search } @@ -106,10 +162,6 @@ func (s Params) AsMap() map[string]string { params[SkipToken] = s.SkipToken } - if s.StatusOnly { - params[StatusOnly] = "true" - } - if s.Top > 0 { params[Top] = strconv.FormatInt(int64(s.Top), 10) } diff --git a/client/resource_groups.go b/client/resource_groups.go index c693fc4..cff4182 100644 --- a/client/resource_groups.go +++ b/client/resource_groups.go @@ -20,116 +20,23 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureResourceGroup(ctx context.Context, subscriptionId, groupName string) (*azure.ResourceGroup, error) { +// ListAzureResourceGroups https://learn.microsoft.com/en-us/rest/api/resources/resource-groups/list?view=rest-resources-2021-04-01 +func (s *azureClient) ListAzureResourceGroups(ctx context.Context, subscriptionId string, params query.RMParams) <-chan AzureResult[azure.ResourceGroup] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourcegroups/%s", subscriptionId, groupName) - params = query.Params{ApiVersion: "2021-04-01"}.AsMap() - headers map[string]string - response azure.ResourceGroup - ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureResourceGroups(ctx context.Context, subscriptionId string, filter string, top int32) (azure.ResourceGroupList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/resourcegroups", subscriptionId) - params = query.Params{ApiVersion: "2021-04-01", Filter: filter, Top: top}.AsMap() - headers map[string]string - response azure.ResourceGroupList + out = make(chan AzureResult[azure.ResourceGroup]) + path = fmt.Sprintf("/subscriptions/%s/resourcegroups", subscriptionId) ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil + if params.ApiVersion == "" { + params.ApiVersion = "2021-04-01" } -} - -func (s *azureClient) ListAzureResourceGroups(ctx context.Context, subscriptionId, filter string) <-chan azure.ResourceGroupResult { - out := make(chan azure.ResourceGroupResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - objectId = fmt.Sprintf("/subscriptions/%s", subscriptionId) - errResult = azure.ResourceGroupResult{SubscriptionId: objectId} - nextLink string - ) - if result, err := s.GetAzureResourceGroups(ctx, subscriptionId, filter, 1000); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ResourceGroupResult{ - SubscriptionId: objectId, - Ok: u, - }); !ok { - return - } - } + go getAzureObjectList[azure.ResourceGroup](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.ResourceGroupList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ResourceGroupResult{ - SubscriptionId: objectId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/rest/client.go b/client/rest/client.go index 919edc4..a403190 100644 --- a/client/rest/client.go +++ b/client/rest/client.go @@ -32,16 +32,17 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client/config" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/constants" ) type RestClient interface { Authenticate() error - Delete(ctx context.Context, path string, body interface{}, params, headers map[string]string) (*http.Response, error) - Get(ctx context.Context, path string, params, headers map[string]string) (*http.Response, error) - Patch(ctx context.Context, path string, body interface{}, params, headers map[string]string) (*http.Response, error) - Post(ctx context.Context, path string, body interface{}, params, headers map[string]string) (*http.Response, error) - Put(ctx context.Context, path string, body interface{}, params, headers map[string]string) (*http.Response, error) + Delete(ctx context.Context, path string, body interface{}, params query.Params, headers map[string]string) (*http.Response, error) + Get(ctx context.Context, path string, params query.Params, headers map[string]string) (*http.Response, error) + Patch(ctx context.Context, path string, body interface{}, params query.Params, headers map[string]string) (*http.Response, error) + Post(ctx context.Context, path string, body interface{}, params query.Params, headers map[string]string) (*http.Response, error) + Put(ctx context.Context, path string, body interface{}, params query.Params, headers map[string]string) (*http.Response, error) Send(req *http.Request) (*http.Response, error) CloseIdleConnections() } @@ -154,45 +155,73 @@ func (s *restClient) Authenticate() error { } } -func (s *restClient) Delete(ctx context.Context, path string, body interface{}, params, headers map[string]string) (*http.Response, error) { +func (s *restClient) Delete(ctx context.Context, path string, body interface{}, params query.Params, headers map[string]string) (*http.Response, error) { endpoint := s.api.ResolveReference(&url.URL{Path: path}) - if req, err := NewRequest(ctx, http.MethodDelete, endpoint, body, params, headers); err != nil { + paramsMap := make(map[string]string) + if params != nil { + paramsMap = params.AsMap() + } + if req, err := NewRequest(ctx, http.MethodDelete, endpoint, body, paramsMap, headers); err != nil { return nil, err } else { return s.Send(req) } } -func (s *restClient) Get(ctx context.Context, path string, params, headers map[string]string) (*http.Response, error) { +func (s *restClient) Get(ctx context.Context, path string, params query.Params, headers map[string]string) (*http.Response, error) { endpoint := s.api.ResolveReference(&url.URL{Path: path}) - if req, err := NewRequest(ctx, http.MethodGet, endpoint, nil, params, headers); err != nil { + paramsMap := make(map[string]string) + + if params != nil { + paramsMap = params.AsMap() + if params.NeedsEventualConsistencyHeaderFlag() { + if headers == nil { + headers = make(map[string]string) + } + headers["ConsistencyLevel"] = "eventual" + } + } + + if req, err := NewRequest(ctx, http.MethodGet, endpoint, nil, paramsMap, headers); err != nil { return nil, err } else { return s.Send(req) } } -func (s *restClient) Patch(ctx context.Context, path string, body interface{}, params, headers map[string]string) (*http.Response, error) { +func (s *restClient) Patch(ctx context.Context, path string, body interface{}, params query.Params, headers map[string]string) (*http.Response, error) { endpoint := s.api.ResolveReference(&url.URL{Path: path}) - if req, err := NewRequest(ctx, http.MethodPatch, endpoint, body, params, headers); err != nil { + paramsMap := make(map[string]string) + if params != nil { + paramsMap = params.AsMap() + } + if req, err := NewRequest(ctx, http.MethodPatch, endpoint, body, paramsMap, headers); err != nil { return nil, err } else { return s.Send(req) } } -func (s *restClient) Post(ctx context.Context, path string, body interface{}, params, headers map[string]string) (*http.Response, error) { +func (s *restClient) Post(ctx context.Context, path string, body interface{}, params query.Params, headers map[string]string) (*http.Response, error) { endpoint := s.api.ResolveReference(&url.URL{Path: path}) - if req, err := NewRequest(ctx, http.MethodPost, endpoint, body, params, headers); err != nil { + paramsMap := make(map[string]string) + if params != nil { + paramsMap = params.AsMap() + } + if req, err := NewRequest(ctx, http.MethodPost, endpoint, body, paramsMap, headers); err != nil { return nil, err } else { return s.Send(req) } } -func (s *restClient) Put(ctx context.Context, path string, body interface{}, params, headers map[string]string) (*http.Response, error) { +func (s *restClient) Put(ctx context.Context, path string, body interface{}, params query.Params, headers map[string]string) (*http.Response, error) { endpoint := s.api.ResolveReference(&url.URL{Path: path}) - if req, err := NewRequest(ctx, http.MethodPost, endpoint, body, params, headers); err != nil { + paramsMap := make(map[string]string) + if params != nil { + paramsMap = params.AsMap() + } + if req, err := NewRequest(ctx, http.MethodPost, endpoint, body, paramsMap, headers); err != nil { return nil, err } else { return s.Send(req) diff --git a/client/rest/mocks/client.go b/client/rest/mocks/client.go index b9df20b..6c2f7ae 100644 --- a/client/rest/mocks/client.go +++ b/client/rest/mocks/client.go @@ -9,6 +9,7 @@ import ( http "net/http" reflect "reflect" + query "github.com/bloodhoundad/azurehound/v2/client/query" gomock "go.uber.org/mock/gomock" ) @@ -62,7 +63,7 @@ func (mr *MockRestClientMockRecorder) CloseIdleConnections() *gomock.Call { } // Delete mocks base method. -func (m *MockRestClient) Delete(arg0 context.Context, arg1 string, arg2 interface{}, arg3, arg4 map[string]string) (*http.Response, error) { +func (m *MockRestClient) Delete(arg0 context.Context, arg1 string, arg2 interface{}, arg3 query.Params, arg4 map[string]string) (*http.Response, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*http.Response) @@ -77,7 +78,7 @@ func (mr *MockRestClientMockRecorder) Delete(arg0, arg1, arg2, arg3, arg4 interf } // Get mocks base method. -func (m *MockRestClient) Get(arg0 context.Context, arg1 string, arg2, arg3 map[string]string) (*http.Response, error) { +func (m *MockRestClient) Get(arg0 context.Context, arg1 string, arg2 query.Params, arg3 map[string]string) (*http.Response, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*http.Response) @@ -92,7 +93,7 @@ func (mr *MockRestClientMockRecorder) Get(arg0, arg1, arg2, arg3 interface{}) *g } // Patch mocks base method. -func (m *MockRestClient) Patch(arg0 context.Context, arg1 string, arg2 interface{}, arg3, arg4 map[string]string) (*http.Response, error) { +func (m *MockRestClient) Patch(arg0 context.Context, arg1 string, arg2 interface{}, arg3 query.Params, arg4 map[string]string) (*http.Response, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Patch", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*http.Response) @@ -107,7 +108,7 @@ func (mr *MockRestClientMockRecorder) Patch(arg0, arg1, arg2, arg3, arg4 interfa } // Post mocks base method. -func (m *MockRestClient) Post(arg0 context.Context, arg1 string, arg2 interface{}, arg3, arg4 map[string]string) (*http.Response, error) { +func (m *MockRestClient) Post(arg0 context.Context, arg1 string, arg2 interface{}, arg3 query.Params, arg4 map[string]string) (*http.Response, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Post", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*http.Response) @@ -122,7 +123,7 @@ func (mr *MockRestClientMockRecorder) Post(arg0, arg1, arg2, arg3, arg4 interfac } // Put mocks base method. -func (m *MockRestClient) Put(arg0 context.Context, arg1 string, arg2 interface{}, arg3, arg4 map[string]string) (*http.Response, error) { +func (m *MockRestClient) Put(arg0 context.Context, arg1 string, arg2 interface{}, arg3 query.Params, arg4 map[string]string) (*http.Response, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Put", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(*http.Response) diff --git a/client/role_assignments.go b/client/role_assignments.go index 34f7da1..f742f54 100644 --- a/client/role_assignments.go +++ b/client/role_assignments.go @@ -20,292 +20,36 @@ package client import ( "context" "fmt" - "net/url" - "strings" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureADRoleAssignment(ctx context.Context, objectId string, selectCols []string) (*azure.UnifiedRoleAssignment, error) { +// ListAzureADRoleAssignments https://learn.microsoft.com/en-us/graph/api/rbacapplication-list-roleassignments?view=graph-rest-beta +func (s *azureClient) ListAzureADRoleAssignments(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.UnifiedRoleAssignment] { var ( - path = fmt.Sprintf("/%s/roleManagement/directory/roleAssignments/%s", constants.GraphApiVersion, objectId) - params = query.Params{Select: selectCols}.AsMap() - response azure.UnifiedRoleAssignment - ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureADRoleAssignments(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.UnifiedRoleAssignmentList, error) { - var ( - path = fmt.Sprintf("/%s/roleManagement/directory/roleAssignments", constants.GraphApiVersion) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count, Expand: expand} - headers map[string]string - response azure.UnifiedRoleAssignmentList + out = make(chan AzureResult[azure.UnifiedRoleAssignment]) + path = fmt.Sprintf("/%s/roleManagement/directory/roleAssignments", constants.GraphApiVersion) ) - count = count || search != "" || (filter != "" && orderBy != "") || strings.Contains(filter, "endsWith") - if count { - headers = make(map[string]string) - headers["ConsistencyLevel"] = "eventual" - } - if res, err := s.msgraph.Get(ctx, path, params.AsMap(), headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil + if params.Top == 0 { + params.Top = 999 } -} - -func (s *azureClient) ListAzureADRoleAssignments(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.UnifiedRoleAssignmentResult { - out := make(chan azure.UnifiedRoleAssignmentResult) - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.UnifiedRoleAssignmentResult{} - nextLink string - ) - - if list, err := s.GetAzureADRoleAssignments(ctx, filter, search, orderBy, expand, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.UnifiedRoleAssignmentResult{Ok: u}); !ok { - return - } - } - - nextLink = list.NextLink - for nextLink != "" { - var list azure.UnifiedRoleAssignmentList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.UnifiedRoleAssignmentResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() + go getAzureObjectList[azure.UnifiedRoleAssignment](s.msgraph, ctx, path, params, out) return out } -func (s *azureClient) GetRoleAssignmentsForResource(ctx context.Context, resourceId string, filter string) (azure.RoleAssignmentList, error) { +// ListRoleAssignmentsForResource https://learn.microsoft.com/en-us/rest/api/authorization/role-assignments/list-for-resource?view=rest-authorization-2015-07-01 +func (s *azureClient) ListRoleAssignmentsForResource(ctx context.Context, resourceId string, filter, tenantId string) <-chan AzureResult[azure.RoleAssignment] { var ( - path = fmt.Sprintf("%s/providers/Microsoft.Authorization/roleAssignments", resourceId) - params = query.Params{ApiVersion: "2015-07-01", Filter: filter}.AsMap() - headers map[string]string - response azure.RoleAssignmentList + out = make(chan AzureResult[azure.RoleAssignment]) + path = fmt.Sprintf("%s/providers/Microsoft.Authorization/roleAssignments", resourceId) + params = query.RMParams{ApiVersion: "2015-07-01", Filter: filter, TenantId: tenantId} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } - -} - -func (s *azureClient) ListRoleAssignmentsForResource(ctx context.Context, resourceId string, filter string) <-chan azure.RoleAssignmentResult { - out := make(chan azure.RoleAssignmentResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.RoleAssignmentResult{ParentId: resourceId} - nextLink string - ) - - if result, err := s.GetRoleAssignmentsForResource(ctx, resourceId, filter); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.RoleAssignmentResult{ - ParentId: resourceId, - Ok: u, - }); !ok { - return - } - } - - nextLink = result.NextLink - for nextLink != "" { - var list azure.RoleAssignmentList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.RoleAssignmentResult{ - ParentId: resourceId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() - return out -} - -func (s *azureClient) GetResourceRoleAssignments(ctx context.Context, subscriptionId string, filter string, expand string) (azure.RoleAssignmentList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Authorization/roleAssignments", subscriptionId) - params = query.Params{ApiVersion: "2015-07-01", Filter: filter, Expand: expand}.AsMap() - headers map[string]string - response azure.RoleAssignmentList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListResourceRoleAssignments(ctx context.Context, subscriptionId string, filter string, expand string) <-chan azure.RoleAssignmentResult { - out := make(chan azure.RoleAssignmentResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.RoleAssignmentResult{ParentId: subscriptionId} - nextLink string - ) - - if result, err := s.GetResourceRoleAssignments(ctx, subscriptionId, filter, expand); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.RoleAssignmentResult{ - ParentId: subscriptionId, - Ok: u, - }); !ok { - return - } - } + go getAzureObjectList[azure.RoleAssignment](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.RoleAssignmentList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.RoleAssignmentResult{ - ParentId: subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/roles.go b/client/roles.go index 1f6fd5a..6299ef2 100644 --- a/client/roles.go +++ b/client/roles.go @@ -20,109 +20,20 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureADRole(ctx context.Context, roleId string, selectCols []string) (*azure.Role, error) { +// ListAzureADRoles https://learn.microsoft.com/en-us/graph/api/rbacapplication-list-roledefinitions?view=graph-rest-beta +func (s *azureClient) ListAzureADRoles(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.Role] { var ( - path = fmt.Sprintf("/%s/roleManagement/directory/roleDefinitions/%s", constants.GraphApiVersion, roleId) - params = query.Params{Select: selectCols}.AsMap() - response azure.RoleList + out = make(chan AzureResult[azure.Role]) + path = fmt.Sprintf("/%s/roleManagement/directory/roleDefinitions", constants.GraphApiVersion) ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response.Value[0], nil - } -} - -func (s *azureClient) GetAzureADRoles(ctx context.Context, filter, expand string) (azure.RoleList, error) { - var ( - path = fmt.Sprintf("/%s/roleManagement/directory/roleDefinitions", constants.GraphApiVersion) - params = query.Params{Filter: filter, Expand: expand} - headers map[string]string - response azure.RoleList - ) - - if res, err := s.msgraph.Get(ctx, path, params.AsMap(), headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureADRoles(ctx context.Context, filter, expand string) <-chan azure.RoleResult { - out := make(chan azure.RoleResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.RoleResult{} - nextLink string - ) - if users, err := s.GetAzureADRoles(ctx, filter, expand); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range users.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.RoleResult{Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.Role](s.msgraph, ctx, path, params, out) - nextLink = users.NextLink - for nextLink != "" { - var users azure.RoleList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &users); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range users.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.RoleResult{Ok: u}); !ok { - return - } - } - nextLink = users.NextLink - } - } - } - }() return out } diff --git a/client/service_principals.go b/client/service_principals.go index 9f8642e..0c56e8c 100644 --- a/client/service_principals.go +++ b/client/service_principals.go @@ -19,204 +19,42 @@ package client import ( "context" + "encoding/json" "fmt" - "net/url" - "strings" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureADServicePrincipal(ctx context.Context, objectId string, selectCols []string) (*azure.ServicePrincipal, error) { +// ListAzureADServicePrincipals https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list?view=graph-rest-beta +func (s *azureClient) ListAzureADServicePrincipals(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.ServicePrincipal] { var ( - path = fmt.Sprintf("/%s/servicePrincipals/%s", constants.GraphApiVersion, objectId) - params = query.Params{Select: selectCols}.AsMap() - response azure.ServicePrincipalList + out = make(chan AzureResult[azure.ServicePrincipal]) + path = fmt.Sprintf("/%s/servicePrincipals", constants.GraphApiVersion) ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response.Value[0], nil - } -} -func (s *azureClient) GetAzureADServicePrincipalOwners(ctx context.Context, objectId string, filter string, search string, orderBy string, selectCols []string, top int32, count bool) (azure.DirectoryObjectList, error) { - var ( - path = fmt.Sprintf("/%s/servicePrincipals/%s/owners", constants.GraphApiBetaVersion, objectId) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count}.AsMap() - response azure.DirectoryObjectList - ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil + if params.Top == 0 { + params.Top = 999 } -} -func (s *azureClient) GetAzureADServicePrincipals(ctx context.Context, filter, search, orderBy, expand string, selectCols []string, top int32, count bool) (azure.ServicePrincipalList, error) { - var ( - path = fmt.Sprintf("/%s/servicePrincipals", constants.GraphApiVersion) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count, Expand: expand} - headers map[string]string - response azure.ServicePrincipalList - ) + go getAzureObjectList[azure.ServicePrincipal](s.msgraph, ctx, path, params, out) - count = count || search != "" || (filter != "" && orderBy != "") || strings.Contains(filter, "endsWith") - if count { - headers = make(map[string]string) - headers["ConsistencyLevel"] = "eventual" - } - if res, err := s.msgraph.Get(ctx, path, params.AsMap(), headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureADServicePrincipals(ctx context.Context, filter, search, orderBy, expand string, selectCols []string) <-chan azure.ServicePrincipalResult { - out := make(chan azure.ServicePrincipalResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.ServicePrincipalResult{} - nextLink string - ) - - if list, err := s.GetAzureADServicePrincipals(ctx, filter, search, orderBy, expand, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ServicePrincipalResult{Ok: u}); !ok { - return - } - } - - nextLink = list.NextLink - for nextLink != "" { - var list azure.ServicePrincipalList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ServicePrincipalResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } -func (s *azureClient) ListAzureADServicePrincipalOwners(ctx context.Context, objectId string, filter, search, orderBy string, selectCols []string) <-chan azure.ServicePrincipalOwnerResult { - out := make(chan azure.ServicePrincipalOwnerResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) +// ListAzureADServicePrincipalOwners https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list-owners?view=graph-rest-beta +func (s *azureClient) ListAzureADServicePrincipalOwners(ctx context.Context, objectId string, params query.GraphParams) <-chan AzureResult[json.RawMessage] { + var ( + out = make(chan AzureResult[json.RawMessage]) + path = fmt.Sprintf("/%s/servicePrincipals/%s/owners", constants.GraphApiBetaVersion, objectId) + ) - var ( - errResult = azure.ServicePrincipalOwnerResult{ - ServicePrincipalId: objectId, - } - nextLink string - ) + if params.Top == 0 { + params.Top = 999 + } - if list, err := s.GetAzureADServicePrincipalOwners(ctx, objectId, filter, search, orderBy, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ServicePrincipalOwnerResult{ - ServicePrincipalId: objectId, - Ok: u, - }); !ok { - return - } - } + go getAzureObjectList[json.RawMessage](s.msgraph, ctx, path, params, out) - nextLink = list.NextLink - for nextLink != "" { - var list azure.DirectoryObjectList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.ServicePrincipalOwnerResult{ - ServicePrincipalId: objectId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/storage_accounts.go b/client/storage_accounts.go index 920deb1..402d247 100644 --- a/client/storage_accounts.go +++ b/client/storage_accounts.go @@ -20,114 +20,21 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureStorageAccount(ctx context.Context, subscriptionId, groupName, saName, expand string) (*azure.StorageAccount, error) { +// ListAzureStorageAccounts https://learn.microsoft.com/en-us/rest/api/storagerp/storage-accounts/list?view=rest-storagerp-2022-05-01 +func (s *azureClient) ListAzureStorageAccounts(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.StorageAccount] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/%s", subscriptionId, groupName, saName) - params = query.Params{ApiVersion: "2021-07-01", Expand: expand}.AsMap() - headers map[string]string - response azure.StorageAccount + out = make(chan AzureResult[azure.StorageAccount]) + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Storage/storageAccounts", subscriptionId) + params = query.RMParams{ApiVersion: "2022-05-01"} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureStorageAccounts(ctx context.Context, subscriptionId string) (azure.StorageAccountList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Storage/storageAccounts", subscriptionId) - params = query.Params{ApiVersion: "2022-05-01"}.AsMap() - headers map[string]string - response azure.StorageAccountList - ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureStorageAccounts(ctx context.Context, subscriptionId string) <-chan azure.StorageAccountResult { - out := make(chan azure.StorageAccountResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.StorageAccountResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureStorageAccounts(ctx, subscriptionId); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.StorageAccountResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.StorageAccount](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.StorageAccountList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.StorageAccountResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } @@ -135,104 +42,15 @@ func (s *azureClient) ListAzureStorageAccounts(ctx context.Context, subscription // Storage containers // == -func (s *azureClient) GetAzureStorageContainer(ctx context.Context, subscriptionId string, resourceGroupName string, saName string, scName string, filter string, includeDeleted string, maxPageSize string) (*azure.StorageContainer, error) { +// ListAzureStorageContainers https://learn.microsoft.com/en-us/rest/api/storagerp/blob-containers/list?view=rest-storagerp-2022-05-01 +func (s *azureClient) ListAzureStorageContainers(ctx context.Context, subscriptionId string, resourceGroupName string, saName string, filter string, includeDeleted string, maxPageSize string) <-chan AzureResult[azure.StorageContainer] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/%s/blobServices/default/containers/%s", subscriptionId, resourceGroupName, saName, scName) - params = query.Params{ApiVersion: "2022-05-01", Filter: filter, IncludeDeleted: includeDeleted, MaxPageSize: maxPageSize}.AsMap() - headers map[string]string - response azure.StorageContainer + out = make(chan AzureResult[azure.StorageContainer]) + path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/%s/blobServices/default/containers", subscriptionId, resourceGroupName, saName) + params = query.RMParams{ApiVersion: "2022-05-01", Filter: filter, IncludeDeleted: includeDeleted, MaxPageSize: maxPageSize} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureStorageContainers(ctx context.Context, subscriptionId string, resourceGroupName string, saName string, filter string, includeDeleted string, maxPageSize string) (azure.StorageContainerList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/%s/blobServices/default/containers", subscriptionId, resourceGroupName, saName) - params = query.Params{ApiVersion: "2022-05-01", Filter: filter, IncludeDeleted: includeDeleted, MaxPageSize: maxPageSize}.AsMap() - headers map[string]string - response azure.StorageContainerList - ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureStorageContainers(ctx context.Context, subscriptionId string, resourceGroupName string, saName string, filter string, includeDeleted string, maxPageSize string) <-chan azure.StorageContainerResult { - out := make(chan azure.StorageContainerResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.StorageContainerResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureStorageContainers(ctx, subscriptionId, resourceGroupName, saName, filter, includeDeleted, maxPageSize); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.StorageContainerResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.StorageContainer](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.StorageContainerList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.StorageContainerResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/subscriptions.go b/client/subscriptions.go index 877d18b..0ed8933 100644 --- a/client/subscriptions.go +++ b/client/subscriptions.go @@ -19,110 +19,20 @@ package client import ( "context" - "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureSubscription(ctx context.Context, objectId string) (*azure.Subscription, error) { +// ListAzureSubscriptions https://learn.microsoft.com/en-us/rest/api/subscription/subscriptions/list?view=rest-subscription-2020-01-01 +func (s *azureClient) ListAzureSubscriptions(ctx context.Context) <-chan AzureResult[azure.Subscription] { var ( - path = fmt.Sprintf("/subscriptions/%s", objectId) - params = query.Params{ApiVersion: "2020-01-01"}.AsMap() - headers map[string]string - response azure.Subscription + out = make(chan AzureResult[azure.Subscription]) + path = "/subscriptions" + params = query.RMParams{ApiVersion: "2020-01-01"} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureSubscriptions(ctx context.Context) (azure.SubscriptionList, error) { - var ( - path = "/subscriptions" - params = query.Params{ApiVersion: "2020-01-01"}.AsMap() - headers map[string]string - response azure.SubscriptionList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureSubscriptions(ctx context.Context) <-chan azure.SubscriptionResult { - out := make(chan azure.SubscriptionResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.SubscriptionResult{} - nextLink string - ) - if result, err := s.GetAzureSubscriptions(ctx); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.SubscriptionResult{Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.Subscription](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.SubscriptionList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.SubscriptionResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/tenants.go b/client/tenants.go index 1584190..4aa8966 100644 --- a/client/tenants.go +++ b/client/tenants.go @@ -20,23 +20,19 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) func (s *azureClient) GetAzureADOrganization(ctx context.Context, selectCols []string) (*azure.Organization, error) { var ( path = fmt.Sprintf("/%s/organization", constants.GraphApiVersion) - params = query.Params{Select: selectCols}.AsMap() response azure.OrganizationList ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { + if res, err := s.msgraph.Get(ctx, path, query.GraphParams{Select: selectCols}, nil); err != nil { return nil, err } else if err := rest.Decode(res.Body, &response); err != nil { return nil, err @@ -48,7 +44,7 @@ func (s *azureClient) GetAzureADOrganization(ctx context.Context, selectCols []s func (s *azureClient) GetAzureADTenants(ctx context.Context, includeAllTenantCategories bool) (azure.TenantList, error) { var ( path = "/tenants" - params = query.Params{ApiVersion: "2020-01-01", IncludeAllTenantCategories: includeAllTenantCategories}.AsMap() + params = query.RMParams{ApiVersion: "2020-01-01", IncludeAllTenantCategories: includeAllTenantCategories} headers map[string]string response azure.TenantList ) @@ -62,67 +58,15 @@ func (s *azureClient) GetAzureADTenants(ctx context.Context, includeAllTenantCat } } -func (s *azureClient) ListAzureADTenants(ctx context.Context, includeAllTenantCategories bool) <-chan azure.TenantResult { - out := make(chan azure.TenantResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.TenantResult{} - nextLink string - ) +// ListAzureADTenants https://learn.microsoft.com/en-us/rest/api/subscription/tenants/list?view=rest-subscription-2020-01-01 +func (s *azureClient) ListAzureADTenants(ctx context.Context, includeAllTenantCategories bool) <-chan AzureResult[azure.Tenant] { + var ( + out = make(chan AzureResult[azure.Tenant]) + path = "/tenants" + params = query.RMParams{ApiVersion: "2020-01-01", IncludeAllTenantCategories: includeAllTenantCategories} + ) - if result, err := s.GetAzureADTenants(ctx, includeAllTenantCategories); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.TenantResult{Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.Tenant](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.TenantList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.TenantResult{Ok: u}); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/users.go b/client/users.go index cea543d..d20f4bf 100644 --- a/client/users.go +++ b/client/users.go @@ -20,114 +20,24 @@ package client import ( "context" "fmt" - "net/url" - "strings" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureADUser(ctx context.Context, objectId string, selectCols []string) (*azure.User, error) { +// ListAzureADUsers https://learn.microsoft.com/en-us/graph/api/user-list?view=graph-rest-beta +func (s *azureClient) ListAzureADUsers(ctx context.Context, params query.GraphParams) <-chan AzureResult[azure.User] { var ( - path = fmt.Sprintf("/%s/users/%s", constants.GraphApiVersion, objectId) - params = query.Params{Select: selectCols}.AsMap() - response azure.UserList - ) - if res, err := s.msgraph.Get(ctx, path, params, nil); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response.Value[0], nil - } -} - -func (s *azureClient) GetAzureADUsers(ctx context.Context, filter string, search string, orderBy string, selectCols []string, top int32, count bool) (azure.UserList, error) { - var ( - path = fmt.Sprintf("/%s/users", constants.GraphApiVersion) - params = query.Params{Filter: filter, Search: search, OrderBy: orderBy, Select: selectCols, Top: top, Count: count} - headers map[string]string - response azure.UserList + out = make(chan AzureResult[azure.User]) + path = fmt.Sprintf("/%s/users", constants.GraphApiVersion) ) - count = count || search != "" || (filter != "" && orderBy != "") || strings.Contains(filter, "endsWith") - if count { - headers = make(map[string]string) - headers["ConsistencyLevel"] = "eventual" + if params.Top == 0 { + params.Top = 999 } - if res, err := s.msgraph.Get(ctx, path, params.AsMap(), headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureADUsers(ctx context.Context, filter string, search string, orderBy string, selectCols []string) <-chan azure.UserResult { - out := make(chan azure.UserResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - var ( - errResult = azure.UserResult{} - nextLink string - ) - if users, err := s.GetAzureADUsers(ctx, filter, search, orderBy, selectCols, 999, false); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range users.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.UserResult{Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.User](s.msgraph, ctx, path, params, out) - nextLink = users.NextLink - for nextLink != "" { - var users azure.UserList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.msgraph.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &users); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range users.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.UserResult{Ok: u}); !ok { - return - } - } - nextLink = users.NextLink - } - } - } - }() return out } diff --git a/client/virtual_machines.go b/client/virtual_machines.go index 1934974..673d36c 100644 --- a/client/virtual_machines.go +++ b/client/virtual_machines.go @@ -20,114 +20,23 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureVirtualMachine(ctx context.Context, subscriptionId, groupName, vmName, expand string) (*azure.VirtualMachine, error) { +// ListAzureVirtualMachines https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/list-all?view=rest-compute-2021-07-01 +func (s *azureClient) ListAzureVirtualMachines(ctx context.Context, subscriptionId string, params query.RMParams) <-chan AzureResult[azure.VirtualMachine] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", subscriptionId, groupName, vmName) - params = query.Params{ApiVersion: "2021-07-01", Expand: expand}.AsMap() - headers map[string]string - response azure.VirtualMachine - ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureVirtualMachines(ctx context.Context, subscriptionId string, statusOnly bool) (azure.VirtualMachineList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/virtualMachines", subscriptionId) - params = query.Params{ApiVersion: "2021-07-01", StatusOnly: statusOnly}.AsMap() - headers map[string]string - response azure.VirtualMachineList + out = make(chan AzureResult[azure.VirtualMachine]) + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/virtualMachines", subscriptionId) ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil + if params.ApiVersion == "" { + params.ApiVersion = "2021-07-01" } -} - -func (s *azureClient) ListAzureVirtualMachines(ctx context.Context, subscriptionId string, statusOnly bool) <-chan azure.VirtualMachineResult { - out := make(chan azure.VirtualMachineResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.VirtualMachineResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureVirtualMachines(ctx, subscriptionId, statusOnly); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.VirtualMachineResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.VirtualMachine](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.VirtualMachineList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.VirtualMachineResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/vm_scale_sets.go b/client/vm_scale_sets.go index 94879ad..8ac1e42 100644 --- a/client/vm_scale_sets.go +++ b/client/vm_scale_sets.go @@ -20,114 +20,20 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureVMScaleSet(ctx context.Context, subscriptionId, groupName, vmssName, expand string) (*azure.VMScaleSet, error) { +// ListAzureVMScaleSets https://learn.microsoft.com/en-us/rest/api/compute/virtual-machine-scale-sets/list-all?view=rest-compute-2022-11-01 +func (s *azureClient) ListAzureVMScaleSets(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.VMScaleSet] { var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachineScaleSets/%s", subscriptionId, groupName, vmssName) - params = query.Params{ApiVersion: "2022-11-01", Expand: expand}.AsMap() - headers map[string]string - response azure.VMScaleSet + out = make(chan AzureResult[azure.VMScaleSet]) + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/virtualMachineScaleSets", subscriptionId) + params = query.RMParams{ApiVersion: "2022-11-01"} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureVMScaleSets(ctx context.Context, subscriptionId string, statusOnly bool) (azure.VMScaleSetList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/virtualMachineScaleSets", subscriptionId) - params = query.Params{ApiVersion: "2022-11-01", StatusOnly: statusOnly}.AsMap() - headers map[string]string - response azure.VMScaleSetList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureVMScaleSets(ctx context.Context, subscriptionId string, statusOnly bool) <-chan azure.VMScaleSetResult { - out := make(chan azure.VMScaleSetResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.VMScaleSetResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureVMScaleSets(ctx, subscriptionId, statusOnly); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.VMScaleSetResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.VMScaleSet](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.VMScaleSetList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.VMScaleSetResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/client/web_apps.go b/client/web_apps.go index d25fdfc..4ae12fc 100644 --- a/client/web_apps.go +++ b/client/web_apps.go @@ -20,114 +20,20 @@ package client import ( "context" "fmt" - "net/url" "github.com/bloodhoundad/azurehound/v2/client/query" - "github.com/bloodhoundad/azurehound/v2/client/rest" "github.com/bloodhoundad/azurehound/v2/models/azure" - "github.com/bloodhoundad/azurehound/v2/panicrecovery" - "github.com/bloodhoundad/azurehound/v2/pipeline" ) -func (s *azureClient) GetAzureWebApp(ctx context.Context, subscriptionId, groupName, waName, expand string) (*azure.WebApp, error) { +// ListAzureWebApps https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/list?view=rest-appservice-2022-03-01 +func (s *azureClient) ListAzureWebApps(ctx context.Context, subscriptionId string) <-chan AzureResult[azure.WebApp] { + out := make(chan AzureResult[azure.WebApp]) var ( - path = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Web/sites/%s", subscriptionId, groupName, waName) - params = query.Params{ApiVersion: "2022-03-01", Expand: expand}.AsMap() - headers map[string]string - response azure.WebApp + path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Web/sites", subscriptionId) + params = query.RMParams{ApiVersion: "2022-03-01"} ) - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return nil, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return nil, err - } else { - return &response, nil - } -} - -func (s *azureClient) GetAzureWebApps(ctx context.Context, subscriptionId string) (azure.WebAppList, error) { - var ( - path = fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Web/sites", subscriptionId) - params = query.Params{ApiVersion: "2022-03-01"}.AsMap() - headers map[string]string - response azure.WebAppList - ) - - if res, err := s.resourceManager.Get(ctx, path, params, headers); err != nil { - return response, err - } else if err := rest.Decode(res.Body, &response); err != nil { - return response, err - } else { - return response, nil - } -} - -func (s *azureClient) ListAzureWebApps(ctx context.Context, subscriptionId string) <-chan azure.WebAppResult { - out := make(chan azure.WebAppResult) - - go func() { - defer panicrecovery.PanicRecovery() - defer close(out) - - var ( - errResult = azure.WebAppResult{ - SubscriptionId: subscriptionId, - } - nextLink string - ) - if result, err := s.GetAzureWebApps(ctx, subscriptionId); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - } else { - for _, u := range result.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.WebAppResult{SubscriptionId: subscriptionId, Ok: u}); !ok { - return - } - } + go getAzureObjectList[azure.WebApp](s.resourceManager, ctx, path, params, out) - nextLink = result.NextLink - for nextLink != "" { - var list azure.WebAppList - if url, err := url.Parse(nextLink); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if req, err := rest.NewRequest(ctx, "GET", url, nil, nil, nil); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if res, err := s.resourceManager.Send(req); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else if err := rest.Decode(res.Body, &list); err != nil { - errResult.Error = err - if ok := pipeline.Send(ctx.Done(), out, errResult); !ok { - return - } - nextLink = "" - } else { - for _, u := range list.Value { - if ok := pipeline.Send(ctx.Done(), out, azure.WebAppResult{ - SubscriptionId: "/subscriptions/" + subscriptionId, - Ok: u, - }); !ok { - return - } - } - nextLink = list.NextLink - } - } - } - }() return out } diff --git a/cmd/list-app-owners.go b/cmd/list-app-owners.go index 5fe7336..56df1b5 100644 --- a/cmd/list-app-owners.go +++ b/cmd/list-app-owners.go @@ -25,6 +25,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -63,6 +64,7 @@ func listAppOwners(ctx context.Context, client client.AzureClient, apps <-chan a out = make(chan azureWrapper[models.AppOwners]) streams = pipeline.Demux(ctx.Done(), apps, 25) wg sync.WaitGroup + params = query.GraphParams{} ) wg.Add(len(streams)) @@ -78,13 +80,13 @@ func listAppOwners(ctx context.Context, client client.AzureClient, apps <-chan a } count = 0 ) - for item := range client.ListAzureADAppOwners(ctx, app.Data.Id, "", "", "", nil) { + for item := range client.ListAzureADAppOwners(ctx, app.Data.Id, params) { if item.Error != nil { log.Error(item.Error, "unable to continue processing owners for this app", "appId", app.Data.AppId) } else { appOwner := models.AppOwner{ Owner: item.Ok, - AppId: item.AppId, + AppId: app.Data.Id, } log.V(2).Info("found app owner", "appOwner", appOwner) count++ diff --git a/cmd/list-app-owners_test.go b/cmd/list-app-owners_test.go index 666d8f1..978b7a8 100644 --- a/cmd/list-app-owners_test.go +++ b/cmd/list-app-owners_test.go @@ -23,6 +23,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" @@ -42,14 +43,14 @@ func TestListAppOwners(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockAppsChannel := make(chan azureWrapper[models.App]) - mockAppOwnerChannel := make(chan azure.AppOwnerResult) - mockAppOwnerChannel2 := make(chan azure.AppOwnerResult) + mockAppOwnerChannel := make(chan client.AzureResult[json.RawMessage]) + mockAppOwnerChannel2 := make(chan client.AzureResult[json.RawMessage]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureADAppOwners(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockAppOwnerChannel).Times(1) - mockClient.EXPECT().ListAzureADAppOwners(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockAppOwnerChannel2).Times(1) + mockClient.EXPECT().ListAzureADAppOwners(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockAppOwnerChannel).Times(1) + mockClient.EXPECT().ListAzureADAppOwners(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockAppOwnerChannel2).Times(1) channel := listAppOwners(ctx, mockClient, mockAppsChannel) go func() { @@ -59,19 +60,19 @@ func TestListAppOwners(t *testing.T) { }() go func() { defer close(mockAppOwnerChannel) - mockAppOwnerChannel <- azure.AppOwnerResult{ + mockAppOwnerChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockAppOwnerChannel <- azure.AppOwnerResult{ + mockAppOwnerChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } }() go func() { defer close(mockAppOwnerChannel2) - mockAppOwnerChannel2 <- azure.AppOwnerResult{ + mockAppOwnerChannel2 <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockAppOwnerChannel2 <- azure.AppOwnerResult{ + mockAppOwnerChannel2 <- client.AzureResult[json.RawMessage]{ Error: mockError, } }() diff --git a/cmd/list-app-role-assignments.go b/cmd/list-app-role-assignments.go index 9e3aa4b..cbff4a7 100644 --- a/cmd/list-app-role-assignments.go +++ b/cmd/list-app-role-assignments.go @@ -26,6 +26,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -96,7 +97,7 @@ func listAppRoleAssignments(ctx context.Context, client client.AzureClient, serv var ( count = 0 ) - for item := range client.ListAzureADAppRoleAssignments(ctx, servicePrincipal.Id, "", "", "", "", nil) { + for item := range client.ListAzureADAppRoleAssignments(ctx, servicePrincipal.Id, query.GraphParams{}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing app role assignments for this service principal", "servicePrincipalId", servicePrincipal) } else { diff --git a/cmd/list-apps.go b/cmd/list-apps.go index 8160eb6..aabec3e 100644 --- a/cmd/list-apps.go +++ b/cmd/list-apps.go @@ -24,6 +24,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -64,7 +65,7 @@ func listApps(ctx context.Context, client client.AzureClient) <-chan azureWrappe defer panicrecovery.PanicRecovery() defer close(out) count := 0 - for item := range client.ListAzureADApps(ctx, "", "", "", "", nil) { + for item := range client.ListAzureADApps(ctx, query.GraphParams{}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing applications") return diff --git a/cmd/list-apps_test.go b/cmd/list-apps_test.go index 136656a..136d8f0 100644 --- a/cmd/list-apps_test.go +++ b/cmd/list-apps_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models/azure" "go.uber.org/mock/gomock" @@ -37,21 +38,21 @@ func TestListApps(t *testing.T) { ctx := context.Background() mockClient := mocks.NewMockAzureClient(ctrl) - mockChannel := make(chan azure.ApplicationResult) + mockChannel := make(chan client.AzureResult[azure.Application]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureADApps(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockChannel) + mockClient.EXPECT().ListAzureADApps(gomock.Any(), gomock.Any()).Return(mockChannel) go func() { defer close(mockChannel) - mockChannel <- azure.ApplicationResult{ + mockChannel <- client.AzureResult[azure.Application]{ Ok: azure.Application{}, } - mockChannel <- azure.ApplicationResult{ + mockChannel <- client.AzureResult[azure.Application]{ Error: mockError, } - mockChannel <- azure.ApplicationResult{ + mockChannel <- client.AzureResult[azure.Application]{ Ok: azure.Application{}, } }() diff --git a/cmd/list-automation-account-role-assignments.go b/cmd/list-automation-account-role-assignments.go index 7b25179..3a15f9d 100644 --- a/cmd/list-automation-account-role-assignments.go +++ b/cmd/list-automation-account-role-assignments.go @@ -98,7 +98,7 @@ func listAutomationAccountRoleAssignments(ctx context.Context, client client.Azu } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this automation account", "automationAccountId", id) } else { @@ -106,7 +106,7 @@ func listAutomationAccountRoleAssignments(ctx context.Context, client client.Azu automationAccountRoleAssignment := models.AzureRoleAssignment{ Assignee: item.Ok, - ObjectId: item.ParentId, + ObjectId: id, RoleDefinitionId: roleDefinitionId, } log.V(2).Info("found automation account role assignment", "automationAccountRoleAssignment", automationAccountRoleAssignment) diff --git a/cmd/list-automation-accounts.go b/cmd/list-automation-accounts.go index bd51f48..1887964 100644 --- a/cmd/list-automation-accounts.go +++ b/cmd/list-automation-accounts.go @@ -97,7 +97,7 @@ func listAutomationAccounts(ctx context.Context, client client.AzureClient, subs resourceGroupId := item.Ok.ResourceGroupId() automationAccount := models.AutomationAccount{ AutomationAccount: item.Ok, - SubscriptionId: item.SubscriptionId, + SubscriptionId: "/subscriptions/" + id, ResourceGroupId: resourceGroupId, TenantId: client.TenantInfo().TenantId, } diff --git a/cmd/list-container-registries.go b/cmd/list-container-registries.go index 93a0932..dcdc379 100644 --- a/cmd/list-container-registries.go +++ b/cmd/list-container-registries.go @@ -102,7 +102,7 @@ func listContainerRegistries(ctx context.Context, client client.AzureClient, sub resourceGroupId := item.Ok.ResourceGroupId() containerRegistry := models.ContainerRegistry{ ContainerRegistry: item.Ok, - SubscriptionId: item.SubscriptionId, + SubscriptionId: "/subscriptions/" + id, ResourceGroupId: resourceGroupId, TenantId: client.TenantInfo().TenantId, } diff --git a/cmd/list-container-registry-role-assignments.go b/cmd/list-container-registry-role-assignments.go index 31c3806..8c3a44a 100644 --- a/cmd/list-container-registry-role-assignments.go +++ b/cmd/list-container-registry-role-assignments.go @@ -103,7 +103,7 @@ func listContainerRegistryRoleAssignments(ctx context.Context, client client.Azu } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this container registry", "containerRegistryId", id) } else { @@ -111,7 +111,7 @@ func listContainerRegistryRoleAssignments(ctx context.Context, client client.Azu containerRegistryRoleAssignment := models.AzureRoleAssignment{ Assignee: item.Ok, - ObjectId: item.ParentId, + ObjectId: id, RoleDefinitionId: roleDefinitionId, } log.V(2).Info("found container registry role assignment", "containerRegistryRoleAssignment", containerRegistryRoleAssignment) diff --git a/cmd/list-device-owners.go b/cmd/list-device-owners.go index 1d0c2e6..4fcc48d 100644 --- a/cmd/list-device-owners.go +++ b/cmd/list-device-owners.go @@ -26,6 +26,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -95,13 +96,13 @@ func listDeviceOwners(ctx context.Context, client client.AzureClient, devices <- } count = 0 ) - for item := range client.ListAzureDeviceRegisteredOwners(ctx, id, false) { + for item := range client.ListAzureDeviceRegisteredOwners(ctx, id, query.GraphParams{}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing owners for this device", "deviceId", id) } else { deviceOwner := models.DeviceOwner{ Owner: item.Ok, - DeviceId: item.DeviceId, + DeviceId: id, } log.V(2).Info("found device owner", "deviceOwner", deviceOwner) count++ diff --git a/cmd/list-device-owners_test.go b/cmd/list-device-owners_test.go index 5f0dc3e..87a2306 100644 --- a/cmd/list-device-owners_test.go +++ b/cmd/list-device-owners_test.go @@ -23,6 +23,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/models/azure" @@ -41,8 +42,8 @@ func TestListDeviceOwners(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockDevicesChannel := make(chan interface{}) - mockDeviceOwnerChannel := make(chan azure.DeviceRegisteredOwnerResult) - mockDeviceOwnerChannel2 := make(chan azure.DeviceRegisteredOwnerResult) + mockDeviceOwnerChannel := make(chan client.AzureResult[json.RawMessage]) + mockDeviceOwnerChannel2 := make(chan client.AzureResult[json.RawMessage]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") @@ -62,19 +63,19 @@ func TestListDeviceOwners(t *testing.T) { }() go func() { defer close(mockDeviceOwnerChannel) - mockDeviceOwnerChannel <- azure.DeviceRegisteredOwnerResult{ + mockDeviceOwnerChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockDeviceOwnerChannel <- azure.DeviceRegisteredOwnerResult{ + mockDeviceOwnerChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } }() go func() { defer close(mockDeviceOwnerChannel2) - mockDeviceOwnerChannel2 <- azure.DeviceRegisteredOwnerResult{ + mockDeviceOwnerChannel2 <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockDeviceOwnerChannel2 <- azure.DeviceRegisteredOwnerResult{ + mockDeviceOwnerChannel2 <- client.AzureResult[json.RawMessage]{ Error: mockError, } }() diff --git a/cmd/list-devices.go b/cmd/list-devices.go index 5e68b8b..184b5bb 100644 --- a/cmd/list-devices.go +++ b/cmd/list-devices.go @@ -24,6 +24,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -64,7 +65,7 @@ func listDevices(ctx context.Context, client client.AzureClient) <-chan interfac defer panicrecovery.PanicRecovery() defer close(out) count := 0 - for item := range client.ListAzureDevices(ctx, "", "", "", "", nil) { + for item := range client.ListAzureDevices(ctx, query.GraphParams{}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing devices") return diff --git a/cmd/list-devices_test.go b/cmd/list-devices_test.go index 1bbf4bf..a2a19ed 100644 --- a/cmd/list-devices_test.go +++ b/cmd/list-devices_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models/azure" "go.uber.org/mock/gomock" @@ -37,21 +38,21 @@ func TestListDevices(t *testing.T) { ctx := context.Background() mockClient := mocks.NewMockAzureClient(ctrl) - mockChannel := make(chan azure.DeviceResult) + mockChannel := make(chan client.AzureResult[azure.Device]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockChannel) + mockClient.EXPECT().ListAzureDevices(gomock.Any(), gomock.Any()).Return(mockChannel) go func() { defer close(mockChannel) - mockChannel <- azure.DeviceResult{ + mockChannel <- client.AzureResult[azure.Device]{ Ok: azure.Device{}, } - mockChannel <- azure.DeviceResult{ + mockChannel <- client.AzureResult[azure.Device]{ Error: mockError, } - mockChannel <- azure.DeviceResult{ + mockChannel <- client.AzureResult[azure.Device]{ Ok: azure.Device{}, } }() diff --git a/cmd/list-function-app-role-assignments.go b/cmd/list-function-app-role-assignments.go index d881936..56d5d9b 100644 --- a/cmd/list-function-app-role-assignments.go +++ b/cmd/list-function-app-role-assignments.go @@ -98,7 +98,7 @@ func listFunctionAppRoleAssignments(ctx context.Context, client client.AzureClie } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this function app", "functionAppId", id) } else { @@ -106,7 +106,7 @@ func listFunctionAppRoleAssignments(ctx context.Context, client client.AzureClie functionAppRoleAssignment := models.AzureRoleAssignment{ Assignee: item.Ok, - ObjectId: item.ParentId, + ObjectId: id, RoleDefinitionId: roleDefinitionId, } log.V(2).Info("Found function app role asignment", "functionAppRoleAssignment", functionAppRoleAssignment) diff --git a/cmd/list-function-apps.go b/cmd/list-function-apps.go index d4e9b56..7f961ab 100644 --- a/cmd/list-function-apps.go +++ b/cmd/list-function-apps.go @@ -94,12 +94,12 @@ func listFunctionApps(ctx context.Context, client client.AzureClient, subscripti if item.Error != nil { log.Error(item.Error, "unable to continue processing function apps for this subscription", "subscriptionId", id) } else { - resourceGroupId := item.Ok.ResourceGroupId() functionApp := models.FunctionApp{ - FunctionApp: item.Ok, - SubscriptionId: item.SubscriptionId, - ResourceGroupId: resourceGroupId, - TenantId: client.TenantInfo().TenantId, + FunctionApp: item.Ok, + SubscriptionId: "/subscriptions/" + id, + ResourceGroupId: item.Ok.ResourceGroupId(), + ResourceGroupName: item.Ok.ResourceGroupName(), + TenantId: client.TenantInfo().TenantId, } if functionApp.Kind == "functionapp" { log.V(2).Info("found function app", "functionApp", functionApp) diff --git a/cmd/list-group-members.go b/cmd/list-group-members.go index 2f534a0..c465d6b 100644 --- a/cmd/list-group-members.go +++ b/cmd/list-group-members.go @@ -26,6 +26,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -35,6 +36,7 @@ import ( func init() { listRootCmd.AddCommand(listGroupMembersCmd) + listGroupMembersCmd.Flags().StringSliceVar(&listGroupMembersSelect, "select", []string{"id,displayName,createdDateTime"}, `Select properties to include. Use "" for Azure default properties. Azurehound default is "id,displayName,createdDateTime" if flag is not supplied.`) } var listGroupMembersCmd = &cobra.Command{ @@ -44,7 +46,9 @@ var listGroupMembersCmd = &cobra.Command{ SilenceUsage: true, } -func listGroupMembersCmdImpl(cmd *cobra.Command, args []string) { +var listGroupMembersSelect []string + +func listGroupMembersCmdImpl(cmd *cobra.Command, _ []string) { ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, os.Kill) defer gracefulShutdown(stop) @@ -64,6 +68,14 @@ func listGroupMembers(ctx context.Context, client client.AzureClient, groups <-c ids = make(chan string) streams = pipeline.Demux(ctx.Done(), ids, 25) wg sync.WaitGroup + params = query.GraphParams{ + Select: unique(listGroupMembersSelect), + Filter: "", + Count: false, + Search: "", + Top: 0, + Expand: "", + } ) go func() { @@ -95,13 +107,13 @@ func listGroupMembers(ctx context.Context, client client.AzureClient, groups <-c } count = 0 ) - for item := range client.ListAzureADGroupMembers(ctx, id, "", "", "", nil) { + for item := range client.ListAzureADGroupMembers(ctx, id, params) { if item.Error != nil { log.Error(item.Error, "unable to continue processing members for this group", "groupId", id) } else { groupMember := models.GroupMember{ Member: item.Ok, - GroupId: item.ParentId, + GroupId: id, } log.V(2).Info("found group member", "groupMember", groupMember) count++ diff --git a/cmd/list-group-members_test.go b/cmd/list-group-members_test.go index f3cf088..495d28f 100644 --- a/cmd/list-group-members_test.go +++ b/cmd/list-group-members_test.go @@ -23,6 +23,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/models/azure" @@ -41,14 +42,14 @@ func TestListGroupMembers(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockGroupsChannel := make(chan interface{}) - mockGroupMemberChannel := make(chan azure.MemberObjectResult) - mockGroupMemberChannel2 := make(chan azure.MemberObjectResult) + mockGroupMemberChannel := make(chan client.AzureResult[json.RawMessage]) + mockGroupMemberChannel2 := make(chan client.AzureResult[json.RawMessage]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureADGroupMembers(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockGroupMemberChannel).Times(1) - mockClient.EXPECT().ListAzureADGroupMembers(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockGroupMemberChannel2).Times(1) + mockClient.EXPECT().ListAzureADGroupMembers(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockGroupMemberChannel).Times(1) + mockClient.EXPECT().ListAzureADGroupMembers(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockGroupMemberChannel2).Times(1) channel := listGroupMembers(ctx, mockClient, mockGroupsChannel) go func() { @@ -62,19 +63,19 @@ func TestListGroupMembers(t *testing.T) { }() go func() { defer close(mockGroupMemberChannel) - mockGroupMemberChannel <- azure.MemberObjectResult{ + mockGroupMemberChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockGroupMemberChannel <- azure.MemberObjectResult{ + mockGroupMemberChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } }() go func() { defer close(mockGroupMemberChannel2) - mockGroupMemberChannel2 <- azure.MemberObjectResult{ + mockGroupMemberChannel2 <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockGroupMemberChannel2 <- azure.MemberObjectResult{ + mockGroupMemberChannel2 <- client.AzureResult[json.RawMessage]{ Error: mockError, } }() diff --git a/cmd/list-group-owners.go b/cmd/list-group-owners.go index e24d13b..ed077d7 100644 --- a/cmd/list-group-owners.go +++ b/cmd/list-group-owners.go @@ -26,6 +26,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -64,6 +65,7 @@ func listGroupOwners(ctx context.Context, client client.AzureClient, groups <-ch ids = make(chan string) streams = pipeline.Demux(ctx.Done(), ids, 25) wg sync.WaitGroup + params = query.GraphParams{} ) go func() { @@ -95,13 +97,13 @@ func listGroupOwners(ctx context.Context, client client.AzureClient, groups <-ch } count = 0 ) - for item := range client.ListAzureADGroupOwners(ctx, id, "", "", "", nil) { + for item := range client.ListAzureADGroupOwners(ctx, id, params) { if item.Error != nil { log.Error(item.Error, "unable to continue processing owners for this group", "groupId", id) } else { groupOwner := models.GroupOwner{ Owner: item.Ok, - GroupId: item.GroupId, + GroupId: id, } log.V(2).Info("found group owner", "groupOwner", groupOwner) count++ diff --git a/cmd/list-group-owners_test.go b/cmd/list-group-owners_test.go index b1e7eea..57e8536 100644 --- a/cmd/list-group-owners_test.go +++ b/cmd/list-group-owners_test.go @@ -23,6 +23,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/models/azure" @@ -41,14 +42,14 @@ func TestListGroupOwners(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockGroupsChannel := make(chan interface{}) - mockGroupOwnerChannel := make(chan azure.GroupOwnerResult) - mockGroupOwnerChannel2 := make(chan azure.GroupOwnerResult) + mockGroupOwnerChannel := make(chan client.AzureResult[json.RawMessage]) + mockGroupOwnerChannel2 := make(chan client.AzureResult[json.RawMessage]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureADGroupOwners(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockGroupOwnerChannel).Times(1) - mockClient.EXPECT().ListAzureADGroupOwners(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockGroupOwnerChannel2).Times(1) + mockClient.EXPECT().ListAzureADGroupOwners(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockGroupOwnerChannel).Times(1) + mockClient.EXPECT().ListAzureADGroupOwners(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockGroupOwnerChannel2).Times(1) channel := listGroupOwners(ctx, mockClient, mockGroupsChannel) go func() { @@ -62,19 +63,19 @@ func TestListGroupOwners(t *testing.T) { }() go func() { defer close(mockGroupOwnerChannel) - mockGroupOwnerChannel <- azure.GroupOwnerResult{ + mockGroupOwnerChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockGroupOwnerChannel <- azure.GroupOwnerResult{ + mockGroupOwnerChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } }() go func() { defer close(mockGroupOwnerChannel2) - mockGroupOwnerChannel2 <- azure.GroupOwnerResult{ + mockGroupOwnerChannel2 <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockGroupOwnerChannel2 <- azure.GroupOwnerResult{ + mockGroupOwnerChannel2 <- client.AzureResult[json.RawMessage]{ Error: mockError, } }() diff --git a/cmd/list-groups.go b/cmd/list-groups.go index 47b8dde..0031d39 100644 --- a/cmd/list-groups.go +++ b/cmd/list-groups.go @@ -24,6 +24,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -42,7 +43,7 @@ var listGroupsCmd = &cobra.Command{ SilenceUsage: true, } -func listGroupsCmdImpl(cmd *cobra.Command, args []string) { +func listGroupsCmdImpl(cmd *cobra.Command, _ []string) { ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, os.Kill) defer gracefulShutdown(stop) @@ -64,7 +65,7 @@ func listGroups(ctx context.Context, client client.AzureClient) <-chan interface defer panicrecovery.PanicRecovery() defer close(out) count := 0 - for item := range client.ListAzureADGroups(ctx, "securityEnabled eq true", "", "", "", nil) { + for item := range client.ListAzureADGroups(ctx, query.GraphParams{Filter: "securityEnabled eq true"}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing groups") return diff --git a/cmd/list-groups_test.go b/cmd/list-groups_test.go index d931ff2..175a74e 100644 --- a/cmd/list-groups_test.go +++ b/cmd/list-groups_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models/azure" "go.uber.org/mock/gomock" @@ -38,21 +39,21 @@ func TestListGroups(t *testing.T) { ctx := context.Background() mockClient := mocks.NewMockAzureClient(ctrl) - mockChannel := make(chan azure.GroupResult) + mockChannel := make(chan client.AzureResult[azure.Group]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureADGroups(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockChannel) + mockClient.EXPECT().ListAzureADGroups(gomock.Any(), gomock.Any()).Return(mockChannel) go func() { defer close(mockChannel) - mockChannel <- azure.GroupResult{ + mockChannel <- client.AzureResult[azure.Group]{ Ok: azure.Group{}, } - mockChannel <- azure.GroupResult{ + mockChannel <- client.AzureResult[azure.Group]{ Error: mockError, } - mockChannel <- azure.GroupResult{ + mockChannel <- client.AzureResult[azure.Group]{ Ok: azure.Group{}, } }() diff --git a/cmd/list-key-vault-role-assignments.go b/cmd/list-key-vault-role-assignments.go index 05b67d2..0780c8b 100644 --- a/cmd/list-key-vault-role-assignments.go +++ b/cmd/list-key-vault-role-assignments.go @@ -97,12 +97,12 @@ func listKeyVaultRoleAssignments(ctx context.Context, client client.AzureClient, } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this key vault", "keyVaultId", id) } else { keyVaultRoleAssignment := models.KeyVaultRoleAssignment{ - KeyVaultId: item.ParentId, + KeyVaultId: id, RoleAssignment: item.Ok, } log.V(2).Info("found key vault role assignment", "keyVaultRoleAssignment", keyVaultRoleAssignment) diff --git a/cmd/list-key-vault-role-assignments_test.go b/cmd/list-key-vault-role-assignments_test.go index db7e4c3..d94018e 100644 --- a/cmd/list-key-vault-role-assignments_test.go +++ b/cmd/list-key-vault-role-assignments_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models" @@ -41,14 +42,14 @@ func TestListKeyVaultRoleAssignments(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockKeyVaultsChannel := make(chan interface{}) - mockKeyVaultRoleAssignmentChannel := make(chan azure.RoleAssignmentResult) - mockKeyVaultRoleAssignmentChannel2 := make(chan azure.RoleAssignmentResult) + mockKeyVaultRoleAssignmentChannel := make(chan client.AzureResult[azure.RoleAssignment]) + mockKeyVaultRoleAssignmentChannel2 := make(chan client.AzureResult[azure.RoleAssignment]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultRoleAssignmentChannel).Times(1) - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultRoleAssignmentChannel2).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultRoleAssignmentChannel).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockKeyVaultRoleAssignmentChannel2).Times(1) channel := listKeyVaultRoleAssignments(ctx, mockClient, mockKeyVaultsChannel) go func() { @@ -62,14 +63,14 @@ func TestListKeyVaultRoleAssignments(t *testing.T) { }() go func() { defer close(mockKeyVaultRoleAssignmentChannel) - mockKeyVaultRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockKeyVaultRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.KeyVaultContributorRoleID, }, }, } - mockKeyVaultRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockKeyVaultRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.ContributorRoleID, @@ -79,14 +80,14 @@ func TestListKeyVaultRoleAssignments(t *testing.T) { }() go func() { defer close(mockKeyVaultRoleAssignmentChannel2) - mockKeyVaultRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockKeyVaultRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.KeyVaultAdministratorRoleID, }, }, } - mockKeyVaultRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockKeyVaultRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Error: mockError, } }() diff --git a/cmd/list-key-vaults.go b/cmd/list-key-vaults.go index 66626d4..6ea7641 100644 --- a/cmd/list-key-vaults.go +++ b/cmd/list-key-vaults.go @@ -26,6 +26,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -91,17 +92,16 @@ func listKeyVaults(ctx context.Context, client client.AzureClient, subscriptions defer wg.Done() for id := range stream { count := 0 - for item := range client.ListAzureKeyVaults(ctx, id, 999) { + for item := range client.ListAzureKeyVaults(ctx, id, query.RMParams{Top: 999}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing key vaults for this subscription", "subscriptionId", id) } else { - resourceGroup := item.Ok.ResourceGroupId() // the embedded struct's values override top-level properties so TenantId // needs to be explicitly set. keyVault := models.KeyVault{ KeyVault: item.Ok, - SubscriptionId: item.SubscriptionId, - ResourceGroup: resourceGroup, + SubscriptionId: id, + ResourceGroup: item.Ok.ResourceGroupId(), TenantId: item.Ok.Properties.TenantId, } log.V(2).Info("found key vault", "keyVault", keyVault) diff --git a/cmd/list-key-vaults_test.go b/cmd/list-key-vaults_test.go index 269288a..9629d02 100644 --- a/cmd/list-key-vaults_test.go +++ b/cmd/list-key-vaults_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/models/azure" @@ -40,8 +41,8 @@ func TestListKeyVaults(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockSubscriptionsChannel := make(chan interface{}) - mockKeyVaultChannel := make(chan azure.KeyVaultResult) - mockKeyVaultChannel2 := make(chan azure.KeyVaultResult) + mockKeyVaultChannel := make(chan client.AzureResult[azure.KeyVault]) + mockKeyVaultChannel2 := make(chan client.AzureResult[azure.KeyVault]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") @@ -61,19 +62,19 @@ func TestListKeyVaults(t *testing.T) { }() go func() { defer close(mockKeyVaultChannel) - mockKeyVaultChannel <- azure.KeyVaultResult{ + mockKeyVaultChannel <- client.AzureResult[azure.KeyVault]{ Ok: azure.KeyVault{}, } - mockKeyVaultChannel <- azure.KeyVaultResult{ + mockKeyVaultChannel <- client.AzureResult[azure.KeyVault]{ Ok: azure.KeyVault{}, } }() go func() { defer close(mockKeyVaultChannel2) - mockKeyVaultChannel2 <- azure.KeyVaultResult{ + mockKeyVaultChannel2 <- client.AzureResult[azure.KeyVault]{ Ok: azure.KeyVault{}, } - mockKeyVaultChannel2 <- azure.KeyVaultResult{ + mockKeyVaultChannel2 <- client.AzureResult[azure.KeyVault]{ Error: mockError, } }() diff --git a/cmd/list-logic-app-role-assignments.go b/cmd/list-logic-app-role-assignments.go index 0dd33aa..8768970 100644 --- a/cmd/list-logic-app-role-assignments.go +++ b/cmd/list-logic-app-role-assignments.go @@ -103,7 +103,7 @@ func listLogicAppRoleAssignments(ctx context.Context, client client.AzureClient, } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this logic app", "logicappId", id) } else { @@ -111,7 +111,7 @@ func listLogicAppRoleAssignments(ctx context.Context, client client.AzureClient, logicappRoleAssignment := models.AzureRoleAssignment{ Assignee: item.Ok, - ObjectId: item.ParentId, + ObjectId: id, RoleDefinitionId: roleDefinitionId, } log.V(2).Info("found logic app role assignment", "logicappRoleAssignment", logicappRoleAssignment) diff --git a/cmd/list-logic-apps.go b/cmd/list-logic-apps.go index 415168d..e60c26c 100644 --- a/cmd/list-logic-apps.go +++ b/cmd/list-logic-apps.go @@ -104,11 +104,10 @@ func listLogicApps(ctx context.Context, client client.AzureClient, subscriptions if item.Error != nil { log.Error(item.Error, "unable to continue processing logic apps for this subscription", "subscriptionId", id) } else { - resourceGroupId := item.Ok.ResourceGroupId() logicapp := models.LogicApp{ LogicApp: item.Ok, - SubscriptionId: item.SubscriptionId, - ResourceGroupId: resourceGroupId, + SubscriptionId: "/subscriptions/" + id, + ResourceGroupId: item.Ok.ResourceGroupId(), TenantId: client.TenantInfo().TenantId, } log.V(2).Info("found logicapp", "logicapp", logicapp) diff --git a/cmd/list-managed-cluster-role-assignments.go b/cmd/list-managed-cluster-role-assignments.go index 102db09..82e09a3 100644 --- a/cmd/list-managed-cluster-role-assignments.go +++ b/cmd/list-managed-cluster-role-assignments.go @@ -103,7 +103,7 @@ func listManagedClusterRoleAssignments(ctx context.Context, client client.AzureC } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this managed cluster", "managedClusterId", id) } else { @@ -111,7 +111,7 @@ func listManagedClusterRoleAssignments(ctx context.Context, client client.AzureC managedClusterRoleAssignment := models.AzureRoleAssignment{ Assignee: item.Ok, - ObjectId: item.ParentId, + ObjectId: id, RoleDefinitionId: roleDefinitionId, } log.V(2).Info("found managed cluster role assignment", "managedClusterRoleAssignment", managedClusterRoleAssignment) diff --git a/cmd/list-managed-clusters.go b/cmd/list-managed-clusters.go index d186666..61fe220 100644 --- a/cmd/list-managed-clusters.go +++ b/cmd/list-managed-clusters.go @@ -95,15 +95,14 @@ func listManagedClusters(ctx context.Context, client client.AzureClient, subscri defer wg.Done() for id := range stream { count := 0 - for item := range client.ListAzureManagedClusters(ctx, id, false) { + for item := range client.ListAzureManagedClusters(ctx, id) { if item.Error != nil { log.Error(item.Error, "unable to continue processing managed clusters for this subscription", "subscriptionId", id) } else { - resourceGroupId := item.Ok.ResourceGroupId() managedCluster := models.ManagedCluster{ ManagedCluster: item.Ok, - SubscriptionId: item.SubscriptionId, - ResourceGroupId: resourceGroupId, + SubscriptionId: "/subscriptions/" + id, + ResourceGroupId: item.Ok.ResourceGroupId(), TenantId: client.TenantInfo().TenantId, } log.V(2).Info("found managed cluster", "managedCluster", managedCluster) diff --git a/cmd/list-management-group-descendants.go b/cmd/list-management-group-descendants.go index 404c56e..803b20f 100644 --- a/cmd/list-management-group-descendants.go +++ b/cmd/list-management-group-descendants.go @@ -91,7 +91,7 @@ func listManagementGroupDescendants(ctx context.Context, client client.AzureClie defer wg.Done() for id := range stream { count := 0 - for item := range client.ListAzureManagementGroupDescendants(ctx, id) { + for item := range client.ListAzureManagementGroupDescendants(ctx, id, 3000) { if item.Error != nil { log.Error(item.Error, "unable to continue processing descendants for this management group", "managementGroupId", id) } else { diff --git a/cmd/list-management-group-descendants_test.go b/cmd/list-management-group-descendants_test.go index 01806b1..3b3e53a 100644 --- a/cmd/list-management-group-descendants_test.go +++ b/cmd/list-management-group-descendants_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/models/azure" @@ -40,14 +41,14 @@ func TestListManagementGroupDescendants(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockManagementGroupsChannel := make(chan interface{}) - mockManagementGroupDescendantChannel := make(chan azure.DescendantInfoResult) - mockManagementGroupDescendantChannel2 := make(chan azure.DescendantInfoResult) + mockManagementGroupDescendantChannel := make(chan client.AzureResult[azure.DescendantInfo]) + mockManagementGroupDescendantChannel2 := make(chan client.AzureResult[azure.DescendantInfo]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureManagementGroupDescendants(gomock.Any(), gomock.Any()).Return(mockManagementGroupDescendantChannel).Times(1) - mockClient.EXPECT().ListAzureManagementGroupDescendants(gomock.Any(), gomock.Any()).Return(mockManagementGroupDescendantChannel2).Times(1) + mockClient.EXPECT().ListAzureManagementGroupDescendants(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockManagementGroupDescendantChannel).Times(1) + mockClient.EXPECT().ListAzureManagementGroupDescendants(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockManagementGroupDescendantChannel2).Times(1) channel := listManagementGroupDescendants(ctx, mockClient, mockManagementGroupsChannel) go func() { @@ -61,13 +62,13 @@ func TestListManagementGroupDescendants(t *testing.T) { }() go func() { defer close(mockManagementGroupDescendantChannel) - mockManagementGroupDescendantChannel <- azure.DescendantInfoResult{} - mockManagementGroupDescendantChannel <- azure.DescendantInfoResult{} + mockManagementGroupDescendantChannel <- client.AzureResult[azure.DescendantInfo]{} + mockManagementGroupDescendantChannel <- client.AzureResult[azure.DescendantInfo]{} }() go func() { defer close(mockManagementGroupDescendantChannel2) - mockManagementGroupDescendantChannel2 <- azure.DescendantInfoResult{} - mockManagementGroupDescendantChannel2 <- azure.DescendantInfoResult{ + mockManagementGroupDescendantChannel2 <- client.AzureResult[azure.DescendantInfo]{} + mockManagementGroupDescendantChannel2 <- client.AzureResult[azure.DescendantInfo]{ Error: mockError, } }() diff --git a/cmd/list-management-group-role-assignments.go b/cmd/list-management-group-role-assignments.go index 6ef9a51..97009b4 100644 --- a/cmd/list-management-group-role-assignments.go +++ b/cmd/list-management-group-role-assignments.go @@ -97,12 +97,12 @@ func listManagementGroupRoleAssignments(ctx context.Context, client client.Azure } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "atScope()") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "atScope()", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this managementGroup", "managementGroupId", id) } else { managementGroupRoleAssignment := models.ManagementGroupRoleAssignment{ - ManagementGroupId: item.ParentId, + ManagementGroupId: id, RoleAssignment: item.Ok, } log.V(2).Info("found managementGroup role assignment", "managementGroupRoleAssignment", managementGroupRoleAssignment) diff --git a/cmd/list-management-group-role-assignments_test.go b/cmd/list-management-group-role-assignments_test.go index eed6954..7f21669 100644 --- a/cmd/list-management-group-role-assignments_test.go +++ b/cmd/list-management-group-role-assignments_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models" @@ -41,14 +42,14 @@ func TestListResourceGroupRoleAssignments(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockResourceGroupsChannel := make(chan interface{}) - mockResourceGroupRoleAssignmentChannel := make(chan azure.RoleAssignmentResult) - mockResourceGroupRoleAssignmentChannel2 := make(chan azure.RoleAssignmentResult) + mockResourceGroupRoleAssignmentChannel := make(chan client.AzureResult[azure.RoleAssignment]) + mockResourceGroupRoleAssignmentChannel2 := make(chan client.AzureResult[azure.RoleAssignment]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockResourceGroupRoleAssignmentChannel).Times(1) - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockResourceGroupRoleAssignmentChannel2).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockResourceGroupRoleAssignmentChannel).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockResourceGroupRoleAssignmentChannel2).Times(1) channel := listResourceGroupRoleAssignments(ctx, mockClient, mockResourceGroupsChannel) go func() { @@ -62,14 +63,14 @@ func TestListResourceGroupRoleAssignments(t *testing.T) { }() go func() { defer close(mockResourceGroupRoleAssignmentChannel) - mockResourceGroupRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockResourceGroupRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.ContributorRoleID, }, }, } - mockResourceGroupRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockResourceGroupRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.OwnerRoleID, @@ -79,14 +80,14 @@ func TestListResourceGroupRoleAssignments(t *testing.T) { }() go func() { defer close(mockResourceGroupRoleAssignmentChannel2) - mockResourceGroupRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockResourceGroupRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.OwnerRoleID, }, }, } - mockResourceGroupRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockResourceGroupRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Error: mockError, } }() diff --git a/cmd/list-management-groups.go b/cmd/list-management-groups.go index 87ddbe0..3af3019 100644 --- a/cmd/list-management-groups.go +++ b/cmd/list-management-groups.go @@ -66,7 +66,7 @@ func listManagementGroups(ctx context.Context, client client.AzureClient) <-chan defer panicrecovery.PanicRecovery() defer close(out) count := 0 - for item := range client.ListAzureManagementGroups(ctx) { + for item := range client.ListAzureManagementGroups(ctx, "") { if item.Error != nil { log.Info("warning: unable to process azure management groups; either the organization has no management groups or azurehound does not have the reader role on the root management group.") return diff --git a/cmd/list-management-groups_test.go b/cmd/list-management-groups_test.go index 47efe17..72f980c 100644 --- a/cmd/list-management-groups_test.go +++ b/cmd/list-management-groups_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models/azure" "go.uber.org/mock/gomock" @@ -37,21 +38,21 @@ func TestListManagementGroups(t *testing.T) { ctx := context.Background() mockClient := mocks.NewMockAzureClient(ctrl) - mockChannel := make(chan azure.ManagementGroupResult) + mockChannel := make(chan client.AzureResult[azure.ManagementGroup]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureManagementGroups(gomock.Any()).Return(mockChannel) + mockClient.EXPECT().ListAzureManagementGroups(gomock.Any(), gomock.Any()).Return(mockChannel) go func() { defer close(mockChannel) - mockChannel <- azure.ManagementGroupResult{ + mockChannel <- client.AzureResult[azure.ManagementGroup]{ Ok: azure.ManagementGroup{}, } - mockChannel <- azure.ManagementGroupResult{ + mockChannel <- client.AzureResult[azure.ManagementGroup]{ Error: mockError, } - mockChannel <- azure.ManagementGroupResult{ + mockChannel <- client.AzureResult[azure.ManagementGroup]{ Ok: azure.ManagementGroup{}, } }() diff --git a/cmd/list-resource-group-role-assignments.go b/cmd/list-resource-group-role-assignments.go index a6d2fb7..dc10820 100644 --- a/cmd/list-resource-group-role-assignments.go +++ b/cmd/list-resource-group-role-assignments.go @@ -98,12 +98,12 @@ func listResourceGroupRoleAssignments(ctx context.Context, client client.AzureCl } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this resourceGroup", "resourceGroupId", id) } else { resourceGroupRoleAssignment := models.ResourceGroupRoleAssignment{ - ResourceGroupId: item.ParentId, + ResourceGroupId: id, RoleAssignment: item.Ok, } log.V(2).Info("found resourceGroup role assignment", "resourceGroupRoleAssignment", resourceGroupRoleAssignment) diff --git a/cmd/list-resource-group-role-assignments_test.go b/cmd/list-resource-group-role-assignments_test.go index 5c8c8bb..8b1ac8d 100644 --- a/cmd/list-resource-group-role-assignments_test.go +++ b/cmd/list-resource-group-role-assignments_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models" @@ -41,14 +42,14 @@ func TestListManagementGroupRoleAssignments(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockManagementGroupsChannel := make(chan interface{}) - mockManagementGroupRoleAssignmentChannel := make(chan azure.RoleAssignmentResult) - mockManagementGroupRoleAssignmentChannel2 := make(chan azure.RoleAssignmentResult) + mockManagementGroupRoleAssignmentChannel := make(chan client.AzureResult[azure.RoleAssignment]) + mockManagementGroupRoleAssignmentChannel2 := make(chan client.AzureResult[azure.RoleAssignment]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockManagementGroupRoleAssignmentChannel).Times(1) - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockManagementGroupRoleAssignmentChannel2).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockManagementGroupRoleAssignmentChannel).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockManagementGroupRoleAssignmentChannel2).Times(1) channel := listManagementGroupRoleAssignments(ctx, mockClient, mockManagementGroupsChannel) go func() { @@ -62,14 +63,14 @@ func TestListManagementGroupRoleAssignments(t *testing.T) { }() go func() { defer close(mockManagementGroupRoleAssignmentChannel) - mockManagementGroupRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockManagementGroupRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.ContributorRoleID, }, }, } - mockManagementGroupRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockManagementGroupRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.OwnerRoleID, @@ -79,14 +80,14 @@ func TestListManagementGroupRoleAssignments(t *testing.T) { }() go func() { defer close(mockManagementGroupRoleAssignmentChannel2) - mockManagementGroupRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockManagementGroupRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.OwnerRoleID, }, }, } - mockManagementGroupRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockManagementGroupRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Error: mockError, } }() diff --git a/cmd/list-resource-groups.go b/cmd/list-resource-groups.go index c3ae948..c9b9516 100644 --- a/cmd/list-resource-groups.go +++ b/cmd/list-resource-groups.go @@ -26,6 +26,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -91,13 +92,13 @@ func listResourceGroups(ctx context.Context, client client.AzureClient, subscrip defer wg.Done() for id := range stream { count := 0 - for item := range client.ListAzureResourceGroups(ctx, id, "") { + for item := range client.ListAzureResourceGroups(ctx, id, query.RMParams{Top: 1000}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing resource groups for this subscription", "subscriptionId", id) } else { resourceGroup := models.ResourceGroup{ ResourceGroup: item.Ok, - SubscriptionId: item.SubscriptionId, + SubscriptionId: "/subscriptions/"+id, TenantId: client.TenantInfo().TenantId, } log.V(2).Info("found resource group", "resourceGroup", resourceGroup) diff --git a/cmd/list-resource-groups_test.go b/cmd/list-resource-groups_test.go index 78ac328..19009a6 100644 --- a/cmd/list-resource-groups_test.go +++ b/cmd/list-resource-groups_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/models/azure" @@ -40,8 +41,8 @@ func TestListResourceGroups(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockSubscriptionsChannel := make(chan interface{}) - mockResourceGroupChannel := make(chan azure.ResourceGroupResult) - mockResourceGroupChannel2 := make(chan azure.ResourceGroupResult) + mockResourceGroupChannel := make(chan client.AzureResult[azure.ResourceGroup]) + mockResourceGroupChannel2 := make(chan client.AzureResult[azure.ResourceGroup]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") @@ -61,19 +62,19 @@ func TestListResourceGroups(t *testing.T) { }() go func() { defer close(mockResourceGroupChannel) - mockResourceGroupChannel <- azure.ResourceGroupResult{ + mockResourceGroupChannel <- client.AzureResult[azure.ResourceGroup]{ Ok: azure.ResourceGroup{}, } - mockResourceGroupChannel <- azure.ResourceGroupResult{ + mockResourceGroupChannel <- client.AzureResult[azure.ResourceGroup]{ Ok: azure.ResourceGroup{}, } }() go func() { defer close(mockResourceGroupChannel2) - mockResourceGroupChannel2 <- azure.ResourceGroupResult{ + mockResourceGroupChannel2 <- client.AzureResult[azure.ResourceGroup]{ Ok: azure.ResourceGroup{}, } - mockResourceGroupChannel2 <- azure.ResourceGroupResult{ + mockResourceGroupChannel2 <- client.AzureResult[azure.ResourceGroup]{ Error: mockError, } }() diff --git a/cmd/list-role-assignments.go b/cmd/list-role-assignments.go index 6e7ca32..b3248a4 100644 --- a/cmd/list-role-assignments.go +++ b/cmd/list-role-assignments.go @@ -26,6 +26,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -100,7 +101,7 @@ func listRoleAssignments(ctx context.Context, client client.AzureClient, roles < filter = fmt.Sprintf("roleDefinitionId eq '%s'", id) ) // We expand directoryScope in order to obtain the appId from app specific scoped role assignments - for item := range client.ListAzureADRoleAssignments(ctx, filter, "", "", "directoryScope", nil) { + for item := range client.ListAzureADRoleAssignments(ctx, query.GraphParams{Filter: filter, Expand: "directoryScope"}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this role", "roleDefinitionId", id) } else { diff --git a/cmd/list-roles.go b/cmd/list-roles.go index 836c9b5..7a377c6 100644 --- a/cmd/list-roles.go +++ b/cmd/list-roles.go @@ -24,6 +24,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -64,7 +65,7 @@ func listRoles(ctx context.Context, client client.AzureClient) <-chan interface{ defer panicrecovery.PanicRecovery() defer close(out) count := 0 - for item := range client.ListAzureADRoles(ctx, "", "") { + for item := range client.ListAzureADRoles(ctx, query.GraphParams{}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing roles") return diff --git a/cmd/list-roles_test.go b/cmd/list-roles_test.go index 0805f0a..09cdcb5 100644 --- a/cmd/list-roles_test.go +++ b/cmd/list-roles_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models/azure" "go.uber.org/mock/gomock" @@ -37,21 +38,21 @@ func TestListRoles(t *testing.T) { ctx := context.Background() mockClient := mocks.NewMockAzureClient(ctrl) - mockChannel := make(chan azure.RoleResult) + mockChannel := make(chan client.AzureResult[azure.Role]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureADRoles(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockChannel) + mockClient.EXPECT().ListAzureADRoles(gomock.Any(), gomock.Any()).Return(mockChannel) go func() { defer close(mockChannel) - mockChannel <- azure.RoleResult{ + mockChannel <- client.AzureResult[azure.Role]{ Ok: azure.Role{}, } - mockChannel <- azure.RoleResult{ + mockChannel <- client.AzureResult[azure.Role]{ Error: mockError, } - mockChannel <- azure.RoleResult{ + mockChannel <- client.AzureResult[azure.Role]{ Ok: azure.Role{}, } }() diff --git a/cmd/list-service-principal-owners.go b/cmd/list-service-principal-owners.go index 7fd4c56..7c13810 100644 --- a/cmd/list-service-principal-owners.go +++ b/cmd/list-service-principal-owners.go @@ -26,6 +26,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -96,13 +97,13 @@ func listServicePrincipalOwners(ctx context.Context, client client.AzureClient, } count = 0 ) - for item := range client.ListAzureADServicePrincipalOwners(ctx, id, "", "", "", nil) { + for item := range client.ListAzureADServicePrincipalOwners(ctx, id, query.GraphParams{}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing owners for this service principal", "servicePrincipalId", id) } else { servicePrincipalOwner := models.ServicePrincipalOwner{ Owner: item.Ok, - ServicePrincipalId: item.ServicePrincipalId, + ServicePrincipalId: id, } log.V(2).Info("found service principal owner", "servicePrincipalOwner", servicePrincipalOwner) count++ diff --git a/cmd/list-service-principal-owners_test.go b/cmd/list-service-principal-owners_test.go index 1dff1d1..61f6eb5 100644 --- a/cmd/list-service-principal-owners_test.go +++ b/cmd/list-service-principal-owners_test.go @@ -23,6 +23,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/models/azure" @@ -41,14 +42,14 @@ func TestListServicePrincipalOwners(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockServicePrincipalsChannel := make(chan interface{}) - mockServicePrincipalOwnerChannel := make(chan azure.ServicePrincipalOwnerResult) - mockServicePrincipalOwnerChannel2 := make(chan azure.ServicePrincipalOwnerResult) + mockServicePrincipalOwnerChannel := make(chan client.AzureResult[json.RawMessage]) + mockServicePrincipalOwnerChannel2 := make(chan client.AzureResult[json.RawMessage]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureADServicePrincipalOwners(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockServicePrincipalOwnerChannel).Times(1) - mockClient.EXPECT().ListAzureADServicePrincipalOwners(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockServicePrincipalOwnerChannel2).Times(1) + mockClient.EXPECT().ListAzureADServicePrincipalOwners(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockServicePrincipalOwnerChannel).Times(1) + mockClient.EXPECT().ListAzureADServicePrincipalOwners(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockServicePrincipalOwnerChannel2).Times(1) channel := listServicePrincipalOwners(ctx, mockClient, mockServicePrincipalsChannel) go func() { @@ -62,19 +63,19 @@ func TestListServicePrincipalOwners(t *testing.T) { }() go func() { defer close(mockServicePrincipalOwnerChannel) - mockServicePrincipalOwnerChannel <- azure.ServicePrincipalOwnerResult{ + mockServicePrincipalOwnerChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockServicePrincipalOwnerChannel <- azure.ServicePrincipalOwnerResult{ + mockServicePrincipalOwnerChannel <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } }() go func() { defer close(mockServicePrincipalOwnerChannel2) - mockServicePrincipalOwnerChannel2 <- azure.ServicePrincipalOwnerResult{ + mockServicePrincipalOwnerChannel2 <- client.AzureResult[json.RawMessage]{ Ok: json.RawMessage{}, } - mockServicePrincipalOwnerChannel2 <- azure.ServicePrincipalOwnerResult{ + mockServicePrincipalOwnerChannel2 <- client.AzureResult[json.RawMessage]{ Error: mockError, } }() diff --git a/cmd/list-service-principals.go b/cmd/list-service-principals.go index 57399bd..5d427af 100644 --- a/cmd/list-service-principals.go +++ b/cmd/list-service-principals.go @@ -24,6 +24,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -64,7 +65,7 @@ func listServicePrincipals(ctx context.Context, client client.AzureClient) <-cha defer panicrecovery.PanicRecovery() defer close(out) count := 0 - for item := range client.ListAzureADServicePrincipals(ctx, "", "", "", "", nil) { + for item := range client.ListAzureADServicePrincipals(ctx, query.GraphParams{}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing service principals") return diff --git a/cmd/list-service-principals_test.go b/cmd/list-service-principals_test.go index c62f028..b9058fa 100644 --- a/cmd/list-service-principals_test.go +++ b/cmd/list-service-principals_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models/azure" "go.uber.org/mock/gomock" @@ -37,21 +38,21 @@ func TestListServicePrincipals(t *testing.T) { ctx := context.Background() mockClient := mocks.NewMockAzureClient(ctrl) - mockChannel := make(chan azure.ServicePrincipalResult) + mockChannel := make(chan client.AzureResult[azure.ServicePrincipal]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureADServicePrincipals(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockChannel) + mockClient.EXPECT().ListAzureADServicePrincipals(gomock.Any(), gomock.Any()).Return(mockChannel) go func() { defer close(mockChannel) - mockChannel <- azure.ServicePrincipalResult{ + mockChannel <- client.AzureResult[azure.ServicePrincipal]{ Ok: azure.ServicePrincipal{}, } - mockChannel <- azure.ServicePrincipalResult{ + mockChannel <- client.AzureResult[azure.ServicePrincipal]{ Error: mockError, } - mockChannel <- azure.ServicePrincipalResult{ + mockChannel <- client.AzureResult[azure.ServicePrincipal]{ Ok: azure.ServicePrincipal{}, } }() diff --git a/cmd/list-storage-account-role-assignments.go b/cmd/list-storage-account-role-assignments.go index 71df35a..a1b2546 100644 --- a/cmd/list-storage-account-role-assignments.go +++ b/cmd/list-storage-account-role-assignments.go @@ -98,7 +98,7 @@ func listStorageAccountRoleAssignments(ctx context.Context, client client.AzureC } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this storage account", "storageAccountId", id) } else { @@ -106,7 +106,7 @@ func listStorageAccountRoleAssignments(ctx context.Context, client client.AzureC storageAccountRoleAssignment := models.AzureRoleAssignment{ Assignee: item.Ok, - ObjectId: item.ParentId, + ObjectId: id, RoleDefinitionId: roleDefinitionId, } log.V(2).Info("found storage account role assignment", "storageAccountRoleAssignment", storageAccountRoleAssignment) diff --git a/cmd/list-storage-accounts.go b/cmd/list-storage-accounts.go index 8c91c71..000ae9c 100644 --- a/cmd/list-storage-accounts.go +++ b/cmd/list-storage-accounts.go @@ -94,13 +94,11 @@ func listStorageAccounts(ctx context.Context, client client.AzureClient, subscri if item.Error != nil { log.Error(item.Error, "unable to continue processing storage accounts for this subscription", "subscriptionId", id) } else { - resourceGroupId := item.Ok.ResourceGroupId() - resourceGroupName := item.Ok.ResourceGroupName() storageAccount := models.StorageAccount{ StorageAccount: item.Ok, - SubscriptionId: item.SubscriptionId, - ResourceGroupId: resourceGroupId, - ResourceGroupName: resourceGroupName, + SubscriptionId: "/subscriptions/" + id, + ResourceGroupId: item.Ok.ResourceGroupId(), + ResourceGroupName: item.Ok.ResourceGroupName(), TenantId: client.TenantInfo().TenantId, } log.V(2).Info("found storage account", "storageAccount", storageAccount) diff --git a/cmd/list-storage-containers.go b/cmd/list-storage-containers.go index 322f456..ff0a494 100644 --- a/cmd/list-storage-containers.go +++ b/cmd/list-storage-containers.go @@ -101,14 +101,12 @@ func listStorageContainers(ctx context.Context, client client.AzureClient, stora if item.Error != nil { log.Error(item.Error, "unable to continue processing storage containers for this subscription", "subscriptionId", stAccount.(models.StorageAccount).SubscriptionId, "storageAccountName", stAccount.(models.StorageAccount).Name) } else { - resourceGroupId := item.Ok.ResourceGroupId() - resourceGroupName := item.Ok.ResourceGroupName() storageContainer := models.StorageContainer{ StorageContainer: item.Ok, StorageAccountId: stAccount.(models.StorageAccount).StorageAccount.Id, - SubscriptionId: item.SubscriptionId, - ResourceGroupId: resourceGroupId, - ResourceGroupName: resourceGroupName, + SubscriptionId: "/subscriptions/"+stAccount.(models.StorageAccount).SubscriptionId, + ResourceGroupId: item.Ok.ResourceGroupId(), + ResourceGroupName: item.Ok.ResourceGroupName(), TenantId: client.TenantInfo().TenantId, } log.V(2).Info("found storage container", "storageContainer", storageContainer) diff --git a/cmd/list-subscription-role-assignments.go b/cmd/list-subscription-role-assignments.go index 94279e3..b23421e 100644 --- a/cmd/list-subscription-role-assignments.go +++ b/cmd/list-subscription-role-assignments.go @@ -97,12 +97,12 @@ func listSubscriptionRoleAssignments(ctx context.Context, client client.AzureCli } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "atScope()") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "atScope()", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this subscription", "subscriptionId", id) } else { subscriptionRoleAssignment := models.SubscriptionRoleAssignment{ - SubscriptionId: item.ParentId, + SubscriptionId: id, RoleAssignment: item.Ok, } log.V(2).Info("found subscription role assignment", "subscriptionRoleAssignment", subscriptionRoleAssignment) diff --git a/cmd/list-subscription-role-assignments_test.go b/cmd/list-subscription-role-assignments_test.go index 1902684..73a74d8 100644 --- a/cmd/list-subscription-role-assignments_test.go +++ b/cmd/list-subscription-role-assignments_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models" @@ -41,14 +42,14 @@ func TestListSubscriptionRoleAssignments(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockSubscriptionsChannel := make(chan interface{}) - mockSubscriptionRoleAssignmentChannel := make(chan azure.RoleAssignmentResult) - mockSubscriptionRoleAssignmentChannel2 := make(chan azure.RoleAssignmentResult) + mockSubscriptionRoleAssignmentChannel := make(chan client.AzureResult[azure.RoleAssignment]) + mockSubscriptionRoleAssignmentChannel2 := make(chan client.AzureResult[azure.RoleAssignment]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockSubscriptionRoleAssignmentChannel).Times(1) - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockSubscriptionRoleAssignmentChannel2).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockSubscriptionRoleAssignmentChannel).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockSubscriptionRoleAssignmentChannel2).Times(1) channel := listSubscriptionRoleAssignments(ctx, mockClient, mockSubscriptionsChannel) go func() { @@ -62,14 +63,14 @@ func TestListSubscriptionRoleAssignments(t *testing.T) { }() go func() { defer close(mockSubscriptionRoleAssignmentChannel) - mockSubscriptionRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockSubscriptionRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.ContributorRoleID, }, }, } - mockSubscriptionRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockSubscriptionRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.OwnerRoleID, @@ -79,14 +80,14 @@ func TestListSubscriptionRoleAssignments(t *testing.T) { }() go func() { defer close(mockSubscriptionRoleAssignmentChannel2) - mockSubscriptionRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockSubscriptionRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.OwnerRoleID, }, }, } - mockSubscriptionRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockSubscriptionRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Error: mockError, } }() diff --git a/cmd/list-subscriptions_test.go b/cmd/list-subscriptions_test.go index 51571c5..3e9f8be 100644 --- a/cmd/list-subscriptions_test.go +++ b/cmd/list-subscriptions_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models/azure" "go.uber.org/mock/gomock" @@ -37,7 +38,7 @@ func TestListSubscriptions(t *testing.T) { ctx := context.Background() mockClient := mocks.NewMockAzureClient(ctrl) - mockChannel := make(chan azure.SubscriptionResult) + mockChannel := make(chan client.AzureResult[azure.Subscription]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() @@ -45,13 +46,13 @@ func TestListSubscriptions(t *testing.T) { go func() { defer close(mockChannel) - mockChannel <- azure.SubscriptionResult{ + mockChannel <- client.AzureResult[azure.Subscription]{ Ok: azure.Subscription{}, } - mockChannel <- azure.SubscriptionResult{ + mockChannel <- client.AzureResult[azure.Subscription]{ Error: mockError, } - mockChannel <- azure.SubscriptionResult{ + mockChannel <- client.AzureResult[azure.Subscription]{ Ok: azure.Subscription{}, } }() diff --git a/cmd/list-tenants_test.go b/cmd/list-tenants_test.go index 5f22399..c0648e3 100644 --- a/cmd/list-tenants_test.go +++ b/cmd/list-tenants_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models/azure" "go.uber.org/mock/gomock" @@ -37,7 +38,7 @@ func TestListTenants(t *testing.T) { ctx := context.Background() mockClient := mocks.NewMockAzureClient(ctrl) - mockChannel := make(chan azure.TenantResult) + mockChannel := make(chan client.AzureResult[azure.Tenant]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() @@ -45,13 +46,13 @@ func TestListTenants(t *testing.T) { go func() { defer close(mockChannel) - mockChannel <- azure.TenantResult{ + mockChannel <- client.AzureResult[azure.Tenant]{ Ok: azure.Tenant{}, } - mockChannel <- azure.TenantResult{ + mockChannel <- client.AzureResult[azure.Tenant]{ Error: mockError, } - mockChannel <- azure.TenantResult{ + mockChannel <- client.AzureResult[azure.Tenant]{ Ok: azure.Tenant{}, } }() diff --git a/cmd/list-users.go b/cmd/list-users.go index 531b7a3..be94d8a 100644 --- a/cmd/list-users.go +++ b/cmd/list-users.go @@ -24,6 +24,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -42,7 +43,7 @@ var listUsersCmd = &cobra.Command{ SilenceUsage: true, } -func listUsersCmdImpl(cmd *cobra.Command, args []string) { +func listUsersCmdImpl(cmd *cobra.Command, _ []string) { ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, os.Kill) defer gracefulShutdown(stop) @@ -60,23 +61,25 @@ func listUsersCmdImpl(cmd *cobra.Command, args []string) { func listUsers(ctx context.Context, client client.AzureClient) <-chan interface{} { out := make(chan interface{}) + params := query.GraphParams{Select: []string{ + "accountEnabled", + "createdDateTime", + "displayName", + "jobTitle", + "lastPasswordChangeDateTime", + "mail", + "onPremisesSecurityIdentifier", + "onPremisesSyncEnabled", + "userPrincipalName", + "userType", + "id", + }} + go func() { defer panicrecovery.PanicRecovery() defer close(out) count := 0 - for item := range client.ListAzureADUsers(ctx, "", "", "", []string{ - "accountEnabled", - "createdDateTime", - "displayName", - "jobTitle", - "lastPasswordChangeDateTime", - "mail", - "onPremisesSecurityIdentifier", - "onPremisesSyncEnabled", - "userPrincipalName", - "userType", - "id", - }) { + for item := range client.ListAzureADUsers(ctx, params) { if item.Error != nil { log.Error(item.Error, "unable to continue processing users") return diff --git a/cmd/list-users_test.go b/cmd/list-users_test.go index d7e6211..59028e0 100644 --- a/cmd/list-users_test.go +++ b/cmd/list-users_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models/azure" "go.uber.org/mock/gomock" @@ -38,21 +39,21 @@ func TestListUsers(t *testing.T) { ctx := context.Background() mockClient := mocks.NewMockAzureClient(ctrl) - mockChannel := make(chan azure.UserResult) + mockChannel := make(chan client.AzureResult[azure.User]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListAzureADUsers(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockChannel) + mockClient.EXPECT().ListAzureADUsers(gomock.Any(), gomock.Any()).Return(mockChannel) go func() { defer close(mockChannel) - mockChannel <- azure.UserResult{ + mockChannel <- client.AzureResult[azure.User]{ Ok: azure.User{}, } - mockChannel <- azure.UserResult{ + mockChannel <- client.AzureResult[azure.User]{ Error: mockError, } - mockChannel <- azure.UserResult{ + mockChannel <- client.AzureResult[azure.User]{ Ok: azure.User{}, } }() diff --git a/cmd/list-virtual-machine-role-assignments.go b/cmd/list-virtual-machine-role-assignments.go index 097055e..389cafd 100644 --- a/cmd/list-virtual-machine-role-assignments.go +++ b/cmd/list-virtual-machine-role-assignments.go @@ -97,12 +97,12 @@ func listVirtualMachineRoleAssignments(ctx context.Context, client client.AzureC } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this virtual machine", "virtualMachineId", id) } else { virtualMachineRoleAssignment := models.VirtualMachineRoleAssignment{ - VirtualMachineId: item.ParentId, + VirtualMachineId: id, RoleAssignment: item.Ok, } log.V(2).Info("found virtual machine role assignment", "virtualMachineRoleAssignment", virtualMachineRoleAssignment) diff --git a/cmd/list-virtual-machine-role-assignments_test.go b/cmd/list-virtual-machine-role-assignments_test.go index dffaf79..efd5112 100644 --- a/cmd/list-virtual-machine-role-assignments_test.go +++ b/cmd/list-virtual-machine-role-assignments_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/constants" "github.com/bloodhoundad/azurehound/v2/models" @@ -41,14 +42,14 @@ func TestListVirtualMachineRoleAssignments(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockVirtualMachinesChannel := make(chan interface{}) - mockVirtualMachineRoleAssignmentChannel := make(chan azure.RoleAssignmentResult) - mockVirtualMachineRoleAssignmentChannel2 := make(chan azure.RoleAssignmentResult) + mockVirtualMachineRoleAssignmentChannel := make(chan client.AzureResult[azure.RoleAssignment]) + mockVirtualMachineRoleAssignmentChannel2 := make(chan client.AzureResult[azure.RoleAssignment]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") mockClient.EXPECT().TenantInfo().Return(mockTenant).AnyTimes() - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockVirtualMachineRoleAssignmentChannel).Times(1) - mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockVirtualMachineRoleAssignmentChannel2).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockVirtualMachineRoleAssignmentChannel).Times(1) + mockClient.EXPECT().ListRoleAssignmentsForResource(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockVirtualMachineRoleAssignmentChannel2).Times(1) channel := listVirtualMachineRoleAssignments(ctx, mockClient, mockVirtualMachinesChannel) go func() { @@ -62,14 +63,14 @@ func TestListVirtualMachineRoleAssignments(t *testing.T) { }() go func() { defer close(mockVirtualMachineRoleAssignmentChannel) - mockVirtualMachineRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockVirtualMachineRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.VirtualMachineContributorRoleID, }, }, } - mockVirtualMachineRoleAssignmentChannel <- azure.RoleAssignmentResult{ + mockVirtualMachineRoleAssignmentChannel <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.AvereContributorRoleID, @@ -79,14 +80,14 @@ func TestListVirtualMachineRoleAssignments(t *testing.T) { }() go func() { defer close(mockVirtualMachineRoleAssignmentChannel2) - mockVirtualMachineRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockVirtualMachineRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Ok: azure.RoleAssignment{ Properties: azure.RoleAssignmentPropertiesWithScope{ RoleDefinitionId: constants.VirtualMachineAdministratorLoginRoleID, }, }, } - mockVirtualMachineRoleAssignmentChannel2 <- azure.RoleAssignmentResult{ + mockVirtualMachineRoleAssignmentChannel2 <- client.AzureResult[azure.RoleAssignment]{ Error: mockError, } }() diff --git a/cmd/list-virtual-machines.go b/cmd/list-virtual-machines.go index d28d6e5..6e4c77e 100644 --- a/cmd/list-virtual-machines.go +++ b/cmd/list-virtual-machines.go @@ -26,6 +26,7 @@ import ( "time" "github.com/bloodhoundad/azurehound/v2/client" + "github.com/bloodhoundad/azurehound/v2/client/query" "github.com/bloodhoundad/azurehound/v2/enums" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/panicrecovery" @@ -90,15 +91,14 @@ func listVirtualMachines(ctx context.Context, client client.AzureClient, subscri defer wg.Done() for id := range stream { count := 0 - for item := range client.ListAzureVirtualMachines(ctx, id, false) { + for item := range client.ListAzureVirtualMachines(ctx, id, query.RMParams{}) { if item.Error != nil { log.Error(item.Error, "unable to continue processing virtual machines for this subscription", "subscriptionId", id) } else { - resourceGroupId := item.Ok.ResourceGroupId() virtualMachine := models.VirtualMachine{ VirtualMachine: item.Ok, - SubscriptionId: item.SubscriptionId, - ResourceGroupId: resourceGroupId, + SubscriptionId: "/subscriptions/" + id, + ResourceGroupId: item.Ok.ResourceGroupId(), TenantId: client.TenantInfo().TenantId, } log.V(2).Info("found virtual machine", "virtualMachine", virtualMachine) diff --git a/cmd/list-virtual-machines_test.go b/cmd/list-virtual-machines_test.go index 58b9113..b955d84 100644 --- a/cmd/list-virtual-machines_test.go +++ b/cmd/list-virtual-machines_test.go @@ -22,6 +22,7 @@ import ( "fmt" "testing" + "github.com/bloodhoundad/azurehound/v2/client" "github.com/bloodhoundad/azurehound/v2/client/mocks" "github.com/bloodhoundad/azurehound/v2/models" "github.com/bloodhoundad/azurehound/v2/models/azure" @@ -40,8 +41,8 @@ func TestListVirtualMachines(t *testing.T) { mockClient := mocks.NewMockAzureClient(ctrl) mockSubscriptionsChannel := make(chan interface{}) - mockVirtualMachineChannel := make(chan azure.VirtualMachineResult) - mockVirtualMachineChannel2 := make(chan azure.VirtualMachineResult) + mockVirtualMachineChannel := make(chan client.AzureResult[azure.VirtualMachine]) + mockVirtualMachineChannel2 := make(chan client.AzureResult[azure.VirtualMachine]) mockTenant := azure.Tenant{} mockError := fmt.Errorf("I'm an error") @@ -61,19 +62,19 @@ func TestListVirtualMachines(t *testing.T) { }() go func() { defer close(mockVirtualMachineChannel) - mockVirtualMachineChannel <- azure.VirtualMachineResult{ + mockVirtualMachineChannel <- client.AzureResult[azure.VirtualMachine]{ Ok: azure.VirtualMachine{}, } - mockVirtualMachineChannel <- azure.VirtualMachineResult{ + mockVirtualMachineChannel <- client.AzureResult[azure.VirtualMachine]{ Ok: azure.VirtualMachine{}, } }() go func() { defer close(mockVirtualMachineChannel2) - mockVirtualMachineChannel2 <- azure.VirtualMachineResult{ + mockVirtualMachineChannel2 <- client.AzureResult[azure.VirtualMachine]{ Ok: azure.VirtualMachine{}, } - mockVirtualMachineChannel2 <- azure.VirtualMachineResult{ + mockVirtualMachineChannel2 <- client.AzureResult[azure.VirtualMachine]{ Error: mockError, } }() diff --git a/cmd/list-vm-scale-set-role-assignments.go b/cmd/list-vm-scale-set-role-assignments.go index 4a82b2e..34841c9 100644 --- a/cmd/list-vm-scale-set-role-assignments.go +++ b/cmd/list-vm-scale-set-role-assignments.go @@ -103,7 +103,7 @@ func listVMScaleSetRoleAssignments(ctx context.Context, client client.AzureClien } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this vm scale set", "vmScaleSetId", id) } else { @@ -111,7 +111,7 @@ func listVMScaleSetRoleAssignments(ctx context.Context, client client.AzureClien vmScaleSetRoleAssignment := models.AzureRoleAssignment{ Assignee: item.Ok, - ObjectId: item.ParentId, + ObjectId: id, RoleDefinitionId: roleDefinitionId, } log.V(2).Info("found vm scale set role assignment", "vmScaleSetRoleAssignment", vmScaleSetRoleAssignment) diff --git a/cmd/list-vm-scale-sets.go b/cmd/list-vm-scale-sets.go index c55f4b2..2ace333 100644 --- a/cmd/list-vm-scale-sets.go +++ b/cmd/list-vm-scale-sets.go @@ -96,15 +96,14 @@ func listVMScaleSets(ctx context.Context, client client.AzureClient, subscriptio defer wg.Done() for id := range stream { count := 0 - for item := range client.ListAzureVMScaleSets(ctx, id, false) { + for item := range client.ListAzureVMScaleSets(ctx, id) { if item.Error != nil { log.Error(item.Error, "unable to continue processing virtual machine scale sets for this subscription", "subscriptionId", id) } else { - resourceGroupId := item.Ok.ResourceGroupId() vmScaleSet := models.VMScaleSet{ VMScaleSet: item.Ok, - SubscriptionId: item.SubscriptionId, - ResourceGroupId: resourceGroupId, + SubscriptionId: "/subscriptions/"+id, + ResourceGroupId: item.Ok.ResourceGroupId(), TenantId: client.TenantInfo().TenantId, } log.V(2).Info("found virtual machine scale set", "vmScaleSet", vmScaleSet) diff --git a/cmd/list-web-app-role-assignments.go b/cmd/list-web-app-role-assignments.go index 90ce0ef..e4d41c2 100644 --- a/cmd/list-web-app-role-assignments.go +++ b/cmd/list-web-app-role-assignments.go @@ -103,7 +103,7 @@ func listWebAppRoleAssignments(ctx context.Context, client client.AzureClient, w } count = 0 ) - for item := range client.ListRoleAssignmentsForResource(ctx, id, "") { + for item := range client.ListRoleAssignmentsForResource(ctx, id, "", "") { if item.Error != nil { log.Error(item.Error, "unable to continue processing role assignments for this web app", "webAppId", id) } else { @@ -111,7 +111,7 @@ func listWebAppRoleAssignments(ctx context.Context, client client.AzureClient, w webAppRoleAssignment := models.AzureRoleAssignment{ Assignee: item.Ok, - ObjectId: item.ParentId, + ObjectId: id, RoleDefinitionId: roleDefinitionId, } log.V(2).Info("Found web app role asignment", "webAppRoleAssignment", webAppRoleAssignment) diff --git a/cmd/list-web-apps.go b/cmd/list-web-apps.go index ac2aaec..ec53067 100644 --- a/cmd/list-web-apps.go +++ b/cmd/list-web-apps.go @@ -99,12 +99,12 @@ func listWebApps(ctx context.Context, client client.AzureClient, subscriptions < if item.Error != nil { log.Error(item.Error, "unable to continue processing web apps for this subscription", "subscriptionId", id) } else { - resourceGroupId := item.Ok.ResourceGroupId() webApp := models.WebApp{ - WebApp: item.Ok, - SubscriptionId: item.SubscriptionId, - ResourceGroupId: resourceGroupId, - TenantId: client.TenantInfo().TenantId, + WebApp: item.Ok, + SubscriptionId: "/subscriptions/" + id, + ResourceGroupId: item.Ok.ResourceGroupId(), + ResourceGroupName: item.Ok.ResourceGroupName(), + TenantId: client.TenantInfo().TenantId, } if webApp.Kind == "app" { log.V(2).Info("found web app", "webApp", webApp) diff --git a/go.mod b/go.mod index f364545..7ac6e40 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/judwhite/go-svc v1.2.1 github.com/manifoldco/promptui v0.9.0 github.com/rs/zerolog v1.26.0 - github.com/spf13/cobra v1.3.0 + github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.10.1 github.com/stretchr/testify v1.7.0 @@ -24,7 +24,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/pelletier/go-toml v1.9.4 // indirect @@ -34,8 +34,10 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect golang.org/x/crypto v0.21.0 // indirect + golang.org/x/mod v0.8.0 // indirect golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.6.0 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index bbbb2cb..caf00fb 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,7 @@ github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWH github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -236,6 +237,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -332,6 +335,8 @@ github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -424,6 +429,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -636,6 +643,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -784,6 +793,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= @@ -803,6 +813,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/models/azure/app_role_assignment.go b/models/azure/app_role_assignment.go index 59c0234..708a4d8 100644 --- a/models/azure/app_role_assignment.go +++ b/models/azure/app_role_assignment.go @@ -37,15 +37,3 @@ type AppRoleAssignment struct { ResourceDisplayName string `json:"resourceDisplayName,omitempty"` ResourceId string `json:"resourceId,omitempty"` } - -type AppRoleAssignmentList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Context string `json:"@odata.context,omitempty"` - Value []AppRoleAssignment `json:"value"` // A list of role assignments. -} - -type AppRoleAssignmentResult struct { - Error error - Ok AppRoleAssignment -} diff --git a/models/azure/application.go b/models/azure/application.go index cec69cf..a44a103 100644 --- a/models/azure/application.go +++ b/models/azure/application.go @@ -17,8 +17,6 @@ package azure -import "encoding/json" - // Represents an application. Any application that outsources authentication to Azure Active Directory (Azure AD) must // be registered in a directory. Application registration involves telling Azure AD about your application, including // the URL where it's located, the URL to send replies after authentication, the URI to identify your application, and @@ -171,20 +169,3 @@ type Application struct { // Specifies settings for a web application. Web WebApplication `json:"web,omitempty"` } - -type ApplicationList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []Application `json:"value"` // A list of applications. -} - -type ApplicationResult struct { - Error error - Ok Application -} - -type AppOwnerResult struct { - AppId string - Error error - Ok json.RawMessage -} diff --git a/models/azure/automation_account.go b/models/azure/automation_account.go index 9e494da..c952a38 100644 --- a/models/azure/automation_account.go +++ b/models/azure/automation_account.go @@ -50,14 +50,3 @@ func (s AutomationAccount) ResourceGroupId() string { return "" } } - -type AutomationAccountList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []AutomationAccount `json:"value"` // A list of automation accounts. -} - -type AutomationAccountResult struct { - SubscriptionId string - Error error - Ok AutomationAccount -} diff --git a/models/azure/container_registry.go b/models/azure/container_registry.go index 68d560f..d476d48 100644 --- a/models/azure/container_registry.go +++ b/models/azure/container_registry.go @@ -47,14 +47,3 @@ func (s ContainerRegistry) ResourceGroupId() string { return "" } } - -type ContainerRegistryList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []ContainerRegistry `json:"value"` // A list of container registries. -} - -type ContainerRegistryResult struct { - SubscriptionId string - Error error - Ok ContainerRegistry -} diff --git a/models/azure/descendant-info.go b/models/azure/descendant-info.go index 003cf91..7f2d400 100644 --- a/models/azure/descendant-info.go +++ b/models/azure/descendant-info.go @@ -60,13 +60,3 @@ type DescendantInfo struct { // - /subscriptions Type string `json:"type,omitempty"` } - -type DescendantInfoList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []DescendantInfo `json:"value"` // A list of management group descendants. -} - -type DescendantInfoResult struct { - Error error - Ok DescendantInfo -} diff --git a/models/azure/device.go b/models/azure/device.go index 503f022..64fffe0 100644 --- a/models/azure/device.go +++ b/models/azure/device.go @@ -18,8 +18,6 @@ package azure import ( - "encoding/json" - "github.com/bloodhoundad/azurehound/v2/enums" ) @@ -131,20 +129,3 @@ type Device struct { // Read-only. TrustType enums.TrustType `json:"trustType,omitempty"` } - -type DeviceList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []Device `json:"value"` // A list of devices. -} - -type DeviceResult struct { - Error error - Ok Device -} - -type DeviceRegisteredOwnerResult struct { - DeviceId string - Error error - Ok json.RawMessage -} diff --git a/models/azure/directory_object.go b/models/azure/directory_object.go index 68e1aa7..149b62e 100644 --- a/models/azure/directory_object.go +++ b/models/azure/directory_object.go @@ -17,10 +17,6 @@ package azure -import ( - "encoding/json" -) - // Represents an Azure Active Directory object. The directoryObject type is the base type for many other directory entity types. type DirectoryObject struct { // The unique identifier for the object. @@ -33,9 +29,3 @@ type DirectoryObject struct { Type string `json:"@odata.type,omitempty"` } - -type DirectoryObjectList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []json.RawMessage `json:"value"` // A list of various Azure AD directory objects. -} diff --git a/models/azure/function_app.go b/models/azure/function_app.go index 92f394e..ab1fc51 100644 --- a/models/azure/function_app.go +++ b/models/azure/function_app.go @@ -49,14 +49,3 @@ func (s FunctionApp) ResourceGroupId() string { return "" } } - -type FunctionAppList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []FunctionApp `json:"value"` // A list of function apps -} - -type FunctionAppResult struct { - SubscriptionId string - Error error - Ok FunctionApp -} diff --git a/models/azure/group.go b/models/azure/group.go index 28fb6d1..de58049 100644 --- a/models/azure/group.go +++ b/models/azure/group.go @@ -18,8 +18,6 @@ package azure import ( - "encoding/json" - "github.com/bloodhoundad/azurehound/v2/enums" ) @@ -281,20 +279,3 @@ type Group struct { // Nullable. Visibility enums.GroupVisibility `json:"visibility,omitempty"` } - -type GroupList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []Group `json:"value"` // A list of groups. -} - -type GroupResult struct { - Error error - Ok Group -} - -type GroupOwnerResult struct { - Error error - GroupId string - Ok json.RawMessage -} diff --git a/models/azure/key_vault.go b/models/azure/key_vault.go index 841eebe..0588310 100644 --- a/models/azure/key_vault.go +++ b/models/azure/key_vault.go @@ -56,14 +56,3 @@ func (s KeyVault) ResourceGroupId() string { return "" } } - -type KeyVaultList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []KeyVault `json:"value"` // A list of key vaults. -} - -type KeyVaultResult struct { - SubscriptionId string - Error error - Ok KeyVault -} diff --git a/models/azure/logic_app.go b/models/azure/logic_app.go index 3dcfa9a..67d8527 100644 --- a/models/azure/logic_app.go +++ b/models/azure/logic_app.go @@ -47,14 +47,3 @@ func (s LogicApp) ResourceGroupId() string { return "" } } - -type LogicAppList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []LogicApp `json:"value"` // A list of logic apps. -} - -type LogicAppResult struct { - SubscriptionId string - Error error - Ok LogicApp -} diff --git a/models/azure/managed_cluster.go b/models/azure/managed_cluster.go index 1502bbc..e0aa74c 100644 --- a/models/azure/managed_cluster.go +++ b/models/azure/managed_cluster.go @@ -50,14 +50,3 @@ func (s ManagedCluster) ResourceGroupId() string { return "" } } - -type ManagedClusterList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []ManagedCluster `json:"value"` // A list of managed clusters. -} - -type ManagedClusterResult struct { - SubscriptionId string - Error error - Ok ManagedCluster -} diff --git a/models/azure/management_group.go b/models/azure/management_group.go index 91f5231..761c7f5 100644 --- a/models/azure/management_group.go +++ b/models/azure/management_group.go @@ -29,13 +29,3 @@ type ManagementGroup struct { // The type of resource: "Microsoft.Management/managementGroups" Type string `json:"type,omitempty"` } - -type ManagementGroupList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []ManagementGroup `json:"value"` // A list of tenants. -} - -type ManagementGroupResult struct { - Error error - Ok ManagementGroup -} diff --git a/models/azure/member_object.go b/models/azure/member_object.go deleted file mode 100644 index 40b14e5..0000000 --- a/models/azure/member_object.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2022 Specter Ops, Inc. -// -// This file is part of AzureHound. -// -// AzureHound is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// AzureHound is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package azure - -import "encoding/json" - -type MemberObjectList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Context string `json:"@odata.context,omitempty"` - Value []json.RawMessage `json:"value"` -} - -type MemberObjectResult struct { - ParentId string - ParentType string - Error error - Ok json.RawMessage -} diff --git a/models/azure/resource_group.go b/models/azure/resource_group.go index 43604b0..3f73076 100644 --- a/models/azure/resource_group.go +++ b/models/azure/resource_group.go @@ -39,14 +39,3 @@ type ResourceGroup struct { // The type of the resource group. Type string `json:"type,omitempty"` } - -type ResourceGroupList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []ResourceGroup `json:"value"` // A list of tenants. -} - -type ResourceGroupResult struct { - SubscriptionId string - Error error - Ok ResourceGroup -} diff --git a/models/azure/role.go b/models/azure/role.go index 5f35dd0..30a25c1 100644 --- a/models/azure/role.go +++ b/models/azure/role.go @@ -67,14 +67,3 @@ type Role struct { // Read-only when isBuiltIn is true Version string `json:"version,omitempty"` } - -type RoleList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []Role `json:"value"` // A list of roles. -} - -type RoleResult struct { - Error error - Ok Role -} diff --git a/models/azure/role_assignment.go b/models/azure/role_assignment.go index 10948c3..07ea735 100644 --- a/models/azure/role_assignment.go +++ b/models/azure/role_assignment.go @@ -42,20 +42,6 @@ type RoleAssignment struct { Properties RoleAssignmentPropertiesWithScope `json:"properties,omitempty"` } -type RoleAssignmentList struct { - // The URL to use for getting the next set of results. - NextLink string `json:"nextLink,omitempty"` - - // The role assignment list. - Value []RoleAssignment `json:"value"` -} - -type RoleAssignmentResult struct { - ParentId string - Error error - Ok RoleAssignment -} - func (s RoleAssignment) GetPrincipalId() string { return s.Properties.PrincipalId } diff --git a/models/azure/service_principal.go b/models/azure/service_principal.go index 3a422c0..5ba521b 100644 --- a/models/azure/service_principal.go +++ b/models/azure/service_principal.go @@ -18,8 +18,6 @@ package azure import ( - "encoding/json" - "github.com/bloodhoundad/azurehound/v2/enums" ) @@ -179,20 +177,3 @@ type ServicePrincipal struct { // Specifies the verified publisher of the application which this service principal represents. VerifiedPublisher VerifiedPublisher `json:"verifiedPublisher,omitempty"` } - -type ServicePrincipalList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []ServicePrincipal `json:"value"` // A list of ServicePrincipals. -} - -type ServicePrincipalResult struct { - Error error - Ok ServicePrincipal -} - -type ServicePrincipalOwnerResult struct { - Error error - ServicePrincipalId string - Ok json.RawMessage -} diff --git a/models/azure/storage_account.go b/models/azure/storage_account.go index 6e2d0ad..a422080 100644 --- a/models/azure/storage_account.go +++ b/models/azure/storage_account.go @@ -50,14 +50,3 @@ func (s StorageAccount) ResourceGroupId() string { return "" } } - -type StorageAccountList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []StorageAccount `json:"value"` // A list of storage accounts. -} - -type StorageAccountResult struct { - SubscriptionId string - Error error - Ok StorageAccount -} diff --git a/models/azure/storage_container.go b/models/azure/storage_container.go index 7e841cf..5a22612 100644 --- a/models/azure/storage_container.go +++ b/models/azure/storage_container.go @@ -63,14 +63,3 @@ func (s StorageContainer) StorageAccountId() string { return "" } } - -type StorageContainerList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []StorageContainer `json:"value"` // A list of storage containers. -} - -type StorageContainerResult struct { - SubscriptionId string - Error error - Ok StorageContainer -} diff --git a/models/azure/subscription.go b/models/azure/subscription.go index 30a710c..b7f32c0 100644 --- a/models/azure/subscription.go +++ b/models/azure/subscription.go @@ -49,13 +49,3 @@ type Subscription struct { // The subscription tenant ID. TenantId string `json:"tenantId,omitempty"` } - -type SubscriptionList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []Subscription `json:"value"` // A list of subscriptions. -} - -type SubscriptionResult struct { - Error error - Ok Subscription -} diff --git a/models/azure/tenant.go b/models/azure/tenant.go index e0d5030..727e0c5 100644 --- a/models/azure/tenant.go +++ b/models/azure/tenant.go @@ -36,8 +36,3 @@ type TenantList struct { NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. Value []Tenant `json:"value"` // A list of tenants. } - -type TenantResult struct { - Error error - Ok Tenant -} diff --git a/models/azure/unified_role_assignment.go b/models/azure/unified_role_assignment.go index 6027e59..fc60a5f 100644 --- a/models/azure/unified_role_assignment.go +++ b/models/azure/unified_role_assignment.go @@ -74,14 +74,3 @@ type UnifiedRoleAssignment struct { // Supports $expand. AppScope AppScope `json:"appScope,omitempty"` } - -type UnifiedRoleAssignmentList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []UnifiedRoleAssignment `json:"value"` // A list of role assignments. -} - -type UnifiedRoleAssignmentResult struct { - Error error - Ok UnifiedRoleAssignment -} diff --git a/models/azure/user.go b/models/azure/user.go index 8106d12..301b5d7 100644 --- a/models/azure/user.go +++ b/models/azure/user.go @@ -465,14 +465,3 @@ type User struct { // Supports $filter (eq, ne, NOT, in). UserType string `json:"userType,omitempty"` } - -type UserList struct { - Count int `json:"@odata.count,omitempty"` // The total count of all results - NextLink string `json:"@odata.nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []User `json:"value"` // A list of users. -} - -type UserResult struct { - Error error - Ok User -} diff --git a/models/azure/virtual_machine.go b/models/azure/virtual_machine.go index 4c996b7..64cbb48 100644 --- a/models/azure/virtual_machine.go +++ b/models/azure/virtual_machine.go @@ -51,14 +51,3 @@ func (s VirtualMachine) ResourceGroupId() string { return "" } } - -type VirtualMachineList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []VirtualMachine `json:"value"` // A list of virtual machines. -} - -type VirtualMachineResult struct { - SubscriptionId string - Error error - Ok VirtualMachine -} diff --git a/models/azure/vm_scale_set.go b/models/azure/vm_scale_set.go index 59b8edd..3d89b1f 100644 --- a/models/azure/vm_scale_set.go +++ b/models/azure/vm_scale_set.go @@ -49,14 +49,3 @@ func (s VMScaleSet) ResourceGroupId() string { return "" } } - -type VMScaleSetList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []VMScaleSet `json:"value"` // A list of virtual machine scale sets. -} - -type VMScaleSetResult struct { - SubscriptionId string - Error error - Ok VMScaleSet -} diff --git a/models/azure/web_app.go b/models/azure/web_app.go index 128bc85..9ef401d 100644 --- a/models/azure/web_app.go +++ b/models/azure/web_app.go @@ -48,14 +48,3 @@ func (s WebApp) ResourceGroupId() string { return "" } } - -type WebAppList struct { - NextLink string `json:"nextLink,omitempty"` // The URL to use for getting the next set of values. - Value []WebApp `json:"value"` // A list of web apps. -} - -type WebAppResult struct { - SubscriptionId string - Error error - Ok WebApp -}