2026-10-13 · 12 min read · Security Hardening

SkyCore's Essential Office 365 MFA Setup Guide for Robust Security Hardening

Abstract image showing layers of digital security protecting a user login, symbolizing multi-factor authentication

In an era where password-only authentication is a critical vulnerability, implementing Multi-Factor Authentication (MFA) is the single most impactful step you can take to bolster your organization's digital defenses. CISA explicitly recommends MFA, stating that it can prevent 99.9% of automated attacks. This guide provides SkyCore's best practices for deploying robust Office 365 MFA using Microsoft Entra ID (formerly Azure Active Directory), focusing on a CLI-first approach to ensure precise and repeatable configurations for comprehensive security hardening.

Prerequisites

Step 1: Verify Administrative Permissions

Before initiating any changes to your authentication infrastructure, it's paramount to confirm that you possess the necessary administrative permissions. Configuring MFA requires specific roles to ensure you have the authority to modify tenant-wide security policies and user authentication settings.

# Install the Microsoft Graph PowerShell SDK if not already installed
Install-Module -Name Microsoft.Graph -Scope CurrentUser

# Connect to Microsoft Graph with required scopes
Connect-MgGraph -Scopes "Directory.Read.All", "Policy.Read.All", "Policy.ReadWrite.ConditionalAccess", "Policy.ReadWrite.AuthenticationMethods"

# Get your current user's assigned roles
Get-MgUserMemberOf -UserId (Get-MgContext).UserId | Select-Object DisplayName, Id, Description

Install-Module -Name Microsoft.Graph -Scope CurrentUser: Installs the Microsoft Graph PowerShell SDK module for the current user.

Connect-MgGraph -Scopes "...": Authenticates your PowerShell session with Microsoft Graph, requesting specific permissions (scopes) needed to read directory information and manage authentication policies.

Get-MgUserMemberOf -UserId (Get-MgContext).UserId: Retrieves all directory roles and groups that the currently authenticated user is a member of.

Portal alternative: Navigate to the Microsoft Entra admin center > Identity > Users > All users. Select your user account, then click Assigned roles to view your current role assignments. Ensure you have Global Administrator, Security Administrator, or Conditional Access Administrator for the respective tasks.

Expected result: A list of roles your account is assigned to, confirming that you hold at least one of the following: Global Administrator, Security Administrator, or Conditional Access Administrator.

Common pitfall: Attempting to configure MFA without sufficient permissions will result in access denied errors, wasting time and potentially creating frustration. Always verify your role assignments first. Microsoft recommends using roles with the fewest permissions required, limiting Global Administrator use to emergency scenarios.

Step 2: Understand Microsoft Entra MFA Strategies

Microsoft Entra ID offers three primary strategies for implementing MFA. Understanding these is critical to choosing the right approach for your organization based on licensing, desired granularity, and security posture. SkyCore strongly recommends either Security Defaults or Conditional Access policies.

While there's no direct CLI command to "understand" these strategies, you manage them via PowerShell in later steps. CISA and NIST SP 800-63B consistently advocate for robust MFA across all user accounts, with a preference for phishing-resistant authenticators. Conditional Access policies align best with this recommendation by allowing fine-grained control over authentication methods and conditions.

Portal alternative: Review the settings in the Microsoft Entra admin center > Identity > Overview > Properties > Security defaults section. If Conditional Access policies are already in use, Security defaults will be disabled.

Expected result: A clear understanding of which MFA strategy is currently active in your tenant and which strategy you intend to implement (preferably Conditional Access for P1/P2 tenants).

Step 3: Disable Legacy Per-User MFA (If Active)

If your organization previously enabled MFA using the legacy per-user setting, it's crucial to disable it before implementing Security Defaults or Conditional Access. Having both active can lead to unexpected behavior and administrative overhead. This step ensures a clean transition to a modern MFA strategy.

# Connect using the MSOnline module if not already connected (needed for legacy MFA management)
# Install-Module -Name MSOnline -Force -AllowClobber
Connect-MsolService

# Get all users with legacy per-user MFA enabled
$UsersWithMfa = Get-MsolUser -All | Where-Object {$_.StrongAuthenticationRequirements -ne $null -and $_.StrongAuthenticationRequirements.Count -gt 0}

# Iterate through users and disable legacy per-user MFA
foreach ($User in $UsersWithMfa) {
    Write-Host "Disabling legacy per-user MFA for $($User.UserPrincipalName)..."
    Set-MsolUser -UserPrincipalName $User.UserPrincipalName -StrongAuthenticationRequirements @()
}

# Verify that no users have legacy per-user MFA enabled
Get-MsolUser -All | Where-Object {$_.StrongAuthenticationRequirements -ne $null -and $_.StrongAuthenticationRequirements.Count -gt 0} | Select-Object UserPrincipalName, StrongAuthenticationRequirements

Connect-MsolService: Establishes a connection to the Microsoft Online Services (MSOL) for managing legacy Entra ID aspects.

Get-MsolUser -All | Where-Object {$_.StrongAuthenticationRequirements -ne $null -and $_.StrongAuthenticationRequirements.Count -gt 0}: Retrieves all users and filters for those with `StrongAuthenticationRequirements` set, indicating legacy per-user MFA.

Set-MsolUser -UserPrincipalName $User.UserPrincipalName -StrongAuthenticationRequirements @(): Sets the `StrongAuthenticationRequirements` for a user to an empty array, effectively disabling legacy per-user MFA for that user.

Portal alternative: Navigate to the Microsoft Entra admin center > Identity > Users > All users > Per-user MFA. In the new tab that opens, select all users who have MFA enabled via this legacy portal, then click Disable in the quick steps on the right.

Expected result: The `Get-MsolUser` verification command returns no users, indicating that legacy per-user MFA is now disabled for all users. This prepares your tenant for a modern MFA approach.

Common pitfall: Disabling legacy per-user MFA without immediately implementing Security Defaults or Conditional Access will leave your users unprotected. This step should be followed immediately by Step 4 or Step 5 to maintain a secure posture.

Step 4: Implement or Review Security Defaults

For organizations without Microsoft Entra ID P1 or P2 licenses, Security Defaults offer a straightforward way to enforce foundational MFA protection. If your tenant was created after October 2019, Security Defaults are likely already enabled. This step guides you through reviewing and, if necessary, enabling them.

# Security Defaults are primarily managed via the Microsoft Entra admin center GUI.
# There is no direct, simple PowerShell cmdlet to toggle Security Defaults as of the reference documentation.
# The following commands help identify if Conditional Access is preventing Security Defaults, which is common.

# List all Conditional Access policies (if any exist)
Get-MgConditionalAccessPolicy | Select-Object DisplayName, State

Get-MgConditionalAccessPolicy: Retrieves existing Conditional Access policies in your tenant. If any policies are enabled or in report-only mode, Security Defaults cannot be enabled.

Portal alternative: Navigate to the Microsoft Entra admin center > Identity > Overview > Properties. Scroll down to the Security defaults section. If Security Defaults are off, you'll see a 'Manage security defaults' option. Select it, choose Enabled from the dropdown, and click Save. If Conditional Access policies are preventing activation, you will need to disable or delete them first, or ideally, move to Conditional Access entirely if you have the licensing.

Expected result: Security Defaults are reported as 'Enabled' in the Microsoft Entra admin center properties. If Conditional Access policies exist, you'll see them listed, indicating you should proceed with Conditional Access instead of Security Defaults.

Step 5: Configure Conditional Access Policy for All Users (Recommended for P1/P2 Licenses)

For organizations with Microsoft Entra ID P1 or P2, Conditional Access policies provide the most robust and flexible approach to MFA. This step outlines creating a policy to enforce MFA for all users accessing all cloud applications, excluding your emergency access accounts. We'll initially create it in 'Report-only' mode for testing.

# Define the User Principal Names (UPNs) for your emergency access accounts to be excluded
$EmergencyAccessUserUPNs = @("breakglassadmin@yourdomain.com", "secondaryemergency@yourdomain.com")

# Get the Object IDs of the emergency access users
$ExcludedUserObjectIds = @()
foreach ($upn in $EmergencyAccessUserUPNs) {
    try {
        $User = Get-MgUser -UserId $upn
        $ExcludedUserObjectIds += $User.Id
    } catch {
        Write-Warning "User $upn not found. Please verify the UPN."
    }
}

# Ensure at least one exclusion is defined to prevent lockout during testing.
# If no emergency users are found, this policy should NOT be deployed in Enabled state.
if ($ExcludedUserObjectIds.Count -eq 0) {
    Write-Warning "No valid emergency access accounts found or specified. It is critical to exclude at least one account to prevent tenant lockout. Exiting policy creation."
    return
}

# Create a Conditional Access policy to require MFA for all users, excluding emergency access accounts
$PolicyName = "SkyCore - Require MFA for All Users"
$Conditions = @{
    users = @{
        includeUsers = @("All")
        excludeUsers = $ExcludedUserObjectIds # Exclude your emergency access accounts
    }
    applications = @{
        includeApplications = @("All")
    }
    clientAppTypes = @(
        "browser",
        "mobileAppsAndDesktopClients",
        "exchangeActiveSync"
    )
}

$GrantControls = @{
    operator = "OR"
    builtInControls = @(
        "mfa" # Require Multi-Factor Authentication
    )
}

# Create the policy in 'Report-only' mode for initial testing
New-MgConditionalAccessPolicy -DisplayName $PolicyName -State "EnabledForReportingButNotEnforced" `
    -Conditions $Conditions -GrantControls $GrantControls

Write-Host "Conditional Access policy '$PolicyName' created in Report-only mode. Review its impact before enabling."

$EmergencyAccessUserUPNs: Defines an array of User Principal Names for accounts that should be excluded from the policy. These are your 'break-glass' accounts.

Get-MgUser -UserId $upn: Retrieves the Microsoft Entra user object by UPN to get its Object ID.

New-MgConditionalAccessPolicy -DisplayName "..." -State "EnabledForReportingButNotEnforced": Creates a new Conditional Access policy with a specified display name and initially sets its state to 'Report-only'.

-Conditions $Conditions: Specifies the conditions for the policy to apply, including 'All' users (with exclusions), 'All' cloud apps, and various client application types.

-GrantControls $GrantControls: Defines the access controls to enforce, in this case, requiring Multi-Factor Authentication (`mfa`).

Portal alternative: Navigate to the Microsoft Entra admin center > Identity > Conditional Access > Policies > New policy. Configure the policy as follows:

Expected result: A new Conditional Access policy is created in 'Report-only' mode. You should then review the sign-in logs in the Microsoft Entra admin center to understand the policy's impact before switching it to 'Enabled'. Microsoft Entra ID provides built-in authentication strengths for Conditional Access policies, including 'Multifactor authentication strength' (less restrictive, used here), 'Passwordless MFA strength', and 'Phishing-resistant MFA strength' (most restrictive), allowing you to further refine required methods.

Production risk: Deploying a "Require MFA for All Users" policy directly to 'Enabled' without exclusions or prior 'Report-only' testing can lead to immediate tenant lockout if misconfigured or if administrators lose access to their primary MFA method. Always use exclusions for emergency accounts and test rigorously in 'Report-only' mode.

Step 6: Define and Manage Allowed Authentication Methods

Not all MFA methods offer the same level of security. NIST SP 800-63B categorizes authenticators by Assurance Levels. SkyCore strongly recommends prioritizing stronger, phishing-resistant methods like FIDO2 security keys or passwordless Microsoft Authenticator, while restricting weaker options like SMS or voice calls where feasible. Microsoft Authenticator push notifications offer a good balance of security (NIST AAL2) and usability.

# Get the current authentication methods policy
$AuthenticationMethodsPolicy = Get-MgPolicyAuthenticationMethodPolicy

# Disable less secure methods like SMS and Voice calls
# Note: FIDO2 and Microsoft Authenticator are typically enabled by default or managed via their specific policies.
# You might need to retrieve specific AuthenticationMethod IDs if they are not already listed.

# Example: Disabling SMS (assuming 'Sms' is a recognized ID, typically 'sms')
try {
    $smsPolicy = $AuthenticationMethodsPolicy.Sms
    if ($smsPolicy) {
        $smsPolicy.State = 'Disabled'
        Update-MgPolicyAuthenticationMethodPolicy -Sms $smsPolicy
        Write-Host "SMS authentication method disabled."
    } else {
        Write-Warning "SMS authentication method policy not found to disable."
    }
} catch {
    Write-Warning "Failed to update SMS authentication policy: $($_.Exception.Message)"
}

# Example: Disabling Voice Calls (assuming 'VoiceDialup' is a recognized ID, typically 'voice')
try {
    $voicePolicy = $AuthenticationMethodsPolicy.VoiceDialup
    if ($voicePolicy) {
        $voicePolicy.State = 'Disabled'
        Update-MgPolicyAuthenticationMethodPolicy -VoiceDialup $voicePolicy
        Write-Host "Voice call authentication method disabled."
    } else {
        Write-Warning "Voice call authentication method policy not found to disable."
    }
} catch {
    Write-Warning "Failed to update Voice Dial-up authentication policy: $($_.Exception.Message)"
}

# Enable and configure Microsoft Authenticator (if not already optimized)
# Note: The Microsoft Authenticator policy is typically managed via its own cmdlet or specific properties.
# This example shows how to ensure it's enabled and set for all users (or targeted groups).
# The method for targeting specific features like passwordless is more granular.
try {
    # This example ensures the Microsoft Authenticator registration is enabled for all users.
    # More advanced settings (e.g., passwordless mode) are configured within the Authenticator policy itself.
    # Get-MgPolicyAuthenticationMethodPolicy can be used to see current settings.
    # To enable/include all users for the Microsoft Authenticator, you would modify its scope.
    # This might require updating the 'AuthenticatorSmsSettings', 'MicrosoftAuthenticator' properties within
    # the overarching AuthenticationMethodsPolicy object or a specific policy endpoint.

    # A more common approach is to set states within the Authenticator method policy:
    # Get current Authenticator policy
    $authMethodPolicy = Get-MgPolicyAuthenticationMethodPolicy
    $microsoftAuthenticator = $authMethodPolicy.MicrosoftAuthenticator

    if ($microsoftAuthenticator) {
        # Ensure it's enabled for desired groups or all users
        if ($microsoftAuthenticator.State -ne 'Enabled') {
            $microsoftAuthenticator.State = 'Enabled'
            Update-MgPolicyAuthenticationMethodPolicy -MicrosoftAuthenticator $microsoftAuthenticator
            Write-Host "Microsoft Authenticator enabled."
        }
        # Further configuration for passwordless or push notifications is done within the Graph API for the specific method configuration.
        # Example: To target passwordless phone sign-in to all users
        # This requires more complex Graph API calls than simple cmdlet property updates directly on the policy.
        # A full Graph API call might look like:
        # $params = @{
        #     authenticationMethodModes = @(
        #         @{
        #             authenticationMethod = "MicrosoftAuthenticator"
        #             state = "Enabled"
        #             featureSettings = @{
        #                 # Example of targeting specific features within Authenticator, like passwordless
        #                 microsoftAuthenticatorPasswordlessSettings = @{
        #                     state = "Enabled"
        #                     is};
        #                 }
        #             }
        #         }
        #     )
        # }
        # Invoke-MgGraphRequest -Method PATCH -Uri "https://graph.microsoft.com/v1.0/policies/authenticationMethodsPolicy" -Body ($params | ConvertTo-Json)
        # Due to complexity and direct cmdlet limitations for granular settings, GUI is often used for these deep configurations.
        Write-Host "For granular control over Microsoft Authenticator features (e.g., passwordless), refer to the portal or advanced Graph API usage."
    } else {
        Write-Warning "Microsoft Authenticator policy not found."
    }
} catch {
    Write-Warning "Failed to update Microsoft Authenticator policy: $($_.Exception.Message)"
}


# Verify the current state of authentication methods
Get-MgPolicyAuthenticationMethodPolicy | Select-Object -ExpandProperty *

Get-MgPolicyAuthenticationMethodPolicy: Retrieves the tenant-wide authentication methods policy object.

$AuthenticationMethodsPolicy.Sms.State = 'Disabled': Sets the state of the SMS authentication method within the policy to disabled.

Update-MgPolicyAuthenticationMethodPolicy -Sms $smsPolicy: Updates the authentication methods policy to apply the changes to the SMS method.

$AuthenticationMethodsPolicy.MicrosoftAuthenticator.State = 'Enabled': Sets the state of the Microsoft Authenticator method to enabled.

Portal alternative: Navigate to the Microsoft Entra admin center > Identity > Protection > Authentication methods. Here you can configure the policy for each method (e.g., Microsoft Authenticator, FIDO2 Security Key, SMS, Voice call). For each method, you can set its State to 'Enabled' or 'Disabled' and target it to 'All users' or 'Select users and groups'. For Microsoft Authenticator, you can also configure 'Authentication mode' (Any, Passwordless, or Push Notification).

Expected result: Less secure methods (like SMS and Voice calls) are disabled, and more secure methods (like Microsoft Authenticator) are enabled and configured according to your security posture. Users will be guided to register preferred methods during their next sign-in if they haven't already.

Step 7: Exclude Emergency Access ('Break-Glass') Accounts

Emergency access accounts are highly privileged, non-personal accounts designed to be used only in extreme circumstances, such as when your normal administrative accounts are locked out or compromised. It is critical to exclude these accounts from MFA policies to prevent a complete tenant lockout. These accounts must be secured with extreme vigilance.

# Ensure you have the Object IDs for your emergency access accounts from Step 5.
# If you don't, retrieve them again:
$EmergencyAccessUserUPNs = @("breakglassadmin@yourdomain.com", "secondaryemergency@yourdomain.com")
$ExcludedUserObjectIds = @()
foreach ($upn in $EmergencyAccessUserUPNs) {
    try {
        $User = Get-MgUser -UserId $upn
        $ExcludedUserObjectIds += $User.Id
    } catch {
        Write-Warning "User $upn not found. Skipping exclusion for this UPN."
    }
}

# Get the Conditional Access policy created in Step 5
$Policy = Get-MgConditionalAccessPolicy | Where-Object {$_.DisplayName -eq "SkyCore - Require MFA for All Users"}

if ($Policy) {
    # Update the policy's conditions to explicitly exclude these users
    $Policy.Conditions.Users.ExcludeUsers = $ExcludedUserObjectIds

    # Update the policy in Microsoft Entra ID
    Update-MgConditionalAccessPolicy -ConditionalAccessPolicyId $Policy.Id -Conditions $Policy.Conditions
    Write-Host "Conditional Access policy updated to exclude emergency access accounts."
} else {
    Write-Warning "Conditional Access policy 'SkyCore - Require MFA for All Users' not found. Ensure it was created in Step 5."
}

# Verify exclusions (optional, review policy details)
Get-MgConditionalAccessPolicy -ConditionalAccessPolicyId $Policy.Id | Select-Object -ExpandProperty Conditions

$EmergencyAccessUserUPNs: Array of UPNs for your designated emergency access accounts.

Get-MgConditionalAccessPolicy | Where-Object {$_.DisplayName -eq "..."}: Retrieves the specific Conditional Access policy by its display name.

$Policy.Conditions.Users.ExcludeUsers = $ExcludedUserObjectIds: Modifies the policy object to include the Object IDs of the emergency access accounts in its exclusion list.

Update-MgConditionalAccessPolicy -ConditionalAccessPolicyId $Policy.Id -Conditions $Policy.Conditions: Submits the updated policy object to Microsoft Entra ID.

Portal alternative: Navigate to the Microsoft Entra admin center > Identity > Conditional Access > Policies. Select your policy (e.g., "SkyCore - Require MFA for All Users"). Under Users, ensure that your emergency access accounts are listed in the 'Exclude' tab.

Expected result: The Conditional Access policy is successfully updated, explicitly excluding your designated emergency access accounts from the MFA requirement. Regularly audit these accounts for any unauthorized activity.

Common pitfall: Forgetting to exclude emergency access accounts is a critical security oversight that can lead to complete tenant lockout. These accounts must have robust, non-MFA dependent security measures in place, such as extremely long, complex passwords stored securely, and their usage strictly audited.

Step 8: Plan User Communication and Rollout Strategy

Technical implementation is only half the battle; successful MFA adoption hinges on effective user communication and a well-structured rollout plan. A phased approach minimizes disruption and allows for addressing issues before they impact the entire organization. NIST emphasizes the importance of user training and awareness as a component of effective identity management.

# This step is purely procedural and does not involve direct CLI commands.
# However, you might use PowerShell for reporting on user MFA registration status.

# Get all users and their MFA registration status
# Note: This attribute might not always be perfectly up-to-date or comprehensive for all methods.
Get-MsolUser -All | Select-Object UserPrincipalName, @{Name="MfaStatus"; Expression={if($_.StrongAuthenticationRequirements.Count -gt 0){"Enabled (Legacy)"} elseif ($_.StrongAuthenticationMethods.Count -gt 0){"Registered (Modern)"} else {"Not Registered"}}}

# For more detailed modern MFA registration status, consider Graph API queries to '/users/{id}/authentication/methods'
# Example (conceptual):
# Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/users//authentication/methods"

Get-MsolUser -All | Select-Object UserPrincipalName, @{Name="MfaStatus"; Expression={...}}: Retrieves all users and attempts to infer their MFA registration status based on legacy or modern method counts. This gives a high-level overview for planning.

Portal alternative: Navigate to the Microsoft Entra admin center > Identity > Users > All users > Per-user MFA. Although used for legacy management, it still provides a quick view of who has MFA "Enabled" (legacy) or "Enforced" (legacy). For modern registration, use Identity > Monitoring & health > Workbooks > Authentication methods activity to view comprehensive MFA registration and usage.

Expected result: A clear, phased rollout plan outlining communication, training materials, support channels, and target groups for MFA enforcement. Users should be well-informed and supported through the transition.

When to bring in a consultant

While this guide provides a detailed, CLI-first approach to Office 365 MFA setup, the real-world complexities of cloud migration, existing security policies, and diverse user environments can be daunting. If your organization has complex Conditional Access requirements, a hybrid identity infrastructure with AD FS, specific compliance needs (e.g., NIST 800-171, HIPAA), or simply lacks the in-house expertise or time to execute this flawlessly, DIY can introduce significant risks. Misconfigured MFA policies can lead to tenant lockout, reduced productivity, or even security gaps. SkyCore Solutions specializes in nuanced Azure Cloud Migration, Security Hardening, and Infrastructure Revamp. We can help you design, implement, and validate an MFA strategy that aligns perfectly with your business needs and regulatory requirements, ensuring robust security without compromising usability.

Book a free consultation

References