Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(paginate): Paginate returned Cert Stores in Discovery job & Script #22

Open
wants to merge 2 commits into
base: release-4.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 149 additions & 29 deletions AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -485,30 +485,60 @@ public OperationResult<IEnumerable<string>> DiscoverApplicationObjectIds()
List<string> oids = new();
OperationResult<IEnumerable<string>> result = new(oids);

_logger.LogDebug($"Retrieving application registrations for tenant ID \"{_tenantId}\"");
ApplicationCollectionResponse apps;
_logger.LogDebug($"Retrieving Applications for tenant ID \"{_tenantId}\"");
List<Application> allApplications = new List<Application>();
try
{
apps = _graphClient.Applications.GetAsync((requestConfiguration) =>
var spsResponse = _graphClient.Applications.GetAsync(requestConfiguration =>
{
requestConfiguration.QueryParameters.Top = 500;
}).Result;

if (spsResponse?.Value == null)
{
_logger.LogWarning("No Applications found.");
return result;
}

// Create the PageIterator to handle pagination
var pageIterator = PageIterator<Application, ApplicationCollectionResponse>
.CreatePageIterator(
_graphClient,
spsResponse,
// Callback executed for each Application in the collection
(application) =>
{
requestConfiguration.QueryParameters.Top = 999;
}).Result;
// Add each application to the list
allApplications.Add(application);
return true; // Continue to the next item
},
// Configure subsequent page requests
(request) =>
{
_logger.LogDebug("Fetching the next page of applications...");
return request;
});

// Execute the pagination
pageIterator.IterateAsync().Wait();

_logger.LogInformation($"Successfully retrieved {allApplications.Count} applications.");
}
catch (AggregateException e)
{
_logger.LogError($"Unable to retrieve application registrations for tenant ID \"{_tenantId}\": {e}");
throw;
}

if (apps?.Value == null || apps.Value.Count == 0)
if (allApplications.Count == 0)
{
_logger.LogWarning($"No application registrations found for tenant ID \"{_tenantId}\"");
_logger.LogWarning($"No Applications found for tenant ID \"{_tenantId}\"");
return result;
}

foreach (Application app in apps.Value)
foreach (Application app in allApplications)
{
_logger.LogDebug($"Found application \"{app.DisplayName}\" ({app.Id})");
_logger.LogDebug($"Found Application \"{app.DisplayName}\" ({app.Id})");

if (string.IsNullOrEmpty(app.Id))
{
Expand All @@ -529,27 +559,57 @@ public OperationResult<IEnumerable<string>> DiscoverServicePrincipalObjectIds()
OperationResult<IEnumerable<string>> result = new(oids);

_logger.LogDebug($"Retrieving Service Principals for tenant ID \"{_tenantId}\"");
ServicePrincipalCollectionResponse sps;
List<ServicePrincipal> allServicePrincipals = new List<ServicePrincipal>();
try
{
sps = _graphClient.ServicePrincipals.GetAsync((requestConfiguration) =>
var spsResponse = _graphClient.ServicePrincipals.GetAsync(requestConfiguration =>
{
requestConfiguration.QueryParameters.Top = 999;
requestConfiguration.QueryParameters.Top = 500;
}).Result;

if (spsResponse?.Value == null)
{
_logger.LogWarning("No service principals found.");
return result;
}

// Create the PageIterator to handle pagination
var pageIterator = PageIterator<ServicePrincipal, ServicePrincipalCollectionResponse>
.CreatePageIterator(
_graphClient,
spsResponse,
// Callback executed for each Service Principal in the collection
(servicePrincipal) =>
{
// Add each service principal to the list
allServicePrincipals.Add(servicePrincipal);
return true; // Continue to the next item
},
// Configure subsequent page requests
(request) =>
{
_logger.LogDebug("Fetching the next page of service principals...");
return request;
});

// Execute the pagination
pageIterator.IterateAsync().Wait();

_logger.LogInformation($"Successfully retrieved {allServicePrincipals.Count} service principals.");
}
catch (AggregateException e)
{
_logger.LogError($"Unable to retrieve Service Principals for tenant ID \"{_tenantId}\": {e}");
throw;
}

if (sps?.Value == null || sps.Value.Count == 0)
if (allServicePrincipals.Count == 0)
{
_logger.LogWarning($"No Service Principals found for tenant ID \"{_tenantId}\"");
return result;
}

foreach (ServicePrincipal sp in sps.Value)
foreach (ServicePrincipal sp in allServicePrincipals)
{
_logger.LogDebug($"Found SP \"{sp.DisplayName}\" ({sp.Id})");

Expand All @@ -571,30 +631,60 @@ public OperationResult<IEnumerable<string>> DiscoverApplicationApplicationIds()
List<string> appIds = new();
OperationResult<IEnumerable<string>> result = new(appIds);

_logger.LogDebug($"Retrieving application registrations for tenant ID \"{_tenantId}\"");
ApplicationCollectionResponse apps;
_logger.LogDebug($"Retrieving Applications in tenant ID \"{_tenantId}\"");
List<Application> allApplications = new List<Application>();
try
{
apps = _graphClient.Applications.GetAsync((requestConfiguration) =>
var spsResponse = _graphClient.Applications.GetAsync(requestConfiguration =>
{
requestConfiguration.QueryParameters.Top = 500;
}).Result;

if (spsResponse?.Value == null)
{
_logger.LogWarning("No applications found.");
return result;
}

// Create the PageIterator to handle pagination
var pageIterator = PageIterator<Application, ApplicationCollectionResponse>
.CreatePageIterator(
_graphClient,
spsResponse,
// Callback executed for each Application in the collection
(application) =>
{
requestConfiguration.QueryParameters.Top = 999;
}).Result;
// Add each application to the list
allApplications.Add(application);
return true; // Continue to the next item
},
// Configure subsequent page requests
(request) =>
{
_logger.LogDebug("Fetching the next page of applications...");
return request;
});

// Execute the pagination
pageIterator.IterateAsync().Wait();

_logger.LogInformation($"Successfully retrieved {allApplications.Count} applications.");
}
catch (AggregateException e)
{
_logger.LogError($"Unable to retrieve application registrations for tenant ID \"{_tenantId}\": {e}");
_logger.LogError($"Unable to retrieve Applications for tenant ID \"{_tenantId}\": {e}");
throw;
}

if (apps?.Value == null || apps.Value.Count == 0)
if (allApplications.Count == 0)
{
_logger.LogWarning($"No application registrations found for tenant ID \"{_tenantId}\"");
_logger.LogWarning($"No Applications found for tenant ID \"{_tenantId}\"");
return result;
}

foreach (Application app in apps.Value)
foreach (Application app in allApplications)
{
_logger.LogDebug($"Found application \"{app.DisplayName}\" ({app.Id})");
_logger.LogDebug($"Found Application \"{app.DisplayName}\" ({app.Id})");

if (string.IsNullOrEmpty(app.AppId))
{
Expand All @@ -615,27 +705,57 @@ public OperationResult<IEnumerable<string>> DiscoverServicePrincipalApplicationI
OperationResult<IEnumerable<string>> result = new(appIds);

_logger.LogDebug($"Retrieving Service Principals for tenant ID \"{_tenantId}\"");
ServicePrincipalCollectionResponse sps;
List<ServicePrincipal> allServicePrincipals = new List<ServicePrincipal>();
try
{
sps = _graphClient.ServicePrincipals.GetAsync((requestConfiguration) =>
var spsResponse = _graphClient.ServicePrincipals.GetAsync(requestConfiguration =>
{
requestConfiguration.QueryParameters.Top = 999;
requestConfiguration.QueryParameters.Top = 500;
}).Result;

if (spsResponse?.Value == null)
{
_logger.LogWarning("No service principals found.");
return result;
}

// Create the PageIterator to handle pagination
var pageIterator = PageIterator<ServicePrincipal, ServicePrincipalCollectionResponse>
.CreatePageIterator(
_graphClient,
spsResponse,
// Callback executed for each Service Principal in the collection
(servicePrincipal) =>
{
// Add each service principal to the list
allServicePrincipals.Add(servicePrincipal);
return true; // Continue to the next item
},
// Configure subsequent page requests
(request) =>
{
_logger.LogDebug("Fetching the next page of service principals...");
return request;
});

// Execute the pagination
pageIterator.IterateAsync().Wait();

_logger.LogInformation($"Successfully retrieved {allServicePrincipals.Count} service principals.");
}
catch (AggregateException e)
{
_logger.LogError($"Unable to retrieve Service Principals for tenant ID \"{_tenantId}\": {e}");
throw;
}

if (sps?.Value == null || sps.Value.Count == 0)
if (allServicePrincipals.Count == 0)
{
_logger.LogWarning($"No Service Principals found for tenant ID \"{_tenantId}\"");
return result;
}

foreach (ServicePrincipal sp in sps.Value)
foreach (ServicePrincipal sp in allServicePrincipals)
{
_logger.LogDebug($"Found SP \"{sp.DisplayName}\" ({sp.Id})");

Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
- Discovery job modified to return available Certificate Stores with Store Path in the format `<ID GUID> (<Friendly Name>)`.
- Before other jobs operate on Certificate Stores, the contents after the ID GUID will be truncated, maintaining backward compatibility.

- 4.1.0
- Paginate discovery method to download all available App Registrations and Enterprise Applications.
- Fix DefineDiscoveredStores.ps1 to paginate Certificate Stores that come back from Command.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ The Azure App Registration and Enterprise Application Universal Orchestrator ext

<details><summary>Azure App Registration (Application) (AzureApp)</summary>


### AzureApp
> **WARNING** AzureApp "Azure App Registration (Application)" is **Depricated**. Please use **AzureApp2** "Azure App Registration 2 (Application)" instead.

Expand All @@ -48,6 +49,7 @@ Azure [App Registration/Application certificates](https://learn.microsoft.com/en

<details><summary>Azure Enterprise Application (Service Principal) (AzureSP)</summary>


### AzureSP
> **WARNING** AzureSP "Azure Enterprise Application (Service Principal)" is **Depricated**. Please use **AzureSP2** "Azure Enterprise Application 2 (Service Principal)" instead.

Expand All @@ -57,13 +59,15 @@ The Azure Enterprise Application/Service Principal certificate operations are im

<details><summary>Azure App Registration 2 (Application) (AzureApp2)</summary>


### AzureApp2

Azure [App Registration/Application certificates](https://learn.microsoft.com/en-us/entra/identity-platform/certificate-credentials) are typically used for client authentication by applications and are typically public key only in Azure. The general model by which these credentials are consumed is that the certificate and private key are accessible by the Application using the App Registration, and are passed to the service that is authenticating the Application. The Azure App Registration and Enterprise Application Orchestrator extension implements the Inventory, Management Add, Management Remove, and Discovery job types for managing these certificates.
</details>

<details><summary>Azure Enterprise Application 2 (Service Principal) (AzureSP2)</summary>


### AzureSP2

The Azure Enterprise Application/Service Principal certificate operations are implemented by the `AzureSP` store type, and supports the management of a single certificate for use in SSO/SAML assertion signing. The Management Add operation is only supported with the certificate replacement option, since adding a new certificate will replace the existing certificate. The Add operation will also set newly added certificates as the active certificate for SSO/SAML usage. The Management Remove operation removes the certificate from the Enterprise Application/Service Principal, which is the same as removing the SSO/SAML signing certificate. The Discovery operation discovers all Enterprise Applications/Service Principals in the tenant.
Expand Down
Loading