microsoft-cloud

v0.1.1 collector

Collects Microsoft Entra ID and Azure security posture metrics

Install

epack install collector microsoft-cloud

Adds to epack.yaml, resolves dependencies, downloads binary.

Usage

Run all configured collectors and build a pack:

epack collect

Runs all collectors in epack.yaml and outputs an evidence pack.

Configuration

Or add manually to epack.yaml:

collectors:
  microsoft-cloud:
    source: https://github.com/locktivity/epack-collector-microsoft-cloud

Then run epack install to lock and sync.

Microsoft Cloud Collector Overview

The Microsoft Cloud collector gathers Microsoft Entra ID and Azure security posture metrics for evidence packs. It is designed for continuous monitoring and posture drift detection across a Microsoft tenant and a configured set of Azure subscriptions.

The collector emits aggregate evidence only. It does not emit usernames, email addresses, raw sign-in rows, resource names, raw tags, policy bodies, tokens, secrets, or credential values.

Artifacts

Every successful run emits the detailed Entra artifact:

  • artifacts/microsoft-cloud.entra.json

When normalized identity posture fields are available, the collector also emits:

  • artifacts/microsoft-cloud.idp-posture.json
  • Schema: evidencepack/idp-posture@v1

When subscription_ids are configured and at least one Azure subscription can be read, the collector also emits:

  • artifacts/microsoft-cloud.azure.json
  • artifacts/microsoft-cloud.cloud-posture.json
  • Normalized cloud schema: evidencepack/cloud-posture@v1

The detailed schema files live in:

  • docs/schema/microsoft-cloud.entra.v1.0.0.json
  • docs/schema/microsoft-cloud.azure.v1.0.0.json

Collection Levels

The optional level config controls collection depth. Levels are cumulative.

Level Adds Does not emit
trust Tenant identity, MFA registration coverage, Conditional Access posture, security defaults, privileged role counts, app credential hygiene, identity secure score, Azure RBAC, Defender, Policy, storage, key vault, SQL, compute, backup, network, and logging posture. Usernames, emails, app names, resource names, sign-in rows, raw policy bodies.
audit Trust plus Azure resource inventory aggregates and ownership tag coverage. Raw resource names, raw tag values, resource IDs.
internal Audit plus aggregate Entra directory activity and sign-in monitoring counts for the last 7 days. Raw audit events, raw sign-in rows, usernames, emails, IP addresses, device details.

If level is omitted, the collector runs at trust.

Microsoft APIs

The collector uses Microsoft Graph v1.0 only. Beta endpoints are not allowed in v1. The route contract is documented in docs/graph-endpoints.md.

Azure posture is collected through Azure Resource Manager management-plane APIs. The collector does not call Azure data-plane APIs and does not read customer content.

Licensing and Partial Data

Some Microsoft surfaces require Entra ID P1, P2, or Entra ID Governance licensing. If a licensed surface is unavailable, the collector does not fail the whole run. It emits null for affected metrics where appropriate and records the unavailable capability in diagnostics.license.capabilities_unavailable.

Common examples:

  • MFA registration report unavailable without the right Entra licensing.
  • signInActivity unavailable without the right Entra licensing.
  • PIM eligibility schedules unavailable without Entra ID P2 or Entra ID Governance.

Privacy Boundary

The collector is built around aggregate posture evidence. It intentionally avoids:

  • Usernames and email addresses.
  • Per-user sign-in event rows.
  • IP addresses and device identifiers.
  • Application display names.
  • Azure resource names and raw tags.
  • Policy bodies and customer-authored policy text.
  • Tokens, secrets, private keys, password hashes, and credential values.

Subscription IDs and tenant IDs are emitted because they identify the scoped Microsoft environment for the evidence pack.

Microsoft Cloud Collector Configuration

This guide walks through the Microsoft-side setup and the epack.yaml configuration for the Microsoft Cloud collector.

1. Create an App Registration

In the Azure portal:

  1. Open App registrations.
  2. Select New registration.
  3. Name it epack-collector-microsoft-cloud.
  4. Use Accounts in this organizational directory only.
  5. Leave the redirect URI empty.
  6. Select Register.

Record these values:

  • Directory (tenant) ID: used as tenant_id.
  • Application (client) ID: used as client_id.

2. Choose an Auth Mode

The collector supports two auth modes.

Client Secret

Use this mode for local testing or environments where OIDC is not available.

In the app registration:

  1. Open Certificates & secrets.
  2. Select New client secret.
  3. Choose an expiration.
  4. Copy the secret value immediately.

Pass the secret as AZURE_CLIENT_SECRET.

GitHub Actions OIDC

Use this mode for GitHub Actions so the workflow does not store a long-lived Azure secret.

In the app registration:

  1. Open Certificates & secrets.
  2. Select Federated credentials.
  3. Select Add credential.
  4. Choose the GitHub Actions scenario.
  5. Enter the repository owner and repository name.
  6. Choose the exact branch, tag, pull request, or environment subject that will run the collector.

For this mode, set auth_mode: oidc.

Common OIDC setup failure: the federated credential subject is too narrow. For example, a credential for the main branch will not work on pull request workflows unless a matching pull request or environment subject is also configured.

3. Grant Microsoft Graph Application Permissions

In the app registration:

  1. Open API permissions.
  2. Select Add a permission.
  3. Choose Microsoft Graph.
  4. Choose Application permissions.
  5. Add the permissions below.
  6. Select Grant admin consent.

Required Microsoft Graph application permissions:

Permission Used for
Organization.Read.All Tenant identity and verified domains.
User.Read.All User posture denominator and account status.
AuditLog.Read.All MFA registration report, sign-in activity, sign-in monitoring, and directory activity.
Policy.Read.All Conditional Access policies and security defaults.
RoleManagement.Read.Directory Directory role assignments.
RoleEligibilitySchedule.Read.Directory PIM eligibility schedules.
Application.Read.All Enterprise app and app credential hygiene posture.
SecurityEvents.Read.All Identity secure score.

If a permission or license is missing for a non-blocking surface, the collector records a warning and continues.

4. Assign Azure Reader on Each Subscription

Azure collection requires the service principal to have Azure Reader on every subscription listed in subscription_ids.

In the Azure portal:

  1. Open Subscriptions.
  2. Select the subscription.
  3. Open Access control (IAM).
  4. Select Add > Add role assignment.
  5. Choose Reader.
  6. Under Assign access to, choose User, group, or service principal.
  7. Search for the app registration by its display name, not the client ID, if the client ID search returns no results.
  8. Select the service principal.
  9. Select Review + assign.

Repeat this for each subscription in subscription_ids.

5. Configure epack.yaml

Client secret mode:

collectors:
  microsoft-cloud:
    source: locktivity/epack-collector-microsoft-cloud@^0.1.0
    config:
      tenant_id: 00000000-0000-0000-0000-000000000000
      client_id: 11111111-1111-1111-1111-111111111111
      auth_mode: client_secret
      subscription_ids:
        - 22222222-2222-2222-2222-222222222222
      level: trust
    secrets:
      AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}

GitHub Actions OIDC mode:

collectors:
  microsoft-cloud:
    source: locktivity/epack-collector-microsoft-cloud@^0.1.0
    config:
      tenant_id: 00000000-0000-0000-0000-000000000000
      client_id: 11111111-1111-1111-1111-111111111111
      auth_mode: oidc
      subscription_ids:
        - 22222222-2222-2222-2222-222222222222
      level: audit

Config Reference

Key Required Description
tenant_id Yes Concrete Microsoft Entra tenant GUID. Do not use common or organizations.
client_id Yes Application client ID for the app registration.
auth_mode Yes client_secret or oidc.
subscription_ids Yes Azure subscription GUIDs to collect.
level No trust, audit, or internal. Defaults to trust.
azure_environment No Reserved for future sovereign cloud support.

Local Test

For local testing with a client secret:

export AZURE_CLIENT_SECRET="..."
cat > /tmp/microsoft-cloud-config.json <<'JSON'
{
  "tenant_id": "00000000-0000-0000-0000-000000000000",
  "client_id": "11111111-1111-1111-1111-111111111111",
  "auth_mode": "client_secret",
  "subscription_ids": ["22222222-2222-2222-2222-222222222222"],
  "level": "trust"
}
JSON

EPACK_COLLECTOR_CONFIG=/tmp/microsoft-cloud-config.json ./epack-collector-microsoft-cloud

Troubleshooting

Symptom Likely cause Fix
Authorization_RequestDenied from Graph Missing application permission or admin consent. Re-check API permissions and grant admin consent.
Azure artifact missing, with subscription errors Service principal lacks Reader on the subscription. Assign Azure Reader to the app service principal at the subscription scope.
PIM metrics are null Missing Entra ID P2 or Entra ID Governance license. Accept the diagnostic or enable the required license.
MFA registration metrics are null MFA registration report is unavailable for the tenant or app. Confirm AuditLog.Read.All, admin consent, and licensing.
OIDC works on main but fails on PRs Federated credential subject only matches main. Add a federated credential for the pull request or environment subject.

Microsoft Cloud Collector Examples

These examples show common configurations and abbreviated output shapes.

Trust-Level Client Secret Run

Use trust for aggregate evidence suitable for routine third-party assurance.

collectors:
  microsoft-cloud:
    source: locktivity/epack-collector-microsoft-cloud@^0.1.0
    config:
      tenant_id: 00000000-0000-0000-0000-000000000000
      client_id: 11111111-1111-1111-1111-111111111111
      auth_mode: client_secret
      subscription_ids:
        - 22222222-2222-2222-2222-222222222222
      level: trust
    secrets:
      AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}

Abbreviated artifacts:

[
  {
    "path": "artifacts/microsoft-cloud.entra.json"
  },
  {
    "path": "artifacts/microsoft-cloud.idp-posture.json",
    "schema": "evidencepack/idp-posture@v1"
  },
  {
    "path": "artifacts/microsoft-cloud.azure.json"
  },
  {
    "path": "artifacts/microsoft-cloud.cloud-posture.json",
    "schema": "evidencepack/cloud-posture@v1"
  }
]

Audit-Level Azure Inventory

Use audit when you need ownership-tag and resource-inventory aggregates for Azure posture drift detection.

collectors:
  microsoft-cloud:
    source: locktivity/epack-collector-microsoft-cloud@^0.1.0
    config:
      tenant_id: 00000000-0000-0000-0000-000000000000
      client_id: 11111111-1111-1111-1111-111111111111
      auth_mode: client_secret
      subscription_ids:
        - 22222222-2222-2222-2222-222222222222
      level: audit
    secrets:
      AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}

Abbreviated Azure inventory output:

{
  "accounts": [
    {
      "account_id": "22222222-2222-2222-2222-222222222222",
      "inventory": {
        "resources_count": 128,
        "resource_groups_count": 12,
        "regions_count": 3,
        "owner_tag_coverage_pct": 86,
        "environment_tag_coverage_pct": 91,
        "production_resources_pct": 42,
        "top_resource_types": [
          {
            "type": "Microsoft.Compute/virtualMachines",
            "count": 18
          }
        ]
      }
    }
  ]
}

The inventory output contains resource types and counts only. It does not emit resource names, resource IDs, or raw tags.

Internal-Level Monitoring

Use internal for deeper aggregate monitoring of Entra directory activity and sign-in posture.

collectors:
  microsoft-cloud:
    source: locktivity/epack-collector-microsoft-cloud@^0.1.0
    config:
      tenant_id: 00000000-0000-0000-0000-000000000000
      client_id: 11111111-1111-1111-1111-111111111111
      auth_mode: oidc
      subscription_ids:
        - 22222222-2222-2222-2222-222222222222
      level: internal

Abbreviated sign-in monitoring output:

{
  "sign_in_monitoring": {
    "lookback_hours": 168,
    "sign_ins_count": 2400,
    "failure_count": 75,
    "unique_users_count": 420,
    "unique_apps_count": 38,
    "legacy_client_sign_ins_count": 0,
    "risky_sign_ins_count": 3,
    "conditional_access_success_count": 2100,
    "conditional_access_failure_count": 22,
    "conditional_access_not_applied_count": 278,
    "applied_policies_count": 9,
    "applied_policy_evaluations_count": 6100,
    "applied_policy_success_count": 5800,
    "applied_policy_failure_count": 22,
    "applied_policy_not_applied_count": 278,
    "applied_policy_report_only_fail_count": 5,
    "top_failure_codes": [
      {
        "code": 50126,
        "count": 21
      }
    ]
  }
}

This output is aggregate only. It does not emit individual sign-ins, users, IP addresses, devices, or application display names.

License-Limited Tenant

If a tenant lacks a required Entra license, the collector still emits the available posture and records the unavailable capability.

{
  "posture": {
    "mfa_coverage": null,
    "denominator_enabled_member_users": 2
  },
  "diagnostics": {
    "warnings": [
      "Entra ID P1/P2 not licensed, mfa registration report reported as not_collected"
    ],
    "license": {
      "entra_tier": "unknown",
      "capabilities_unavailable": [
        "mfa_registration_report"
      ]
    }
  }
}

Subscription Reader Missing

If the app registration can read Graph but lacks Azure Reader on a listed subscription, the collector emits Entra artifacts and records a subscription error. Assign Reader to the app service principal at the subscription scope and rerun.

{
  "diagnostics": {
    "subscription_errors": [
      "22222222-2222-2222-2222-222222222222: arm request /subscriptions/[subscription_id] failed: status=403 code=AuthorizationFailed message=denied"
    ]
  }
}
v0.1.1 Latest
2026-05-29

**Full Changelog**: https://github.com/locktivity/epack-collector-microsoft-cloud/compare/v0.1.0...v0.1.1

darwin/amd64 darwin/arm64 linux/amd64 linux/arm64

Details

Publisher
locktivity
Latest
v0.1.1
Protocol
v1

Platforms

darwin/amd64 darwin/arm64 linux/amd64 linux/arm64

Links