3

I am trying to connect the Azure SQL Database from Azure functions for python using ActiveDirectoryMSI Authentication.

Please check the below code:-

import logging
from . import hy_param
import sys
import pyodbc
import azure.functions as func


def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')
    try:
        connection = pyodbc.connect('driver={%s};server=%s;database=%s;Authentication=ActiveDirectoryMSI' % (hy_param.sql_driver, hy_param.server_name, hy_param.database_name))
        sql_db = connection.cursor()
        logging.info("MSSQL Database Connected")
    except Exception as e:
        return func.HttpResponse(f"Error in sql database connection : {e}", status_code=400)
        sys.exit()
    return func.HttpResponse(
            "Database Connected",
            status_code=200
    )

Please check the below Error :-

Error in sql database connection : ('08001', '[08001] [Microsoft][ODBC Driver 17 for SQL Server]SSL Provider: [error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed:self signed certificate] (-1) (SQLDriverConnect)')

Is there any way to connect Azure, SQL Database from Azure functions using ActiveDirectoryMSI?

Akshay Godase
  • 239
  • 7
  • 15

2 Answers2

4

You can try the code below to use MSI access token to connect to your Azure SQL(Before you run this code , pls make sure your function MSI has been enabled and it has permission to access your Azure SQL ):

import logging
import os
import azure.functions as func
import pyodbc
import requests 
import struct

msi_endpoint = os.environ["MSI_ENDPOINT"]
msi_secret = os.environ["MSI_SECRET"]

def main(req: func.HttpRequest) -> func.HttpResponse:

   token_auth_uri = f"{msi_endpoint}?resource=https%3A%2F%2Fdatabase.windows.net%2F&api-version=2017-09-01"
   head_msi = {'Secret':msi_secret}
   resp = requests.get(token_auth_uri, headers=head_msi)
   access_token = resp.json()['access_token']

   accessToken = bytes(access_token, 'utf-8');
   exptoken = b"";
   for i in accessToken:
        exptoken += bytes({i});
        exptoken += bytes(1);
   tokenstruct = struct.pack("=i", len(exptoken)) + exptoken;

   conn = pyodbc.connect("Driver={ODBC Driver 17 for SQL Server};Server=tcp:andyserver.database.windows.net,1433;Database=database2", attrs_before = { 1256:bytearray(tokenstruct) });

   cursor = conn.cursor()
   cursor.execute("select @@version")
   row = cursor.fetchall()
   return func.HttpResponse(str(row))

Pls edit the connection string with your won server name and db name

This is the test result on my side : enter image description here

Stanley Gong
  • 11,522
  • 1
  • 8
  • 16
  • Thanks a ton man. I was missing permission to SQL Database. Issue is resolved. I am able to connect Azure function to SQL Server without password. – MarsTelnet Sep 14 '19 at 16:12
  • Ah , Glad to know that @LetsLearn, pls mark me if it is helpful : ) – Stanley Gong Sep 16 '19 at 01:11
  • @StanleyGong I am not able to understand below two things:- 1. msi_endpoint = os.environ["MSI_ENDPOINT"] 2. msi_secret = os.environ["MSI_SECRET"] Can you please share me more details on it? – Akshay Godase Oct 31 '19 at 06:34
  • 1
    Hi @AkshayGodase , Sure , that is the process getting access token using MSI in Azure function , you can see details in this doc : https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?context=azure%2Factive-directory%2Fmanaged-identities-azure-resources%2Fcontext%2Fmsi-context&tabs=dotnet#obtaining-tokens-for-azure-resources – Stanley Gong Oct 31 '19 at 06:43
  • @StanleyGong Not getting properly. I am not able to understand after execution azure function which values come in below two variables. 1. msi_endpoint 2. msi_secret I want to execute the above code. – Akshay Godase Oct 31 '19 at 06:47
  • 1
    Hi @AkshayGodase If you enabled MSI for your function, this two variables will be defined automatically , there is no need to config it in app settings, you can get it directly . If you print this two out , you will find that is a Azure internal endpoint to get token and a secret . – Stanley Gong Oct 31 '19 at 07:21
  • @StanleyGong Ohk I got the localhost `http://localhost:8081/msi/token` msi_endpoind. is this right? – Akshay Godase Oct 31 '19 at 09:30
  • @StanleyGong resource_uri = `https://database.windows.net/` is this right? – Akshay Godase Oct 31 '19 at 09:35
  • @AkshayGodase That's right as you want to get a token to connect to Azure SQL , that is the right resource – Stanley Gong Oct 31 '19 at 09:53
  • @StanleyGong It's work successfully. Thank you so much for helping. – Akshay Godase Oct 31 '19 at 10:27
  • @StanleyGong Hi I want to access the Azure SQL Database from Azure Machine Learning Service using MSI. Can I use the above code for that purpose? – Akshay Godase Oct 31 '19 at 10:29
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/201649/discussion-between-akshay-godase-and-stanley-gong). – Akshay Godase Oct 31 '19 at 10:29
  • Hi @AkshayGodase , this way that using MSI to get access token to connect to Azure SQL only for Azure function . I am not so familiar with Azure ML, if you have such requirement , could you pls post another query and I'll look into it . Thanks – Stanley Gong Nov 01 '19 at 01:27
  • @StanleyGong whenever I am trying to connect the database from azure function using MSI at that time I got `http://localhost:8081/msi/token` from `msi_endpoind` variable. Is this right? I am not able to understand why `msi_endpoint` is the localhost URL. – Akshay Godase Nov 01 '19 at 06:03
  • @AkshayGodase, that is an Azure app service localhost which designed by Azure for MSI to get token. What's why this solution only for Azure function/Azure app service – Stanley Gong Nov 01 '19 at 07:06
  • @StanleyGong Ok. I have already posted another query. Yes, I have such a requirement. Please check the below link https://stackoverflow.com/questions/58642295/trying-to-connect-azure-sql-database-from-azure-ml-service-using-msi-authenticat. If you got any idea related to this question please let me know. – Akshay Godase Nov 01 '19 at 07:10
1

There is a new better way to connect to Azure SQL directly by using the SDK and ODBC driver.

You need to:

  1. Enable Azure Function Managed Service Identity (MSI)
  2. Enable AAD integration for Azure SQL Server
  3. Add The Azure Function MSI User to the DB
  4. Use Authentication=ActiveDirectoryMsi in your pyodbc.connect.

To add the MSI user to the DB you must connect using the AAD admin and then run this query:

CREATE USER "<MSI user display name>" FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER "<MSI user display name>" -- grant permission to read to database
ALTER ROLE db_datawriter ADD MEMBER "<MSI user display name>" -- grant permission to write to database

<MSI user display name> is usually the Azure Function Name. You can also get it using Get-AzureADObjectByObjectId -ObjectIds in PowerShell

This is the source code of a hello-world function:

import logging
import azure.functions as func

# Sql driver
import pyodbc

def main(req: func.HttpRequest) -> func.HttpResponse:

    try:

        logging.info('Python HTTP trigger function processed a request.')

        # Connecting to Azure SQl the standard way
        server = 'tcp:<servername>.database.windows.net' 
        database = '<dbname>' 
        driver = '{ODBC Driver 17 for SQL Server}' # Driver 13 did not work for me

        with pyodbc.connect(
            "Driver="
            + driver
            + ";Server="
            + server
            + ";PORT=1433;Database="
            + database
            + ";Authentication=ActiveDirectoryMsi", # This is important :)
        ) as conn:

            logging.info("Successful connection to database")

            with conn.cursor() as cursor:
                #Sample select query
                cursor.execute("SELECT Name FROM People;") 

                peopleNames = ''
                row = cursor.fetchone() 
                while row: 
                    peopleNames += str(row[0]).strip() + " " 
                    row = cursor.fetchone()

                return func.HttpResponse(f"Hello {peopleNames}!")
    except Exception as e:
        return func.HttpResponse(str(e))

Here there is a full project that you can take as example: https://github.com/crgarcia12/azure-function-msi-python

Carlos Garcia
  • 2,771
  • 1
  • 17
  • 32