├── .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 | "id | sample_date | sample_value |
",
572 | "1 | 2018-01-01 00:00:00.0000000 | 826.000 |
",
573 | "2 | 2018-02-01 00:00:00.0000000 | 799.000 |
",
574 | "3 | 2018-03-01 00:00:00.0000000 | 890.000 |
",
575 | "4 | 2018-04-01 00:00:00.0000000 | 900.000 |
",
576 | "5 | 2018-05-01 00:00:00.0000000 | 961.000 |
",
577 | "6 | 2018-06-01 00:00:00.0000000 | 935.000 |
",
578 | "7 | 2018-07-01 00:00:00.0000000 | 894.000 |
",
579 | "8 | 2018-08-01 00:00:00.0000000 | 855.000 |
",
580 | "9 | 2018-09-01 00:00:00.0000000 | 809.000 |
",
581 | "10 | 2018-10-01 00:00:00.0000000 | 810.000 |
",
582 | "11 | 2018-11-01 00:00:00.0000000 | 766.000 |
",
583 | "12 | 2018-12-01 00:00:00.0000000 | 805.000 |
",
584 | "13 | 2019-01-01 00:00:00.0000000 | 821.000 |
",
585 | "14 | 2019-02-01 00:00:00.0000000 | 773.000 |
",
586 | "15 | 2019-03-01 00:00:00.0000000 | 883.000 |
",
587 | "16 | 2019-04-01 00:00:00.0000000 | 898.000 |
",
588 | "17 | 2019-05-01 00:00:00.0000000 | 957.000 |
",
589 | "18 | 2019-06-01 00:00:00.0000000 | 924.000 |
",
590 | "19 | 2019-07-01 00:00:00.0000000 | 881.000 |
",
591 | "20 | 2019-08-01 00:00:00.0000000 | 837.000 |
",
592 | "21 | 2019-09-01 00:00:00.0000000 | 784.000 |
",
593 | "22 | 2019-10-01 00:00:00.0000000 | 791.000 |
",
594 | "23 | 2019-11-01 00:00:00.0000000 | 760.000 |
",
595 | "24 | 2019-12-01 00:00:00.0000000 | 802.000 |
",
596 | "25 | 2020-01-01 00:00:00.0000000 | 828.000 |
",
597 | "26 | 2020-02-01 00:00:00.0000000 | 1030.000 |
",
598 | "27 | 2020-03-01 00:00:00.0000000 | 889.000 |
",
599 | "28 | 2020-04-01 00:00:00.0000000 | 902.000 |
",
600 | "29 | 2020-05-01 00:00:00.0000000 | 969.000 |
",
601 | "30 | 2020-06-01 00:00:00.0000000 | 947.000 |
",
602 | "31 | 2020-07-01 00:00:00.0000000 | 908.000 |
",
603 | "32 | 2020-08-01 00:00:00.0000000 | 867.000 |
",
604 | "33 | 2020-09-01 00:00:00.0000000 | 815.000 |
",
605 | "34 | 2020-10-01 00:00:00.0000000 | 812.000 |
",
606 | "35 | 2020-11-01 00:00:00.0000000 | 773.000 |
",
607 | "36 | 2020-12-01 00:00:00.0000000 | 813.000 |
",
608 | "37 | 2021-01-01 00:00:00.0000000 | 834.000 |
",
609 | "38 | 2021-02-01 00:00:00.0000000 | 782.000 |
",
610 | "39 | 2021-03-01 00:00:00.0000000 | 892.000 |
",
611 | "40 | 2021-04-01 00:00:00.0000000 | 903.000 |
",
612 | "41 | 2021-05-01 00:00:00.0000000 | 966.000 |
",
613 | "42 | 2021-06-01 00:00:00.0000000 | 937.000 |
",
614 | "43 | 2021-07-01 00:00:00.0000000 | 896.000 |
",
615 | "44 | 2021-08-01 00:00:00.0000000 | 858.000 |
",
616 | "45 | 2021-09-01 00:00:00.0000000 | 817.000 |
",
617 | "46 | 2021-10-01 00:00:00.0000000 | 827.000 |
",
618 | "47 | 2021-11-01 00:00:00.0000000 | 797.000 |
",
619 | "48 | 2021-12-01 00:00:00.0000000 | 843.000 |
",
620 | "
"
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 | "request |
",
781 | "{"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"} |
",
782 | "
"
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 | "id | request | response |
",
865 | "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]}} |
",
866 | "
"
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 | "id | sample_date | sample_value | expectedValues | isAnomaly |
",
966 | "26 | 2020-02-01 00:00:00.0000000 | 1030.000 | 798.040419 | 1 |
",
967 | "
"
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 | "\n",
151 | "\n",
152 | " and click **+ Add**. Then in the dropdown, click **Add role assignment**.\n",
153 | "\n",
154 | "\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 | "\n",
159 | "\n",
160 | "On the next page, click the **Managed identity** radio button then click the **+ Select members** link.\n",
161 | "\n",
162 | "\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 | "\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 | "\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 | "\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 | "\n",
179 | "\n",
180 | "You can verify the role has been assigned on the **Role assignments** tab.\n",
181 | "\n",
182 | "\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 | ""
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 | ""
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 | }
--------------------------------------------------------------------------------