0

I have an Azure Function app in Python 3.9 that connects to an API and inserts data into a microsoft sql database. When running the code locally it works as expected. However when the app is running/hosted in Azure it gets an error:

sqlalchemy.exc.DBAPIError: (pyodbc.Error) ('HY000', '[HY000] [Microsoft][ODBC Driver 17 for SQL Server]SSPI Provider: No Kerberos credentials available (default cache: FILE:/tmp/krb5cc_0) (851968) (SQLDriverConnect)')
(Background on this error at: https://sqlalche.me/e/20/dbapi)

From the research I have done most of what is being suggested to do is to configure Kerberos on the linux machine that is hosting the application. I'm pretty new to Azure, and these serverless functions but my impression is that there isn't like a dedicated machine that is hosting these that I can go and configure as many of the suggestions online I have found are pointing to as a solution.

We have a virtual network set up, and a subnet for the function app. It has been tested by the infrastructure team and has been given the green light that it works and can successfully connect to the on premise infrastructure from Azure Land.

My code:

    user = "my_user"
    password = "my_pass"
    db_name = "my_db"
    port = "default_port"
    host = "fully_qualified_host"
    driver = "ODBC Driver 17 for SQL Server"
    
    sql_statement = "TRUNCATE TABLE adp.stg_hr_workers"

    connection_url = URL.create(
        "mssql+pyodbc",
        query={
            "username": user,
            "password": password,
            "host": host,
            "port": port,
            "database": db_name,
            "driver": driver
        },
    )
    
    engine = create_engine(connection_url, echo=False)

    Session = sessionmaker(bind=engine)

    with Session.begin() as session:
        session.execute(text(sql_statement))

When running locally this truncates the table. When hosting in Azure this give the Kerberos credentialing error.

Sampath
  • 810
  • 2
  • 2
  • 13
  • Please read the tag summary when choosing them. This question has nothing to do with Azure DevOps. – Daniel Mann Aug 01 '23 at 19:45
  • Did you intend to put all of your `URL.create()` parameters inside `query`? They're meant to be named parameters to `URL.create()` itself, ref: [sqlalchemy.dialects.mssql.pyodbc](https://pydoc.dev/sqlalchemy/latest/sqlalchemy.dialects.mssql.pyodbc.html). The parameters inside `query` tend to get passed to the ODBC Driver as connection string properties so if you wanted to do it that way you'd need to use `UID` and `PWD` instead of `Username` and `Password`, etc.. (See the _Pass through exact Pyodbc string_ example at the link.) – AlwaysLearning Aug 02 '23 at 08:50
  • @DanielMann sorry about the mis-tag, I tagged this way because it was working on my local but was not working on Azure so I thought there may be a problem with the Azure configuration. I have removed those tags as the helpful comments have suggested some other options! Thanks for reading. – ZahlenZ Aug 02 '23 at 13:42
  • @AlwaysLearning I'm still pretty new to all of this, didn't realize I had the URL create incorrect. I have switched it out to having them as named parameters, with the query just having the driver. Now it looks like the service account that was set up may be incorrect as it isn't allowed to login. With the original way that I had my URL String set up, with everything in the query, when running this locally would it have just used the windows account I was logged in as on my machine to authenticate? If so this could be why it was working local, and not in azure. Thanks! – ZahlenZ Aug 02 '23 at 13:46
  • @ZahlenZ When you had everything in the query and ran the code locally, SQLAlchemy would have used your Windows account (the one you were logged in as on your machine) for authentication. This is because when you run code on your local machine, it typically uses your local credentials to access the resources. However, when the Azure Function app is running in the Azure cloud environment, it uses a different set of credentials or service account (Managed Service Identity or another account you specified) to authenticate with the on-premise SQL Server. – Sampath Aug 02 '23 at 17:30
  • @ZahlenZ So The error you are encountering now indicates that the service account used by the Azure Function app doesn't have the necessary permissions to log in to the SQL Server. – Sampath Aug 02 '23 at 17:33
  • @ZahlenZ One of the possible solution is to Create an Azure SQL server and database. Change the configuration in [local](https://learn.microsoft.com/en-us/azure/azure-sql/database/connect-query-python?view=azuresql-mi) and deploy it again. I have given the code after Creating an Azure SQL Server and Azure database step. – Sampath Aug 02 '23 at 18:07
  • @ZahlenZ if face ip address issues add the respective [IP address](https://i.imgur.com/IpYECfL.png) in the [networking tab](https://i.imgur.com/BbJyJ8a.png) – Sampath Aug 02 '23 at 18:11
  • Azure SQL vs. SQL [Serve](https://www.accelebrate.com/blog/azure-sql-versus-sql-server#:~:text=Azure%20SQL%20vs%20SQL%20Server%20Performance%20%26%20Features&text=Azure%20SQL%20Database%20has%20some,SQL%20query%20processing%20are%20supported). – Sampath Aug 02 '23 at 19:24
  • @Sampath. Using this as the connection strings: connection_string = f"Driver={{ODBC Driver 17 for SQL Server}};Server={host};Database={db_name};Uid={user};Pwd={password}" connection_url = URL.create("mssql+pyodbc", query={"odbc_connect": connection_string}). It seems like this will only try and authenticate with SQL and not with windows authentication. We have 2 accounts, one SQL account and one windows account from our AD. Both have the same permissions, when using the SQL account it works, when using the AD account it gets the login error. – ZahlenZ Aug 03 '23 at 13:39
  • @ZahlenZ ``` #Set the environment of the database through the url authority_url='url' context = adal.AuthenticationContext(authority_url, api_version=None) applicationID='ID' secret='secret' token = context.acquire_token_with_client_credentials("https://database.windows.net/", applicationID, secret) #Create connection string driver='{ODBC Driver 18 for SQL Server}' server='server' database='db' connString = f"Driver={driver};SERVER={server};DATABASE={database}" ``` – Sampath Aug 03 '23 at 15:11
  • ```#Convert the token to a byte string tokenb = bytes(token['accessToken'], 'UTF-8') exptoken = b'' for i in tokenb: exptoken += bytes({i}) exptoken += bytes(1) tokenstruct = struct.pack('=i', len(exptoken)) + exptoken #Connect to the database SQL_COPT_SS_ACCESS_TOKEN = 1256 dbEngine = pyodbc.connect(connString, attrs_before={SQL_COPT_SS_ACCESS_TOKEN: tokenstruct})```` – Sampath Aug 03 '23 at 15:12
  • @ZahlenZ ```token = credential.get_token("https://database.windows.net/.default").token.encode("UTF-16-LE") token_struct = struct.pack(f'.database.windows.net;DATABASE=" conn = pyodbc.connect(connString, attrs_before={SQL_COPT_SS_ACCESS_TOKEN: token_struct})``` – Sampath Aug 03 '23 at 15:17

1 Answers1

0

The error you are encountering is related to authentication when connecting to the on-premises SQL Server. Thank you for the comment @AlwaysLearning.

  • Code refers from Python to query a database - Azure SQL Database & SQL Managed Instance.

enter image description here

enter image description here

In MSSql Server local:

server_name = 'sampath23'
database_name = 'sampath'
username = 'sampath'
password = 'Ra@80muravi'
connection_string = f"Driver={{ODBC Driver 17 for SQL Server}};Server={server_name};Database={database_name};Uid={username};Pwd={password};Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;"
  • Add Trusted_Connection=yes for Trusted_Connection connection for this reference link.
  • Create an Azure SQL Sever and Azure database .
import  logging
import  azure.functions  as  func
import  pyodbc
def  main(req:  func.HttpRequest)  ->  func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
server_name = 'sampath23.database.windows.net'
database_name = 'sampath'
username = 'sampath'
password = 'Ra@80muravi'
connection_string = f"Driver={{ODBC Driver 17 for SQL Server}};Server={server_name};Database={database_name};Uid={username};Pwd={password};Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;"
try:
with  pyodbc.connect(connection_string)  as  conn:
logging.info("Connection to the database established successfully.")
with  conn.cursor()  as  cursor:
try:
cursor.execute("TRUNCATE TABLE stg_hr_workers")
conn.commit()
logging.info("Table 'stg_hr_workers' truncated successfully.")
return  func.HttpResponse("Table 'stg_hr_workers' truncated successfully.",  status_code=200)
except  pyodbc.Error  as  e:
error_message = f"Error occurred while truncating the table: {str(e)}"
logging.error(error_message)
return  func.HttpResponse(error_message,  status_code=500)
except  pyodbc.Error  as  ex:
error_message = f"Error connecting to the database: {str(ex)}"
logging.error(error_message)
return  func.HttpResponse(error_message,  status_code=500)

Output:

enter image description here

enter image description here

In Azure:

enter image description here

enter image description here

token = credential.get_token("https://database.windows.net/.default").token.encode("UTF-16-LE")
token_struct = struct.pack(f'<I{len(token)}s', len(token), token)

# Connect with the token
SQL_COPT_SS_ACCESS_TOKEN = 1256
connString = f"Driver={{ODBC Driver 17 for SQL Server}};SERVER=<server-name>.database.windows.net;DATABASE=<database-name>"
conn = pyodbc.connect(connString, attrs_before={SQL_COPT_SS_ACCESS_TOKEN: token_struct})
  • We use Driver={ODBC Driver 18 for SQL Server};Server={server};UID=myuser;PWD=myPass;Authentication=ActiveDirectoryPassword;Encrypt=yes; from the reference MSDOC for more details refer this SO.For ODBC 17 refer this MSDOC.
Sampath
  • 810
  • 2
  • 2
  • 13
  • Thanks for these tips, they were able to get the code working using a SQL account, but it doesn't work while using a Windows Account. The goal is to host the program in Azure and use a Windows Account from our active directory as the service account that will perform the database actions. – ZahlenZ Aug 03 '23 at 13:40
  • @ZahlenZ we use `Driver={ODBC Driver 18 for SQL Server};Server={server};UID=myuser;PWD=myPass;Authentication=ActiveDirectoryPassword` from the reference [MSDOC](https://learn.microsoft.com/en-us/sql/connect/odbc/using-azure-active-directory?view=sql-server-2017) for more details refer this [SO](https://stackoverflow.com/questions/53965056/in-python-is-it-possible-to-connect-azure-sql-server-using-active-directory-pas). for ODBC 17 [refer](https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/app-service/tutorial-connect-msi-azure-database.md). – Sampath Aug 03 '23 at 13:59