As I was going over the latest CIS guidelines for securing your Microsoft 365 estate, I noticed that some of the controls are marked as “manual”, even though programmatic methods to collect/remediate them do exists. Now I’m definitely no expert on the benchmark and I am not familiar with the methodology used to generate it, so I am not voicing any criticism on it. I simply wanted to publish some guidance for people that might be interested in automating some of those “manual” controls. Let’s dig into it.
Idle session timeout
Reporting on and configuring Idle session timeout for Microsoft 365 apps is possible via the activityBasedTimeoutPolicy Graph API resource. Here are some examples on how to do that:
#Use direct request GET https://graph.microsoft.com/v1.0/policies/activityBasedTimeoutPolicies #Use the Graph SDK Connect-MgGraph -Scopes Policy.Read.All Get-MgPolicyActivityBasedTimeoutPolicy | fl AppliesTo : Definition : {{"ActivityBasedTimeoutPolicy":{"Version":1,"ApplicationPolicies":[{"ApplicationId":"default","WebSessionIdleTimeout":"06:00:00"}]}}} DeletedDateTime : Description : DisplayName : ActivityBasedTimeoutPolicy Id : c1a12545-9935-4ee1-b8bf-6e7bbefedf33 IsOrganizationDefault : True AdditionalProperties : {}
As the idle timeout policy has some limitations (see the official documentation), it is recommended to supplement it with a Conditional access policy that has the “” control configured or ideally, sign-in frequency. We will touch on CA policies later on.
User owned apps and services
Found under the Microsoft 365 Admin Center, two settings are of note in this category: Let users access the Office store and Let users start trials on behalf of your organization. Programmatic access to said settings is available thanks to the adminAppsAndServices Graph API (beta) resource.
#Use direct request GET https://graph.microsoft.com/beta/admin/appsAndServices #Use the Graph SDK Connect-MgGraph -Scopes OrgSettings-AppsAndServices.Read.All -NoWelcome Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/admin/appsAndServices" Name Value ---- ----- @odata.context https://graph.microsoft.com/beta/$metadata#admin/appsAndServices/$entity settings {[isOfficeStoreEnabled, True], [isAppAndServicesTrialEnabled, True]}
Microsoft Forms
Similar to the above, the Graph API also exposes the adminForms resource, which allows us to query and set (some of) the settings found under Microsoft Forms category. In particular, the guidance is around the Add internal phishing protection (isInOrgFormsPhishingScanEnabled) setting, but you might as well consider leveraging some of the external sharing related settings exposed via said resource (isExternal*).
#Use direct request GET https://graph.microsoft.com/beta/admin/forms #Use the Graph SDK Connect-MgGraph -Scopes OrgSettings-Forms.Read.All -NoWelcome Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/admin/Forms/settings" | ft -AutoSize Name Value ---- ----- isExternalShareCollaborationEnabled True isBingImageSearchEnabled False isExternalShareTemplateEnabled True isExternalShareResultEnabled True isInOrgFormsPhishingScanEnabled True isExternalSendFormEnabled True @odata.context https://graph.microsoft.com/beta/$metadata#admin/forms/settings isRecordIdentityByDefaultEnabled True
Microsoft 365 on the web
Under this category, you can find the Let users open files stored in third-party storage services in Microsoft 365 on the web setting. Controlling this one takes a different approach, namely disabling the Office Online Third Party Storage service principal (with appID value of c1f33bc0-bdb4-4248-ba9b-096807ddb43e).
#Get the SP via the Graph API GET https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq %27c1f33bc0-bdb4-4248-ba9b-096807ddb43e%27 #Use the Graph SDK to get the SP and block it $SP = Get-MgServicePrincipal -Filter "appId eq 'c1f33bc0-bdb4-4248-ba9b-096807ddb43e'" Update-MgServicePrincipal -ServicePrincipalId $SP.Id -AccountEnabled:$false
Do note that the SP might not exist in the tenant, in which case you will need to create it first! Credit for this solution goes to the good folks over at the Microsoft 365 DSC initiative.
Priority account protection
This one is found under the Security center > Settings. To control it, we can use Exchange Online PowerShell (no Graph API equivalent!) and the set of *-EmailTenantSettings cmdlets:
#Check whether the setting is enabled Get-EmailTenantSettings | select EnablePriorityAccountProtection EnablePriorityAccountProtection ------------------------------- True #Configure the setting Set-EmailTenantSettings -EnablePriorityAccountProtection $true
Per-user MFA
As the MSOnline PowerShell module is now deprecated, we need an alternative method to report on and configure the per-user MFA controls. Luckily, Microsoft just released such, via the /authentication/requirements Graph API (beta) endpoint! The documentation is a bit flaky now, but thanks to Daniel’s post, we know the permissions required for these operations are Policy.Read.All and Policy.ReadWrite.AuthenticationMethod, respectively. So here are some examples on how to report on or toggle per-user MFA:
GET https://graph.microsoft.com/beta/users/user@domain.com/authentication/requirements
PATCH https://graph.microsoft.com/beta/users/user@domain.com/authentication/requirements { "perUserMfaState": "enabled" }
Despite what Microsoft’s sales and marketing teams want you to believe, per-user MFA remains viable (and free!) option, so you might as well take advantage of it, even it goes against the recommendations. One can even argue that it represents a better option than Security defaults, as you can actually exert some control over it, instead of the “all or nothing approach”.
Microsoft Authenticator configuration
This control instructs us to configure some settings on the Microsoft Authenticator Authentication Method Policy, which will help prevent MFA fatigue. While the settings can be configured manually via the Entra Admin Center, there is also a Graph API endpoint we can leverage, namely /authenticationMethodConfigurations/microsoftAuthenticator (found under the /policies/authenticationMethodsPolicy node). The settings in question are part of microsoftAuthenticatorFeatureSettings resource, and we can report on them or configure them as follows:
#Use direct request GET https://graph.microsoft.com/v1.0/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator #Use the Graph SDK for PowerShell (Get-MgPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration -AuthenticationMethodConfigurationId microsoftAuthenticator | select -ExpandProperty AdditionalProperties).featureSettings Key Value --- ----- displayAppInformationRequiredState {[state, disabled]... displayLocationInformationRequiredState {[state, enabled]...
Only two of the available properties are shown above, the rest will be visible if you use the /beta endpoint instead. As the Require number matching for push notifications (numberMatchingRequiredState) setting is now on by default, you can ignore it. The Microsoft Authenticator on companion applications setting does not fall into the scope of the benchmark. The Show application name in push and passwordless notifications (displayAppInformationRequiredState) and Show geographic location in push and passwordless notifications (displayLocationInformationRequiredState) properties are the ones to check and configure.
As PATCH-ing the policy requires you to provide its full object as JSON, I’ll leave out the example here.
Additional resources
Because I’m getting bored already, here are some additional resources that can help you with some more of the controls.
- Everything under section 5.2.2 can be automated via the Graph API endpoints for Conditional access policies
- The admin consent settings can be managed via the Admin consent policy resource
- Password protection and Group-related settings can be managed via directory settings
- Some additional controls can be taken care of by using (unsupported) calls to the https://main.iam.ad.ext.azure.com/api endpoint
- Most of the compliance-related controls can be addressed via cmdlets from the SCC PowerShell module