diff --git a/AzureEnterpriseApplicationOrchestrator.Tests/AzureEnterpriseApplicationOrchestrator.Tests.csproj b/AzureEnterpriseApplicationOrchestrator.Tests/AzureEnterpriseApplicationOrchestrator.Tests.csproj
index 8a9f5e9..865dc95 100644
--- a/AzureEnterpriseApplicationOrchestrator.Tests/AzureEnterpriseApplicationOrchestrator.Tests.csproj
+++ b/AzureEnterpriseApplicationOrchestrator.Tests/AzureEnterpriseApplicationOrchestrator.Tests.csproj
@@ -11,8 +11,11 @@
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/AzureEnterpriseApplicationOrchestrator/AzureEnterpriseApplicationOrchestrator.csproj b/AzureEnterpriseApplicationOrchestrator/AzureEnterpriseApplicationOrchestrator.csproj
index ffd4b51..0d8fe15 100644
--- a/AzureEnterpriseApplicationOrchestrator/AzureEnterpriseApplicationOrchestrator.csproj
+++ b/AzureEnterpriseApplicationOrchestrator/AzureEnterpriseApplicationOrchestrator.csproj
@@ -16,13 +16,15 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
-
-
+
+
+
+
+
diff --git a/AzureEnterpriseApplicationOrchestrator/AzureSP2Jobs/Inventory.cs b/AzureEnterpriseApplicationOrchestrator/AzureSP2Jobs/Inventory.cs
index 8b966e7..c35f273 100644
--- a/AzureEnterpriseApplicationOrchestrator/AzureSP2Jobs/Inventory.cs
+++ b/AzureEnterpriseApplicationOrchestrator/AzureSP2Jobs/Inventory.cs
@@ -105,5 +105,4 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd
return result;
}
-}
-
+}
\ No newline at end of file
diff --git a/AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs b/AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs
index e90ddc9..c2ff301 100644
--- a/AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs
+++ b/AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs
@@ -800,9 +800,25 @@ private OperationResult> InventoryFromKeyCrede
foreach (KeyCredential keyCredential in keyCredentials)
{
- string customKeyIdentifier = Convert.ToHexString(keyCredential.CustomKeyIdentifier);
+ // CustomKeyIdentifier can be null for key credentials returned by Graph. Fall back to the
+ // KeyId so that the credential can still be tracked and reported instead of throwing.
+ string customKeyIdentifier;
+ if (keyCredential.CustomKeyIdentifier != null)
+ {
+ customKeyIdentifier = Convert.ToHexString(keyCredential.CustomKeyIdentifier);
+ }
+ else if (keyCredential.KeyId != null)
+ {
+ customKeyIdentifier = keyCredential.KeyId.ToString();
+ _logger.LogWarning($"Key credential with DisplayName \"{keyCredential.DisplayName}\" has no CustomKeyIdentifier - using KeyId \"{customKeyIdentifier}\" instead");
+ }
+ else
+ {
+ _logger.LogWarning($"Skipping key credential with DisplayName \"{keyCredential.DisplayName}\" because it has neither a CustomKeyIdentifier nor a KeyId");
+ continue;
+ }
- if (!string.IsNullOrWhiteSpace(keyCredential.Usage) && keyCredential.Usage.Equals("Sign", StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrWhiteSpace(keyCredential.Usage))
{
_logger.LogDebug($"Certificate with CustomKeyIdentifier \"{customKeyIdentifier}\" has a private key entry");
privateKeyMap[customKeyIdentifier] = true;
@@ -810,7 +826,32 @@ private OperationResult> InventoryFromKeyCrede
// We only track the case where the private key exists because there will be several keyCredentials
// for Service Principals where one is the certificate and the other is the private key.
- X509Certificate2 certificate = GetCertificateFromKeyCredential(keyCredential);
+ // If the thumbprint is already in the map, skip it
+ if (keyIdMap.ContainsKey(customKeyIdentifier))
+ {
+ _logger.LogTrace($"Skipping certificate with CustomKeyIdentifier \"{customKeyIdentifier}\" because it's already in the inventory");
+ continue;
+ }
+
+ X509Certificate2 certificate;
+ try
+ {
+ certificate = GetCertificateFromKeyCredential(keyCredential);
+ }
+ catch (Exception ex)
+ {
+ string message = $"Exception retrieving certificate for key credential with DisplayName \"{keyCredential.DisplayName}\" ({customKeyIdentifier}): {ex.Message}";
+ _logger.LogWarning(message);
+ failedCertificateMap[customKeyIdentifier] = message;
+ continue;
+ }
+
+ if (!string.Equals(keyCredential.Type, "AsymmetricX509Cert", StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.LogDebug($"Skipping key credential with DisplayName \"{keyCredential.DisplayName}\" because its type is \"{keyCredential.Type}\", not a certificate");
+ continue;
+ }
+
if (certificate == null)
{
string message = $"Unable to retrieve certificate for key credential with DisplayName \"{keyCredential.DisplayName}\" ({customKeyIdentifier})";
@@ -824,12 +865,6 @@ private OperationResult> InventoryFromKeyCrede
failedCertificateMap.Remove(customKeyIdentifier);
}
- // If the thumbprint is already in the map, skip it
- if (keyIdMap.ContainsKey(customKeyIdentifier))
- {
- _logger.LogTrace($"Skipping certificate with CustomKeyIdentifier \"{customKeyIdentifier}\" because it's already in the inventory");
- continue;
- }
keyIdMap[customKeyIdentifier] = true;
// Assemble the certificates for the inventory
@@ -967,7 +1002,9 @@ protected IEnumerable DeepCopyPasswordList(List