Tag Archives: Python

Python logo
Working with SharePoint from Python application

Python connect to SharePoint via Graph API with Delegated Permissions

Below is the sample Python code to authenticate against Microsoft 365 as current user with MSA library and to call Microsoft Graph API – specifically get SharePoint Site, get Site lists with requests library.

But first, you have to have an App Registration in Azure (Entra ID) with delegated permissions consented and authentication configured.

Delegated Permissions

If your solution needs access to all SharePoint sites – consider Sites.FullControl.All or Sites.Manage.All or Sites.ReadWrite.All or Sites.Read.All depending on access level you need. Effective permissions would be a min from both – permissions configured for app registration and permissions current user have. Once consented at the app registration – these permissions will work right away.

If your solution needs access to one (or a few) SharePoint sites – consider Sites.Selected API permissions as it will scope down access to only sites that are required for your solution to work. Remember, Sites.Selected API permissions, even consented at the app registration, does require second step – SharePoint admin should provide (read or write or manage or fullcontrol) permissions for the app registration to a specific site or sites.

Authentication

You’d also need to configure authentication blade. How? It depends on the kind of application you are building etc. For example for native apps I do:
– add platform – “Mobile and Desktop app”
– select “https://login.microsoftonline.com/common/oauth2/nativeclient”
– select “msal096fd951-7285-4e4f-9c1f-23a393556b19://auth (MSAL only)”
– add custom Redirect URI: “http://localhost”

This config works for Python code below

Python Code

You’d need to install/import the libraries: json, configparser, msal, requests

Here is the code:

import json
import configparser
import msal
import requests

config = configparser.ConfigParser()
config.read('config.cfg')
client_id = config.get('delegated','clientId')
authority = config.get('delegated','authority')
scopes = config.get('delegated','scope').split()
siteId = config.get('delegated','siteId')
print( client_id)
print( authority)
print( scopes)
print( siteId)

global_token_cache = msal.TokenCache()
global_app = msal.PublicClientApplication(
    client_id,
    authority=authority,  # For Entra ID or External ID
    token_cache=global_token_cache,  
    )

def acquire_and_use_token():
    # The pattern to acquire a token looks like this.
    result = None
    result = global_app.acquire_token_interactive(scopes)

    if "access_token" in result:
        print("Token was obtained from:", result["token_source"])  # Since MSAL 1.25
        # print("Token acquisition result", json.dumps(result, indent=2))
        return result["access_token"]
    else:
        print("Token acquisition failed", result)  # Examine result["error_description"] etc. to diagnose error
        return None


token = acquire_and_use_token()


http_headers = {'Authorization': 'Bearer ' + token,
                'Accept': 'application/json',
                'Content-Type': 'application/json'}

graph_url = 'https://graph.microsoft.com/v1.0/sites/' + siteId + '?$select=id,webUrl,displayName'

siteResponse = requests.get(graph_url, headers=http_headers)
print(siteResponse.status_code)
site = json.loads(siteResponse.content)
# print("Site (raw) : ")
# print(site)
print("Site webUrl : ", site["webUrl"])
print("Site displayName : ", site["displayName"])

# Lists
graph_url = 'https://graph.microsoft.com/v1.0/sites/' + siteId + '/lists'
listsResponse = requests.get(graph_url, headers=http_headers)
print(listsResponse.status_code)
lists = json.loads(listsResponse.content)
# print("Site lists (raw):")
# print(lists)
print("Site lists:")
for list in lists["value"]:
    print("  Display Name:", list["displayName"])
    print("   Id:", list["id"])
    print("   Web Url:", list["webUrl"])
    print("   Created Date:", list["createdDateTime"])
    print("   Last Modified Date:", list["lastModifiedDateTime"])

Application permissions

If your scenario is to call Graph API from Python with application permissions (aka unattended or daemon app) – the main difference is authentication. It is described here. It also requires App registration configured like this.

Working with SharePoint from Python code via Graph API

Python code samples published by Microsoft at the Microsoft Graph API reference pages use GraphServiceClient module. But you also can use just requests module and call Microsoft graph API directly, using requests.post or requests.get methods. Here I’m sharing my Python code samples.

https://github.com/VladilenK/m365-with-Python/tree/main/Graph-API-Plain

Calling Microsoft Graph API from Python

Below is how I authenticate and call Microsoft Graph API to work with SharePoint from Python application.

Plain

no MSAL or Azure libraries used:

import requests
import json
from secrets import clientSc 

clientId = "7e60c372-ec15-4069-a4df-0ab47912da46"
# clientSc = "<imported>" 
tenantId = "7ddc7314-9f01-45d5-b012-71665bb1c544"

apiUri = "https://login.microsoftonline.com/" + tenantId + "/oauth2/v2.0/token"

body = {
    "client_id"     : clientId,
    "client_secret" : clientSc,
    "scope"         : "https://graph.microsoft.com/.default",
    "grant_type"    : "client_credentials" 
}

response = requests.post(apiUri, data=body)
token = json.loads(response.content)["access_token"]

graph_url = 'https://graph.microsoft.com/v1.0/sites/root'
site = requests.get(
    graph_url,
    headers={'Authorization': 'Bearer {0}'.format(token)}
)

print(site.content)
print(json.loads(site.content)["webUrl"])

secrets is a Python file where I assign client secret to variable clientSc (so my secret is not shared on github). This is ok for demo purposes but generally, you should not hard-code secrets but keep secrets somewhere safe (Vault).

MSAL

Using MSAL library to get bearer token:
https://github.com/VladilenK/m365-with-Python/tree/main/Graph-API-MSAL

References

Using SharePoint REST API from Python code

Using Microsoft Graph API is a preferred and recommended way to connect to SharePoint Online and other Microsoft 365 resources programmatically from Python code. But if by some reason you are required to use classic SharePoint REST API, here is how it is done.

Prerequisites:

  • Azure Registered Application
    you must register your application in Azure and configure it properly
    • Authentication blade must be configured for authenticate as current user
    • Certificates and/or secrets must be configured for daemon app (unattended access)
  • API permissions
    your Azure app registration must have API permissions configured based on resources you need access to and authentication method chosen
    here is how to configure API permissions for your app
  • Office365-REST-Python-Client library installed

Using client Id and client secret to access SharePoint REST API from Python

Errors

Possible errors and diagnostic messages

HTTPError: 401 Client Error: Unauthorized for url…
The provided client secret keys for app … are expired. Visit the Azure portal to create new keys for your app or consider using certificate credentials for added security

Code samples at GitHub

WIP…

References

FastAPI on Azure Functions with Azure API Management

Following Pamela Fox tutorial “FastAPI on Azure Functions with Azure API Management“.

The idea is to deploy FastAPI to Azure Functions the way auto-generated interactive documentation would be public, but actual API would be protected. Pamela solved it with Azure API Management and subscription keys:

“One of my goals was to have the documentation be publicly viewable (with no key) but the FastAPI API calls themselves require a subscription key. That split was the trickiest part of this whole architecture, and it started at the API Management level.”

Pamela published it in 3 parts:
– the idea and solution explained under her blog: FastAPI on Azure Functions with Azure API Management
– code and some initial steps at GitHub: pamelafox/fastapi-azure-function-apim
– video with more deploying details at YouTube: Deploying FastAPI app to Azure Functions + API Management

I will just repeat all the steps in this one-pager.

Environment I use: Linux Ubuntu + VS Code with “Dev Containers” extension and azd

  1. Clone https://github.com/pamelafox/fastapi-azure-function-apim
  2. Start visual studio code and reopen the project with container
  3. ensure it works locally with
    PYTHON_ISOLATE_WORKER_DEPENDENCIES=1 func host start
  4. Deploy it to Azure functions with (you’d answer questions):
    $ azd auth login
    $ azd init
    $ azd up
  5. Go to API management Service, Subscriptions, Add subscription, copy the key (secure it)
  6. From API management service, Overview – open Gateway URL and append it with “/public/docs”
  7. Try GET /generate_name as is – you’ll get “401”
  8. Try the same with subscription key – you’ll get “200”
  9. Save Request Url to call the API from your front-end app

Nest steps:

  • calling other APIs
  • connecting to Databases
  • using secrets
Python Logo

Python dev env memo

This is just a memo for myself how to Python with Anaconda Jupyter Notebooks

Anaconda: https://www.anaconda.com/
Anaconda prompt
cd your code
jupyter notebook

!pip install matplotlib
import matplotlib as plt
plt.__version__

Eclipse
Java IDE + Install New Software from http://www.pydev.org/updates/

PyCharm
File – Other Settings – Settings for New Projects – Project Interpreter – “+”

Spyder
adfsgv

Django

pip install django==3.0.3
django-admin startproject wisdompets
Set-Location "C:\Users\Vlad\code\Django2\wisdompets"
Get-ChildItem
python manage.py runserver
python manage.py startapp adoptions
python manage.py makemigrations

Virtual environment under Windows

python -m venv ./venv
.\venv\Scripts\activate
pip install Office365-REST-Python-Client
...
pip freeze > requirements.txt
deactivate

reference: Building Your First Python Analytics Solution by Janani Ravi