├── .devcontainer └── devcontainer.json ├── .env.sample ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── SECURITY.md ├── dependabot.yaml └── workflows │ ├── azure-dev.yaml │ └── template-validation.yaml ├── .gitignore ├── LICENSE ├── README.md ├── azure.yaml ├── diagram.png ├── example.py ├── infra ├── main.bicep └── main.parameters.json ├── requirements.txt ├── write_dot_env.ps1 └── write_dot_env.sh /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azure-openai-keyless", 3 | "image": "mcr.microsoft.com/devcontainers/python:3.12-bullseye", 4 | "features": { 5 | "ghcr.io/azure/azure-dev/azd:latest": {} 6 | }, 7 | "customizations": { 8 | "vscode": { 9 | "extensions": [ 10 | "ms-azuretools.azure-dev", 11 | "ms-azuretools.vscode-bicep" 12 | ] 13 | } 14 | }, 15 | "postCreateCommand": "", 16 | "remoteUser": "vscode", 17 | "hostRequirements": { 18 | "memory": "8gb" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | AZURE_OPENAI_GPT_DEPLOYMENT= 2 | AZURE_OPENAI_SERVICE= 3 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to azure-openai-keyless-python 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 6 | 7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 14 | 15 | - [Code of Conduct](#coc) 16 | - [Issues and Bugs](#issue) 17 | - [Feature Requests](#feature) 18 | - [Submission Guidelines](#submit) 19 | 20 | ## Code of Conduct 21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 22 | 23 | ## Found an Issue? 24 | If you find a bug in the source code or a mistake in the documentation, you can help us by 25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can 26 | [submit a Pull Request](#submit-pr) with a fix. 27 | 28 | ## Want a Feature? 29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub 30 | Repository. If you would like to *implement* a new feature, please submit an issue with 31 | a proposal for your work first, to be sure that we can use it. 32 | 33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 34 | 35 | ## Submission Guidelines 36 | 37 | ### Submitting an Issue 38 | Before you submit an issue, search the archive, maybe your question was already answered. 39 | 40 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 41 | Help us to maximize the effort we can spend fixing issues and adding new 42 | features, by not reporting duplicate issues. Providing the following information will increase the 43 | chances of your issue being dealt with quickly: 44 | 45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps 46 | * **Version** - what version is affected (e.g. 0.1.2) 47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 48 | * **Browsers and Operating System** - is this a problem with all browsers? 49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps 50 | * **Related Issues** - has a similar issue been reported before? 51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 52 | causing the problem (line of code or commit) 53 | 54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/Azure-samples/azure-openai-keyless-python/issues/new]. 55 | 56 | ### Submitting a Pull Request (PR) 57 | Before you submit your Pull Request (PR) consider the following guidelines: 58 | 59 | * Search the repository (https://github.com/Azure-samples/azure-openai-keyless-python/pulls) for an open or closed PR 60 | that relates to your submission. You don't want to duplicate effort. 61 | 62 | * Make your changes in a new git fork: 63 | 64 | * Commit your changes using a descriptive commit message 65 | * Push your fork to GitHub: 66 | * In GitHub, create a pull request 67 | * If we suggest changes then: 68 | * Make the required updates. 69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 70 | 71 | ```shell 72 | git rebase master -i 73 | git push -f 74 | ``` 75 | 76 | That's it! Thank you for your contribution! -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | > Please provide us with the following information: 5 | > --------------------------------------------------------------- 6 | 7 | ### This issue is for a: (mark with an `x`) 8 | ``` 9 | - [ ] bug report -> please search issues before submitting 10 | - [ ] feature request 11 | - [ ] documentation issue or request 12 | - [ ] regression (a behavior that used to work and stopped in a new release) 13 | ``` 14 | 15 | ### Minimal steps to reproduce 16 | > 17 | 18 | ### Any log messages given by the failure 19 | > 20 | 21 | ### Expected/desired behavior 22 | > 23 | 24 | ### OS and Version? 25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) 26 | 27 | ### Versions 28 | > 29 | 30 | ### Mention any other details that might be useful 31 | 32 | > --------------------------------------------------------------- 33 | > Thanks! We'll be in touch soon. 34 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 4 | 5 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](), please report it to us as described below. 6 | 7 | ## Reporting Security Issues 8 | 9 | **Please do not report security vulnerabilities through public GitHub issues.** 10 | 11 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 12 | 13 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/msrc/pgp-key-msrc). 14 | 15 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 16 | 17 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 18 | 19 | - Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 20 | - Full paths of source file(s) related to the manifestation of the issue 21 | - The location of the affected source code (tag/branch/commit or direct URL) 22 | - Any special configuration required to reproduce the issue 23 | - Step-by-step instructions to reproduce the issue 24 | - Proof-of-concept or exploit code (if possible) 25 | - Impact of the issue, including how an attacker might exploit the issue 26 | 27 | This information will help us triage your report more quickly. 28 | 29 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 30 | 31 | ## Preferred Languages 32 | 33 | We prefer all communications to be in English. 34 | 35 | ## Policy 36 | 37 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/msrc/cvd). 38 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | groups: 10 | github-actions: 11 | patterns: 12 | - "*" 13 | -------------------------------------------------------------------------------- /.github/workflows/azure-dev.yaml: -------------------------------------------------------------------------------- 1 | name: Provision with azd 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | # Run when commits are pushed to mainline branch (main or master) 7 | # Set this to the mainline branch you are using 8 | branches: 9 | - main 10 | 11 | # GitHub Actions workflow to deploy to Azure using azd 12 | # To configure required secrets for connecting to Azure, simply run `azd pipeline config` 13 | 14 | # Set up permissions for deploying with secretless Azure federated credentials 15 | # https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#set-up-azure-login-with-openid-connect-authentication 16 | permissions: 17 | id-token: write 18 | contents: read 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | env: 24 | # azd required 25 | AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} 26 | AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} 27 | AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} 28 | AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} 29 | AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | 34 | - name: Install azd 35 | uses: Azure/setup-azd@v2.1.0 36 | 37 | - name: Install Nodejs 38 | uses: actions/setup-node@v4 39 | with: 40 | node-version: 18 41 | 42 | - name: Log in with Azure (Federated Credentials) 43 | if: ${{ env.AZURE_CLIENT_ID != '' }} 44 | run: | 45 | azd auth login ` 46 | --client-id "$Env:AZURE_CLIENT_ID" ` 47 | --federated-credential-provider "github" ` 48 | --tenant-id "$Env:AZURE_TENANT_ID" 49 | shell: pwsh 50 | 51 | - name: Provision Infrastructure 52 | run: azd provision --no-prompt 53 | env: 54 | AZD_INITIAL_ENVIRONMENT_CONFIG: ${{ secrets.AZD_INITIAL_ENVIRONMENT_CONFIG }} 55 | AZURE_SERVER_APP_SECRET: ${{ secrets.AZURE_SERVER_APP_SECRET }} 56 | AZURE_CLIENT_APP_SECRET: ${{ secrets.AZURE_CLIENT_APP_SECRET }} 57 | -------------------------------------------------------------------------------- /.github/workflows/template-validation.yaml: -------------------------------------------------------------------------------- 1 | # This is for internal use to make sure our samples follow best practices. You can delete this in your fork. 2 | name: Template validation sample workflow 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | contents: read 8 | id-token: write 9 | pull-requests: write 10 | 11 | jobs: 12 | template_validation_job: 13 | runs-on: ubuntu-latest 14 | name: template validation 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - uses: microsoft/template-validation-action@v0.4.1 19 | env: 20 | AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} 21 | AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} 22 | AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} 23 | AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} 24 | AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | 27 | - name: print result 28 | run: cat ${{ steps.validation.outputs.resultFile }} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Azure az webapp deployment details 2 | .azure 3 | *_env 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 102 | __pypackages__/ 103 | 104 | # Celery stuff 105 | celerybeat-schedule 106 | celerybeat.pid 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | .dmypy.json 133 | dmypy.json 134 | 135 | # Pyre type checker 136 | .pyre/ 137 | 138 | # pytype static type analyzer 139 | .pytype/ 140 | 141 | # Cython debug symbols 142 | cython_debug/ 143 | 144 | # NPM 145 | npm-debug.log* 146 | node_modules 147 | static/ 148 | 149 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Azure Samples 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 16 | # Azure OpenAI resource with keyless auth (Python) 17 | 18 | [![Open in GitHub Codespaces](https://img.shields.io/static/v1?style=for-the-badge&label=GitHub+Codespaces&message=Open&color=brightgreen&logo=github)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&skip_quickstart=true&machine=basicLinux32gb&repo=784926917&devcontainer_path=.devcontainer%2Fdevcontainer.json&geo=WestUs2) 19 | [![Open in Dev Containers](https://img.shields.io/static/v1?style=for-the-badge&label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/Azure-Samples/azure-openai-keyless-python) 20 | 21 | The purpose of this repository is to provision an Azure OpenAI account with an RBAC role permission for your user account to access, 22 | so that you can use the OpenAI API SDKs with keyless (Entra) authentication. 23 | 24 | * [Features](#features) 25 | * [Getting started](#getting-started) 26 | * [GitHub Codespaces](#github-codespaces) 27 | * [VS Code Dev Containers](#vs-code-dev-containers) 28 | * [Local environment](#local-environment) 29 | * [Deployment](#deployment) 30 | * [Running the Python example](#running-the-python-example) 31 | * [Guidance](#guidance) 32 | * [Costs](#costs) 33 | * [Security guidelines](#security-guidelines) 34 | * [Resources](#resources) 35 | 36 | ## Features 37 | 38 | * Provisions an Azure OpenAI account with keyless authentication enabled 39 | * Grants the "Cognitive Services OpenAI User" RBAC role to your user account 40 | * Deploys a gpt-4o-mini model by default, but you can modify the [Bicep template](infra/main.bicep) to deploy other models 41 | * Example script uses the [openai](https://pypi.org/project/openai/) Python package to make a request to the Azure OpenAI API 42 | 43 | ### Architecture diagram 44 | 45 | ![Architecture diagram: Microsoft Entra managed identity connecting to Azure AI services](./diagram.png) 46 | 47 | ## Getting started 48 | 49 | You have a few options for getting started with this template. 50 | The quickest way to get started is GitHub Codespaces, since it will setup all the tools for you, but you can also [set it up locally](#local-environment). 51 | 52 | ### GitHub Codespaces 53 | 54 | You can run this template virtually by using GitHub Codespaces. The button will open a web-based VS Code instance in your browser: 55 | 56 | 1. Open the template (this may take several minutes): 57 | 58 | [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/Azure-Samples/azure-openai-keyless-python) 59 | 60 | 2. Open a terminal window 61 | 3. Continue with the [deployment steps](#deployment) 62 | 63 | ### VS Code Dev Containers 64 | 65 | A related option is VS Code Dev Containers, which will open the project in your local VS Code using the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers): 66 | 67 | 1. Start Docker Desktop (install it if not already installed) 68 | 2. Open the project: 69 | 70 | [![Open in Dev Containers](https://img.shields.io/static/v1?style=for-the-badge&label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/azure-samples/azure-openai-keyless-python) 71 | 72 | 3. In the VS Code window that opens, once the project files show up (this may take several minutes), open a terminal window. 73 | 4. Continue with the [deployment steps](#deployment) 74 | 75 | ### Local environment 76 | 77 | 1. Make sure the following tools are installed: 78 | 79 | * [Azure Developer CLI (azd)](https://aka.ms/install-azd) 80 | * [Python 3.9+](https://www.python.org/downloads/) 81 | 82 | 2. Make a new directory called `azure-openai-keyless-python` and clone this template into it using the `azd` CLI: 83 | 84 | ```shell 85 | azd init -t azure-openai-keyless-python 86 | ``` 87 | 88 | You can also use git to clone the repository if you prefer. 89 | 90 | 3. Continue with the [deployment steps](#deployment) 91 | 92 | ## Deployment 93 | 94 | 1. Login to Azure: 95 | 96 | ```shell 97 | azd auth login 98 | ``` 99 | 100 | For GitHub Codespaces users, if the previous command fails, try: 101 | 102 | ```shell 103 | azd auth login --use-device-code 104 | ``` 105 | 106 | 2. Provision the OpenAI account: 107 | 108 | ```shell 109 | azd provision 110 | ``` 111 | 112 | It will prompt you to provide an `azd` environment name (like "chat-app"), select a subscription from your Azure account, and select a [location where the OpenAI model is available](https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-availability) (like "canadaeast"). Then it will provision the resources in your account and deploy the latest code. 113 | 114 | ⚠️ If you get an error or timeout with deployment, changing the location can help, as there may be availability constraints for the OpenAI resource. To change the location run: 115 | 116 | ```shell 117 | azd env set AZURE_LOCATION "yournewlocationname" 118 | ``` 119 | 120 | 3. When `azd` has finished, you should have an OpenAI account you can use locally when logged into your Azure account. You can output the necessary environment variables into an `.env` file by running a script: 121 | 122 | For Mac OS X / Linux: 123 | 124 | ```shell 125 | ./write_dot_env.sh 126 | ``` 127 | 128 | For Windows: 129 | 130 | ```shell 131 | pwsh ./write_dot_env.ps1 132 | ``` 133 | 134 | 4. Then you can proceed to [run the Python example](#running-the-python-example). 135 | 136 | ## Running the Python example 137 | 138 | 1. If you're not already running in a Codespace or Dev Container, create a Python virtual environment. 139 | 140 | 2. Install the requirements: 141 | 142 | ```shell 143 | python -m pip install -r requirements.txt 144 | ``` 145 | 146 | 3. Run the example: 147 | 148 | ```shell 149 | python example.py 150 | ``` 151 | 152 | This will use the OpenAI API SDK to make a request to the OpenAI API and print the response. 153 | 154 | ## Guidance 155 | 156 | ### Costs 157 | 158 | This template creates only the Azure OpenAI resource, which is free to provision. However, you will be charged for the usage of the Azure OpenAI chat completions API. The pricing is based on the number of tokens used, with around 1-3 tokens used per word. You can find the pricing details for the OpenAI API on the [Azure Cognitive Services pricing page](https://azure.microsoft.com/pricing/details/cognitive-services/openai-service/). 159 | 160 | ### Security guidelines 161 | 162 | This template uses [keyless authentication](https://learn.microsoft.com/en-us/azure/developer/ai/keyless-connections) for authenticating to the Azure OpenAI resource. This is a secure way to authenticate to Azure resources without needing to store credentials in your code. Your Azure user account is assigned the "Cognitive Services OpenAI User" role, which allows you to access the OpenAI resource. You can find more information about the permissions of this role in the [Azure OpenAI documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/role-based-access-control). 163 | 164 | For further security, you could also deploy the Azure OpenAI inside a private virtual network (VNet) and use a private endpoint to access it. This would prevent the OpenAI resource from being accessed from the public internet. 165 | 166 | ## Resources 167 | 168 | * [Video: Using keyless auth with Azure AI services](https://www.youtube.com/watch?v=IkDcQvKoQ8k) 169 | * [Sample app: Azure OpenAI + Container Apps + Managed Identity](https://github.com/Azure-Samples/openai-chat-app-quickstart) 170 | -------------------------------------------------------------------------------- /azure.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json 2 | 3 | name: azure-openai-keyless-python 4 | metadata: 5 | template: azure-openai-keyless-python@0.0.4 6 | -------------------------------------------------------------------------------- /diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-openai-keyless-python/2eba23bddf6424efbc1e365419885816701a729a/diagram.png -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | import azure.identity 5 | import openai 6 | from dotenv import load_dotenv 7 | 8 | load_dotenv() 9 | # Change to logging.DEBUG for more verbose logging from Azure and OpenAI SDKs 10 | logging.basicConfig(level=logging.WARNING) 11 | 12 | 13 | if not os.getenv("AZURE_OPENAI_SERVICE") or not os.getenv("AZURE_OPENAI_GPT_DEPLOYMENT"): 14 | logging.warning("AZURE_OPENAI_SERVICE and AZURE_OPENAI_GPT_DEPLOYMENT environment variables are empty. See README.") 15 | exit(1) 16 | 17 | 18 | credential = azure.identity.DefaultAzureCredential() 19 | token_provider = azure.identity.get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default") 20 | 21 | client = openai.AzureOpenAI( 22 | api_version="2024-03-01-preview", 23 | azure_endpoint=f"https://{os.getenv('AZURE_OPENAI_SERVICE')}.openai.azure.com", 24 | azure_ad_token_provider=token_provider, 25 | ) 26 | 27 | response = client.chat.completions.create( 28 | # For Azure OpenAI, the model parameter must be set to the deployment name 29 | model=os.getenv("AZURE_OPENAI_GPT_DEPLOYMENT"), 30 | temperature=0.7, 31 | n=1, 32 | messages=[ 33 | {"role": "system", "content": "You are a helpful assistant that makes lots of cat references and uses emojis."}, 34 | {"role": "user", "content": "Write a haiku about a hungry cat who wants tuna"}, 35 | ], 36 | ) 37 | 38 | print("Response: ") 39 | print(response.choices[0].message.content) 40 | -------------------------------------------------------------------------------- /infra/main.bicep: -------------------------------------------------------------------------------- 1 | targetScope = 'subscription' 2 | 3 | @minLength(1) 4 | @maxLength(64) 5 | @description('Name of the the environment which is used to generate a short unique hash used in all resources.') 6 | param environmentName string 7 | 8 | @minLength(1) 9 | @description('Location for the OpenAI resource') 10 | // https://learn.microsoft.com/azure/ai-services/openai/concepts/models?tabs=python-secure%2Cglobal-standard%2Cstandard-chat-completions#models-by-deployment-type 11 | @allowed([ 12 | 'australiaeast' 13 | 'brazilsouth' 14 | 'canadaeast' 15 | 'eastus' 16 | 'eastus2' 17 | 'francecentral' 18 | 'germanywestcentral' 19 | 'japaneast' 20 | 'koreacentral' 21 | 'northcentralus' 22 | 'norwayeast' 23 | 'polandcentral' 24 | 'southafricanorth' 25 | 'southcentralus' 26 | 'southindia' 27 | 'spaincentral' 28 | 'swedencentral' 29 | 'switzerlandnorth' 30 | 'uksouth' 31 | 'westeurope' 32 | 'westus' 33 | 'westus3' 34 | ]) 35 | @metadata({ 36 | azd: { 37 | type: 'location' 38 | } 39 | }) 40 | param location string 41 | 42 | @description('Name of the GPT model to deploy') 43 | param gptModelName string = 'gpt-4o-mini' 44 | 45 | @description('Version of the GPT model to deploy') 46 | // See version availability in this table: 47 | // https://learn.microsoft.com/azure/ai-services/openai/concepts/models?tabs=python-secure%2Cglobal-standard%2Cstandard-chat-completions#models-by-deployment-type 48 | param gptModelVersion string = '2024-07-18' 49 | 50 | @description('Name of the model deployment (can be different from the model name)') 51 | param gptDeploymentName string = 'gpt-4o-mini' 52 | 53 | @description('Capacity of the GPT deployment') 54 | // You can increase this, but capacity is limited per model/region, so you will get errors if you go over 55 | // https://learn.microsoft.com/en-us/azure/ai-services/openai/quotas-limits 56 | param gptDeploymentCapacity int = 30 57 | 58 | @description('Id of the user or app to assign application roles') 59 | param principalId string = '' 60 | 61 | @description('Non-empty if the deployment is running on GitHub Actions') 62 | param runningOnGitHub string = '' 63 | 64 | var principalType = empty(runningOnGitHub) ? 'User' : 'ServicePrincipal' 65 | 66 | var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) 67 | var prefix = '${environmentName}${resourceToken}' 68 | var tags = { 'azd-env-name': environmentName } 69 | 70 | // Organize resources in a resource group 71 | resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { 72 | name: '${prefix}-rg' 73 | location: location 74 | tags: tags 75 | } 76 | 77 | var openAiServiceName = '${prefix}-openai' 78 | module openAi 'br/public:avm/res/cognitive-services/account:0.7.1' = { 79 | name: 'openai' 80 | scope: resourceGroup 81 | params: { 82 | name: openAiServiceName 83 | location: location 84 | tags: tags 85 | kind: 'OpenAI' 86 | sku: 'S0' 87 | customSubDomainName: openAiServiceName 88 | networkAcls: { 89 | defaultAction: 'Allow' 90 | bypass: 'AzureServices' 91 | } 92 | deployments: [ 93 | { 94 | name: gptDeploymentName 95 | model: { 96 | format: 'OpenAI' 97 | name: gptModelName 98 | version: gptModelVersion 99 | } 100 | sku: { 101 | name: 'GlobalStandard' 102 | capacity: gptDeploymentCapacity 103 | } 104 | } 105 | ] 106 | roleAssignments: [ 107 | { 108 | principalId: principalId 109 | roleDefinitionIdOrName: 'Cognitive Services OpenAI User' 110 | principalType: principalType 111 | } 112 | ] 113 | } 114 | } 115 | 116 | output AZURE_LOCATION string = location 117 | output AZURE_TENANT_ID string = tenant().tenantId 118 | output AZURE_RESOURCE_GROUP string = resourceGroup.name 119 | 120 | // Specific to Azure OpenAI 121 | output AZURE_OPENAI_SERVICE string = openAi.outputs.name 122 | output AZURE_OPENAI_GPT_MODEL string = gptModelName 123 | output AZURE_OPENAI_GPT_DEPLOYMENT string = gptDeploymentName 124 | -------------------------------------------------------------------------------- /infra/main.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "environmentName": { 6 | "value": "${AZURE_ENV_NAME}" 7 | }, 8 | "location": { 9 | "value": "${AZURE_LOCATION}" 10 | }, 11 | "principalId": { 12 | "value": "${AZURE_PRINCIPAL_ID}" 13 | }, 14 | "runningOnGitHub": { 15 | "value": "${GITHUB_ACTIONS}" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | azure-identity 2 | openai 3 | python-dotenv -------------------------------------------------------------------------------- /write_dot_env.ps1: -------------------------------------------------------------------------------- 1 | # Clear the contents of the .env file 2 | Set-Content -Path .env -Value "" 3 | 4 | # Append new values to the .env file 5 | $azureOpenAiDeployment = azd env get-value AZURE_OPENAI_GPT_DEPLOYMENT 6 | $azureOpenAiService = azd env get-value AZURE_OPENAI_SERVICE 7 | 8 | Add-Content -Path .env -Value "AZURE_OPENAI_GPT_DEPLOYMENT=$azureOpenAiDeployment" 9 | Add-Content -Path .env -Value "AZURE_OPENAI_SERVICE=$azureOpenAiService" 10 | -------------------------------------------------------------------------------- /write_dot_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Clear the contents of the .env file 4 | > .env 5 | 6 | # Append new values to the .env file 7 | echo "AZURE_OPENAI_GPT_DEPLOYMENT=$(azd env get-value AZURE_OPENAI_GPT_DEPLOYMENT)" >> .env 8 | echo "AZURE_OPENAI_SERVICE=$(azd env get-value AZURE_OPENAI_SERVICE)" >> .env 9 | --------------------------------------------------------------------------------