Skip to content

Latest commit

 

History

History
615 lines (486 loc) · 16.7 KB

File metadata and controls

615 lines (486 loc) · 16.7 KB

Microsoft 365 Provider

The Microsoft 365 provider scans your M365 tenant for security compliance, covering identity protection, conditional access, devices, applications, and security configuration.

Overview

Use the Microsoft 365 provider to validate:

  • Identity protection (MFA, sign-in risk, user risk)
  • Conditional Access policies
  • Security defaults configuration
  • Directory roles and permissions
  • Guest access settings
  • Device compliance and encryption
  • Application registrations
  • Teams security settings

Quick Start

# Scan M365 tenant with client credentials
kspec scan ms365 <tenant-id> \
  --client-id <client-id> \
  --client-secret <client-secret> \
  -f policies/ms365-security.yml

# Using environment variables
export AZURE_TENANT_ID="your-tenant-id"
export AZURE_CLIENT_ID="your-client-id"
export AZURE_CLIENT_SECRET="your-client-secret"

kspec scan ms365 $AZURE_TENANT_ID -f policies/ms365-security.yml

Prerequisites

  • Microsoft 365 tenant
  • Azure AD App Registration with Graph API permissions
  • Global Administrator consent for required permissions

Authentication

Creating an App Registration

  1. Navigate to the Azure Portal (https://portal.azure.com)
  2. Go to Azure Active Directory > App registrations
  3. Click New registration
  4. Configure:
    • Name: kspec-scanner
    • Supported account types: Accounts in this organizational directory only
    • Redirect URI: Leave blank
  5. Click Register

Note Application Details

After registration, note down:

  • Application (client) ID: Found on the Overview page
  • Directory (tenant) ID: Found on the Overview page

Create Client Secret

  1. Go to Certificates & secrets
  2. Click New client secret
  3. Add a description and select expiration period
  4. Click Add
  5. Important: Copy the secret value immediately (shown only once)

Configure API Permissions

Go to API permissions > Add a permission > Microsoft Graph > Application permissions

Required Permissions:

Permission Description
Organization.Read.All Read organization information
User.Read.All Read all users' full profiles
Group.Read.All Read all groups
Application.Read.All Read all applications
Directory.Read.All Read directory data
Policy.Read.All Read all policies
SecurityEvents.Read.All Read security events
IdentityRiskEvent.Read.All Read identity risk events
Domain.Read.All Read domain data

Optional Permissions:

Permission Description
Team.ReadBasic.All Read Teams info
TeamSettings.Read.All Read Teams settings
DeviceManagementConfiguration.Read.All Read Intune configs
DeviceManagementManagedDevices.Read.All Read managed devices
RoleManagement.Read.Directory Read directory role info

Grant Admin Consent

  1. Click Grant admin consent for [Your Organization]
  2. Confirm by clicking Yes
  3. Verify all permissions show "Granted"

Resources

The MS365 provider discovers the following resources:

Identity

Resource Description
ms365_tenant Tenant/organization information
ms365_user User accounts and settings
ms365_group Microsoft 365 groups
ms365_directory_role Directory roles and members
ms365_risky_user Users flagged as risky

Applications

Resource Description
ms365_application Registered applications
ms365_service_principal Service principals

Devices

Resource Description
ms365_device Azure AD devices
ms365_managed_device Intune managed devices
ms365_device_configuration Intune device configurations
ms365_device_compliance_policy Device compliance policies

Policies

Resource Description
ms365_conditional_access_policy Conditional Access policies
ms365_authorization_policy Authorization policies
ms365_authentication_method_policy Authentication method policies
ms365_security_defaults_policy Security defaults policy
ms365_named_location Named locations for CA

Security

Resource Description
ms365_secure_score Secure Score with control scores

Collaboration

Resource Description
ms365_team Microsoft Teams
ms365_domain Verified domains
ms365_directory_setting Directory settings

Resource Fields

ms365_user

Field Type Description
id string User ID
displayName string Display name
userPrincipalName string User principal name (email)
mail string Email address
accountEnabled bool Account is enabled
userType string User type (Member, Guest)
passwordPolicies string Password policies
onPremisesSyncEnabled bool Synced from on-premises
assignedLicenses []object Assigned license SKUs
authenticationMethods []object Authentication methods
mfaEnabled bool MFA is enabled (computed)

ms365_secure_score

Field Type Description
id string Secure Score ID
currentScore double Current security score
maxScore double Maximum possible score
controlScores []object Individual control scores

Control Score Fields:

Field Type Description
controlName string Control identifier
score double Current score for control
description string Control description

ms365_conditional_access_policy

Field Type Description
id string Policy ID
displayName string Policy name
state string State (enabled, disabled, enabledForReportingButNotEnforced)
conditions object Policy conditions
grantControls object Grant controls (MFA, etc.)
sessionControls object Session controls

ms365_authorization_policy

Field Type Description
id string Policy ID
displayName string Policy name
guestUserRoleId string Guest user role GUID
allowInvitesFrom string Who can invite guests
blockMsolPowerShell bool Block legacy PowerShell
defaultUserRolePermissions object Default user permissions

defaultUserRolePermissions Fields:

Field Type Description
allowedToCreateApps bool Users can register apps
allowedToCreateSecurityGroups bool Users can create security groups
allowedToCreateTenants bool Users can create tenants
allowedToReadOtherUsers bool Users can read other users

ms365_security_defaults_policy

Field Type Description
id string Policy ID
displayName string Policy name
isEnabled bool Security defaults enabled

ms365_directory_role

Field Type Description
id string Role ID
displayName string Role name
description string Role description
memberCount int Number of members

ms365_domain

Field Type Description
id string Domain name
isDefault bool Is default domain
isInitial bool Is initial domain
isVerified bool Domain is verified
passwordValidityPeriodInDays int Password validity period
serviceConfigurationRecords []object Required DNS records

ms365_device_configuration

Field Type Description
id string Configuration ID
displayName string Configuration name
configurationType string Configuration OData type
storageRequireDeviceEncryption bool Require device encryption
passwordMinimumLength int Minimum password length
passcodeMinimumLength int Minimum passcode length (iOS)

ms365_team

Field Type Description
id string Team ID
displayName string Team name
guestSettings object Guest permissions

guestSettings Fields:

Field Type Description
allowCreateUpdateChannels bool Guests can create/update channels
allowDeleteChannels bool Guests can delete channels

ms365_risky_user

Field Type Description
id string User ID
userDisplayName string Display name
userPrincipalName string UPN
riskLevel string Risk level (none, low, medium, high)
riskState string Risk state (atRisk, remediated, dismissed)
riskLastUpdatedDateTime timestamp Last update time

Example Policies

Identity Protection

queries:
  - uid: sign-in-risk-policy
    title: Sign-in risk policies are configured
    resource: ms365_secure_score
    severity: high
    query: |
      resource.controlScores.exists(c,
        c.controlName == "SigninRiskPolicy" && c.score >= 7.0
      )
    docs: |
      Sign-in risk policies detect suspicious sign-in attempts.
    remediation: |
      Configure a sign-in risk policy in Conditional Access.

  - uid: user-risk-policy
    title: User risk policies are configured
    resource: ms365_secure_score
    severity: critical
    query: |
      resource.controlScores.exists(c,
        c.controlName == "UserRiskPolicy" && c.score >= 7.0
      )
    docs: |
      User risk policies detect compromised accounts.
    remediation: |
      Configure a user risk policy in Conditional Access.

  - uid: admin-mfa-enabled
    title: MFA enabled for administrators
    resource: ms365_secure_score
    severity: critical
    query: |
      resource.controlScores.exists(c,
        c.controlName == "AdminMFAV2" && c.score >= 10.0
      )
    docs: |
      All administrative accounts should have MFA enabled.
    remediation: |
      Create a Conditional Access policy requiring MFA for admin roles.

Conditional Access

queries:
  - uid: conditional-access-enabled
    title: Conditional Access policies are active
    resource: ms365_conditional_access_policy
    severity: high
    query: resource.state == "enabled"
    docs: |
      Conditional Access policies should be enabled, not in report-only mode.
    remediation: |
      Set policy state to "On" instead of "Report-only".

  - uid: legacy-auth-blocked
    title: Legacy authentication is blocked
    resource: ms365_secure_score
    severity: high
    query: |
      resource.controlScores.exists(c,
        c.controlName == "BlockLegacyAuthentication" && c.score >= 8.0
      )
    docs: |
      Legacy protocols don't support MFA and are security risks.
    remediation: |
      Create a CA policy blocking Exchange ActiveSync and other clients.

Security Defaults

queries:
  - uid: security-defaults-disabled
    title: Security defaults disabled for custom CA
    resource: ms365_security_defaults_policy
    severity: medium
    query: resource.isEnabled == false
    docs: |
      Disable security defaults when using custom Conditional Access.
    remediation: |
      Disable security defaults before enabling custom CA policies.

Directory Roles

queries:
  - uid: global-admin-count
    title: Global admins between 2 and 4
    resource: ms365_directory_role
    severity: high
    query: |
      resource.displayName != "Global Administrator" ||
      (resource.memberCount >= 2 && resource.memberCount <= 4)
    docs: |
      Too few admins risks lockout; too many increases attack surface.
    remediation: |
      Ensure 2-4 Global Administrators are assigned.

Guest Access

queries:
  - uid: guest-access-restricted
    title: Guest users have restricted access
    resource: ms365_authorization_policy
    severity: medium
    query: |
      resource.guestUserRoleId == "2af84b1e-32c8-42b7-82bc-daa82404023b" ||
      resource.guestUserRoleId == "10dae51f-b6af-4016-8d66-8c2a99b929b3"
    docs: |
      Guests should have limited directory access.
      - Restricted Guest: 2af84b1e-32c8-42b7-82bc-daa82404023b
      - Guest User: 10dae51f-b6af-4016-8d66-8c2a99b929b3
    remediation: |
      Set guest user role to "Restricted Guest User" in External Identities.

Device Configuration

queries:
  - uid: android-encryption
    title: Android devices require encryption
    resource: ms365_device_configuration
    severity: high
    query: |
      resource.configurationType != "#microsoft.graph.androidGeneralDeviceConfiguration" ||
      resource.storageRequireDeviceEncryption == true
    docs: |
      Android devices should require storage encryption.
    remediation: |
      Enable encryption requirement in Intune device configuration.

  - uid: minimum-password-length
    title: Minimum password length enforced
    resource: ms365_device_configuration
    severity: high
    query: |
      !has(resource.passwordMinimumLength) ||
      resource.passwordMinimumLength >= 8
    docs: |
      Passwords should be at least 8 characters (NIST SP 800-63).
    remediation: |
      Set minimum password length to 8+ in device configuration.

CLI Reference

# Basic scan
kspec scan ms365 <tenant-id> \
  --client-id <client-id> \
  --client-secret <client-secret> \
  -f <policy-file>

# Using environment variables
kspec scan ms365 <tenant-id> -f <policy-file>

# Scan with policy directory
kspec scan ms365 <tenant-id> \
  --client-id <client-id> \
  --client-secret <client-secret> \
  -d <policy-directory>

Options

Flag Description
-f, --policy Policy file to use
-d, --policy-dir Directory containing policy files
--client-id Application (client) ID
--client-secret Client secret

CI/CD Integration

GitHub Actions

name: MS365 Security Scan

on:
  schedule:
    - cron: '0 6 * * *'
  workflow_dispatch:

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.21'

      - name: Build kspec
        run: go build -o kspec ./cmd/kspec

      - name: Run MS365 Security Scan
        env:
          AZURE_TENANT_ID: ${{ secrets.MS365_TENANT_ID }}
          AZURE_CLIENT_ID: ${{ secrets.MS365_CLIENT_ID }}
          AZURE_CLIENT_SECRET: ${{ secrets.MS365_CLIENT_SECRET }}
        run: |
          ./kspec scan ms365 $AZURE_TENANT_ID \
            -f policies/ms365-security.yml

Azure DevOps

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: GoTool@0
    inputs:
      version: '1.21'

  - script: go build -o kspec ./cmd/kspec
    displayName: 'Build kspec'

  - script: |
      ./kspec scan ms365 $(MS365_TENANT_ID) \
        --client-id $(MS365_CLIENT_ID) \
        --client-secret $(MS365_CLIENT_SECRET) \
        -f policies/ms365-security.yml
    displayName: 'Run MS365 Security Scan'

License Requirements

Some features require specific licenses:

Feature Required License
Identity Protection (risky users) Azure AD Premium P2
Conditional Access Azure AD Premium P1
Intune Device Management Microsoft Intune
Advanced Security Features Microsoft 365 E5 Security

Troubleshooting

Authentication Failed

Error: failed to create client secret credential

Causes:

  • Invalid credentials
  • Expired client secret
  • Wrong tenant ID

Solutions:

  • Verify tenant ID, client ID, and client secret
  • Regenerate client secret if expired
  • Ensure app registration exists in correct tenant

Permission Errors

Error: Insufficient privileges to complete the operation

Causes:

  • Missing API permissions
  • Admin consent not granted
  • Wrong permission type (Delegated vs Application)

Solutions:

  • Add required permissions in app registration
  • Grant admin consent
  • Use Application permissions, not Delegated

License Errors

Error: failed to get risky users: Resource not found

Causes:

  • Feature requires premium license
  • License not assigned to tenant

Solutions:

  • Check license requirements for the feature
  • Acquire appropriate license (e.g., Azure AD P2 for Identity Protection)

Rate Limiting

Error: Request throttled

Causes:

  • Too many API requests
  • Tenant-level throttling

Solutions:

  • Wait and retry
  • Reduce scan frequency
  • Contact Microsoft support for throttling limits