Reputation: 159
I'm trying to create a system where PowerShell gathers data from multiple tenants and deplays in a report. One of the datapoints that needs to be checked, is whether administrators have MFA enabled or not. In order to pull this data, I use the following
$credentials = <credentials>;
Connect-MSOLService -Credential $credentials;
foreach ($role in Get-MsolRole) {
foreach ($adminUser in (Get-MsolRoleMember -All -RoleObjectId $role.ObjectId -MemberObjectTypes @("User"))) {
$isMFA = ($adminUser.StrongAuthenticationRequirements -match 'Microsoft.Online.Administration.StrongAuthenticationRequirement').Count -gt 0;
#Do stuff
}
}
This works. Problem is, this script is running in a queue triggered azure function. It is triggered on a timer, meaning all triggers will run simultaneously. When the first connection is made, all other data requests pulls data from the same tenant.
Is there any way I can ensure each requests makes its own connection, or to limit the scope of the msol connection?
The only solution I can think of is running the scripts synchronous, but that would result in very poor performance.
Upvotes: 1
Views: 2468
Reputation: 14336
Indeed, when triggered by a queue, it would seem that multiple runs of the same function aren't at all isolated.
One approach you can take is to wrap your PowerShell code which should run isolated, into it's own PowerShell job, using Start-Job
. Here's an example I tested successfully.
# Receive queue message
$input = Get-Content $queueItem -Raw
# Pass the input queue message as a parameter to a new job.
$job = Start-Job -ArgumentList $input -ScriptBlock {
param($queueMessage)
# Load the MSOnline PowerShell module
Import-Module $env:CONTOSO_PathToMSOnline
# Retrieve the credentials from where they're securely stored
$credentials = ... # e.g. get from Key Vault
# Connect to Azure AD. This connection is only used by this job.
Connect-MsolService -Credential $credentials
# Do something with MSOnline...
}
# Wait for the job to complete, receive results, then clean up.
Receive-Job -Wait -Job $job -AutoRemoveJob
Based on my testing, this should cover your isolation needs. However, do keep in mind that you're spinning up a whole new PowerShell host instance for this, which might have unintended consequences (e.g. greater memory usage, more time to load).
While I'm at it, I'd like to suggest a tweak to your process to identify admins who have per-user MFA enabled (assuming you don't want to double-count admins who are members of multiple roles):
# Iterate over all admins of all roles, and check if they have per-user MFA enabled.
$admins = @{} # To keep track of which admins we've already seen
foreach ($role in Get-MsolRole) {
$roleMembers = Get-MsolRoleMember -All -RoleObjectId $role.ObjectId `#`
-MemberObjectTypes @("User")
foreach ($user in $roleMembers) {
if ($admins.ContainsKey($user.ObjectId)) {
# We've already seen this user, skip it.
} else {
$admins[$user.ObjectId] = $true # Mark as admin we've seen
# Determine if per-user MFA is enabled or enforced
$isMfaEnabledOrEnforced = $user.StrongAuthenticationRequirements.Count -gt 0
# Do something...
}
}
}
Upvotes: 2