Invalid value specified for connection string attribute 'Authentication' (0) (SQLDriverConnect)

Shashi Kumar Soni 1 Reputation point
2024-07-09T13:02:36.6466667+00:00

I am trying to connect to Azure SQL Database using user assigned managed identity. While configuring the Azure functions I have added the db_reader permission to it. But when I am connecting to the database using the pyodbc driver I am getting authentication error with authentication value as incorrect.

Can someone help me with this.

This is the connection string in my local.setting.json file

"sql_connection_string": "Driver={ODBC Driver 17 for SQL Server}; Server=<server>.database.windows.net; Authentication=Active Directory Default; User=a730-f3456ygdfgh-456tfcgyuc9a; Database=<db-name>",


cnxn = pyodbc.connect(os.environ["sql_connection_string"])
cursor = cnxn.cursor()

This is the detailed error:

Result: Failure Exception: OperationalError: ('08001', "[08001] [Microsoft][ODBC Driver 17 for SQL Server]Invalid value specified for connection string attribute 'Authentication' (0) (SQLDriverConnect)")

Azure SQL Database
Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
4,654 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Oury Ba-MSFT 17,946 Reputation points Microsoft Employee
    2024-07-09T17:10:14.4033333+00:00

    @Shashi Kumar Soni

    Thank you for reaching out.

    The detailed error message you provided indicates an issue with the authentication attribute in the connection string. Double-check the value and make sure it’s set to Authentication=ActiveDirectoryMsi.

    Please check if you have enabled managed identity to azure functions.

    https://video2.skills-academy.com/en-us/azure/azure-functions/functions-identity-access-azure-sql-with-managed-identity

    Steps:

    1. Create a Function App

    thumbnail image 2 of blog post titled  How to connect Azure SQL database from Python Function App using managed identity or access token

    1. Set up the managed identity in the new Function App by enable Identity and saving from portal. It will generate an Object(principal) ID for you automatically.

    thumbnail image 3 of blog post titled  How to connect Azure SQL database from Python Function App using managed identity or access token

    1. Assign role in Azure SQL database.

    thumbnail image 4 of blog post titled  How to connect Azure SQL database from Python Function App using managed identity or access token

    Search for your own account and save as admin.

    Note: Alternatively, you can search for the function app's name and set it as admin, then that function app would own admin permission on the database and you can skip step 4 and 5 as well.

     

    1. Got to Query editor in database and be sure to login using your account set in previous step rather than username and password. Or step 5 will fail with below exception.

    "Failed to execute query. Error: Principal 'xxxx' could not be created. Only connections established with Active Directory accounts can create other Active Directory users."

    thumbnail image 5 of blog post titled  How to connect Azure SQL database from Python Function App using managed identity or access token

     

    1. Run below queries to create user for the function app and alter roles. You can choose to alter part of these roles per your demand.

     

    CREATE USER "yourfunctionappname" FROM EXTERNAL PROVIDER;
    ALTER ROLE db_datareader ADD MEMBER "yourfunctionappname"
    ALTER ROLE db_datawriter ADD MEMBER "yourfunctionappname"
    ALTER ROLE db_ddladmin ADD MEMBER "yourfunctionappname"
    

     

     

    1. Leverage below sample code to build your own project and deploy to the function app.

     

    Sample Code:

    Below is the sample code on how to use Azure access token when run it from local and use managed identity when run in Function app. The token part needs to be replaced with your own. Basically, it is using "pyodbc.connect(connection_string+';Authentication=ActiveDirectoryMsi')" to authenticate with managed identity.

    Also,  "MSI_SECRET" is used to tell if we are running it from local or function app, it will be created automatically as environment variable when the function app is enabled with Managed Identity.

    The complete demo project can be found from: https://github.com/kevin808/azure-function-pyodbc-MI

     

    
    
    import logging
    import azure.functions as func
    import os
    import pyodbc
    import struct
    
    def main(req: func.HttpRequest) -> func.HttpResponse:
        logging.info('Python HTTP trigger function processed a request.')
        server="your-sqlserver.database.windows.net"
        database="your_db"
        driver="{ODBC Driver 17 for SQL Server}"
        query="SELECT * FROM dbo.users"
        # Optional to use username and password for authentication
        # username = 'name' 
        # password = 'pass'
        db_token = ''
        connection_string = 'DRIVER='+driver+';SERVER='+server+';DATABASE='+database
        #When MSI is enabled
        if os.getenv("MSI_SECRET"):
            conn = pyodbc.connect(connection_string+';Authentication=ActiveDirectoryMsi')
        
        #Used when run from local
        else:
            SQL_COPT_SS_ACCESS_TOKEN = 1256
    
            exptoken = b''
            for i in bytes(db_token, "UTF-8"):
                exptoken += bytes({i})
                exptoken += bytes(1)
    
            tokenstruct = struct.pack("=i", len(exptoken)) + exptoken
            conn = pyodbc.connect(connection_string, attrs_before = { SQL_COPT_SS_ACCESS_TOKEN:tokenstruct })
            # Uncomment below line when use username and password for authentication
            # conn = pyodbc.connect('DRIVER='+driver+';SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)
    
        cursor = conn.cursor()
        cursor.execute(query) 
        row = cursor.fetchone()
    
        while row:
            print(row[0])
            row = cursor.fetchone()
    
        return func.HttpResponse(
                'Success',
                status_code=200
        )