I assume this is a role I am missing..
I created an app registration that has certificate based authentication. When I authenticate I want to be able to pull all subscriptions because I plan on looping through them and finding all VMs in each subscription/resource group.
I also want to pull down the global admins in the entra tenant from this certificate.
Permissions I am assigning my app registration
$app = Get-AzADApplication -ApplicationId $sp.AppId
$appObjectId = $app.Id
# GraphAPI - Directory.Read.All
# Add-AzADAppPermission -ObjectId $appObjectId -ApiId "00000003-0000-0000-c000-000000000000" -PermissionId "4e9b66a2-74a1-4d86-a0cb-1a01e40e7d77"
# # Add-AzADAppPermission -ObjectId "55fad647-6f5d-489f-b85e-00f96406ee9b" -ApiId "00000003-0000-0000-c000-000000000000" -PermissionId "5f8c59db-677d-491f-a6b8-5f174b11ec1d"
# Add Microsoft Graph Directory.Read.All permission
Add-AzADAppPermission -ObjectId $appObjectId -ApiId "00000003-0000-0000-c000-000000000000" -PermissionId "aef1f2db-fc2a-4d63-bcf5-e9a7a7802c9b"
# Add Microsoft Graph Directory.Read.All permission
Add-AzADAppPermission -ObjectId $appObjectId -ApiId "00000003-0000-0000-c000-000000000000" -PermissionId "df021288-bdef-4463-88db-98f22de89214"
# Azure Service Management - Reader
Add-AzADAppPermission -ObjectId $appObjectId -ApiId "00000002-0000-0000-c000-000000000000" -PermissionId "b7d27f52-6659-42b8-8164-4a0e63a2c156"
Creating app registration:
$cert = New-SelfSignedCertificate -CertStoreLocation "cert:\CurrentUser\My" -Subject $applicationName -KeySpec KeyExchange -NotAfter (Get-Date).AddYears(1)
$keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData())
# Assign Certificate to App in Entra
$sp = New-AzADServicePrincipal -DisplayName $applicationName -CertValue $keyValue -EndDate $cert.NotAfter -StartDate $cert.NotBefore
Start-Sleep -Seconds 3
# Export the certificate to a PFX file
$cert | Export-PfxCertificate -FilePath $certFilePath -Password (ConvertTo-SecureString -String $certPassword -Force -AsPlainText)
Initially, I created one certificate same as you and uploaded it to app registration as below:
To add Directory.Read.All
permission of Application type and Reader
role to service principal at root level, make use of below updated script:
$applicationName = "SriCertApp"
$spId = (Get-AzADServicePrincipal -DisplayName $applicationName).Id
# Assign Reader role to the Service Principal under specific subscription
New-AzRoleAssignment -ObjectId $spId `
-RoleDefinitionName "Reader" `
-Scope "/"
Write-Host "Reader role assigned to Service Principal."
# Get the Azure AD Application by AppId
$app = Get-AzADApplication -ApplicationId $sp.AppId
$appObjectId = $app.Id
# Add Microsoft Graph Directory.Read.All permission of Application type
Add-AzADAppPermission -ObjectId $appObjectId `
-ApiId "00000003-0000-0000-c000-000000000000" `
-PermissionId "7ab1d382-f21e-4acd-a863-ba3e13f7da61" `
-Type Role
Write-Host "Microsoft Graph Directory.Read.All permission granted."
Response:
To confirm that, you can check the same in Azure Portal where role and permission assigned successfully like this:
Reader role:
Directory.Read.All permission
Make sure to grant admin consent to above permission that allows service principal to read directory data:
Now, make use of below script to fetch all VMs across all subscriptions connecting with certificate authentication:
$appId = "appId"
$tenantId = "tenantId"
$thumbprint = "cert_thumbprint"
Connect-AzAccount -ApplicationId $appId -Tenant $tenantId -CertificateThumbprint $thumbprint
$subscriptions = Get-AzSubscription
$allVms = @()
foreach ($subscription in $subscriptions) {
Set-AzContext -SubscriptionId $subscription.Id
$vms = Get-AzVM
foreach ($vm in $vms) {
$vmDetails = [PSCustomObject]@{
VMName = $vm.Name
SubscriptionName = $subscription.Name
ResourceGroupName = $vm.ResourceGroupName
}
$allVms += $vmDetails
}
}
$allVms | Format-Table -AutoSize
$allVms | Export-Csv -Path "C:\test\vms.csv" -NoTypeInformation
Response:
To pull down the global admins in the Microsoft Entra tenant with certificate authentication, make use of below Microsoft Graph script:
$appId = "appId"
$tenantId = "tenantId"
$thumbprint = "thumbprint"
$roleName = "Global Administrator"
Connect-MgGraph -ClientId $appId -TenantId $tenantId -CertificateThumbprint $thumbprint
$adminRole = Get-MgDirectoryRole -Filter "DisplayName eq '$roleName'"
if ($adminRole) {
$roleMembers = Get-MgDirectoryRoleMember -DirectoryRoleId $adminRole.Id
if ($roleMembers) {
$roleMembers | ForEach-Object {
[PSCustomObject]@{
DisplayName = $_.AdditionalProperties["displayName"]
Id = $_.Id
}
} | Format-Table DisplayName, Id
} else {
Write-Host "No members found for the role: $roleName"
}
} else {
Write-Host "Role '$roleName' not found"
}
Response: