AWS Lambda

Run the Microsoft Entra ID and Microsoft licenses syncs on a schedule from AWS Lambda with the Viio Python SDK.

Last updated

This example runs the Microsoft Entra ID and Microsoft licenses scripts as two scheduled AWS Lambda functions. Each invocation runs one complete batch.

For SDK requirements and environment variables, see Shared Setup.

Project Layout

Put the two example scripts next to a handler module, a requirements file, and an AWS SAM template:

.
├── aws_lambda.py
├── entra_employees.py
├── microsoft_licenses.py
├── requirements.txt
└── template.yaml

requirements.txt:

viio-sync-api-sdk>=0.5.0

The scripts use only the Python standard library and the SDK.

Lambda Handlers

Create aws_lambda.py:

import asyncio
from typing import Any

import entra_employees
import microsoft_licenses


def employees_handler(event: dict[str, Any], context: object) -> dict[str, Any]:
    del event, context
    counts = asyncio.run(entra_employees.run_batch(entra_employees.sync_employees))
    return {"ok": True, **counts}


def licenses_handler(event: dict[str, Any], context: object) -> dict[str, Any]:
    del event, context
    counts = asyncio.run(microsoft_licenses.run_batch(microsoft_licenses.sync_microsoft_licenses))
    return {"ok": True, **counts}

asyncio.run creates the Sync API client and closes it inside the invocation's event loop. This prevents a reused Lambda execution environment from keeping a gRPC AsyncIO channel bound to an earlier event loop.

If a sync fails, run_batch aborts the batch and the handler raises the error, so the invocation is reported as failed.

Schedule with AWS SAM

Create template.yaml. It deploys both functions with Python 3.11 on Arm64 and a 15-minute timeout. Amazon EventBridge Scheduler runs the employee sync at 02:00 UTC and the license sync at 03:00 UTC, so the two batches don't overlap:

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Nightly Microsoft Graph syncs to the Viio Sync API

Parameters:
  MicrosoftTenantId:
    Type: String
  MicrosoftClientId:
    Type: String
  MicrosoftClientSecret:
    Type: String
    NoEcho: true
  ViioSyncApiEndpoint:
    Type: String
    Default: https://sync.viio.io
  ViioSyncApiKey:
    Type: String
    NoEcho: true
  ViioDirectIntegrationInstallationId:
    Type: String

Globals:
  Function:
    Architectures:
      - arm64
    CodeUri: .
    Runtime: python3.11
    MemorySize: 512
    Timeout: 900
    Environment:
      Variables:
        MICROSOFT_TENANT_ID: !Ref MicrosoftTenantId
        MICROSOFT_CLIENT_ID: !Ref MicrosoftClientId
        MICROSOFT_CLIENT_SECRET: !Ref MicrosoftClientSecret
        VIIO_SYNC_API_ENDPOINT: !Ref ViioSyncApiEndpoint
        VIIO_SYNC_API_KEY: !Ref ViioSyncApiKey
        VIIO_DIRECT_INTEGRATION_INSTALLATION_ID: !Ref ViioDirectIntegrationInstallationId

Resources:
  EmployeeSyncFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: aws_lambda.employees_handler
      Events:
        NightlyEmployeeSync:
          Type: ScheduleV2
          Properties:
            ScheduleExpression: cron(0 2 * * ? *)
            ScheduleExpressionTimezone: UTC

  LicenseSyncFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: aws_lambda.licenses_handler
      Events:
        NightlyLicenseSync:
          Type: ScheduleV2
          Properties:
            ScheduleExpression: cron(0 3 * * ? *)
            ScheduleExpressionTimezone: UTC

Lambda stops a function after 15 minutes. If a sync can take longer for your tenant, run the script on a scheduler without that limit, such as a container task.

Package Native Dependencies

grpcio contains native code. When building an Arm64 Lambda ZIP or layer on another operating system, install the matching Linux wheel:

python -m pip install \
  --platform manylinux2014_aarch64 \
  --implementation cp \
  --python-version 3.11 \
  --only-binary=:all: \
  --target package \
  "viio-sync-api-sdk>=0.5.0"

Replace 3.11 with the target Lambda runtime version. For an x86-64 function, use manylinux2014_x86_64. Python Lambda container images can install the package normally.

Back to the examplesLearn the batch lifecycle