IBM Bob Use Case · Arrow Experience Center

Liberating M365: Nine Python Tools for Open AI Integration

Microsoft 365 is your enterprise backbone — but Microsoft Copilot shouldn't be the only AI that gets to use it. These nine Python tools open M365 mail, calendar, scheduling, Teams, and SharePoint to IBM watsonx Orchestrate, using the same Graph API Copilot uses. Enterprise productivity data, freed from platform lock-in.

IBM watsonx Orchestrate Microsoft Graph API Microsoft 365 MSAL OAuth2 Azure AD M365 Liberation Frank Welder · Arrow ECS

The Numbers

9
Python AI Tools
6
M365 Services Opened
1
Auth Model — OAuth2
0
Hard-coded Credentials

The Problem: M365 Locked Behind One Platform

Microsoft 365 is the productivity backbone of virtually every enterprise — email, calendar, scheduling, Teams conversations, SharePoint knowledge bases, Excel workbooks, OneDrive files. Years of institutional knowledge live in these systems. Workflows are built on top of them. People depend on them every day.

But when it comes to AI, there's a problem hiding in plain sight: by default, the only AI that gets first-class access to all of it is Microsoft Copilot.

If your enterprise has invested in IBM watsonx — or any other AI orchestration platform — you can't point your AI agent at your calendar and say "schedule this meeting." You can't give your agent access to the SharePoint site your teams have been maintaining for three years. You can't trigger a workflow from Teams, extract insight from a shared Excel workbook, or monitor file collaboration activity — not without Microsoft's AI in the loop. The data is there. The need is real. The API is open and well-documented. The restriction is a platform default, not a technical wall.

This is platform lock-in disguised as a feature. Enterprise productivity data should serve the enterprise's full AI strategy — not just the productivity suite vendor's AI product.

🤖 BOB AS INTEGRATION ARCHITECT

Bob's role on this project was integration architect and security design partner. Given the goal — open M365 to watsonx Orchestrate — Bob mapped the full Microsoft Graph API surface relevant to enterprise AI agents, identified the single unified authentication model that spans all nine tools (service principal with client_credentials grant), designed each tool's Python interface to match watsonx Orchestrate's agent tool contract, and enforced zero-credential-on-disk discipline throughout. What emerged wasn't nine separate integrations — it was one coherent open-platform pattern with nine manifestations. The Azure Graph token validation already proven live in the AI Dev Session (Gate 5) was the seed. This is the full harvest.

The Solution: One Auth Model, Nine Capabilities

The Microsoft Graph API is Microsoft's unified gateway to M365 — it's what Copilot calls under the hood, and it's fully open to any authenticated application. The key insight is that one Azure AD app registration with the right Graph permissions unlocks everything. Mail, calendar, SharePoint, OneDrive, Teams, Power BI — all reachable from the same token.

The authentication pattern across all nine tools is identical: msal.ConfidentialClientApplication acquires a bearer token using a service principal (client_credentials grant). That token is passed as a request header. The M365 service responds. The watsonx Orchestrate agent gets structured data it can reason over and act on. No user interaction required. No Copilot required.

The nine tools below are deployed as native IBM watsonx Orchestrate agent tool actions — each wrapped in the wxO tool decorator pattern and registered against the AEC's on-premises Orchestrate instance. The same pattern is transferable to any platform that can invoke a Python function.

The Nine Tools

Tool 1

Automated Email Distribution via Outlook

Business problem unlocked: AI agents can now send, read, search, and manage email on behalf of enterprise users or shared mailboxes — directly from watsonx Orchestrate workflows — without routing through a human or requiring Copilot to draft or dispatch messages.

Calls the MS Graph /v1.0/users/{userId}/sendMail endpoint. The agent composes a structured message payload — recipients, subject, HTML body, attachments — and posts it via an MSAL-authenticated requests session. Read and search operations use /v1.0/users/{userId}/messages with OData filter parameters. Key libraries: msal, requests.

import msal, requests app = msal.ConfidentialClientApplication( CLIENT_ID, authority=f"https://login.microsoftonline.com/{TENANT_ID}", client_credential=CLIENT_SECRET ) token = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"]) headers = {"Authorization": f"Bearer {token['access_token']}", "Content-Type": "application/json"} payload = { "message": { "subject": subject, "body": {"contentType": "HTML", "content": html_body}, "toRecipients": [{"emailAddress": {"address": recipient}}] } } requests.post( f"https://graph.microsoft.com/v1.0/users/{sender_upn}/sendMail", headers=headers, json=payload )

Benefit: AI-initiated email workflows that previously required a human relay now execute end-to-end. Automated notifications, summaries, and distribution list communications are fully agent-driven.

Tool 2

Document Management Integration with SharePoint

Business problem unlocked: SharePoint is where enterprise knowledge lives — policy documents, project files, technical specs accumulated over years. AI agents can now read, search, upload, and manage those documents directly, making SharePoint a live knowledge source rather than a silo.

Uses the Graph /v1.0/sites/{siteId}/drive/items endpoint family for file operations, and /v1.0/sites/{siteId}/lists for SharePoint list access. The office365-rest-python-client library provides a higher-level abstraction for complex document library operations. Authentication follows the same service principal pattern. Key libraries: office365-rest-python-client, msal.

from office365.sharepoint.client_context import ClientContext from office365.runtime.auth.client_credential import ClientCredential credentials = ClientCredential(CLIENT_ID, CLIENT_SECRET) ctx = ClientContext(SHAREPOINT_SITE_URL).with_credentials(credentials) # Read a document library library = ctx.web.lists.get_by_title("Project Documents") items = library.items.select(["Title", "FileRef", "Modified"]).get().execute_query() # Upload a file generated by the agent target_folder = ctx.web.get_folder_by_server_relative_url("/sites/aec/Shared Documents") with open(local_file_path, "rb") as f: target_folder.upload_file(file_name, f.read()).execute_query()

Benefit: Agents can retrieve relevant documents before answering questions, generate reports and publish them to SharePoint, and keep document libraries current — without any human copy-paste workflow.

Tool 3

Calendar Sync and Event Creation for Teams Meetings

Business problem unlocked: Scheduling has always required a human intermediary — even when the AI agent knows exactly who needs to meet, when they're available, and what the meeting is for. This tool gives agents the ability to check availability, create calendar events, and book Teams meetings directly.

Calls /v1.0/users/{userId}/calendar/events to create events with Teams meeting links (via isOnlineMeeting: true and onlineMeetingProvider: teamsForBusiness). Availability queries use the /v1.0/users/{userId}/getSchedule batch endpoint to check free/busy across multiple attendees before proposing a time. Key libraries: msal, requests.

# Check availability across attendees avail_payload = { "schedules": [attendee["emailAddress"]["address"] for attendee in attendees], "startTime": {"dateTime": start_iso, "timeZone": "Eastern Standard Time"}, "endTime": {"dateTime": end_iso, "timeZone": "Eastern Standard Time"}, "availabilityViewInterval": 30 } schedule = requests.post( f"https://graph.microsoft.com/v1.0/users/{organizer_upn}/getSchedule", headers=headers, json=avail_payload ).json() # Create the Teams meeting event event_payload = { "subject": meeting_title, "attendees": attendees, "start": {"dateTime": confirmed_start, "timeZone": "Eastern Standard Time"}, "end": {"dateTime": confirmed_end, "timeZone": "Eastern Standard Time"}, "isOnlineMeeting": True, "onlineMeetingProvider": "teamsForBusiness" } requests.post( f"https://graph.microsoft.com/v1.0/users/{organizer_upn}/events", headers=headers, json=event_payload )

Benefit: End-to-end scheduling from a single agent intent — availability checked, Teams meeting created, invites sent. Eliminates the back-and-forth that typically requires a human scheduler or Copilot.

Tool 4

Data Extraction from Excel Workbooks using Power Query

Business problem unlocked: Enterprise Excel workbooks are packed with structured data — financial models, project trackers, inventory sheets — that AI agents have never been able to directly read and reason over. This tool bridges that gap, turning any shared Excel file into a live data source for agent analysis.

Retrieves Excel files from OneDrive/SharePoint via Graph /v1.0/drives/{driveId}/items/{itemId}/content, then parses them locally with openpyxl or pandas. For workbooks with Power Query transformations already defined, the tool extracts the refreshed data from the worksheet output range rather than the raw source. Key libraries: openpyxl, pandas, msal.

import io, openpyxl, pandas as pd # Download workbook bytes from SharePoint/OneDrive response = requests.get( f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/{item_id}/content", headers=headers ) # Parse with openpyxl for structured sheet access wb = openpyxl.load_workbook(io.BytesIO(response.content), data_only=True) ws = wb[sheet_name] # Convert to DataFrame for agent reasoning data = ws.values cols = next(data) df = pd.DataFrame(data, columns=cols) # Return structured summary to the watsonx Orchestrate agent return df.describe().to_dict()

Benefit: Agents can answer data questions ("what's the Q3 pipeline total?", "which projects are over budget?") from live Excel workbooks without any manual export or copy-paste step.

Tool 5

Real-time Collaboration Monitoring in OneDrive

Business problem unlocked: AI agents operating in a collaborative enterprise environment need awareness of what's changing — who edited what, when new files appear, when a document reaches a new version. This tool gives agents live visibility into OneDrive activity so they can act on change events rather than polling for stale data.

Uses Graph /v1.0/drives/{driveId}/root/delta — the delta query endpoint — to retrieve only changed items since the last sync token, making it efficient for continuous monitoring. File metadata, version history, and shared-with information are pulled from /v1.0/drives/{driveId}/items/{itemId}/versions and /v1.0/drives/{driveId}/items/{itemId}/permissions. Key libraries: msal, requests.

# Initial delta call — get current state + delta link delta_url = f"https://graph.microsoft.com/v1.0/drives/{drive_id}/root/delta" response = requests.get(delta_url, headers=headers).json() # Store the delta link for incremental polling delta_link = response.get("@odata.deltaLink") changed_items = response.get("value", []) # On next poll — only changed/new items are returned next_response = requests.get(delta_link, headers=headers).json() for item in next_response.get("value", []): if item.get("deleted"): agent.notify(f"Deleted: {item['id']}") else: agent.notify(f"Changed: {item['name']} — last modified {item['lastModifiedDateTime']}")

Benefit: Agents stay current with file activity without expensive full-drive scans. Change-driven workflows — notifications, approvals, archival triggers — execute based on real events, not scheduled guesses.

Tool 6

Custom Reporting via Power BI Dashboards

Business problem unlocked: Business intelligence data locked in Power BI dashboards has been inaccessible to AI agents that could act on it. This tool lets agents query Power BI datasets, retrieve report data, and even embed or export dashboard snapshots — turning BI insights into agent-actionable intelligence.

Authenticates to Power BI via the Power BI REST API scope (https://analysis.windows.net/powerbi/api/.default) using the same MSAL service principal pattern. Queries dataset tables via POST /v1.0/myorg/datasets/{datasetId}/executeQueries with a DAX expression. Report exports use POST /v1.0/myorg/reports/{reportId}/ExportTo. Key libraries: msal, requests.

# Power BI uses a different scope — same MSAL pattern token = app.acquire_token_for_client( scopes=["https://analysis.windows.net/powerbi/api/.default"] ) pbi_headers = {"Authorization": f"Bearer {token['access_token']}", "Content-Type": "application/json"} # Execute a DAX query against a dataset dax_payload = { "queries": [{"query": "EVALUATE SUMMARIZECOLUMNS('Sales'[Region], 'Sales'[Revenue])"}], "serializerSettings": {"includeNulls": True} } result = requests.post( f"https://api.powerbi.com/v1.0/myorg/datasets/{dataset_id}/executeQueries", headers=pbi_headers, json=dax_payload ).json() # Agent receives structured table results rows = result["results"][0]["tables"][0]["rows"]

Benefit: AI agents can answer questions grounded in live BI data, generate narrative summaries of dashboard results, and trigger downstream workflows based on KPI thresholds — all without a human reading the dashboard first.

Tool 7

Workflow Automation Using Azure Logic Apps

Business problem unlocked: Azure Logic Apps automate hundreds of enterprise business processes — but triggering them has always required a human action or a scheduled timer. This tool gives AI agents the ability to fire Logic App workflows programmatically, turning agent decisions into enterprise automation triggers.

Uses azure-mgmt-logic to list and inspect Logic App definitions, and fires HTTP-triggered Logic Apps via their callback URL (retrieved from WorkflowTriggers.list_callback_url()). Authentication uses azure-identity's ClientSecretCredential — the same service principal, different SDK. Key libraries: azure-mgmt-logic, azure-identity, requests.

from azure.identity import ClientSecretCredential from azure.mgmt.logic import LogicManagementClient credential = ClientSecretCredential(TENANT_ID, CLIENT_ID, CLIENT_SECRET) logic_client = LogicManagementClient(credential, SUBSCRIPTION_ID) # Get the HTTP trigger callback URL for the target Logic App callback = logic_client.workflow_triggers.list_callback_url( resource_group_name=RG_NAME, workflow_name=LOGIC_APP_NAME, trigger_name="manual" ) # Fire the workflow with agent-constructed payload response = requests.post( callback.value, json={"triggered_by": "watsonx_orchestrate", "payload": agent_payload} ) # HTTP 202 Accepted = Logic App queued successfully

Benefit: Agent decisions ("this approval request is low-risk — auto-approve and notify") become real enterprise actions. Logic App workflows that previously waited on human triggers now respond to AI reasoning in real time.

Tool 8

Secure File Transfer and Sharing Solutions

Business problem unlocked: Enterprise file sharing involves more than moving bytes — it requires setting correct permissions, managing share links with appropriate access levels, and ensuring sensitive files don't become publicly accessible. This tool gives agents the ability to handle file transfers and share operations with the same security discipline a careful human administrator would apply.

Creates permission-scoped sharing links via Graph /v1.0/drives/{driveId}/items/{itemId}/createLink with explicit type ("view"/"edit") and scope ("organization"/"anonymous") controls. Sensitive files are optionally encrypted client-side with cryptography (Fernet symmetric encryption) before upload, with the key stored in HashiCorp Vault — not in M365. Key libraries: office365-rest-python-client, cryptography, msal.

from cryptography.fernet import Fernet # Encrypt file before upload (key lives in Vault — not in M365) fernet_key = vault_client.secrets.kv.v2.read_secret(path="m365/file-enc-key") f = Fernet(fernet_key["data"]["data"]["key"].encode()) encrypted_data = f.encrypt(open(local_file_path, "rb").read()) # Upload encrypted bytes to SharePoint requests.put( f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/root:/{file_name}:/content", headers={**headers, "Content-Type": "application/octet-stream"}, data=encrypted_data ) # Create org-scoped view-only share link (no anonymous access) link_payload = {"type": "view", "scope": "organization"} link_result = requests.post( f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/{item_id}/createLink", headers=headers, json=link_payload ).json() share_url = link_result["link"]["webUrl"]

Benefit: Files generated or collected by AI agents are shared securely with the right people and the right access levels — automatically. Encryption keys never touch M365. Share links are scoped, not open.

Tool 9

Enhanced User Authentication with Conditional Access Policies

Business problem unlocked: Enterprise AI systems must understand who is allowed to do what — and enforce those boundaries dynamically. This tool gives agents the ability to query Azure AD Conditional Access policies, validate user access entitlements, and surface compliance state for any user or resource — enabling AI-driven access governance rather than static role checks.

Uses azure-mgmt-authorization to query role assignments and msal to verify token claims and user attributes. Conditional Access policy state is retrieved via Graph /v1.0/identity/conditionalAccess/policies (requires Policy.Read.All permission). Agent decisions about what data to surface or what actions to permit are gated on live policy evaluation. Key libraries: msal, azure-mgmt-authorization, azure-identity.

from azure.mgmt.authorization import AuthorizationManagementClient from azure.identity import ClientSecretCredential credential = ClientSecretCredential(TENANT_ID, CLIENT_ID, CLIENT_SECRET) authz_client = AuthorizationManagementClient(credential, SUBSCRIPTION_ID) # Check a user's role assignments on a specific resource scope assignments = list(authz_client.role_assignments.list_for_scope( scope=f"/subscriptions/{SUBSCRIPTION_ID}/resourceGroups/{RG_NAME}", filter=f"assignedTo('{user_object_id}')" )) # Fetch active Conditional Access policies via Graph ca_policies = requests.get( "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies", headers=headers ).json() # Agent gates its action on access state — not a hard-coded role list user_can_proceed = any( a.role_definition_id.endswith(REQUIRED_ROLE_ID) for a in assignments )

Benefit: AI agents operate within the enterprise's live access control posture — not a snapshot from last week. Policy changes propagate immediately. Agents that handle sensitive data do so with real-time entitlement awareness, not static permission lists.

Integration Architecture

All nine tools share the same authentication and data flow. One Azure AD app registration. One MSAL token acquisition call. One bearer token passed as a header. The specific M365 service — mail, calendar, SharePoint, Power BI, Logic Apps — is just a Graph API path away. This is the architectural point: one credential unlocks the entire M365 estate for any platform that can run Python.

Azure AD App Registration
MSAL OAuth2 Token
MS Graph API
M365 Service
watsonx Agent Tool
Enterprise Action

The Azure credentials — AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET — are stored in HashiCorp Vault and loaded into the shell session at startup via the AI Dev Session startup protocol (Gate 3). They are never written to disk. This pattern is already proven live: the AI Dev Session use case validates an Azure Graph bearer token as part of Gate 5 every time the dev environment boots.

Technology Stack

Microsoft Graph API

Unified REST gateway to all M365 services — the same API Copilot calls. Open, documented, and fully accessible to any authenticated application.

MSAL (msal)

Microsoft Authentication Library for Python — handles OAuth2 token acquisition with service principal credentials. Consistent across all nine tools.

Azure Identity (azure-identity)

ClientSecretCredential for Azure SDK-native tools (Logic Apps, Authorization). Interoperable with MSAL service principal registrations.

openpyxl / pandas

Excel workbook parsing and structured data extraction. Turns shared workbooks into live, agent-queryable data sources.

office365-rest-python-client

High-level SharePoint and OneDrive operations. Handles document library traversal, list access, and file management cleanly.

cryptography (Fernet)

Client-side symmetric encryption for sensitive file transfer. Keys stored in HashiCorp Vault — never in M365 or on disk.

IBM watsonx Orchestrate

On-premises CPD deployment on the AEC's bare-metal fusion1 cluster. Each Python tool is registered as a native wxO agent tool action.

HashiCorp Vault

Azure AD credentials loaded into the shell session at dev startup. Zero hard-coded credentials across all nine tools.

Outcomes

M365 mail, calendar, Teams, SharePoint, and files opened to IBM watsonx Orchestrate
Microsoft Copilot is no longer the only AI with first-class M365 access
One Azure AD app registration — one auth model — nine unlocked capabilities
Azure AD credentials loaded from Vault at startup — zero hard-coded secrets
Agent-driven scheduling, email, document management, and reporting without human relay
Power BI and Logic Apps brought into the watsonx Orchestrate action surface
File transfers with client-side encryption and org-scoped share links — not open URLs
Pattern is platform-agnostic — transferable to any AI system that can call a Python function

See the full AEC environmentAll R Co programs and use cases at rcoace.com — including the AI Dev Session that first proved the Azure Graph connection live.