fellow StackOverflow users!
I am currently working on a Python project where I intend to create numerous MS Power Automate workflows (around 60). To facilitate this, I am planning to utilize the Web API to automate the workflow creation process through Python.
As per the MS Power Automate documentation, it is indicated that the API does not support creating "My Flows" directly. However, it mentions a workaround involving creating the workflows in "Solutions" through MS Flow Web API. However, my code ends in in a 405 response error.
Despite spending the last two days going through the documentation and numerous Google searches, I haven't found definitive information on whether it's even possible to create workflows via the API. I am beginning to wonder if I am missing something fundamental here.
I am hoping to get clarifications on the following:
Feasibility: Is it even possible to create workflows in "Solutions" from Python code through an API?
API Endpoint: Assuming it is possible, what is the correct endpoint to initiate the creation of a workflow?
HTTP Methods: What HTTP methods (GET, POST, PUT, DELETE, etc.) are permitted when interacting with this endpoint?
Request Payload: Are there any specific parameters or structures the request payload should adhere to?
Authentication: I have set up the necessary tokens and secret keys through Azure. What authentication method is recommended for securing the API calls?
Relevant Code:
CLIENT_ID="..."
CLIENT_SECRET="..."
TENANT_ID="..."
API_SCOPE="https://graph.microsoft.com/.default"
"""
This module is used to authenticate with the Microsoft Graph API using
OAuth 2.0 client credentials grant flow. It retrieves necessary credentials
from environment variables and requests an access token from Azure AD authentication endpoint.
Environment Variables:
- CLIENT_ID: The client ID of your Azure AD application
- CLIENT_SECRET: The client secret of your Azure AD application
- TENANT_ID: The ID of your Azure AD tenant
Functions:
- authenticate(): Obtain an OAuth 2.0 access token using client credentials grant flow
Usage:
- Ensure the required environment variables are set
- Import this module and call authenticate to retrieve an access token
Example:
from api_authenticate import authenticate
token = authenticate()
print(token)
"""
import os
import requests
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
def authenticate(client_id=None, client_secret=None, tenant_id=None, scope=None):
"""Authenticate with the MS Power Automate API and return an authentication token."""
# Retrieve environment variables
client_id = client_id or os.getenv('CLIENT_ID')
client_secret = client_secret or os.getenv('CLIENT_SECRET')
tenant_id = tenant_id or os.getenv('TENANT_ID')
scope = scope or os.getenv('API_SCOPE')
if not client_id or not client_secret or not tenant_id or not scope:
raise EnvironmentError("Required environment variables are not set.")
# Azure AD authentication endpoint
AUTH_URL = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
payload = {
'scope': scope,
'client_id': client_id,
'client_secret': client_secret,
'grant_type': 'client_credentials'
}
# Request to obtain token
response = requests.post(AUTH_URL, data=payload)
# Raise an exception if the request was unsuccessful
if response.status_code != 200:
print("Response Content:", response.content)
response.raise_for_status()
# Parse & return token
try:
token = response.json().get("access_token")
except ValueError:
raise ValueError("Failed to decode JSON response")
# Verify token is not None
if not token:
raise ValueError("Failed to retrieve access token")
return token
This function is called in the main file:
import os
import requests
from api_authenticate import authenticate
def create_workflow(robot_elements, scope=None):
"""
Create a workflow with the given data.
Parameters:
- robot_elements (dict): Dictionary containing robot elements to be used in the workflow
Returns:
- None
"""
# Validate robot_elements is a dictionary
if not isinstance(robot_elements, dict):
raise TypeError("robot_elements must be a dictionary.")
# Define Power Automate API endpoint
scope = scope or os.getenv('API_SCOPE')
if not scope:
raise EnvironmentError("Required scope is not set.")
# Authenticate & retrieve auhentification token
token = authenticate()
# Set up headers
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
}
# Create workflow using a PUT request
try:
response = requests.put(scope, robot_elements, headers = headers)
except requests.exceptions.RequestException as e:
print(f"Network error: {e}")
return None
if response.status_code in [200, 201]:
print("Workflow created successfully")
workflow_id = response.json().get("id") # Store the workflow ID for future use
if not workflow_id:
raise ValueError("Workflow ID is missing in the response.")
else:
print(f"Failed to create workflow. Status code: {response.status_code}")
print("Response:", response)
return None
# Iterate through elements and add them to the workflow using API calls
# (details would depend on the exact structure of dictionary and the Power Automate API capabilities at the time of implementation
for element in robot_elements:
# Here, you would make API calls to add each element to the workflow, using the workflow ID obtained in step 3
pass
What did I try?
In my efforts to resolve the 405 response error, I have debugged the code extensively. It breaks during the execution of the "create_workflow" function within the try section at the line:
response = requests.put(scope, robot_elements, headers = headers)
I even experimented with changing the HTTP method to POST instead of PUT, but this approach did not bear fruit.
I suspect that the issue might revolve around the scope or the Content-type parameter within the headers. However, I am unsure what to alter them to in order to rectify this.
Moreover, I validated the client secrets and tokens thoroughly, and they appear to be functioning as expected.
Considering the possibility of a wrongly structured payload, I exported a template workflow from MS Power Automate's web interface and converted the resultant JSON data into a Python dictionary to be used as the robot_elements. Unfortunately, this tactic did not solve the problem either.
I sincerely appreciate any insights or advice you might have to offer. Thank you in advance :)