├── .gitattributes ├── .github └── CODE_OF_CONDUCT.md ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── assets ├── azure-sql-db-managed-identity.png ├── chatPlayground.png ├── files-add-roll0.png ├── files-add-roll1.png ├── files-add-roll2.png ├── files-add-roll3.png ├── files-add-roll4.png ├── files-add-roll5.png ├── files-add-roll6.png ├── files-add-roll7.png ├── files-add-roll8.png ├── keysEndpoints.png ├── power-bi-result.png ├── power-bi-sample-dataset.png └── sampleCode.png ├── azure-api-management.ipynb ├── azure-cognitive-services.ipynb ├── azure-event-hubs.ipynb ├── azure-functions.ipynb ├── azure-sql-enable-msi.ipynb ├── azure-storage.ipynb ├── openAI.ipynb └── power-bi.ipynb /.gitattributes: -------------------------------------------------------------------------------- 1 | # Thanks to: https://rehansaeed.com/gitattributes-best-practices/ 2 | 3 | # Set default behavior to automatically normalize line endings. 4 | * text=auto 5 | 6 | # Force batch scripts to always use CRLF line endings so that if a repo is accessed 7 | # in Windows via a file share from Linux, the scripts will work. 8 | *.{cmd,[cC][mM][dD]} text eol=crlf 9 | *.{bat,[bB][aA][tT]} text eol=crlf 10 | 11 | # Force bash scripts to always use LF line endings so that if a repo is accessed 12 | # in Unix via a file share from Windows, the scripts will work. 13 | *.sh text eol=lf -------------------------------------------------------------------------------- /.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 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## REST Endpoint Invocation Samples/Examples Changelog 2 | 3 | 4 | # Aug 10, 2023 5 | 6 | *Features* 7 | * Added a notebok on working with Azure Blob Storage 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Azure SQL DB sp_invoke_external_rest_endpoint samples 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/[organization-name]/[repository-name]/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/[organization-name]/[repository-name]/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! 77 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 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 | --- 2 | page_type: sample 3 | languages: 4 | - sql 5 | products: 6 | - azure-functions 7 | - azure-event-hubs 8 | - azure-openai 9 | - azure-sql-database 10 | - power-bi 11 | - azure-api-management 12 | urlFragment: azure-sql-db-invoke-external-rest-endpoints 13 | name: Call REST endpoints from Azure SQL database 14 | description: | 15 | sp_invoke_external_rest_endpoint is a system stored procedure that allows native invocation of an HTTPS REST endpoint from Azure SQL DB. 16 | --- 17 | 18 | # Azure SQL DB sp_invoke_external_rest_endpoint samples 19 | 20 | `sp_invoke_external_rest_endpoint` is a system stored procedure that allows native invocation of an HTTPS REST endpoint from Azure SQL DB. 21 | 22 | For full details on this stored procedure, please take a look at the official documentation here: [sp_invoke_external_rest_endpoint (Transact-SQL)](https://learn.microsoft.com/sql/relational-databases/system-stored-procedures/sp-invoke-external-rest-endpoint-transact-sql) 23 | 24 | ## Samples 25 | 26 | Samples are made available via Jupyter Notebook, natively supported by GitHub and also by [Azure Data Studio](https://learn.microsoft.com/en-us/sql/azure-data-studio/notebooks/notebooks-guidance) 27 | 28 | ### Azure OpenAI 29 | 30 | These samples and workshops show how you can call Azure OpenAI to get text embeddings and then find similar text using cosine similarity. 31 | 32 | - [Vector similarity search with Azure SQL & Azure OpenAI](https://github.com/azure-samples/azure-sql-db-openai) 33 | - [Build new AI applications with Azure SQL Database](https://github.com/Azure-Samples/sql-ai-embeddings-workshop) 34 | - [Use Azure SQL DB and REST endpoints to enable AI content moderation](https://github.com/AzureSQLDB/ContentSafetyLab) 35 | 36 | More on Azure SQL and AI can be found [here](https://aka.ms/sqlai). 37 | 38 | ### [Azure Functions](./azure-functions.ipynb) 39 | 40 | In the provided notebook you can find samples on how to invoke an [Azure Function](https://learn.microsoft.com/azure/azure-functions/functions-overview) using a [HTTP Trigger](https://learn.microsoft.com/azure/azure-functions/functions-bindings-http-webhook-trigger): 41 | 42 | - Invoke a public (or anonymous) Azure Function 43 | - Invoke an Azure Function protected by a secret key 44 | - Invoke an Azure Function protected by Azure AD 45 | 46 | ### [Azure Event Hubs](./azure-event-hubs.ipynb) 47 | 48 | The notebook contains samples on how to send messages to [Azure Event Hubs](https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-about): 49 | 50 | - Send events using a SAS Token 51 | - Send events using a Managed Identity 52 | 53 | ### [Power BI](./power-bi.ipynb) 54 | 55 | The notebook shows how DAX queries can be executed from Azure SQL DB using the `executeQueries` REST endpoint provided by Power BI datasets 56 | 57 | - Execute DAX queries in Power BI 58 | 59 | ### [Azure Cognitive Services](./azure-cognitive-services.ipynb) 60 | 61 | The notebook shows how to send data back and forth between an Azure Cognitive services, configured to perform Anomaly Detection. 62 | 63 | ### [Azure SignalR] 64 | 65 | Work in progress 66 | 67 | ### [Azure Logic Apps] 68 | 69 | Work in progress 70 | 71 | ### [Azure API Management](./azure-api-management.ipynb) 72 | 73 | The notebook contains samples on how to invoke an HTTPS REST endpoint no matter if it is residing in Azure or in any other cloud or hosting platform, by securely publishing and API using [Azure API Management](https://learn.microsoft.com/en-us/azure/api-management/) 74 | 75 | - Call an external REST endpoint using API Management 76 | 77 | ### [Azure Blob Storage](./azure-storage.ipynb) 78 | 79 | This notebook has examples on working with Azure Blob Storage with SAS tokens and Managed Identities. Use the REST APIs for creating files, containers and then reading them back. 80 | 81 | ## Contribute 82 | 83 | Do you want to contribute adding a sample? Do you see something missing or incorrect? Please help this repository to grow, and submit an issue or fork it a submit a Pull Request. Details on how to contribute can be found here: [Contributing](./CONTRIBUTING.md) 84 | -------------------------------------------------------------------------------- /assets/azure-sql-db-managed-identity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/azure-sql-db-managed-identity.png -------------------------------------------------------------------------------- /assets/chatPlayground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/chatPlayground.png -------------------------------------------------------------------------------- /assets/files-add-roll0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/files-add-roll0.png -------------------------------------------------------------------------------- /assets/files-add-roll1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/files-add-roll1.png -------------------------------------------------------------------------------- /assets/files-add-roll2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/files-add-roll2.png -------------------------------------------------------------------------------- /assets/files-add-roll3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/files-add-roll3.png -------------------------------------------------------------------------------- /assets/files-add-roll4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/files-add-roll4.png -------------------------------------------------------------------------------- /assets/files-add-roll5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/files-add-roll5.png -------------------------------------------------------------------------------- /assets/files-add-roll6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/files-add-roll6.png -------------------------------------------------------------------------------- /assets/files-add-roll7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/files-add-roll7.png -------------------------------------------------------------------------------- /assets/files-add-roll8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/files-add-roll8.png -------------------------------------------------------------------------------- /assets/keysEndpoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/keysEndpoints.png -------------------------------------------------------------------------------- /assets/power-bi-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/power-bi-result.png -------------------------------------------------------------------------------- /assets/power-bi-sample-dataset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/power-bi-sample-dataset.png -------------------------------------------------------------------------------- /assets/sampleCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-db-invoke-external-rest-endpoints/e1325b77d6d1762a5efb6666ce598dc4d97c023f/assets/sampleCode.png -------------------------------------------------------------------------------- /azure-api-management.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "SQL", 5 | "display_name": "SQL", 6 | "language": "sql" 7 | }, 8 | "language_info": { 9 | "name": "sql", 10 | "version": "" 11 | } 12 | }, 13 | "nbformat_minor": 2, 14 | "nbformat": 4, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "source": [ 19 | "# Call an REST API published via API Management\r\n", 20 | "\r\n", 21 | "Make sure you have create an API Management instance. If you need help, follow the Getting Started guide here: [Create a new Azure API Management service instance by using the Azure portal](https://learn.microsoft.com/en-us/azure/api-management/get-started-create-service-instance)\r\n", 22 | "\r\n", 23 | "As an example of an API not residing in Azure that you can call, you may use the [NIST Certified cryptographically secure random number generator](https://csrng.net/) API, that are available here: https://csrng.net/csrng/csrng.php\r\n", 24 | "\r\n", 25 | "For example: https://csrng.net/csrng/csrng.php?min=0&max=100\r\n", 26 | "\r\n", 27 | "will return the something like:\r\n", 28 | "\r\n", 29 | "```\r\n", 30 | "[{\"status\":\"success\",\"min\":0,\"max\":100,\"random\":99}]\r\n", 31 | "```\r\n", 32 | "\r\n", 33 | "The HTTPS REST API is not in an domain that is allowed for `sp_invoke_external_rest_endpoint` usage. See the allowed list of endpoints here: [Allowed Endpoints](https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-invoke-external-rest-endpoint-transact-sql?view=azuresqldb-current&tabs=request-headers#allowed-endpoints)\r\n", 34 | "\r\n", 35 | "In order to make the NIST endpoint available to be used by `sp_invoke_external_rest_endpoint`, it must published using Azure API Management.\r\n", 36 | "\r\n", 37 | "In the next samples is assumed that the API Management instance is deployed at https://external.azure-api.net and that the NIST API has been published at the `/random` path. \r\n", 38 | "\r\n", 39 | "Once the API is published, get the Subscription key you can use to call the created API and store it into a Database Scoped Credential\r\n", 40 | "" 41 | ], 42 | "metadata": { 43 | "azdata_cell_guid": "2fa150ef-9af9-491d-9c6b-50f49a0b67d9" 44 | }, 45 | "attachments": {} 46 | }, 47 | { 48 | "cell_type": "code", 49 | "source": [ 50 | "-- make sure a database master key exists\r\n", 51 | "if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') begin\r\n", 52 | " create master key encryption by password = 'LONg_Pa$$_w0rd!'\r\n", 53 | "end\r\n", 54 | "\r\n", 55 | "-- create database scoped credential\r\n", 56 | "if exists(select * from sys.database_scoped_credentials where [name] = 'https://api-mgmt.azure-api.net/random') begin\r\n", 57 | " drop database scoped credential [https://api-mgmt.azure-api.net/random];\r\n", 58 | "end\r\n", 59 | "create database scoped credential [https://api-mgmt.azure-api.net/random]\r\n", 60 | "with identity = 'HTTPEndpointHeaders', secret = '{\"Ocp-Apim-Subscription-Key\":\"***\"}';\r\n", 61 | "go" 62 | ], 63 | "metadata": { 64 | "azdata_cell_guid": "6fd989ea-205e-424d-9c72-980b6cd9fc50", 65 | "language": "sql" 66 | }, 67 | "outputs": [], 68 | "execution_count": null 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "source": [ 73 | "And then call random number generator via the API Management endpoint:" 74 | ], 75 | "metadata": { 76 | "azdata_cell_guid": "22ff69f0-86d3-40e1-9689-16e6a43762ee" 77 | }, 78 | "attachments": {} 79 | }, 80 | { 81 | "cell_type": "code", 82 | "source": [ 83 | "declare @ret int, @response nvarchar(max);\r\n", 84 | "exec @ret = sp_invoke_external_rest_endpoint \r\n", 85 | " @url = N'https://api-mgmt.azure-api.net/random?min=0&max=100',\r\n", 86 | " @method = 'GET', \r\n", 87 | " @credential = [https://api-mgmt.azure-api.net/random],\r\n", 88 | " @response = @response OUTPUT\r\n", 89 | "select \r\n", 90 | " @ret as ReturnCode, \r\n", 91 | " json_query(@response, '$.response') as Response,\r\n", 92 | " json_query(@response, '$.result') as Result;\r\n", 93 | "\r\n", 94 | "" 95 | ], 96 | "metadata": { 97 | "azdata_cell_guid": "e7b5fbba-1938-4a0e-a433-7874b79cf47c", 98 | "language": "sql" 99 | }, 100 | "outputs": [], 101 | "execution_count": null 102 | } 103 | ] 104 | } -------------------------------------------------------------------------------- /azure-cognitive-services.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "SQL", 5 | "display_name": "SQL", 6 | "language": "sql" 7 | }, 8 | "language_info": { 9 | "name": "sql", 10 | "version": "" 11 | } 12 | }, 13 | "nbformat_minor": 2, 14 | "nbformat": 4, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "source": [ 19 | "# Call an Azure Cognitive Services\n", 20 | "\n", 21 | "Make sure to have an Azure Cognitive Service Anomaly Detector running. [Create an Anomaly Detector resource](https://portal.azure.com/#create/Microsoft.CognitiveServicesAnomalyDetector) in the Azure portal to get your key and endpoint. Wait for it to deploy and select the Go to resource button. You can use the free pricing tier (F0) to try the service, and upgrade later to a paid tier for production. For more details please refer to the documentation here: [Anomaly Detector API Documentation](https://learn.microsoft.com/en-us/azure/cognitive-services/anomaly-detector/)\n", 22 | "\n", 23 | "In the next samples is assumed that the Anomaly Detector's endpoint is available at `https://azure-sql-anomaly-detector.cognitiveservices.azure.com/`. To have the samples working in your environment make sure to use the endpoint of your Anomaly Detector" 24 | ], 25 | "metadata": { 26 | "azdata_cell_guid": "cba5ef59-eaf6-468e-9e78-6281339299df" 27 | }, 28 | "attachments": {} 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "source": [ 33 | "## Load sample data\r\n", 34 | "\r\n", 35 | "First thing needes is some sample data. You can use the one provided by the anomaly detection sample [Quickstart: Use the Univariate Anomaly Detector client library](https://learn.microsoft.com/en-us/azure/cognitive-services/anomaly-detector/quickstarts/client-libraries?pivots=rest-api&tabs=command-line) here: [Request body sample](https://westus2.dev.cognitive.microsoft.com/docs/services/AnomalyDetector/operations/post-timeseries-entire-detect).\r\n", 36 | "Using Azure SQL JSON capabilities, it is very easy to load everything into a sample table named `datapoints`:\r\n", 37 | "" 38 | ], 39 | "metadata": { 40 | "azdata_cell_guid": "efd662c7-6f56-49b3-8c4d-dab1f013ea74" 41 | }, 42 | "attachments": {} 43 | }, 44 | { 45 | "cell_type": "code", 46 | "source": [ 47 | "declare @s nvarchar(max) = N'{ \r\n", 48 | " \"series\": [\r\n", 49 | " {\r\n", 50 | " \"timestamp\": \"1972-01-01T00:00:00Z\",\r\n", 51 | " \"value\": 826\r\n", 52 | " },\r\n", 53 | " {\r\n", 54 | " \"timestamp\": \"1972-02-01T00:00:00Z\",\r\n", 55 | " \"value\": 799\r\n", 56 | " },\r\n", 57 | " {\r\n", 58 | " \"timestamp\": \"1972-03-01T00:00:00Z\",\r\n", 59 | " \"value\": 890\r\n", 60 | " },\r\n", 61 | " {\r\n", 62 | " \"timestamp\": \"1972-04-01T00:00:00Z\",\r\n", 63 | " \"value\": 900\r\n", 64 | " },\r\n", 65 | " {\r\n", 66 | " \"timestamp\": \"1972-05-01T00:00:00Z\",\r\n", 67 | " \"value\": 961\r\n", 68 | " },\r\n", 69 | " {\r\n", 70 | " \"timestamp\": \"1972-06-01T00:00:00Z\",\r\n", 71 | " \"value\": 935\r\n", 72 | " },\r\n", 73 | " {\r\n", 74 | " \"timestamp\": \"1972-07-01T00:00:00Z\",\r\n", 75 | " \"value\": 894\r\n", 76 | " },\r\n", 77 | " {\r\n", 78 | " \"timestamp\": \"1972-08-01T00:00:00Z\",\r\n", 79 | " \"value\": 855\r\n", 80 | " },\r\n", 81 | " {\r\n", 82 | " \"timestamp\": \"1972-09-01T00:00:00Z\",\r\n", 83 | " \"value\": 809\r\n", 84 | " },\r\n", 85 | " {\r\n", 86 | " \"timestamp\": \"1972-10-01T00:00:00Z\",\r\n", 87 | " \"value\": 810\r\n", 88 | " },\r\n", 89 | " {\r\n", 90 | " \"timestamp\": \"1972-11-01T00:00:00Z\",\r\n", 91 | " \"value\": 766\r\n", 92 | " },\r\n", 93 | " {\r\n", 94 | " \"timestamp\": \"1972-12-01T00:00:00Z\",\r\n", 95 | " \"value\": 805\r\n", 96 | " },\r\n", 97 | " {\r\n", 98 | " \"timestamp\": \"1973-01-01T00:00:00Z\",\r\n", 99 | " \"value\": 821\r\n", 100 | " },\r\n", 101 | " {\r\n", 102 | " \"timestamp\": \"1973-02-01T00:00:00Z\",\r\n", 103 | " \"value\": 773\r\n", 104 | " },\r\n", 105 | " {\r\n", 106 | " \"timestamp\": \"1973-03-01T00:00:00Z\",\r\n", 107 | " \"value\": 883\r\n", 108 | " },\r\n", 109 | " {\r\n", 110 | " \"timestamp\": \"1973-04-01T00:00:00Z\",\r\n", 111 | " \"value\": 898\r\n", 112 | " },\r\n", 113 | " {\r\n", 114 | " \"timestamp\": \"1973-05-01T00:00:00Z\",\r\n", 115 | " \"value\": 957\r\n", 116 | " },\r\n", 117 | " {\r\n", 118 | " \"timestamp\": \"1973-06-01T00:00:00Z\",\r\n", 119 | " \"value\": 924\r\n", 120 | " },\r\n", 121 | " {\r\n", 122 | " \"timestamp\": \"1973-07-01T00:00:00Z\",\r\n", 123 | " \"value\": 881\r\n", 124 | " },\r\n", 125 | " {\r\n", 126 | " \"timestamp\": \"1973-08-01T00:00:00Z\",\r\n", 127 | " \"value\": 837\r\n", 128 | " },\r\n", 129 | " {\r\n", 130 | " \"timestamp\": \"1973-09-01T00:00:00Z\",\r\n", 131 | " \"value\": 784\r\n", 132 | " },\r\n", 133 | " {\r\n", 134 | " \"timestamp\": \"1973-10-01T00:00:00Z\",\r\n", 135 | " \"value\": 791\r\n", 136 | " },\r\n", 137 | " {\r\n", 138 | " \"timestamp\": \"1973-11-01T00:00:00Z\",\r\n", 139 | " \"value\": 760\r\n", 140 | " },\r\n", 141 | " {\r\n", 142 | " \"timestamp\": \"1973-12-01T00:00:00Z\",\r\n", 143 | " \"value\": 802\r\n", 144 | " },\r\n", 145 | " {\r\n", 146 | " \"timestamp\": \"1974-01-01T00:00:00Z\",\r\n", 147 | " \"value\": 828\r\n", 148 | " },\r\n", 149 | " {\r\n", 150 | " \"timestamp\": \"1974-02-01T00:00:00Z\",\r\n", 151 | " \"value\": 1030\r\n", 152 | " },\r\n", 153 | " {\r\n", 154 | " \"timestamp\": \"1974-03-01T00:00:00Z\",\r\n", 155 | " \"value\": 889\r\n", 156 | " },\r\n", 157 | " {\r\n", 158 | " \"timestamp\": \"1974-04-01T00:00:00Z\",\r\n", 159 | " \"value\": 902\r\n", 160 | " },\r\n", 161 | " {\r\n", 162 | " \"timestamp\": \"1974-05-01T00:00:00Z\",\r\n", 163 | " \"value\": 969\r\n", 164 | " },\r\n", 165 | " {\r\n", 166 | " \"timestamp\": \"1974-06-01T00:00:00Z\",\r\n", 167 | " \"value\": 947\r\n", 168 | " },\r\n", 169 | " {\r\n", 170 | " \"timestamp\": \"1974-07-01T00:00:00Z\",\r\n", 171 | " \"value\": 908\r\n", 172 | " },\r\n", 173 | " {\r\n", 174 | " \"timestamp\": \"1974-08-01T00:00:00Z\",\r\n", 175 | " \"value\": 867\r\n", 176 | " },\r\n", 177 | " {\r\n", 178 | " \"timestamp\": \"1974-09-01T00:00:00Z\",\r\n", 179 | " \"value\": 815\r\n", 180 | " },\r\n", 181 | " {\r\n", 182 | " \"timestamp\": \"1974-10-01T00:00:00Z\",\r\n", 183 | " \"value\": 812\r\n", 184 | " },\r\n", 185 | " {\r\n", 186 | " \"timestamp\": \"1974-11-01T00:00:00Z\",\r\n", 187 | " \"value\": 773\r\n", 188 | " },\r\n", 189 | " {\r\n", 190 | " \"timestamp\": \"1974-12-01T00:00:00Z\",\r\n", 191 | " \"value\": 813\r\n", 192 | " },\r\n", 193 | " {\r\n", 194 | " \"timestamp\": \"1975-01-01T00:00:00Z\",\r\n", 195 | " \"value\": 834\r\n", 196 | " },\r\n", 197 | " {\r\n", 198 | " \"timestamp\": \"1975-02-01T00:00:00Z\",\r\n", 199 | " \"value\": 782\r\n", 200 | " },\r\n", 201 | " {\r\n", 202 | " \"timestamp\": \"1975-03-01T00:00:00Z\",\r\n", 203 | " \"value\": 892\r\n", 204 | " },\r\n", 205 | " {\r\n", 206 | " \"timestamp\": \"1975-04-01T00:00:00Z\",\r\n", 207 | " \"value\": 903\r\n", 208 | " },\r\n", 209 | " {\r\n", 210 | " \"timestamp\": \"1975-05-01T00:00:00Z\",\r\n", 211 | " \"value\": 966\r\n", 212 | " },\r\n", 213 | " {\r\n", 214 | " \"timestamp\": \"1975-06-01T00:00:00Z\",\r\n", 215 | " \"value\": 937\r\n", 216 | " },\r\n", 217 | " {\r\n", 218 | " \"timestamp\": \"1975-07-01T00:00:00Z\",\r\n", 219 | " \"value\": 896\r\n", 220 | " },\r\n", 221 | " {\r\n", 222 | " \"timestamp\": \"1975-08-01T00:00:00Z\",\r\n", 223 | " \"value\": 858\r\n", 224 | " },\r\n", 225 | " {\r\n", 226 | " \"timestamp\": \"1975-09-01T00:00:00Z\",\r\n", 227 | " \"value\": 817\r\n", 228 | " },\r\n", 229 | " {\r\n", 230 | " \"timestamp\": \"1975-10-01T00:00:00Z\",\r\n", 231 | " \"value\": 827\r\n", 232 | " },\r\n", 233 | " {\r\n", 234 | " \"timestamp\": \"1975-11-01T00:00:00Z\",\r\n", 235 | " \"value\": 797\r\n", 236 | " },\r\n", 237 | " {\r\n", 238 | " \"timestamp\": \"1975-12-01T00:00:00Z\",\r\n", 239 | " \"value\": 843\r\n", 240 | " }\r\n", 241 | " ],\r\n", 242 | " \"maxAnomalyRatio\": 0.25,\r\n", 243 | " \"sensitivity\": 95,\r\n", 244 | " \"granularity\": \"monthly\"\r\n", 245 | "}';\r\n", 246 | "\r\n", 247 | "drop table if exists dbo.datapoints;\r\n", 248 | "create table dbo.datapoints \r\n", 249 | "(\r\n", 250 | "\tid int not null identity primary key nonclustered,\r\n", 251 | "\tsample_date datetime2 not null,\r\n", 252 | "\tsample_value numeric(18,3) not null\r\n", 253 | ")\t\r\n", 254 | ";\r\n", 255 | "\r\n", 256 | "insert into \r\n", 257 | "\tdbo.datapoints ([sample_date], [sample_value])\r\n", 258 | "select \r\n", 259 | "\tsample_date,\r\n", 260 | "\tsample_value\r\n", 261 | "from openjson(@s, '$.series') with \r\n", 262 | "\t(\r\n", 263 | "\t\t[sample_date] datetime2 '$.timestamp',\r\n", 264 | "\t\t[sample_value] numeric(18,3) '$.value'\r\n", 265 | "\t)\r\n", 266 | ";\r\n", 267 | "\r\n", 268 | "update [dbo].[datapoints] set [sample_date] = dateadd(year, 46, [sample_date]);\r\n", 269 | "\r\n", 270 | "select * from dbo.[datapoints]\r\n", 271 | "" 272 | ], 273 | "metadata": { 274 | "azdata_cell_guid": "29d317f6-0cfc-421b-ad1a-82e1afd69605", 275 | "language": "sql", 276 | "tags": [] 277 | }, 278 | "outputs": [ 279 | { 280 | "output_type": "display_data", 281 | "data": { 282 | "text/html": "(48 rows affected)" 283 | }, 284 | "metadata": {} 285 | }, 286 | { 287 | "output_type": "display_data", 288 | "data": { 289 | "text/html": "(48 rows affected)" 290 | }, 291 | "metadata": {} 292 | }, 293 | { 294 | "output_type": "display_data", 295 | "data": { 296 | "text/html": "(48 rows affected)" 297 | }, 298 | "metadata": {} 299 | }, 300 | { 301 | "output_type": "display_data", 302 | "data": { 303 | "text/html": "Total execution time: 00:00:00.107" 304 | }, 305 | "metadata": {} 306 | }, 307 | { 308 | "output_type": "execute_result", 309 | "metadata": {}, 310 | "execution_count": 1, 311 | "data": { 312 | "application/vnd.dataresource+json": { 313 | "schema": { 314 | "fields": [ 315 | { 316 | "name": "id" 317 | }, 318 | { 319 | "name": "sample_date" 320 | }, 321 | { 322 | "name": "sample_value" 323 | } 324 | ] 325 | }, 326 | "data": [ 327 | { 328 | "0": "1", 329 | "1": "2018-01-01 00:00:00.0000000", 330 | "2": "826.000" 331 | }, 332 | { 333 | "0": "2", 334 | "1": "2018-02-01 00:00:00.0000000", 335 | "2": "799.000" 336 | }, 337 | { 338 | "0": "3", 339 | "1": "2018-03-01 00:00:00.0000000", 340 | "2": "890.000" 341 | }, 342 | { 343 | "0": "4", 344 | "1": "2018-04-01 00:00:00.0000000", 345 | "2": "900.000" 346 | }, 347 | { 348 | "0": "5", 349 | "1": "2018-05-01 00:00:00.0000000", 350 | "2": "961.000" 351 | }, 352 | { 353 | "0": "6", 354 | "1": "2018-06-01 00:00:00.0000000", 355 | "2": "935.000" 356 | }, 357 | { 358 | "0": "7", 359 | "1": "2018-07-01 00:00:00.0000000", 360 | "2": "894.000" 361 | }, 362 | { 363 | "0": "8", 364 | "1": "2018-08-01 00:00:00.0000000", 365 | "2": "855.000" 366 | }, 367 | { 368 | "0": "9", 369 | "1": "2018-09-01 00:00:00.0000000", 370 | "2": "809.000" 371 | }, 372 | { 373 | "0": "10", 374 | "1": "2018-10-01 00:00:00.0000000", 375 | "2": "810.000" 376 | }, 377 | { 378 | "0": "11", 379 | "1": "2018-11-01 00:00:00.0000000", 380 | "2": "766.000" 381 | }, 382 | { 383 | "0": "12", 384 | "1": "2018-12-01 00:00:00.0000000", 385 | "2": "805.000" 386 | }, 387 | { 388 | "0": "13", 389 | "1": "2019-01-01 00:00:00.0000000", 390 | "2": "821.000" 391 | }, 392 | { 393 | "0": "14", 394 | "1": "2019-02-01 00:00:00.0000000", 395 | "2": "773.000" 396 | }, 397 | { 398 | "0": "15", 399 | "1": "2019-03-01 00:00:00.0000000", 400 | "2": "883.000" 401 | }, 402 | { 403 | "0": "16", 404 | "1": "2019-04-01 00:00:00.0000000", 405 | "2": "898.000" 406 | }, 407 | { 408 | "0": "17", 409 | "1": "2019-05-01 00:00:00.0000000", 410 | "2": "957.000" 411 | }, 412 | { 413 | "0": "18", 414 | "1": "2019-06-01 00:00:00.0000000", 415 | "2": "924.000" 416 | }, 417 | { 418 | "0": "19", 419 | "1": "2019-07-01 00:00:00.0000000", 420 | "2": "881.000" 421 | }, 422 | { 423 | "0": "20", 424 | "1": "2019-08-01 00:00:00.0000000", 425 | "2": "837.000" 426 | }, 427 | { 428 | "0": "21", 429 | "1": "2019-09-01 00:00:00.0000000", 430 | "2": "784.000" 431 | }, 432 | { 433 | "0": "22", 434 | "1": "2019-10-01 00:00:00.0000000", 435 | "2": "791.000" 436 | }, 437 | { 438 | "0": "23", 439 | "1": "2019-11-01 00:00:00.0000000", 440 | "2": "760.000" 441 | }, 442 | { 443 | "0": "24", 444 | "1": "2019-12-01 00:00:00.0000000", 445 | "2": "802.000" 446 | }, 447 | { 448 | "0": "25", 449 | "1": "2020-01-01 00:00:00.0000000", 450 | "2": "828.000" 451 | }, 452 | { 453 | "0": "26", 454 | "1": "2020-02-01 00:00:00.0000000", 455 | "2": "1030.000" 456 | }, 457 | { 458 | "0": "27", 459 | "1": "2020-03-01 00:00:00.0000000", 460 | "2": "889.000" 461 | }, 462 | { 463 | "0": "28", 464 | "1": "2020-04-01 00:00:00.0000000", 465 | "2": "902.000" 466 | }, 467 | { 468 | "0": "29", 469 | "1": "2020-05-01 00:00:00.0000000", 470 | "2": "969.000" 471 | }, 472 | { 473 | "0": "30", 474 | "1": "2020-06-01 00:00:00.0000000", 475 | "2": "947.000" 476 | }, 477 | { 478 | "0": "31", 479 | "1": "2020-07-01 00:00:00.0000000", 480 | "2": "908.000" 481 | }, 482 | { 483 | "0": "32", 484 | "1": "2020-08-01 00:00:00.0000000", 485 | "2": "867.000" 486 | }, 487 | { 488 | "0": "33", 489 | "1": "2020-09-01 00:00:00.0000000", 490 | "2": "815.000" 491 | }, 492 | { 493 | "0": "34", 494 | "1": "2020-10-01 00:00:00.0000000", 495 | "2": "812.000" 496 | }, 497 | { 498 | "0": "35", 499 | "1": "2020-11-01 00:00:00.0000000", 500 | "2": "773.000" 501 | }, 502 | { 503 | "0": "36", 504 | "1": "2020-12-01 00:00:00.0000000", 505 | "2": "813.000" 506 | }, 507 | { 508 | "0": "37", 509 | "1": "2021-01-01 00:00:00.0000000", 510 | "2": "834.000" 511 | }, 512 | { 513 | "0": "38", 514 | "1": "2021-02-01 00:00:00.0000000", 515 | "2": "782.000" 516 | }, 517 | { 518 | "0": "39", 519 | "1": "2021-03-01 00:00:00.0000000", 520 | "2": "892.000" 521 | }, 522 | { 523 | "0": "40", 524 | "1": "2021-04-01 00:00:00.0000000", 525 | "2": "903.000" 526 | }, 527 | { 528 | "0": "41", 529 | "1": "2021-05-01 00:00:00.0000000", 530 | "2": "966.000" 531 | }, 532 | { 533 | "0": "42", 534 | "1": "2021-06-01 00:00:00.0000000", 535 | "2": "937.000" 536 | }, 537 | { 538 | "0": "43", 539 | "1": "2021-07-01 00:00:00.0000000", 540 | "2": "896.000" 541 | }, 542 | { 543 | "0": "44", 544 | "1": "2021-08-01 00:00:00.0000000", 545 | "2": "858.000" 546 | }, 547 | { 548 | "0": "45", 549 | "1": "2021-09-01 00:00:00.0000000", 550 | "2": "817.000" 551 | }, 552 | { 553 | "0": "46", 554 | "1": "2021-10-01 00:00:00.0000000", 555 | "2": "827.000" 556 | }, 557 | { 558 | "0": "47", 559 | "1": "2021-11-01 00:00:00.0000000", 560 | "2": "797.000" 561 | }, 562 | { 563 | "0": "48", 564 | "1": "2021-12-01 00:00:00.0000000", 565 | "2": "843.000" 566 | } 567 | ] 568 | }, 569 | "text/html": [ 570 | "", 571 | "", 572 | "", 573 | "", 574 | "", 575 | "", 576 | "", 577 | "", 578 | "", 579 | "", 580 | "", 581 | "", 582 | "", 583 | "", 584 | "", 585 | "", 586 | "", 587 | "", 588 | "", 589 | "", 590 | "", 591 | "", 592 | "", 593 | "", 594 | "", 595 | "", 596 | "", 597 | "", 598 | "", 599 | "", 600 | "", 601 | "", 602 | "", 603 | "", 604 | "", 605 | "", 606 | "", 607 | "", 608 | "", 609 | "", 610 | "", 611 | "", 612 | "", 613 | "", 614 | "", 615 | "", 616 | "", 617 | "", 618 | "", 619 | "", 620 | "
idsample_datesample_value
12018-01-01 00:00:00.0000000826.000
22018-02-01 00:00:00.0000000799.000
32018-03-01 00:00:00.0000000890.000
42018-04-01 00:00:00.0000000900.000
52018-05-01 00:00:00.0000000961.000
62018-06-01 00:00:00.0000000935.000
72018-07-01 00:00:00.0000000894.000
82018-08-01 00:00:00.0000000855.000
92018-09-01 00:00:00.0000000809.000
102018-10-01 00:00:00.0000000810.000
112018-11-01 00:00:00.0000000766.000
122018-12-01 00:00:00.0000000805.000
132019-01-01 00:00:00.0000000821.000
142019-02-01 00:00:00.0000000773.000
152019-03-01 00:00:00.0000000883.000
162019-04-01 00:00:00.0000000898.000
172019-05-01 00:00:00.0000000957.000
182019-06-01 00:00:00.0000000924.000
192019-07-01 00:00:00.0000000881.000
202019-08-01 00:00:00.0000000837.000
212019-09-01 00:00:00.0000000784.000
222019-10-01 00:00:00.0000000791.000
232019-11-01 00:00:00.0000000760.000
242019-12-01 00:00:00.0000000802.000
252020-01-01 00:00:00.0000000828.000
262020-02-01 00:00:00.00000001030.000
272020-03-01 00:00:00.0000000889.000
282020-04-01 00:00:00.0000000902.000
292020-05-01 00:00:00.0000000969.000
302020-06-01 00:00:00.0000000947.000
312020-07-01 00:00:00.0000000908.000
322020-08-01 00:00:00.0000000867.000
332020-09-01 00:00:00.0000000815.000
342020-10-01 00:00:00.0000000812.000
352020-11-01 00:00:00.0000000773.000
362020-12-01 00:00:00.0000000813.000
372021-01-01 00:00:00.0000000834.000
382021-02-01 00:00:00.0000000782.000
392021-03-01 00:00:00.0000000892.000
402021-04-01 00:00:00.0000000903.000
412021-05-01 00:00:00.0000000966.000
422021-06-01 00:00:00.0000000937.000
432021-07-01 00:00:00.0000000896.000
442021-08-01 00:00:00.0000000858.000
452021-09-01 00:00:00.0000000817.000
462021-10-01 00:00:00.0000000827.000
472021-11-01 00:00:00.0000000797.000
482021-12-01 00:00:00.0000000843.000
" 621 | ] 622 | } 623 | } 624 | ], 625 | "execution_count": 1 626 | }, 627 | { 628 | "cell_type": "markdown", 629 | "source": [ 630 | "## Securely store the API Key\r\n", 631 | "\r\n", 632 | "Azure Cogntives services uses a key to authenticate requests. The key must be passed in the `Ocp-Apim-Subscription-Key` request header, as described in the documentation: [Anomaly Detector API Documentation](https://westus2.dev.cognitive.microsoft.com/docs/services/AnomalyDetector/operations/post-timeseries-entire-detect).\r\n", 633 | "\r\n", 634 | "Since the authorization key is a sensitive value, it is recommended to store its value in a `DATABASE SCOPED CREDENTIAL` (more info here: [Credentials (Database Engine)](https://learn.microsoft.com/sql/relational-databases/security/authentication-access/credentials-database-engine))" 635 | ], 636 | "metadata": { 637 | "azdata_cell_guid": "941d1c14-daad-4ab6-8591-2efdfeb46f44" 638 | }, 639 | "attachments": {} 640 | }, 641 | { 642 | "cell_type": "code", 643 | "source": [ 644 | "-- make sure a database master key exists\r\n", 645 | "if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') begin\r\n", 646 | " create master key encryption by password = 'LONg_Pa$$_w0rd!'\r\n", 647 | "end\r\n", 648 | "\r\n", 649 | "-- create database scoped credential\r\n", 650 | "create database scoped credential [https://azure-sql-anomaly-detector.cognitiveservices.azure.com/]\r\n", 651 | "with identity = 'HTTPEndpointHeaders', secret = '{\"Ocp-Apim-Subscription-Key\":\"\"}';\r\n", 652 | "go" 653 | ], 654 | "metadata": { 655 | "azdata_cell_guid": "69ae5373-ffd7-4a44-8fb6-334a43506c59", 656 | "language": "sql" 657 | }, 658 | "outputs": [], 659 | "execution_count": null 660 | }, 661 | { 662 | "cell_type": "markdown", 663 | "source": [ 664 | "## Prepare a temp table to store request and response\r\n", 665 | "\r\n", 666 | "To make it easier to try out this sample, a temporary table will be used to store the request and the response payload. This is not technically needed as everything could be done using variables, but it will make it harder to split the sample in smaller steps, that are easier to follow." 667 | ], 668 | "metadata": { 669 | "azdata_cell_guid": "28f90aeb-ef46-4274-a9bc-3b3751510fd0" 670 | }, 671 | "attachments": {} 672 | }, 673 | { 674 | "cell_type": "code", 675 | "source": [ 676 | "drop table if exists #temp;\r\n", 677 | "create table #temp (id int not null primary key, request nvarchar(max), response nvarchar(max));" 678 | ], 679 | "metadata": { 680 | "azdata_cell_guid": "dfd4b89f-f03f-4d92-902a-91b39588b73e", 681 | "language": "sql" 682 | }, 683 | "outputs": [ 684 | { 685 | "output_type": "display_data", 686 | "data": { 687 | "text/html": "Commands completed successfully." 688 | }, 689 | "metadata": {} 690 | }, 691 | { 692 | "output_type": "display_data", 693 | "data": { 694 | "text/html": "Total execution time: 00:00:00.021" 695 | }, 696 | "metadata": {} 697 | } 698 | ], 699 | "execution_count": 6 700 | }, 701 | { 702 | "cell_type": "markdown", 703 | "source": [ 704 | "## Use `OPENJSON` to shape the JSON document as requested by Cognitive Services\n", 705 | "\n", 706 | "As Cognitive Services expect the receving JSON with a specific schema, as documented here: [Anomaly Detector API Documentation](https://westus2.dev.cognitive.microsoft.com/docs/services/AnomalyDetector/operations/post-timeseries-entire-detect), the data stored in the sample `datapoints` table must be converted into a JSON using the `FOR JSON` operator and then put the generated JSON into the `#temp` table for later use." 707 | ], 708 | "metadata": { 709 | "azdata_cell_guid": "61155428-6203-4a3d-a13f-03fafb71fc98" 710 | }, 711 | "attachments": {} 712 | }, 713 | { 714 | "cell_type": "code", 715 | "source": [ 716 | "declare @payload nvarchar(max);\r\n", 717 | "\r\n", 718 | "set @payload = (\r\n", 719 | "\tselect\r\n", 720 | "\t\tseries = json_query((\t\r\n", 721 | "\t\t\tselect \r\n", 722 | "\t\t\t\tsample_date as [timestamp],\r\n", 723 | "\t\t\t\tsample_value as [value]\r\n", 724 | "\t\t\tfrom \r\n", 725 | "\t\t\t\tdbo.[datapoints] as series for json path\r\n", 726 | "\t\t)),\r\n", 727 | "\t\t[T].[maxAnomalyRatio],\r\n", 728 | "\t\t[T].[sensitivity],\r\n", 729 | "\t\t[T].[granularity]\r\n", 730 | "\tfrom\r\n", 731 | "\t\t(values (0.25, 95, 'monthly')) T ([maxAnomalyRatio], [sensitivity], [granularity])\r\n", 732 | "\tfor\r\n", 733 | "\t\tjson path, without_array_wrapper\r\n", 734 | ");\r\n", 735 | "\r\n", 736 | "delete from #temp;\r\n", 737 | "insert into #temp (id, request) values (1, @payload);\r\n", 738 | "select request from #temp where id = 1;" 739 | ], 740 | "metadata": { 741 | "azdata_cell_guid": "6d978928-a94b-46ce-a692-fea6b2ab0d79", 742 | "language": "sql" 743 | }, 744 | "outputs": [ 745 | { 746 | "output_type": "display_data", 747 | "data": { 748 | "text/html": "Commands completed successfully." 749 | }, 750 | "metadata": {} 751 | }, 752 | { 753 | "output_type": "display_data", 754 | "data": { 755 | "text/html": "Total execution time: 00:00:00.024" 756 | }, 757 | "metadata": {} 758 | }, 759 | { 760 | "output_type": "execute_result", 761 | "metadata": {}, 762 | "execution_count": 7, 763 | "data": { 764 | "application/vnd.dataresource+json": { 765 | "schema": { 766 | "fields": [ 767 | { 768 | "name": "request" 769 | } 770 | ] 771 | }, 772 | "data": [ 773 | { 774 | "0": "{\"series\":[{\"timestamp\":\"2018-01-01T00:00:00\",\"value\":826.000},{\"timestamp\":\"2018-02-01T00:00:00\",\"value\":799.000},{\"timestamp\":\"2018-03-01T00:00:00\",\"value\":890.000},{\"timestamp\":\"2018-04-01T00:00:00\",\"value\":900.000},{\"timestamp\":\"2018-05-01T00:00:00\",\"value\":961.000},{\"timestamp\":\"2018-06-01T00:00:00\",\"value\":935.000},{\"timestamp\":\"2018-07-01T00:00:00\",\"value\":894.000},{\"timestamp\":\"2018-08-01T00:00:00\",\"value\":855.000},{\"timestamp\":\"2018-09-01T00:00:00\",\"value\":809.000},{\"timestamp\":\"2018-10-01T00:00:00\",\"value\":810.000},{\"timestamp\":\"2018-11-01T00:00:00\",\"value\":766.000},{\"timestamp\":\"2018-12-01T00:00:00\",\"value\":805.000},{\"timestamp\":\"2019-01-01T00:00:00\",\"value\":821.000},{\"timestamp\":\"2019-02-01T00:00:00\",\"value\":773.000},{\"timestamp\":\"2019-03-01T00:00:00\",\"value\":883.000},{\"timestamp\":\"2019-04-01T00:00:00\",\"value\":898.000},{\"timestamp\":\"2019-05-01T00:00:00\",\"value\":957.000},{\"timestamp\":\"2019-06-01T00:00:00\",\"value\":924.000},{\"timestamp\":\"2019-07-01T00:00:00\",\"value\":881.000},{\"timestamp\":\"2019-08-01T00:00:00\",\"value\":837.000},{\"timestamp\":\"2019-09-01T00:00:00\",\"value\":784.000},{\"timestamp\":\"2019-10-01T00:00:00\",\"value\":791.000},{\"timestamp\":\"2019-11-01T00:00:00\",\"value\":760.000},{\"timestamp\":\"2019-12-01T00:00:00\",\"value\":802.000},{\"timestamp\":\"2020-01-01T00:00:00\",\"value\":828.000},{\"timestamp\":\"2020-02-01T00:00:00\",\"value\":1030.000},{\"timestamp\":\"2020-03-01T00:00:00\",\"value\":889.000},{\"timestamp\":\"2020-04-01T00:00:00\",\"value\":902.000},{\"timestamp\":\"2020-05-01T00:00:00\",\"value\":969.000},{\"timestamp\":\"2020-06-01T00:00:00\",\"value\":947.000},{\"timestamp\":\"2020-07-01T00:00:00\",\"value\":908.000},{\"timestamp\":\"2020-08-01T00:00:00\",\"value\":867.000},{\"timestamp\":\"2020-09-01T00:00:00\",\"value\":815.000},{\"timestamp\":\"2020-10-01T00:00:00\",\"value\":812.000},{\"timestamp\":\"2020-11-01T00:00:00\",\"value\":773.000},{\"timestamp\":\"2020-12-01T00:00:00\",\"value\":813.000},{\"timestamp\":\"2021-01-01T00:00:00\",\"value\":834.000},{\"timestamp\":\"2021-02-01T00:00:00\",\"value\":782.000},{\"timestamp\":\"2021-03-01T00:00:00\",\"value\":892.000},{\"timestamp\":\"2021-04-01T00:00:00\",\"value\":903.000},{\"timestamp\":\"2021-05-01T00:00:00\",\"value\":966.000},{\"timestamp\":\"2021-06-01T00:00:00\",\"value\":937.000},{\"timestamp\":\"2021-07-01T00:00:00\",\"value\":896.000},{\"timestamp\":\"2021-08-01T00:00:00\",\"value\":858.000},{\"timestamp\":\"2021-09-01T00:00:00\",\"value\":817.000},{\"timestamp\":\"2021-10-01T00:00:00\",\"value\":827.000},{\"timestamp\":\"2021-11-01T00:00:00\",\"value\":797.000},{\"timestamp\":\"2021-12-01T00:00:00\",\"value\":843.000}],\"maxAnomalyRatio\":0.25,\"sensitivity\":95,\"granularity\":\"monthly\"}" 775 | } 776 | ] 777 | }, 778 | "text/html": [ 779 | "", 780 | "", 781 | "", 782 | "
request
{"series":[{"timestamp":"2018-01-01T00:00:00","value":826.000},{"timestamp":"2018-02-01T00:00:00","value":799.000},{"timestamp":"2018-03-01T00:00:00","value":890.000},{"timestamp":"2018-04-01T00:00:00","value":900.000},{"timestamp":"2018-05-01T00:00:00","value":961.000},{"timestamp":"2018-06-01T00:00:00","value":935.000},{"timestamp":"2018-07-01T00:00:00","value":894.000},{"timestamp":"2018-08-01T00:00:00","value":855.000},{"timestamp":"2018-09-01T00:00:00","value":809.000},{"timestamp":"2018-10-01T00:00:00","value":810.000},{"timestamp":"2018-11-01T00:00:00","value":766.000},{"timestamp":"2018-12-01T00:00:00","value":805.000},{"timestamp":"2019-01-01T00:00:00","value":821.000},{"timestamp":"2019-02-01T00:00:00","value":773.000},{"timestamp":"2019-03-01T00:00:00","value":883.000},{"timestamp":"2019-04-01T00:00:00","value":898.000},{"timestamp":"2019-05-01T00:00:00","value":957.000},{"timestamp":"2019-06-01T00:00:00","value":924.000},{"timestamp":"2019-07-01T00:00:00","value":881.000},{"timestamp":"2019-08-01T00:00:00","value":837.000},{"timestamp":"2019-09-01T00:00:00","value":784.000},{"timestamp":"2019-10-01T00:00:00","value":791.000},{"timestamp":"2019-11-01T00:00:00","value":760.000},{"timestamp":"2019-12-01T00:00:00","value":802.000},{"timestamp":"2020-01-01T00:00:00","value":828.000},{"timestamp":"2020-02-01T00:00:00","value":1030.000},{"timestamp":"2020-03-01T00:00:00","value":889.000},{"timestamp":"2020-04-01T00:00:00","value":902.000},{"timestamp":"2020-05-01T00:00:00","value":969.000},{"timestamp":"2020-06-01T00:00:00","value":947.000},{"timestamp":"2020-07-01T00:00:00","value":908.000},{"timestamp":"2020-08-01T00:00:00","value":867.000},{"timestamp":"2020-09-01T00:00:00","value":815.000},{"timestamp":"2020-10-01T00:00:00","value":812.000},{"timestamp":"2020-11-01T00:00:00","value":773.000},{"timestamp":"2020-12-01T00:00:00","value":813.000},{"timestamp":"2021-01-01T00:00:00","value":834.000},{"timestamp":"2021-02-01T00:00:00","value":782.000},{"timestamp":"2021-03-01T00:00:00","value":892.000},{"timestamp":"2021-04-01T00:00:00","value":903.000},{"timestamp":"2021-05-01T00:00:00","value":966.000},{"timestamp":"2021-06-01T00:00:00","value":937.000},{"timestamp":"2021-07-01T00:00:00","value":896.000},{"timestamp":"2021-08-01T00:00:00","value":858.000},{"timestamp":"2021-09-01T00:00:00","value":817.000},{"timestamp":"2021-10-01T00:00:00","value":827.000},{"timestamp":"2021-11-01T00:00:00","value":797.000},{"timestamp":"2021-12-01T00:00:00","value":843.000}],"maxAnomalyRatio":0.25,"sensitivity":95,"granularity":"monthly"}
" 783 | ] 784 | } 785 | } 786 | ], 787 | "execution_count": 7 788 | }, 789 | { 790 | "cell_type": "markdown", 791 | "source": [ 792 | "## Invoke Anomaly Detector's API from Azure SQL\r\n", 793 | "\r\n", 794 | "It is now possible to invoke the Cognitive Service API to perform anomaly detection using `sp_invoke_external_rest_endpoint`. The result is stored back into the `#temp` table created before." 795 | ], 796 | "metadata": { 797 | "azdata_cell_guid": "8b15e26b-4f9e-449f-9ed3-3e6af6396176" 798 | }, 799 | "attachments": {} 800 | }, 801 | { 802 | "cell_type": "code", 803 | "source": [ 804 | "declare @payload nvarchar(max) = (select request from #temp where id = 1);\r\n", 805 | "declare @ret int, @response nvarchar(max);\r\n", 806 | "\r\n", 807 | "exec @ret = sp_invoke_external_rest_endpoint \r\n", 808 | "\t@url = 'https://azure-sql-anomaly-detector.cognitiveservices.azure.com/anomalydetector/v1.0/timeseries/entire/detect',\r\n", 809 | "\t@credential = [https://azure-sql-anomaly-detector.cognitiveservices.azure.com/],\r\n", 810 | "\t@payload = @payload,\r\n", 811 | "\t@response = @response output;\r\n", 812 | "\t\r\n", 813 | "update #temp set response = @response where id = 1;\r\n", 814 | "select * from #temp where id = 1;" 815 | ], 816 | "metadata": { 817 | "azdata_cell_guid": "4f670f18-028d-425b-8bf8-b590f200dc7d", 818 | "language": "sql" 819 | }, 820 | "outputs": [ 821 | { 822 | "output_type": "display_data", 823 | "data": { 824 | "text/html": "Commands completed successfully." 825 | }, 826 | "metadata": {} 827 | }, 828 | { 829 | "output_type": "display_data", 830 | "data": { 831 | "text/html": "Total execution time: 00:00:00.215" 832 | }, 833 | "metadata": {} 834 | }, 835 | { 836 | "output_type": "execute_result", 837 | "metadata": {}, 838 | "execution_count": 8, 839 | "data": { 840 | "application/vnd.dataresource+json": { 841 | "schema": { 842 | "fields": [ 843 | { 844 | "name": "id" 845 | }, 846 | { 847 | "name": "request" 848 | }, 849 | { 850 | "name": "response" 851 | } 852 | ] 853 | }, 854 | "data": [ 855 | { 856 | "0": "1", 857 | "1": "{\"series\":[{\"timestamp\":\"2018-01-01T00:00:00\",\"value\":826.000},{\"timestamp\":\"2018-02-01T00:00:00\",\"value\":799.000},{\"timestamp\":\"2018-03-01T00:00:00\",\"value\":890.000},{\"timestamp\":\"2018-04-01T00:00:00\",\"value\":900.000},{\"timestamp\":\"2018-05-01T00:00:00\",\"value\":961.000},{\"timestamp\":\"2018-06-01T00:00:00\",\"value\":935.000},{\"timestamp\":\"2018-07-01T00:00:00\",\"value\":894.000},{\"timestamp\":\"2018-08-01T00:00:00\",\"value\":855.000},{\"timestamp\":\"2018-09-01T00:00:00\",\"value\":809.000},{\"timestamp\":\"2018-10-01T00:00:00\",\"value\":810.000},{\"timestamp\":\"2018-11-01T00:00:00\",\"value\":766.000},{\"timestamp\":\"2018-12-01T00:00:00\",\"value\":805.000},{\"timestamp\":\"2019-01-01T00:00:00\",\"value\":821.000},{\"timestamp\":\"2019-02-01T00:00:00\",\"value\":773.000},{\"timestamp\":\"2019-03-01T00:00:00\",\"value\":883.000},{\"timestamp\":\"2019-04-01T00:00:00\",\"value\":898.000},{\"timestamp\":\"2019-05-01T00:00:00\",\"value\":957.000},{\"timestamp\":\"2019-06-01T00:00:00\",\"value\":924.000},{\"timestamp\":\"2019-07-01T00:00:00\",\"value\":881.000},{\"timestamp\":\"2019-08-01T00:00:00\",\"value\":837.000},{\"timestamp\":\"2019-09-01T00:00:00\",\"value\":784.000},{\"timestamp\":\"2019-10-01T00:00:00\",\"value\":791.000},{\"timestamp\":\"2019-11-01T00:00:00\",\"value\":760.000},{\"timestamp\":\"2019-12-01T00:00:00\",\"value\":802.000},{\"timestamp\":\"2020-01-01T00:00:00\",\"value\":828.000},{\"timestamp\":\"2020-02-01T00:00:00\",\"value\":1030.000},{\"timestamp\":\"2020-03-01T00:00:00\",\"value\":889.000},{\"timestamp\":\"2020-04-01T00:00:00\",\"value\":902.000},{\"timestamp\":\"2020-05-01T00:00:00\",\"value\":969.000},{\"timestamp\":\"2020-06-01T00:00:00\",\"value\":947.000},{\"timestamp\":\"2020-07-01T00:00:00\",\"value\":908.000},{\"timestamp\":\"2020-08-01T00:00:00\",\"value\":867.000},{\"timestamp\":\"2020-09-01T00:00:00\",\"value\":815.000},{\"timestamp\":\"2020-10-01T00:00:00\",\"value\":812.000},{\"timestamp\":\"2020-11-01T00:00:00\",\"value\":773.000},{\"timestamp\":\"2020-12-01T00:00:00\",\"value\":813.000},{\"timestamp\":\"2021-01-01T00:00:00\",\"value\":834.000},{\"timestamp\":\"2021-02-01T00:00:00\",\"value\":782.000},{\"timestamp\":\"2021-03-01T00:00:00\",\"value\":892.000},{\"timestamp\":\"2021-04-01T00:00:00\",\"value\":903.000},{\"timestamp\":\"2021-05-01T00:00:00\",\"value\":966.000},{\"timestamp\":\"2021-06-01T00:00:00\",\"value\":937.000},{\"timestamp\":\"2021-07-01T00:00:00\",\"value\":896.000},{\"timestamp\":\"2021-08-01T00:00:00\",\"value\":858.000},{\"timestamp\":\"2021-09-01T00:00:00\",\"value\":817.000},{\"timestamp\":\"2021-10-01T00:00:00\",\"value\":827.000},{\"timestamp\":\"2021-11-01T00:00:00\",\"value\":797.000},{\"timestamp\":\"2021-12-01T00:00:00\",\"value\":843.000}],\"maxAnomalyRatio\":0.25,\"sensitivity\":95,\"granularity\":\"monthly\"}", 858 | "2": "{\"response\":{\"status\":{\"http\":{\"code\":200,\"description\":\"\"}},\"headers\":{\"Date\":\"Fri, 03 Feb 2023 19:11:07 GMT\",\"Content-Length\":\"3582\",\"Content-Type\":\"application\\/json\",\"csp-billing-usage\":\"CognitiveServices.AnomalyDetector.DataPoints=1\",\"model-id\":\"10\",\"x-envoy-upstream-service-time\":\"39\",\"apim-request-id\":\"68498343-e890-401a-a7ee-09cd360e28f2\",\"strict-transport-security\":\"max-age=31536000; includeSubDomains; preload\",\"x-content-type-options\":\"nosniff\",\"x-ms-region\":\"East US\"}},\"result\":{\"expectedValues\":[827.7940908243968,798.9133774671927,888.6058431807189,900.5606407986661,962.8389426378304,933.2591606306954,891.0784104799666,856.1781601363697,809.8987227908941,807.375129007505,764.3196682448518,803.933498594564,823.5900620883058,794.0905641334288,883.164245249282,894.8419000690953,956.8430591101258,927.6285055190114,885.812983784303,851.7622285698933,806.3322863536049,804.8024303608446,762.74070738882,804.0251702513732,825.3523662579559,798.0404188724976,889.3016505577698,902.4226124345937,965.867078532635,937.2113627931791,895.9546789101294,862.0087368413656,816.4662342097423,814.4297745524709,771.8614479159354,811.859271346729,831.8998279215521,802.947544797165,892.5684407435083,904.5488214533809,966.8527063844707,937.3168391003043,895.3975195019448,860.7889417178712,814.801176931919,812.7134983694949,770.0939528278067,810.3738137939964],\"isAnomaly\":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],\"isNegativeAnomaly\":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],\"isPositiveAnomaly\":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],\"lowerMargins\":[41.389704541219885,38.91337746719273,44.43029215903596,45.02803203993335,48.14194713189147,46.6629580315348,44.553920523998386,42.808908006818456,40.494936139544734,40.36875645037526,4.319668244851755,40.19667492972815,41.17950310441529,34.09056413342876,44.158212262464076,44.74209500345478,47.84215295550632,46.38142527595062,44.29064918921517,42.5881114284947,40.316614317680205,40.2401215180422,2.7407073888200557,40.20125851256864,41.26761831289775,38.04041887249764,44.46508252788851,45.12113062172966,48.29335392663177,46.860568139658994,44.79773394550648,43.100436842068234,40.82331171048713,40.72148872762352,11.8614479159354,40.59296356733648,41.5949913960776,40.1473772398582,44.62842203717537,45.22744107266908,48.34263531922352,46.865841955015185,44.76987597509719,43.039447085893585,40.74005884659596,40.635674918474706,10.093952827806675,40.51869068969984],\"period\":12,\"upperMargins\":[41.389704541219885,39.94566887335964,44.43029215903596,45.02803203993335,48.14194713189147,46.6629580315348,44.553920523998386,42.808908006818456,40.494936139544734,40.36875645037526,38.21598341224262,40.19667492972815,41.17950310441529,39.7045282066714,44.158212262464076,44.74209500345478,47.84215295550632,46.38142527595062,44.29064918921517,42.5881114284947,40.316614317680205,40.2401215180422,38.13703536944104,40.20125851256864,41.26761831289775,39.90202094362485,44.46508252788851,45.12113062172966,48.29335392663177,46.860568139658994,44.79773394550648,43.100436842068234,40.82331171048713,40.72148872762352,38.59307239579675,40.59296356733648,41.5949913960776,40.1473772398582,44.62842203717537,45.22744107266908,48.34263531922352,46.865841955015185,44.76987597509719,43.039447085893585,40.74005884659596,40.635674918474706,38.504697641390294,40.51869068969984]}}" 859 | } 860 | ] 861 | }, 862 | "text/html": [ 863 | "", 864 | "", 865 | "", 866 | "
idrequestresponse
1{"series":[{"timestamp":"2018-01-01T00:00:00","value":826.000},{"timestamp":"2018-02-01T00:00:00","value":799.000},{"timestamp":"2018-03-01T00:00:00","value":890.000},{"timestamp":"2018-04-01T00:00:00","value":900.000},{"timestamp":"2018-05-01T00:00:00","value":961.000},{"timestamp":"2018-06-01T00:00:00","value":935.000},{"timestamp":"2018-07-01T00:00:00","value":894.000},{"timestamp":"2018-08-01T00:00:00","value":855.000},{"timestamp":"2018-09-01T00:00:00","value":809.000},{"timestamp":"2018-10-01T00:00:00","value":810.000},{"timestamp":"2018-11-01T00:00:00","value":766.000},{"timestamp":"2018-12-01T00:00:00","value":805.000},{"timestamp":"2019-01-01T00:00:00","value":821.000},{"timestamp":"2019-02-01T00:00:00","value":773.000},{"timestamp":"2019-03-01T00:00:00","value":883.000},{"timestamp":"2019-04-01T00:00:00","value":898.000},{"timestamp":"2019-05-01T00:00:00","value":957.000},{"timestamp":"2019-06-01T00:00:00","value":924.000},{"timestamp":"2019-07-01T00:00:00","value":881.000},{"timestamp":"2019-08-01T00:00:00","value":837.000},{"timestamp":"2019-09-01T00:00:00","value":784.000},{"timestamp":"2019-10-01T00:00:00","value":791.000},{"timestamp":"2019-11-01T00:00:00","value":760.000},{"timestamp":"2019-12-01T00:00:00","value":802.000},{"timestamp":"2020-01-01T00:00:00","value":828.000},{"timestamp":"2020-02-01T00:00:00","value":1030.000},{"timestamp":"2020-03-01T00:00:00","value":889.000},{"timestamp":"2020-04-01T00:00:00","value":902.000},{"timestamp":"2020-05-01T00:00:00","value":969.000},{"timestamp":"2020-06-01T00:00:00","value":947.000},{"timestamp":"2020-07-01T00:00:00","value":908.000},{"timestamp":"2020-08-01T00:00:00","value":867.000},{"timestamp":"2020-09-01T00:00:00","value":815.000},{"timestamp":"2020-10-01T00:00:00","value":812.000},{"timestamp":"2020-11-01T00:00:00","value":773.000},{"timestamp":"2020-12-01T00:00:00","value":813.000},{"timestamp":"2021-01-01T00:00:00","value":834.000},{"timestamp":"2021-02-01T00:00:00","value":782.000},{"timestamp":"2021-03-01T00:00:00","value":892.000},{"timestamp":"2021-04-01T00:00:00","value":903.000},{"timestamp":"2021-05-01T00:00:00","value":966.000},{"timestamp":"2021-06-01T00:00:00","value":937.000},{"timestamp":"2021-07-01T00:00:00","value":896.000},{"timestamp":"2021-08-01T00:00:00","value":858.000},{"timestamp":"2021-09-01T00:00:00","value":817.000},{"timestamp":"2021-10-01T00:00:00","value":827.000},{"timestamp":"2021-11-01T00:00:00","value":797.000},{"timestamp":"2021-12-01T00:00:00","value":843.000}],"maxAnomalyRatio":0.25,"sensitivity":95,"granularity":"monthly"}{"response":{"status":{"http":{"code":200,"description":""}},"headers":{"Date":"Fri, 03 Feb 2023 19:11:07 GMT","Content-Length":"3582","Content-Type":"application\\/json","csp-billing-usage":"CognitiveServices.AnomalyDetector.DataPoints=1","model-id":"10","x-envoy-upstream-service-time":"39","apim-request-id":"68498343-e890-401a-a7ee-09cd360e28f2","strict-transport-security":"max-age=31536000; includeSubDomains; preload","x-content-type-options":"nosniff","x-ms-region":"East US"}},"result":{"expectedValues":[827.7940908243968,798.9133774671927,888.6058431807189,900.5606407986661,962.8389426378304,933.2591606306954,891.0784104799666,856.1781601363697,809.8987227908941,807.375129007505,764.3196682448518,803.933498594564,823.5900620883058,794.0905641334288,883.164245249282,894.8419000690953,956.8430591101258,927.6285055190114,885.812983784303,851.7622285698933,806.3322863536049,804.8024303608446,762.74070738882,804.0251702513732,825.3523662579559,798.0404188724976,889.3016505577698,902.4226124345937,965.867078532635,937.2113627931791,895.9546789101294,862.0087368413656,816.4662342097423,814.4297745524709,771.8614479159354,811.859271346729,831.8998279215521,802.947544797165,892.5684407435083,904.5488214533809,966.8527063844707,937.3168391003043,895.3975195019448,860.7889417178712,814.801176931919,812.7134983694949,770.0939528278067,810.3738137939964],"isAnomaly":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"isNegativeAnomaly":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"isPositiveAnomaly":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"lowerMargins":[41.389704541219885,38.91337746719273,44.43029215903596,45.02803203993335,48.14194713189147,46.6629580315348,44.553920523998386,42.808908006818456,40.494936139544734,40.36875645037526,4.319668244851755,40.19667492972815,41.17950310441529,34.09056413342876,44.158212262464076,44.74209500345478,47.84215295550632,46.38142527595062,44.29064918921517,42.5881114284947,40.316614317680205,40.2401215180422,2.7407073888200557,40.20125851256864,41.26761831289775,38.04041887249764,44.46508252788851,45.12113062172966,48.29335392663177,46.860568139658994,44.79773394550648,43.100436842068234,40.82331171048713,40.72148872762352,11.8614479159354,40.59296356733648,41.5949913960776,40.1473772398582,44.62842203717537,45.22744107266908,48.34263531922352,46.865841955015185,44.76987597509719,43.039447085893585,40.74005884659596,40.635674918474706,10.093952827806675,40.51869068969984],"period":12,"upperMargins":[41.389704541219885,39.94566887335964,44.43029215903596,45.02803203993335,48.14194713189147,46.6629580315348,44.553920523998386,42.808908006818456,40.494936139544734,40.36875645037526,38.21598341224262,40.19667492972815,41.17950310441529,39.7045282066714,44.158212262464076,44.74209500345478,47.84215295550632,46.38142527595062,44.29064918921517,42.5881114284947,40.316614317680205,40.2401215180422,38.13703536944104,40.20125851256864,41.26761831289775,39.90202094362485,44.46508252788851,45.12113062172966,48.29335392663177,46.860568139658994,44.79773394550648,43.100436842068234,40.82331171048713,40.72148872762352,38.59307239579675,40.59296356733648,41.5949913960776,40.1473772398582,44.62842203717537,45.22744107266908,48.34263531922352,46.865841955015185,44.76987597509719,43.039447085893585,40.74005884659596,40.635674918474706,38.504697641390294,40.51869068969984]}}
" 867 | ] 868 | } 869 | } 870 | ], 871 | "execution_count": 8 872 | }, 873 | { 874 | "cell_type": "markdown", 875 | "source": [ 876 | "## Join result with stored data to identify anomalies\r\n", 877 | "\r\n", 878 | "Now that the result is available, it is possible to extract the received results and join them with the original `datapoints` table to see what values are considered anomalies." 879 | ], 880 | "metadata": { 881 | "azdata_cell_guid": "d48993a2-9cc2-4aa2-b7b4-692773b6aecb" 882 | }, 883 | "attachments": {} 884 | }, 885 | { 886 | "cell_type": "code", 887 | "source": [ 888 | "with \r\n", 889 | "\tev as (select [key], [value] from #temp cross apply openjson(response, '$.result.expectedValues') where id = 1)\r\n", 890 | ", \ta as (select [key], [value] from #temp cross apply openjson(response, '$.result.isAnomaly') where id = 1)\r\n", 891 | "select\r\n", 892 | "\td.id\r\n", 893 | ",\td.sample_date\r\n", 894 | ",\td.sample_value\r\n", 895 | ",\tcast(ev.[value] as numeric(18,6)) [expectedValues]\r\n", 896 | ",\tcast(a.[value] as bit) as isAnomaly\r\n", 897 | "from\r\n", 898 | "\tdbo.[datapoints] as d\r\n", 899 | "inner join\r\n", 900 | "\tev on [ev].[key] = [d].[id] - 1\r\n", 901 | "inner join \r\n", 902 | "\ta on [a].[key] = [ev].[key]\r\n", 903 | "where \r\n", 904 | " a.[value] = 'true'\r\n", 905 | "order by\r\n", 906 | "\t[d].[id]\r\n", 907 | "\t" 908 | ], 909 | "metadata": { 910 | "azdata_cell_guid": "fe17c608-eff3-43ee-9691-af5606543a5f", 911 | "language": "sql" 912 | }, 913 | "outputs": [ 914 | { 915 | "output_type": "display_data", 916 | "data": { 917 | "text/html": "Commands completed successfully." 918 | }, 919 | "metadata": {} 920 | }, 921 | { 922 | "output_type": "display_data", 923 | "data": { 924 | "text/html": "Total execution time: 00:00:00.041" 925 | }, 926 | "metadata": {} 927 | }, 928 | { 929 | "output_type": "execute_result", 930 | "metadata": {}, 931 | "execution_count": 16, 932 | "data": { 933 | "application/vnd.dataresource+json": { 934 | "schema": { 935 | "fields": [ 936 | { 937 | "name": "id" 938 | }, 939 | { 940 | "name": "sample_date" 941 | }, 942 | { 943 | "name": "sample_value" 944 | }, 945 | { 946 | "name": "expectedValues" 947 | }, 948 | { 949 | "name": "isAnomaly" 950 | } 951 | ] 952 | }, 953 | "data": [ 954 | { 955 | "0": "26", 956 | "1": "2020-02-01 00:00:00.0000000", 957 | "2": "1030.000", 958 | "3": "798.040419", 959 | "4": "1" 960 | } 961 | ] 962 | }, 963 | "text/html": [ 964 | "", 965 | "", 966 | "", 967 | "
idsample_datesample_valueexpectedValuesisAnomaly
262020-02-01 00:00:00.00000001030.000798.0404191
" 968 | ] 969 | } 970 | } 971 | ], 972 | "execution_count": 16 973 | } 974 | ] 975 | } -------------------------------------------------------------------------------- /azure-event-hubs.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "SQL", 5 | "display_name": "SQL", 6 | "language": "sql" 7 | }, 8 | "language_info": { 9 | "name": "sql", 10 | "version": "" 11 | } 12 | }, 13 | "nbformat_minor": 2, 14 | "nbformat": 4, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "source": [ 19 | "# Send an Event to an Azure Event Hub\r\n", 20 | "\r\n", 21 | "Make sure to have an Azure Event Hub deployed in Azure to run the following samples. If you need help in creating your first Azure Event Hubs, please take a look here: [Quickstart: Create an event hub using Azure portal](https://learn.microsoft.com/azure/event-hubs/event-hubs-create). \r\n", 22 | "\r\n", 23 | "Event Hubs REST endpoint are documented here: [Event Hubs service REST](https://learn.microsoft.com/en-us/rest/api/eventhub/event-hubs-runtime-rest). Specifically in the next samples the [Send Event](https://learn.microsoft.com/en-us/rest/api/eventhub/send-event) API will be used.\r\n", 24 | "\r\n", 25 | "In the next samples is assumed that there is an Azure Event Hubs deployed at `https://azure-event-hubs.azurewebsites.net/`, that has a Event Hubs Instance named `myeventhub`. To have the samples working in your environment make sure adjust URL and the Event Hub Instance name so that they will match yours." 26 | ], 27 | "metadata": { 28 | "azdata_cell_guid": "3f79ad9b-7872-479a-9813-26e34e49ba4f" 29 | }, 30 | "attachments": {} 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "source": [ 35 | "## Send Events using SAS Token\n", 36 | "\n", 37 | "Only authenticated requests can send events to Event Hubs. One way to authenticate a request is to provide a Shared Access Signature token: \n", 38 | "- [Authorizing access to Event Hubs resources using Shared Access Signatures](https://learn.microsoft.com/en-us/azure/event-hubs/authorize-access-shared-access-signature)\n", 39 | "- [Generate SAS token](https://learn.microsoft.com/en-us/rest/api/eventhub/generate-sas-token). \n", 40 | "\n", 41 | "At the moment is not possible to generate a SAS token directly from Azure SQL database, but you can put the code for generating such a token in an Azure Function and call it from Azure SQL database using `sp_invoke_external_rest_point` as well.\n", 42 | "\n", 43 | "Once you have the token you can add it into a Database Scoped Credential:" 44 | ], 45 | "metadata": { 46 | "azdata_cell_guid": "ad7d9205-5d90-47aa-916a-56f9378a9fcc" 47 | }, 48 | "attachments": {} 49 | }, 50 | { 51 | "cell_type": "code", 52 | "source": [ 53 | "-- make sure a database master key exists\r\n", 54 | "if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') begin\r\n", 55 | " create master key encryption by password = 'LONg_Pa$$_w0rd!'\r\n", 56 | "end\r\n", 57 | "\r\n", 58 | "-- create database scoped credential\r\n", 59 | "create database scoped credential [https://azure-event-hubs.servicebus.windows.net]\r\n", 60 | "with identity = 'HTTPEndpointHeaders', \r\n", 61 | "secret = '{\"Authorization\": \"SharedAccessSignature sr=azure-event-hubs.servicebus.windows.net%2fmyeventhub&sig=RVDJM1cSo71j73%2bWR0t7ZCZukIjMEvBn%2bWWqSlqkJeM%3d&se=1697310598&skn=RootManageSharedAccessKey\"}';" 62 | ], 63 | "metadata": { 64 | "azdata_cell_guid": "3fdacfa3-b5ac-4c3a-8cd3-e77f287cd992", 65 | "language": "sql", 66 | "tags": [] 67 | }, 68 | "outputs": [], 69 | "execution_count": null 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "source": [ 74 | "You can then send messages to Event Hubs using the \"Send Event\" API, which is available at `https://azure-event-hubs.servicebus.windows.net/myeventhub/messages` :" 75 | ], 76 | "metadata": { 77 | "azdata_cell_guid": "5e6e4469-209d-4946-9546-a6acd793b82a" 78 | }, 79 | "attachments": {} 80 | }, 81 | { 82 | "cell_type": "code", 83 | "source": [ 84 | "declare @payload nvarchar(max) = '{\"UserId\": \"6C5E29A2-A5E7-449D-BD14-259D61ADC6BE\", \"FirstName\": \"John\", \"LastName\": \"Doe\"}';\r\n", 85 | "declare @headers nvarchar(4000) = N'{\"BrokerProperties\": \"' + string_escape('{\"PartitionKey\": \"6C5E29A2-A5E7-449D-BD14-259D61ADC6BE\"}', 'json') + '\"}'\r\n", 86 | "declare @ret int, @response nvarchar(max)\r\n", 87 | "\r\n", 88 | "exec @ret = sp_invoke_external_rest_endpoint \r\n", 89 | " @url = 'https://azure-event-hubs.servicebus.windows.net/myeventhub/messages',\r\n", 90 | " @headers = @headers,\r\n", 91 | " @payload = @payload,\r\n", 92 | "\t\t@credential = [https://azure-event-hubs.servicebus.windows.net],\r\n", 93 | " @response = @response output;\r\n", 94 | "\r\n", 95 | "select @response;" 96 | ], 97 | "metadata": { 98 | "azdata_cell_guid": "a87411a4-84c5-493d-9a84-dea4eeffa98b", 99 | "language": "sql", 100 | "tags": [] 101 | }, 102 | "outputs": [], 103 | "execution_count": null 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "source": [ 108 | "## Send Events using Managed Identities\n", 109 | "\n", 110 | "Follow the instructions here: [Enable Managed Identity in Azure SQL](./azure-sql-enable-msi.ipynb) to make sure you have Managed Identity enabled for your Azure SQL database, and then check how to grant to right permission on Event Hubs to the Azure SQL Manage Identity, following the instructions here: [Grant permissions to a managed identity in Azure AD](https://learn.microsoft.com/azure/event-hubs/authenticate-managed-identity?tabs=latest#grant-permissions-to-a-managed-identity-in-azure-ad).\n", 111 | "\n", 112 | "Once that is done you just need to create a Database Scoped Credentials with the string `Managed Identity` as identity and `https://eventhubs.azure.net` as the `resourceid`:" 113 | ], 114 | "metadata": { 115 | "azdata_cell_guid": "0afadd91-d62b-4d30-80d2-b7f0c14753ec" 116 | }, 117 | "attachments": {} 118 | }, 119 | { 120 | "cell_type": "code", 121 | "source": [ 122 | "-- make sure a database master key exists\r\n", 123 | "if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') begin\r\n", 124 | " create master key encryption by password = 'LONg_Pa$$_w0rd!'\r\n", 125 | "end\r\n", 126 | "\r\n", 127 | "-- create database scoped credential\r\n", 128 | "if exists(select * from sys.database_scoped_credentials where [name] = 'https://azure-event-hubs.servicebus.windows.net') begin\r\n", 129 | " drop database scoped credential [https://azure-event-hubs.servicebus.windows.net];\r\n", 130 | "end;\r\n", 131 | "create database scoped credential [https://azure-event-hubs.servicebus.windows.net]\r\n", 132 | "with identity = 'Managed Identity', \r\n", 133 | "secret = '{\"resourceid\": \"https://eventhubs.azure.net\" }';" 134 | ], 135 | "metadata": { 136 | "azdata_cell_guid": "8a8775d3-73cf-4e7b-a0d4-82d707c611f8", 137 | "language": "sql" 138 | }, 139 | "outputs": [], 140 | "execution_count": null 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "source": [ 145 | "Once this is done you can send the message using the same code as before:" 146 | ], 147 | "metadata": { 148 | "azdata_cell_guid": "59fb3012-317e-4aba-a59e-d4f97efea998" 149 | }, 150 | "attachments": {} 151 | }, 152 | { 153 | "cell_type": "code", 154 | "source": [ 155 | "declare @payload nvarchar(max) = '{\"UserId\": \"6C5E29A2-A5E7-449D-BD14-259D61ADC6BE\", \"FirstName\": \"John\", \"LastName\": \"Doe\"}';\r\n", 156 | "declare @headers nvarchar(4000) = N'{\"BrokerProperties\": \"' + string_escape('{\"PartitionKey\": \"6C5E29A2-A5E7-449D-BD14-259D61ADC6BE\"}', 'json') + '\"}'\r\n", 157 | "declare @ret int, @response nvarchar(max)\r\n", 158 | "\r\n", 159 | "exec @ret = sp_invoke_external_rest_endpoint \r\n", 160 | " @url = 'https://azure-event-hubs.servicebus.windows.net/myeventhub/messages',\r\n", 161 | " @headers = @headers,\r\n", 162 | " @payload = @payload,\r\n", 163 | "\t\t@credential = [https://azure-event-hubs.servicebus.windows.net],\r\n", 164 | " @response = @response output;\r\n", 165 | "\r\n", 166 | "select @response;" 167 | ], 168 | "metadata": { 169 | "azdata_cell_guid": "823b7c9d-0d13-4e45-914a-7c322db9bafe", 170 | "language": "sql" 171 | }, 172 | "outputs": [], 173 | "execution_count": null 174 | } 175 | ] 176 | } -------------------------------------------------------------------------------- /azure-functions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "SQL", 5 | "display_name": "SQL", 6 | "language": "sql" 7 | }, 8 | "language_info": { 9 | "name": "sql", 10 | "version": "" 11 | } 12 | }, 13 | "nbformat_minor": 2, 14 | "nbformat": 4, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "source": [ 19 | "# Call an Azure Function from Azure SQL DB\r\n", 20 | "\r\n", 21 | "Make sure to have an Azure Function deployed in Azure to run the following samples. If you need help in creating your first Azure Function, please take a look here: [Getting started with Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-get-started). \r\n", 22 | "\r\n", 23 | "**Please note** that the Azure Function must have an [HTTP Trigger](https://learn.microsoft.com/azure/azure-functions/functions-bindings-http-webhook) to be able to be called by Azure SQL DB: [Azure Functions HTTP trigger](https://learn.microsoft.com/azure/azure-functions/functions-bindings-http-webhook-trigger)\r\n", 24 | "\r\n", 25 | "In the next samples is assumed that there is an Azure Function with HTTP Trigger support deployed at `https://azure-sql-function.azurewebsites.net/api/sample-function`. To have the samples working in your environment make sure to use the URL of your Azure Function. " 26 | ], 27 | "metadata": { 28 | "azdata_cell_guid": "8411c7b8-5798-44aa-8f07-87c8ecd0496d" 29 | }, 30 | "attachments": {} 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "source": [ 35 | "## Call a public (or anonymous) Azure Function\r\n", 36 | "\r\n", 37 | "If the function doesn't require any authentication, it can be called without any additional requirement" 38 | ], 39 | "metadata": { 40 | "azdata_cell_guid": "5d1731bf-3fbb-4f66-8c36-be5a3e2a71d3" 41 | }, 42 | "attachments": {} 43 | }, 44 | { 45 | "cell_type": "code", 46 | "source": [ 47 | "declare @url nvarchar(4000) = N'https://azure-sql-function.azurewebsites.net/api/sample-function';\r\n", 48 | "declare @headers nvarchar(4000) = N'{\"header1\":\"value_a\", \"header2\":\"value2\", \"header1\":\"value_b\"}'\r\n", 49 | "declare @payload nvarchar(max) = N'{\"some\":{\"data\":\"here\"}}'\r\n", 50 | "declare @ret int, @response nvarchar(max);\r\n", 51 | "\r\n", 52 | "exec @ret = sp_invoke_external_rest_endpoint \r\n", 53 | "\t@url = @url,\r\n", 54 | "\t@method = 'GET',\r\n", 55 | "\t@headers = @headers,\r\n", 56 | "\t@payload = @payload,\r\n", 57 | "\t@response = @response output;\r\n", 58 | "\t\r\n", 59 | "select @ret as ReturnCode, @response as Response;" 60 | ], 61 | "metadata": { 62 | "azdata_cell_guid": "a07b64e8-b0f4-42fc-9252-8028a6f0036e", 63 | "language": "sql" 64 | }, 65 | "outputs": [], 66 | "execution_count": null 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "source": [ 71 | "## Call an Azure Function protected by a secret key\n", 72 | "\n", 73 | "Azure Funtions can be protected via the usage of a \"authorization key\" that must be passed to the function in order to execution to happen: [Authorization level](https://learn.microsoft.com/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-csharp#http-auth)\n", 74 | "\n", 75 | "Since the authorization key is a sensitive value, it is recommended to store its value in a `DATABASE SCOPED CREDENTIAL` (more info here: [Credentials (Database Engine)](https://learn.microsoft.com/sql/relational-databases/security/authentication-access/credentials-database-engine))" 76 | ], 77 | "metadata": { 78 | "azdata_cell_guid": "3d4540a5-eb05-4e38-b8f9-5aea80121945" 79 | }, 80 | "attachments": {} 81 | }, 82 | { 83 | "cell_type": "code", 84 | "source": [ 85 | "-- make sure a database master key exists\r\n", 86 | "if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') begin\r\n", 87 | " create master key encryption by password = 'LONg_Pa$$_w0rd!'\r\n", 88 | "end\r\n", 89 | "\r\n", 90 | "-- create database scoped credential\r\n", 91 | "create database scoped credential [https://azure-sql-function.azurewebsites.net/api/sample-function]\r\n", 92 | "with identity = 'HTTPEndpointHeaders', secret = '{\"x-functions-key\":\"\"}';\r\n", 93 | "go" 94 | ], 95 | "metadata": { 96 | "azdata_cell_guid": "854cb13b-d2bc-4f27-a051-518819f1b1e3", 97 | "language": "sql" 98 | }, 99 | "outputs": [], 100 | "execution_count": null 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "source": [ 105 | "Once the `DATABASE SCOPED CREDENTIAL` has been defined, it can be used by anyone who has been granted the `REFERENCE` permissions on it (see: [Grant permissions to use credential](https://learn.microsoft.com/sql/relational-databases/system-stored-procedures/sp-invoke-external-rest-endpoint-transact-sql?view=azuresqldb-current&tabs=request-headers#grant-permissions-to-use-credential)):" 106 | ], 107 | "metadata": { 108 | "language": "sql", 109 | "azdata_cell_guid": "1a72e6b1-6a7c-4a48-85b4-ebcc236e8634" 110 | }, 111 | "attachments": {} 112 | }, 113 | { 114 | "cell_type": "code", 115 | "source": [ 116 | "declare @url nvarchar(4000) = N'https://azure-sql-function.azurewebsites.net/api/sample-function';\r\n", 117 | "declare @headers nvarchar(4000) = N'{\"header1\":\"value_a\", \"header2\":\"value2\", \"header1\":\"value_b\"}'\r\n", 118 | "declare @payload nvarchar(max) = N'{\"some\":{\"data\":\"here\"}}'\r\n", 119 | "declare @ret int, @response nvarchar(max);\r\n", 120 | "\r\n", 121 | "exec @ret = sp_invoke_external_rest_endpoint \r\n", 122 | "\t@url = @url,\r\n", 123 | "\t@method = 'GET',\r\n", 124 | "\t@headers = @headers,\r\n", 125 | "\t@payload = @payload,\r\n", 126 | " @credential = [https://azure-sql-function.azurewebsites.net/api/sample-function],\r\n", 127 | "\t@response = @response output;\r\n", 128 | "\t\r\n", 129 | "select @ret as ReturnCode, @response as Response;" 130 | ], 131 | "metadata": { 132 | "language": "sql", 133 | "azdata_cell_guid": "cf6bc5cd-1a33-446d-9e97-becca68f5065" 134 | }, 135 | "outputs": [], 136 | "execution_count": null 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "source": [ 141 | "## Call an Azure Function protected by Azure AD\n", 142 | "\n", 143 | "Follow the instructions here: [Enable Managed Identity in Azure SQL](./azure-sql-enable-msi.ipynb) to make sure you have Managed Identity enabled for your Azure SQL database and then enable Azure AD authentication in your Azure Function as explained here: [Tutorial: Add app authentication to your web app running on Azure App Service](https://learn.microsoft.com/azure/app-service/scenario-secure-app-authentication-app-service). \n", 144 | "\n", 145 | "After the Azure AD principal has been enabled on Azure Function, you'll see that there is a App (client) ID available (for example: `02f5c654-0d70-4074-a82f-40d6a0dce8ff`). The provided App ID must be used to allow Azure SQL Database to correctly make the authenticated call to the Azure Function. Create a `DATABASE SCOPED CREDENTIAL` and specify `Managed Identity` as the `identity` value. The `secret` value must be a flat JSON that contains the APP ID value in the `resourceid` property:" 146 | ], 147 | "metadata": { 148 | "language": "sql", 149 | "azdata_cell_guid": "9b0da9bc-ce54-482b-8277-fab07e51a0d5" 150 | }, 151 | "attachments": {} 152 | }, 153 | { 154 | "cell_type": "code", 155 | "source": [ 156 | "-- make sure a database master key exists\r\n", 157 | "if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') begin\r\n", 158 | " create master key encryption by password = 'LONg_Pa$$_w0rd!'\r\n", 159 | "end\r\n", 160 | "\r\n", 161 | "-- create database scoped credential\r\n", 162 | "create database scoped credential [https://azure-sql-function.azurewebsites.net/api/sample-function]\r\n", 163 | "with identity = 'Managed Identity', secret = '{\"resourceid\":\"02f5c654-0d70-4074-a82f-40d6a0dce8ff\"}';\r\n", 164 | "go" 165 | ], 166 | "metadata": { 167 | "azdata_cell_guid": "a1337a1c-f307-4501-ad4a-645a83c09f6c", 168 | "language": "sql" 169 | }, 170 | "outputs": [], 171 | "execution_count": null 172 | }, 173 | { 174 | "cell_type": "markdown", 175 | "source": [ 176 | "Once the `DATABASE SCOPED CREDENTIAL` has been defined, it can be used by anyone who has been granted the `REFERENCE` permissions on it (see: [Grant permissions to use credential](https://learn.microsoft.com/sql/relational-databases/system-stored-procedures/sp-invoke-external-rest-endpoint-transact-sql?view=azuresqldb-current&tabs=request-headers#grant-permissions-to-use-credential)):" 177 | ], 178 | "metadata": { 179 | "azdata_cell_guid": "d6579560-6d31-4880-9014-09de408af25e" 180 | }, 181 | "attachments": {} 182 | }, 183 | { 184 | "cell_type": "code", 185 | "source": [ 186 | "declare @url nvarchar(4000) = N'https://azure-sql-function.azurewebsites.net/api/sample-function';\r\n", 187 | "declare @headers nvarchar(4000) = N'{\"header1\":\"value_a\", \"header2\":\"value2\", \"header1\":\"value_b\"}'\r\n", 188 | "declare @payload nvarchar(max) = N'{\"some\":{\"data\":\"here\"}}'\r\n", 189 | "declare @ret int, @response nvarchar(max);\r\n", 190 | "\r\n", 191 | "exec @ret = sp_invoke_external_rest_endpoint \r\n", 192 | "\t@url = @url,\r\n", 193 | "\t@method = 'GET',\r\n", 194 | "\t@headers = @headers,\r\n", 195 | "\t@payload = @payload,\r\n", 196 | " @credential = [https://azure-sql-function.azurewebsites.net/api/sample-function],\r\n", 197 | "\t@response = @response output;\r\n", 198 | "\t\r\n", 199 | "select @ret as ReturnCode, @response as Response;" 200 | ], 201 | "metadata": { 202 | "azdata_cell_guid": "bdccbcf9-e54e-4ce6-a0d9-3fac412a1a17", 203 | "language": "sql" 204 | }, 205 | "outputs": [], 206 | "execution_count": null 207 | } 208 | ] 209 | } 210 | -------------------------------------------------------------------------------- /azure-storage.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": { 7 | "azdata_cell_guid": "3f79ad9b-7872-479a-9813-26e34e49ba4f" 8 | }, 9 | "source": [ 10 | "# Work with Azure Blob Storage\n", 11 | "\n", 12 | "To start, ensure that you have a Azure Blob Storage account. Reference the documentation on how to create a storage account if needed. [Create a storage account](https://learn.microsoft.com/azure/storage/common/storage-account-create?tabs=azure-portal)\n", 13 | "\n", 14 | "Also, have a container created in the Azure Blob Storage account. You can use this quick-start to help guide you in creating a container. [Quickstart: Upload, download, and list blobs with the Azure portal](https://learn.microsoft.com/azure/storage/blobs/storage-quickstart-blobs-portal)\n", 15 | "\n", 16 | "Blob Storage REST endpoint are documented here: [Azure Blob Storage REST API](https://learn.microsoft.com/rest/api/storageservices/blob-service-rest-api). \n", 17 | "The samples use the [Get Blob](https://learn.microsoft.com/rest/api/storageservices/get-blob?tabs=azure-ad), [Put Blob](https://learn.microsoft.com/en-us/rest/api/storageservices/put-blob?tabs=azure-ad), and the (Create Container)[https://learn.microsoft.com/rest/api/storageservices/create-container?tabs=azure-ad] APIs.\n", 18 | "\n", 19 | "The following samples assume that there is an Azure Blob Storage Account deployed at `https://blobby.blob.core.windows.net/` and a container named `myblobs`. To have the samples working in your environment, make sure adjust URL and container name so that they will match your account." 20 | ] 21 | }, 22 | { 23 | "attachments": {}, 24 | "cell_type": "markdown", 25 | "metadata": { 26 | "azdata_cell_guid": "ad7d9205-5d90-47aa-916a-56f9378a9fcc" 27 | }, 28 | "source": [ 29 | "## Work with files in Azure Blob Storage using a SAS Token\n", 30 | "\n", 31 | "Only authenticated requests can send REST requests to Azure Blob Storage. One way to authenticate a request is to provide a Shared Access Signature token: \n", 32 | "- [Delegate access by using a shared access signature](https://learn.microsoft.com/rest/api/storageservices/delegate-access-with-shared-access-signature)\n", 33 | "- [Create an account SAS](https://learn.microsoft.com/rest/api/storageservices/create-account-sas). \n", 34 | "\n", 35 | "At the moment is not possible to generate a SAS token directly from Azure SQL database, but you can put the code for generating such a token in an Azure Function and call it from Azure SQL database using `sp_invoke_external_rest_point` as well.\n", 36 | "\n", 37 | "Once you have the token you can add it into a Database Scoped Credential:" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": { 44 | "azdata_cell_guid": "3fdacfa3-b5ac-4c3a-8cd3-e77f287cd992", 45 | "language": "sql", 46 | "tags": [] 47 | }, 48 | "outputs": [], 49 | "source": [ 50 | "-- make sure a database master key exists\n", 51 | "if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') begin\n", 52 | " create master key encryption by password = 'LONg_Pa$$_w0rd!'\n", 53 | "end\n", 54 | "\n", 55 | "-- create database scoped credential\n", 56 | "create database scoped credential [filestore]\n", 57 | "with identity='SHARED ACCESS SIGNATURE', \n", 58 | "secret='sv=2022-11-02&ss=bfqt&srt=sco&sp=seespotrun&se=2023-08-03T02:21:25Z&st=2023-08-02T18:21:25Z&spr=https&sig=WWwwWWwwWWYaKCheeseNXCCCCCCDDDDDSSSSSU%3D'\n", 59 | "go" 60 | ] 61 | }, 62 | { 63 | "attachments": {}, 64 | "cell_type": "markdown", 65 | "metadata": { 66 | "azdata_cell_guid": "5e6e4469-209d-4946-9546-a6acd793b82a" 67 | }, 68 | "source": [ 69 | "You can now create a file and add content to the file:" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": { 76 | "azdata_cell_guid": "a87411a4-84c5-493d-9a84-dea4eeffa98b", 77 | "language": "sql", 78 | "tags": [] 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "declare @response nvarchar(max);\n", 83 | "declare @payload nvarchar(max) = (select * from (values('Hello from Azure SQL!', sysdatetime())) payload([message], [timestamp])for json auto, without_array_wrapper)\n", 84 | "declare @url nvarchar(max) = 'https://blobby.blob.core.windows.net/myblobs/test-me-from-azure-sql.json'\n", 85 | "exec sp_invoke_external_rest_endpoint\n", 86 | " @url = @url,\n", 87 | " @method = 'PUT',\n", 88 | " @headers = '{\"x-ms-blob-type\": \"BlockBlob\", \"Content-Type\":\"application/json\", \"Accept\":\"application/xml\"}',\n", 89 | " @payload = @payload,\n", 90 | " @credential = [filestore],\n", 91 | " @response = @response output\n", 92 | "select cast(@response as xml)\n", 93 | "go\n", 94 | "\n", 95 | "declare @response nvarchar(max);\n", 96 | "declare @url nvarchar(max) = 'https://blobby.blob.core.windows.net/myblobs/test-me-from-azure-sql.json'\n", 97 | "exec sp_invoke_external_rest_endpoint\n", 98 | " @url = @url,\n", 99 | " @headers = '{\"Accept\":\"application/xml\"}',\n", 100 | " @credential = [filestore],\n", 101 | " @method = 'GET',\n", 102 | " @response = @response output\n", 103 | "select cast(@response as xml)\n", 104 | "go" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "Now, using the Get Blob REST endpoint, you can read the contents of the file:" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "declare @response nvarchar(max);\n", 121 | "declare @url nvarchar(max) = 'https://blobby.blob.core.windows.net/myblobs/test-me-from-azure-sql.json'\n", 122 | "exec sp_invoke_external_rest_endpoint\n", 123 | " @url = @url,\n", 124 | " @headers = '{\"Accept\":\"application/xml\"}',\n", 125 | " @credential = [filestore],\n", 126 | " @method = 'GET',\n", 127 | " @response = @response output\n", 128 | "select cast(@response as xml)\n", 129 | "go" 130 | ] 131 | }, 132 | { 133 | "attachments": {}, 134 | "cell_type": "markdown", 135 | "metadata": { 136 | "azdata_cell_guid": "0afadd91-d62b-4d30-80d2-b7f0c14753ec" 137 | }, 138 | "source": [ 139 | "## Work with files in Azure Blob Storage using Managed Identities\n", 140 | "\n", 141 | "### Enabling Managed Identity in Azure SQL\n", 142 | "\n", 143 | "First, follow the instructions here: [Enable Managed Identity in Azure SQL](./azure-sql-enable-msi.ipynb) to enable Managed Identity for your Azure SQL database. \n", 144 | "\n", 145 | "### Adding the database to a storage role\n", 146 | "\n", 147 | "Next, we need to add the Azure SQL Database to the Storage Blob Data Owner role. This is done via Access Control (IAM) in the Azure Portal or via Azure CLI.\n", 148 | "To start, navigate to the Access Control page in the Azure Storage Account you are using\n", 149 | "\n", 150 | "![A picture of navigating to the Access Control page for an Azure Storage Account](./assets/files-add-roll0.png)\n", 151 | "\n", 152 | " and click **+ Add**. Then in the dropdown, click **Add role assignment**.\n", 153 | "\n", 154 | "![A picture of adding a role assignment on the Access Control page for an Azure Storage Account](./assets/files-add-roll1.png)\n", 155 | "\n", 156 | "On the following page, use the search box and enter **blob owner**. The roles list should then show the **Storage Blob Data Owner** role. **Click the role** and then click the **Next** button on the bottom of the page.\n", 157 | "\n", 158 | "![A picture of selecting the Storage Blob Data Owner role and then clicking Next](./assets/files-add-roll2.png)\n", 159 | "\n", 160 | "On the next page, click the **Managed identity** radio button then click the **+ Select members** link.\n", 161 | "\n", 162 | "![A picture of clicking the Managed identity radio button then clicking the + Select members link](./assets/files-add-roll3.png)\n", 163 | "\n", 164 | "In the slide out blade on the right, use the **Subscription** drop down to select the subscription where your Azure SQL Database is located. Then use the **Managed identity** dropdown to select **SQL server**. Use the **Select** field to filter the databases by name.\n", 165 | "\n", 166 | "![A picture of using the Subscription drop down to select the subscription where your Azure SQL Database is located then using the Managed identity dropdown to select SQL server](./assets/files-add-roll4.png)\n", 167 | "\n", 168 | "Once you find the database that is using the REST Endpoint Invocation feature from, select it. It then appears as a selected managed identity. Now, click the **Select** button on the bottom left of the blade.\n", 169 | "\n", 170 | "![A picture of clicking the select button on the bottom left of the blade after a database managed identity has been selected](./assets/files-add-roll5.png)\n", 171 | "\n", 172 | "Back on the Add role assignments page, you will see the selected database managed identity. Click the blue **Review + assign** button in the lower left of the page.\n", 173 | "\n", 174 | "![A picture of viewing the selected database managed identity on the page and then clicking the blue **Review + assign** button in the lower left of the page](./assets/files-add-roll6.png)\n", 175 | "\n", 176 | "On the final page, again click the **Review + assign** button in the lower left of the page.\n", 177 | "\n", 178 | "![A picture of clicking the **Review + assign** button in the lower left of the page](./assets/files-add-roll7.png)\n", 179 | "\n", 180 | "You can verify the role has been assigned on the **Role assignments** tab.\n", 181 | "\n", 182 | "![A picture of verifying the role has been assigned on the Role assignments tab](./assets/files-add-roll8.png)\n", 183 | "\n", 184 | "### Create the database scoped credentials for managed identity\n", 185 | "\n", 186 | "We need to create a new set of database scoped credentials. Use the following code to create a set of credentials for communicating with Azure Blob Storage." 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": { 193 | "azdata_cell_guid": "8a8775d3-73cf-4e7b-a0d4-82d707c611f8", 194 | "language": "sql" 195 | }, 196 | "outputs": [], 197 | "source": [ 198 | "-- make sure a database master key exists\n", 199 | "if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') begin\n", 200 | " create master key encryption by password = 'LONg_Pa$$_w0rd!'\n", 201 | "end\n", 202 | "\n", 203 | "-- create database scoped credential\n", 204 | "if exists(select * from sys.database_scoped_credentials where [name] = 'blobby.blob.core.windows.net') begin\n", 205 | " drop database scoped credential [https://blobby.blob.core.windows.net];\n", 206 | "end;\n", 207 | "create database scoped credential [https://blobby.blob.core.windows.net]\n", 208 | "with identity = 'Managed Identity', \n", 209 | "secret = '{\"resourceid\": \"https://storage.azure.com\" }';" 210 | ] 211 | }, 212 | { 213 | "attachments": {}, 214 | "cell_type": "markdown", 215 | "metadata": { 216 | "azdata_cell_guid": "59fb3012-317e-4aba-a59e-d4f97efea998" 217 | }, 218 | "source": [ 219 | "Once this is done, you can send a request to Azure Blob Storage with the managed identity credentials. In this example, we will create a new container. Just note, you need to update the date (\"x-ms-date\" : \"Wed, 09 Aug 2023 19:54:40 GMT\") to a recent timestamp otherwise you will get an error on submission of the request." 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": null, 225 | "metadata": { 226 | "azdata_cell_guid": "823b7c9d-0d13-4e45-914a-7c322db9bafe", 227 | "language": "sql" 228 | }, 229 | "outputs": [], 230 | "source": [ 231 | "declare @response nvarchar(max);\n", 232 | "declare @url nvarchar(max) = 'https://blobby.blob.core.windows.net/mycontainer?restype=container'\n", 233 | "exec sp_invoke_external_rest_endpoint\n", 234 | " @url = @url,\n", 235 | " @headers = '{\"Accept\":\"application/xml\",\"x-ms-version\" : \"2023-08-03\",\"x-ms-date\" : \"Wed, 09 Aug 2023 19:54:40 GMT\"}',\n", 236 | " @method = 'PUT',\n", 237 | " @credential = [https://blobby.blob.core.windows.net],\n", 238 | " @response = @response output\n", 239 | "select cast(@response as xml)\n", 240 | "go" 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": {}, 246 | "source": [ 247 | "We can also read blob just as before as well. Use the following code to read the json file we created earlier but now using a managed identity. As before, you need to update the date (\"x-ms-date\" : \"Wed, 09 Aug 2023 19:54:40 GMT\") to a recent timestamp otherwise you will get an error on submission of the request." 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "metadata": {}, 254 | "outputs": [], 255 | "source": [ 256 | "declare @response nvarchar(max);\n", 257 | "declare @url nvarchar(max) = 'https://blobby.blob.core.windows.net/myblobs/test-me-from-azure-sql.json'\n", 258 | "exec sp_invoke_external_rest_endpoint\n", 259 | " @url = @url,\n", 260 | " @headers = '{\"Accept\":\"application/xml\",\"x-ms-version\" : \"2023-08-03\",\"x-ms-date\" : \"Wed, 09 Aug 2023 20:04:55 GMT\",\"Content-Type\":\"application/xml\"}',\n", 261 | " @method = 'GET',\n", 262 | " @credential = [https://blobby.blob.core.windows.net],\n", 263 | " @response = @response output\n", 264 | "select cast(@response as xml)\n", 265 | "go\n" 266 | ] 267 | } 268 | ], 269 | "metadata": { 270 | "kernelspec": { 271 | "display_name": "SQL", 272 | "language": "sql", 273 | "name": "SQL" 274 | }, 275 | "language_info": { 276 | "name": "sql", 277 | "version": "" 278 | } 279 | }, 280 | "nbformat": 4, 281 | "nbformat_minor": 2 282 | } 283 | -------------------------------------------------------------------------------- /power-bi.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "kernelspec": { 4 | "name": "SQL", 5 | "display_name": "SQL", 6 | "language": "sql" 7 | }, 8 | "language_info": { 9 | "name": "sql", 10 | "version": "" 11 | } 12 | }, 13 | "nbformat_minor": 2, 14 | "nbformat": 4, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "source": [ 19 | "# Execute DAX queries in your Power BI environment via REST endpoint\n", 20 | "\n", 21 | "Power BI allows the execution of DAX queries against a published Dataset via the _executeQueries_ endpoint:\n", 22 | "\n", 23 | "[Datasets - Execute Queries](https://learn.microsoft.com/en-us/rest/api/power-bi/datasets/execute-queries)\n", 24 | "\n", 25 | "If you don't have a Dataset available to use, you can create a sample dataset using one of the [built in samples](https://learn.microsoft.com/en-us/power-bi/create-reports/sample-datasets#install-built-in-samples). This document will use the \"Retail Analysis Sample\" example.\n", 26 | "\n", 27 | "![power-bi-1.png](attachment:power-bi-1.png)" 28 | ], 29 | "metadata": { 30 | "azdata_cell_guid": "2fa150ef-9af9-491d-9c6b-50f49a0b67d9" 31 | }, 32 | "attachments": { 33 | "power-bi-1.png": { 34 | "image/png": "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wN5QJZoLWrBq166dX3P1z3/+U1988YV/e/7555WVleU/tuk235az5QEAAABgb6j0IevSSy/VgAEDNHToUL355pt65513dNxxx6lJkyb+LSUlRcOHD/ebC86bN89fzpa3adhzy5Yt0znnnKMFCxYEUyqOinxslYHVCF922WWaNm1aMAUAAABlUalDltVIPfXUU/rggw/8poBWS/Wf//xHF110kd544w1/utVmWdNBazZoRo4c6U8fN25cparRsqDQrVu3nW4PP/xwsET5sAK3FbytAL47tmzZolGjRvnhx0JQZRJV6HDbCb+PlfF8AEC87779Vtdd+3tdftmQIrenn34yWGLP5Obm6oEH/qr//vfNYMrOPvnkE4285WZlb9gQTNm37LWXdLyoGPb2tWvcPkq6HmyZe+65a7evX7v+7bhL+4zYMna/t9jx2+cwyvNX2VTakOUKqhaarI/VaaedpoMOOsivrRo7dqy6d+/u98my8PXYY4/p4osv1u233+7XfNl0W29PwsK+0KxZMz3zzDOaPXu2f3v11Vf14Ycf+qHFwktZuXO3L2p4fvjhB3333Xdq2LDhXv1wV1R2zs8880xdc801he+j3c4+++xgCQCo3Jo0ber9O3y/Hv3nBP/24EOPaO3atRUq+ACJ7O1rt9WBB+q++/+mn/+8VzBl75nx9tsJj9m+qHjrrb0f+tPr1dPtd9ypCy+8OJiy/6m0Icua+1mosrDUv39//7E1F0w0qIWFCmsiaCGrdevW/jRbz9apzM0G7bVaYd1GUrTwUh769Onj92/b3cFDLFh16dLFr7mxGsVdCYf7mr1me+12DnaHvVZX09q5c+dgaswll1xSeG0CQFWSmpqqYcP+oMOPOFxTpr4UTAUqvsp67bY99FDVrlNb33z7bTBlh6zVq7XauzVt1iyYgr2l0oYsa+p36623aubMmf6Q7cUFLJtntVy2vIUq65P15JNP+uvZ+lOnTg2WrJwaNGjg1wpVBhZ2LWRY87gOHTr4NVrlFQ4rAgtZK1as8K9DANjf9Oje0/+7775dj2+eFd+syJo7uXm2nC0fZsu7+YmaRlkB020/0frx+7dmiPYtv1PS8bmmULPef9+/D9d0hI9rf24qVZWU9dotrjmrPbfpa3780b9Wwi15wtuy+2+++SaYs4Nr3udupbUESktLU8cOHf0aq/A1bWbM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" 35 | } 36 | } 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "source": [ 41 | "## Get the Dataset id\n", 42 | "\n", 43 | "To discover the Dataset Id that is needed to execute a DAX query using the _executeQuery_ endpoint, you can go to your Power BI portal, and click on the Dataset link:\n", 44 | "\n", 45 | "The Dataset page will open up and the URL will be something like:\n", 46 | "\n", 47 | "```\n", 48 | "https://xyz.powerbi.com/groups/me/datasets/DATASET_ID/details\n", 49 | "\n", 50 | "```" 51 | ], 52 | "metadata": { 53 | "azdata_cell_guid": "1c80629c-4ddc-4e96-b93c-cfa6b005d373" 54 | }, 55 | "attachments": {} 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "source": [ 60 | "## Setup up the Database Scoped Credentials\n", 61 | "\n", 62 | "Power BI REST endpoint requires authentication. You can get a token that represent your user by using AZ CLI:\n", 63 | "\n", 64 | "```bash\n", 65 | "az account get-access-token --resource \"https://analysis.windows.net/powerbi/api\" --query \"accessToken\" -o tsv\n", 66 | "```\n", 67 | "\n", 68 | "the command will output something similar to the following string:\n", 69 | "\n", 70 | "```text\n", 71 | "eyJ0eXAiOiJK.....5aOYpZSFrSiKYYD6Q\n", 72 | "```\n", 73 | "\n", 74 | "copy the whole string and use in the the following code replacing the `TOKEN` string after the `Bearer` text. Make sure also to replace the `DATASET_ID` placeholder with the Dataset id you got in the previous step. Execute the T-SQL code. It will safely store the authentication token in a Database Scoped Credential so that it will be possible to use the token later with `sp_invoke_external_rest_endpoint`" 75 | ], 76 | "metadata": { 77 | "azdata_cell_guid": "6cf54ac8-0e87-4698-84a6-ae9b81934b5a" 78 | }, 79 | "attachments": {} 80 | }, 81 | { 82 | "cell_type": "code", 83 | "source": [ 84 | "-- make sure a database master key exists\n", 85 | "if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') begin\n", 86 | " create master key encryption by password = 'LONg_Pa$$_w0rd!'\n", 87 | "end\n", 88 | "\n", 89 | "-- create database scoped credential\n", 90 | "if exists(select * from sys.database_scoped_credentials where [name] = 'https://api.powerbi.com/v1.0/myorg/datasets/DATASET_ID/executeQueries') begin\n", 91 | " drop database scoped credential [https://api.powerbi.com/v1.0/myorg/datasets/DATASET_ID/executeQueries];\n", 92 | "end\n", 93 | "create database scoped credential [https://api.powerbi.com/v1.0/myorg/datasets/DATASET_ID/executeQueries]\n", 94 | "with identity = 'HTTPEndpointHeaders', secret = '{\"Authorization\": \"Bearer TOKEN\"}';\n", 95 | "go" 96 | ], 97 | "metadata": { 98 | "azdata_cell_guid": "6fd989ea-205e-424d-9c72-980b6cd9fc50", 99 | "language": "sql" 100 | }, 101 | "outputs": [], 102 | "execution_count": null 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "source": [ 107 | "## Execute DAX Query\r\n", 108 | "\r\n", 109 | "Now you can call the *executeQueries* endpoint, passing the DAX query that you want to execute. Use the `string_escape` function to make sure everything will be correctly escaped when passed in the JSON payload.\r\n", 110 | "\r\n", 111 | "When querying the returned result, remember that Power BI make extensive use of square brackes, which needs to be escaped in Azure SQL database, as used to delimit object names. You can use the double quotes to wrap the column name so that it will be considered valid and the square brackets will be treated as regulat characters (for example: `\"District[District]\"`), or you can escape the square brackets by adding one bracket at the start and *two* at the end (for example: `[District[District]]]`)" 112 | ], 113 | "metadata": { 114 | "azdata_cell_guid": "22ff69f0-86d3-40e1-9689-16e6a43762ee" 115 | }, 116 | "attachments": {} 117 | }, 118 | { 119 | "cell_type": "code", 120 | "source": [ 121 | "\n", 122 | "declare @url nvarchar(4000) = N'https://api.powerbi.com/v1.0/myorg/datasets/DATASET_ID/executeQueries';\n", 123 | "declare @payload nvarchar(max) = N'{\n", 124 | " \"queries\": [\n", 125 | " {\n", 126 | " \"query\": \"' + string_escape('\n", 127 | " DEFINE VAR __DS0Core = \n", 128 | " SUMMARIZECOLUMNS(\n", 129 | " ROLLUPADDISSUBTOTAL(''District''[District], \"IsGrandTotalRowTotal\"),\n", 130 | " \"This_Year_Sales\", ''Sales''[This Year Sales]\n", 131 | " )\n", 132 | "\n", 133 | " EVALUATE\n", 134 | " __DS0Core\n", 135 | "\n", 136 | " ORDER BY\n", 137 | " [IsGrandTotalRowTotal] DESC, ''District''[District]\n", 138 | " ', 'json') + '\"\n", 139 | " }\n", 140 | " ],\n", 141 | " \"serializerSettings\": {\n", 142 | " \"includeNulls\": true\n", 143 | " }\n", 144 | "}'\n", 145 | "\n", 146 | "declare @ret int, @response nvarchar(max);\n", 147 | "\n", 148 | "exec @ret = sys.sp_invoke_external_rest_endpoint \n", 149 | "\t@method = 'POST',\n", 150 | "\t@url = @url,\n", 151 | "\t@payload = @payload,\n", 152 | "\t@credential = [https://api.powerbi.com/v1.0/myorg/datasets/DATASET_ID/executeQueries],\n", 153 | "\t@response = @response output;\n", 154 | "\n", 155 | "select * from openjson(@response, '$.result.results[0].tables[0].rows') with\n", 156 | " (\n", 157 | " \"District[District]\" nvarchar(100),\n", 158 | " \"[IsGrandTotalRowTotal]\" bit,\n", 159 | " \"[This_Year_Sales]\" numeric(18,9)\n", 160 | " )" 161 | ], 162 | "metadata": { 163 | "azdata_cell_guid": "e7b5fbba-1938-4a0e-a433-7874b79cf47c", 164 | "language": "sql", 165 | "tags": [] 166 | }, 167 | "outputs": [], 168 | "execution_count": null 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "source": [ 173 | "The result will be something like:\r\n", 174 | "\r\n", 175 | "![power-bi-2.png](attachment:power-bi-2.png)" 176 | ], 177 | "metadata": { 178 | "azdata_cell_guid": "3e7513e7-7906-41cf-a27d-b2b9fbf3ff7d" 179 | }, 180 | "attachments": { 181 | "power-bi-2.png": { 182 | "image/png": "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" 183 | } 184 | } 185 | } 186 | ] 187 | } --------------------------------------------------------------------------------