├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── fabricbot.json └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SAMPLES.md ├── SECURITY.md ├── app-config ├── .env-sample ├── .vscode │ └── launch.json ├── get-configuration-setting.js ├── list-configuration-settings.js ├── package-lock.json └── package.json ├── apps ├── apps │ ├── interactive-download-certificate-orders.js │ ├── interactive-download-settings.js │ └── interactive-download-settings.json ├── azure-functions-http-trigger-typescript │ ├── .devcontainer │ │ ├── Dockerfile │ │ └── devcontainer.json │ ├── .funcignore │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── babel.config.js │ ├── host.json │ ├── package-lock.json │ ├── package.json │ ├── proxies.json │ ├── readme.md │ ├── tsconfig.json │ ├── validateTweet │ │ ├── function.json │ │ ├── index.test.ts │ │ └── index.ts │ └── yarn.lock └── nodejs-redirect │ ├── index.js │ └── package.json ├── cognitive-services ├── face-detection-result.json ├── face.js └── package.json ├── database ├── cassandra │ ├── index.js │ └── package.json ├── cosmos-db-sql-core-api │ ├── find_product_by_query_with_param_like_keyword.js │ ├── package.json │ ├── update-products.js │ └── upsert_products.js ├── mariadb │ ├── index.js │ └── package.json ├── mongodb │ ├── .env │ ├── azure-cosmosdb-mongodb.ts │ ├── books.csv │ ├── bulk_insert_mongodb.js │ ├── index_mongodb.js │ ├── index_mongoose.js │ ├── package-lock.json │ ├── package.json │ └── readme.md ├── mssql │ └── index.js ├── mysql │ ├── index.js │ └── package.json ├── postgresql │ ├── index.js │ └── package.json ├── redis │ ├── .devcontainer │ │ └── devcontainer.json │ ├── .env.sample │ ├── MOCK_DATA.csv │ ├── bulk_insert.js │ ├── config.js │ ├── get-set.js │ ├── package-lock.json │ ├── package.json │ ├── quickstart.js │ └── readme.md └── sql │ ├── .env.sample │ ├── .vscode │ └── launch.json │ ├── package.json │ └── quickstart.js ├── docs └── templates │ └── end-to-end-scenario.md ├── events └── event-hubs │ ├── package.json │ ├── receive-passwordless.js │ ├── receive.js │ ├── send-passwordless.js │ └── send.js ├── graph ├── my-profile-from-rest-api.js └── my-profile-from-sdk.js ├── keyvault ├── azure-keyvault-secrets.js ├── package-lock.json └── package.json ├── package-lock.json ├── resources ├── .env ├── authorization │ ├── list.js │ ├── package-lock.json │ └── package.json ├── billing │ ├── billing.js │ ├── package-lock.json │ └── package.json ├── create-resource-default-credential.js ├── create-resource.js ├── monitor │ ├── .vscode │ │ └── launch.json │ ├── package-lock.json │ ├── package.json │ └── resource-creation-history.js ├── package.json ├── resource-group-create │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── resource-group-create.js ├── resource-group-default-credential.js ├── resource-group.js ├── resource-groups-list │ ├── README.md │ ├── package.json │ └── resource-groups-list.js ├── resources-list-in-subscription │ └── list-resources-in-subscription.js ├── subscriptions │ ├── list-locations.js │ ├── list.js │ ├── package-lock.json │ └── package.json └── virtual-machines │ ├── README.md │ ├── create-vm.js │ ├── delete-resources.js │ ├── list-vms.js │ ├── package-lock.json │ ├── package.json │ ├── start-vm.js │ ├── status-vms-all.js │ ├── status.js │ ├── stop-vm.js │ └── vm-info.js ├── search ├── bulk-insert-books-from-csv │ ├── books.csv │ ├── books.schema.json │ └── bulk_insert_books.js ├── package.json └── readme.md ├── storage ├── blob-paging │ ├── blob-paging.js │ ├── package-lock.json │ └── package.json ├── file-paging │ ├── README.md │ ├── package.json │ └── page-through-files.js ├── queue │ └── convert-into-and-out-of-string.js ├── upload-files-from-url │ ├── .vscode │ │ └── launch.json │ ├── README.md │ ├── copy-from-files-to-blobs.js │ ├── package-lock.json │ └── package.json ├── upload-string-to-file │ ├── package-lock.json │ ├── package.json │ └── upload-string-to-file.js └── upload-url-to-blob-poll-until-done │ ├── .vscode │ └── launch.json │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── upload-url-to-blob-poll-until-done-by-page.js │ └── upload-url-to-blob-poll-until-done.js ├── vscode-docs-linter └── config.json └── windows-terminal └── readme.md /.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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | > Please provide us with the following information: 5 | > --------------------------------------------------------------- 6 | 7 | ### This issue is for a: (mark with an `x`) 8 | ``` 9 | - [ ] bug report -> please search issues before submitting 10 | - [ ] feature request 11 | - [ ] documentation issue or request 12 | - [ ] regression (a behavior that used to work and stopped in a new release) 13 | ``` 14 | 15 | ### Minimal steps to reproduce 16 | > 17 | 18 | ### Any log messages given by the failure 19 | > 20 | 21 | ### Expected/desired behavior 22 | > 23 | 24 | ### OS and Version? 25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) 26 | 27 | ### Versions 28 | > 29 | 30 | ### Mention any other details that might be useful 31 | 32 | > --------------------------------------------------------------- 33 | > Thanks! We'll be in touch soon. 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | * ... 4 | 5 | ## Does this introduce a breaking change? 6 | 7 | ``` 8 | [ ] Yes 9 | [ ] No 10 | ``` 11 | 12 | ## Pull Request Type 13 | What kind of change does this Pull Request introduce? 14 | 15 | 16 | ``` 17 | [ ] Bugfix 18 | [ ] Feature 19 | [ ] Code style update (formatting, local variables) 20 | [ ] Refactoring (no functional changes, no api changes) 21 | [ ] Documentation content changes 22 | [ ] Other... Please describe: 23 | ``` 24 | 25 | ## How to Test 26 | * Get the code 27 | 28 | ``` 29 | git clone [repo-address] 30 | cd [repo-name] 31 | git checkout [branch-name] 32 | npm install 33 | ``` 34 | 35 | * Test the code 36 | 37 | ``` 38 | ``` 39 | 40 | ## What to Check 41 | Verify that the following are valid 42 | * ... 43 | 44 | ## Other Information 45 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '43 14 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [project-title] Changelog 2 | 3 | 4 | # x.y.z (yyyy-mm-dd) 5 | 6 | *Features* 7 | * ... 8 | 9 | *Bug Fixes* 10 | * ... 11 | 12 | *Breaking Changes* 13 | * ... 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to [project-title] 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 | - javascript 5 | - typescript 6 | - nodejs 7 | name: "JavaScript end-to-end samples" 8 | description: "These samples are part of the documentation for the Azure JavaScript Developer Center - https://docs.microsoft.com/azure/developer/javascript/." 9 | products: 10 | - azure 11 | - azure-functions 12 | - azure-cognitive-services 13 | - azure-cosmos-db 14 | - azure-database-mariadb 15 | - azure-database-mysql 16 | - azure-database-postgresql 17 | - azure-cache-redis 18 | - azure-key-vault 19 | - azure-resource-manager 20 | - azure-cognitive-search 21 | - azure-portal 22 | - vs-code 23 | --- 24 | 25 | # JavaScript end to end samples 26 | 27 | These samples are part of the documentation for the [Azure JavaScript Developer Center](https://docs.microsoft.com/azure/developer/javascript/). 28 | 29 | ## Resources 30 | 31 | * Azure 32 | * Learn TV 33 | * [The Launch Space](https://github.com/microsoft/TheLaunchSpace) 34 | * [Web wednesday](https://channel9.msdn.com/Shows/Web-Wednesday/) 35 | * [Developer Advocate resources](https://docs.microsoft.com/azure/developer/javascript/whats-new-developer-advocacy) - produced by advocates 36 | * [Community resources](https://docs.microsoft.com/javascript/) - produced by members of the community 37 | * [Code Samples of Azure JavaScript/TypeScript SDK Management Libraries](https://github.com/Azure-Samples/azure-samples-js-management) 38 | * [JS e2e Samples list](https://docs.microsoft.com/azure/developer/javascript/how-to/common-javascript-tasks#samples-supporting-these-tasks) 39 | 40 | ### Azure functions 41 | 42 | * Documentation 43 | * Tools 44 | * [OpenAPI for Azure Functions](https://github.com/aaronpowell/azure-functions-nodejs-openapi) - This is an extension for Azure Functions to generate OpenAPI spec files from annotated Azure Functions. 45 | 46 | ### Microsoft JavaScript packages 47 | 48 | * [Azure SDKs](https://github.com/Azure/azure-sdk-for-js) 49 | * [PlayWright](https://github.com/microsoft/playwright) 50 | * [Application Insights](https://github.com/microsoft/ApplicationInsights-node.js) 51 | * [Rooster](https://www.npmjs.com/package/roosterjs-react) 52 | 53 | ## Let us know 54 | 55 | If the code, documentation, or other information has errors or is incomplete, let us know. 56 | -------------------------------------------------------------------------------- /SAMPLES.md: -------------------------------------------------------------------------------- 1 | ### E2E applications 2 | 3 | These are client, server, or full-stack applications using Azure services - found in other repositories. 4 | 5 | |Sample|corresponding docs| 6 | |--|--| 7 | |[Client Azure login button](https://github.com/Azure-Samples/js-e2e-client-azure-login-button)|[docs](https://docs.microsoft.com/en-us/azure/developer/javascript/tutorial/single-page-application-azure-login-button-sdk-msal)| 8 | |Add easy authentication to app|[docs](https://docs.microsoft.com/en-us/azure/developer/javascript/how-to/with-web-app/add-authentication-to-web-app)| 9 | |[Add MSAL authentication to Express.js app](https://github.com/Azure-Samples/js-e2e-web-app-server-auth.git)|| 10 | |[Client file upload to Azure Storage Blobs](https://github.com/Azure-Samples/js-e2e-browser-file-upload-storage-blob)|[docs](https://docs.microsoft.com/en-us/azure/developer/javascript/tutorial/browser-file-upload-azure-storage-blob)| 11 | |[Static Web App Client using Cognitive Services Computer Vision](https://github.com/Azure-Samples/js-e2e-client-cognitive-services/blob/main/.github/workflows/sample-github-workflow.yml)|[docs](https://docs.microsoft.com/azure/developer/javascript/tutorial/static-web-app/introduction)| 12 | |[Server](https://github.com/Azure-Samples/js-e2e-express-server)|[docs](https://docs.microsoft.com/azure/developer/javascript/tutorial/tutorial-vscode-azure-cli-node/tutorial-vscode-azure-cli-node-01)| 13 | |[Server as VM deployed to Azure](https://github.com/Azure-Samples/js-e2e-vm)|[docs](https://docs.microsoft.com/en-us/azure/developer/javascript/tutorial/nodejs-virtual-machine-vm/introduction)| 14 | |[Server - Text to Speech conversion to MP3 files using Cognitive Services Speech](https://github.com/Azure-Samples/js-e2e-express-server-cognitive-services)|[docs](https://docs.microsoft.com/azure/developer/javascript/tutorial/convert-text-to-speech-cognitive-services)| 15 | |[Server with MongoDB](https://github.com/Azure-Samples/js-e2e-express-mongodb)|[docs](https://docs.microsoft.com/azure/developer/javascript/tutorial/deploy-nodejs-mongodb-app-service-from-visual-studio-code)| 16 | |[Full stack with MongoDB](https://github.com/Azure-Samples/js-e2e-express-react-mongodb)|| 17 | |[GraphQL serverless function](https://github.com/azure-samples/js-e2e-azure-function-graphql-hello) - Hello world with TypeScript, ready to deploy to Azure Function.|[docs](https://docs.microsoft.com/en-us/azure/developer/javascript/how-to/with-web-app/graphql/get-started)| 18 | |[GraphQL serverless CRUD function](https://github.com/azure-samples/js-e2e-azure-function-graphql-crud-operations)|[docs](https://docs.microsoft.com/en-us/azure/developer/javascript/how-to/with-web-app/graphql/get-started#reading-and-writing-apollo-graphql-with-queries-and-mutations)| 19 | |[GraphQL static web app with Function API](https://github.com/azure-samples/js-e2e-graphql-cosmosdb-static-web-app)|[docs](https://docs.microsoft.com/en-us/azure/developer/javascript/how-to/with-web-app/graphql/static-web-app-graphql/introduction?branch=master)| 20 | 21 | ## Other Samples 22 | 23 | * [Azure SDK for JS - samples, snippets, and how-to guides](https://github.com/Azure/azure-sdk-for-js/tree/main/samples) 24 | * [Microsoft Authentication Library for JavaScript (MSAL.js)](https://github.com/AzureAD/microsoft-authentication-library-for-js) 25 | * [Microsoft Graph](https://developer.microsoft.com/en-us/graph/gallery/) 26 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | This repo isn't currently versioned. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | Please open an issue if you find a vulnerability. 10 | -------------------------------------------------------------------------------- /app-config/.env-sample: -------------------------------------------------------------------------------- 1 | AZURE_TENANT_ID= 2 | AZURE_CLIENT_ID= 3 | AZURE_CLIENT_SECRET= 4 | KEYVAULT_URI=https://YOUR-RESOURCE-NAME.vault.azure.net/ 5 | APPCONFIG_CONNECTION_STRING="Endpoint=https://YOUR-RESOURCE-NAME.azconfig.io;Id=YOUR-ID;Secret=YOUR-SECRET" -------------------------------------------------------------------------------- /app-config/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\${file}" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /app-config/get-configuration-setting.js: -------------------------------------------------------------------------------- 1 | const { AppConfigurationClient } = require("@azure/app-configuration"); 2 | 3 | const connectionString = 4 | process.env["APPCONFIG_CONNECTION_STRING"] || 5 | "Endpoint=https://YOUR-RESOURCE-NAME..."; 6 | const appConfigClient = new AppConfigurationClient(connectionString); 7 | 8 | const getConfigurationSetting = async (key) => { 9 | return await appConfigClient.getConfigurationSetting({ 10 | key, 11 | }); 12 | }; 13 | 14 | const settingName = "AppTitle"; 15 | getConfigurationSetting(settingName) 16 | .then((result) => { 17 | console.log(result); 18 | 19 | /* 20 | { 21 | value: 'Text title of app - used in build pipeline', 22 | syncToken: '98765', 23 | lastModified: 2021-11-10T18:52:24.000Z, 24 | 'access-control-allow-credentials': 'true', 25 | 'access-control-allow-origin': '*', 26 | 'access-control-expose-headers': 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', 27 | connection: 'close', 28 | 'content-type': 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 29 | date: 'Wed, 10 Nov 2021 19:36:17 GMT', 30 | server: 'openresty/1.17.8.2', 31 | 'strict-transport-security': 'max-age=15724800; includeSubDomains', 32 | 'transfer-encoding': 'chunked', 33 | 'x-ms-correlation-request-id': '123456', 34 | 'x-ms-request-id': '123456', 35 | key: 'AppTitle', 36 | label: null, 37 | contentType: '', 38 | tags: {}, 39 | etag: 'YDxy2NHJexhh1X4LZZc9TK8mrnt', 40 | isReadOnly: false, 41 | statusCode: 200 42 | } 43 | */ 44 | }) 45 | .catch((ex) => { 46 | console.log(ex); 47 | }); 48 | -------------------------------------------------------------------------------- /app-config/list-configuration-settings.js: -------------------------------------------------------------------------------- 1 | const { 2 | AppConfigurationClient, 3 | parseSecretReference, 4 | parseFeatureFlag, 5 | isSecretReference, 6 | isFeatureFlag, 7 | } = require("@azure/app-configuration"); 8 | const { SecretClient, parseKeyVaultSecretIdentifier } = require("@azure/keyvault-secrets"); 9 | const { DefaultAzureCredential } = require("@azure/identity"); 10 | const { parse } = require("dotenv"); 11 | 12 | // Load the .env file if it exists 13 | require("dotenv").config(); 14 | 15 | const credential = new DefaultAzureCredential(); 16 | const url = process.env["KEYVAULT_URI"] || ""; 17 | const connectionString = 18 | process.env["APPCONFIG_CONNECTION_STRING"] || ""; 19 | 20 | const listSettings = async () => { 21 | const secretClient = new SecretClient(url, credential); 22 | const appConfigClient = new AppConfigurationClient(connectionString); 23 | const settingsIterator = appConfigClient.listConfigurationSettings(); 24 | 25 | const settingList = []; 26 | 27 | for await (const setting of settingsIterator) { 28 | let parsed = null; 29 | 30 | // Feature Flat 31 | if (isFeatureFlag(setting)) { 32 | parsed = parseFeatureFlag(setting); 33 | } else if (isSecretReference(setting)) { 34 | // Secret retrieved from Key Vault 35 | parsed = parseSecretReference(setting); 36 | const secretInKeyVault = parseKeyVaultSecretIdentifier(parsed.value.secretId); 37 | 38 | try{ 39 | parsed.secretValue = await secretClient.getSecret(secretInKeyVault.name); 40 | }catch(ex){ 41 | // handle case where secret has been removed from key vault but not from app config 42 | parsed.secretValue = null; 43 | //throw(ex); 44 | } 45 | 46 | } else { 47 | // Configuration Setting 48 | parsed = setting; 49 | } 50 | 51 | settingList.push(parsed); 52 | } 53 | return settingList; 54 | }; 55 | 56 | listSettings() 57 | .then((list) => { 58 | console.log(list); 59 | }) 60 | .catch((ex) => { 61 | console.log(ex); 62 | }); 63 | -------------------------------------------------------------------------------- /app-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-config", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "get-configuration-setting.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/app-configuration": "^1.3.0", 14 | "@azure/identity": "^2.0.1", 15 | "@azure/keyvault-secrets": "^4.3.0", 16 | "dotenv": "^10.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /apps/apps/interactive-download-certificate-orders.js: -------------------------------------------------------------------------------- 1 | const { InteractiveBrowserCredential } = require("@azure/identity"); 2 | const { WebSiteManagementClient } = require("@azure/arm-appservice"); 3 | 4 | // CHANGE THESE VALUES TO YOUR OWN 5 | // Either set environment variables 6 | // or set your value to the string at the end 7 | const subscriptionId = process.env["MY-SUBSCRIPTION"] || ""; 8 | const resourceGroupName = process.env["MY-RESOURCE-GROUP"] || ""; 9 | const resourceName = process.env["MY-RESOURCE-NAME"] || ""; 10 | 11 | const creds = new InteractiveBrowserCredential(); 12 | 13 | async function getCertificates(creds) { 14 | const client = new WebSiteManagementClient(creds, subscriptionId); 15 | const certificateOrdersList = new Array(); 16 | for await (const item of client.appServiceCertificateOrders.list()) { 17 | certificateOrdersList.push(item); 18 | } 19 | return certificateOrdersList; 20 | } 21 | 22 | getCertificates(creds) 23 | .then((res) => { 24 | console.log(JSON.stringify(res)); 25 | }) 26 | .catch((err) => { 27 | console.log(err); 28 | }); 29 | -------------------------------------------------------------------------------- /apps/apps/interactive-download-settings.js: -------------------------------------------------------------------------------- 1 | const { InteractiveBrowserCredential } = require("@azure/identity"); 2 | const { WebSiteManagementClient } = require("@azure/arm-appservice"); 3 | 4 | // CHANGE THESE VALUES TO YOUR OWN 5 | // Either set environment variables 6 | // or set your value to the string at the end 7 | const subscriptionId = process.env["MY-SUBSCRIPTION"] || ""; 8 | const resourceGroupName = process.env["MY-RESOURCE-GROUP"] || ""; 9 | const resourceName = process.env["MY-RESOURCE-NAME"] || ""; 10 | 11 | const creds = new InteractiveBrowserCredential(); 12 | 13 | async function getSettings(creds) { 14 | const client = new WebSiteManagementClient(creds, subscriptionId); 15 | const ApplicationSettingsList = new Array(); 16 | for await (const item of client.webApps.listApplicationSettings( 17 | resourceGroupName, 18 | resourceName 19 | )) { 20 | ApplicationSettingsList.push(item); 21 | } 22 | return ApplicationSettingsList; 23 | } 24 | 25 | getSettings(creds) 26 | .then((result) => { 27 | console.log(JSON.stringify(result)); 28 | }) 29 | .catch((err) => { 30 | console.log(err); 31 | }); 32 | -------------------------------------------------------------------------------- /apps/apps/interactive-download-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "/subscriptions/MY-SUBSCRIPTION/resourceGroups/MY-RESOURCE-GROUP/providers/Microsoft.Web/sites/MY-RESOURCE-NAME/config/appsettings", 3 | "name": "appsettings", 4 | "type": "Microsoft.Web/sites/config", 5 | "properties": { 6 | "APPINSIGHTS_INSTRUMENTATIONKEY": "MY-APP-INSIGHTS-KEY", 7 | "APPLICATIONINSIGHTS_CONNECTION_STRING": "InstrumentationKey=MY-APP-INSIGHTS-KEY;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/", 8 | "ApplicationInsightsAgent_EXTENSION_VERSION": "~2", 9 | "CUSTOM_KEY": "ABC_123", 10 | "MICROSOFT_PROVIDER_AUTHENTICATION_SECRET": "MY-SECRET", 11 | "XDT_MicrosoftApplicationInsights_Mode": "default" 12 | }, 13 | "location": "Central US", 14 | "tags": {} 15 | } -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Find the Dockerfile for mcr.microsoft.com/azure-functions/node at the following URLs: 2 | # Node 10: https://github.com/Azure/azure-functions-docker/blob/master/host/3.0/buster/amd64/node/node10/node10-core-tools.Dockerfile 3 | # Node 12: https://github.com/Azure/azure-functions-docker/blob/master/host/3.0/buster/amd64/node/node12/node12-core-tools.Dockerfile 4 | ARG VARIANT=12 5 | FROM mcr.microsoft.com/azure-functions/node:3.0-node${VARIANT}-core-tools 6 | 7 | # [Optional] Uncomment this section to install additional OS packages. 8 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 9 | && apt-get -y install --no-install-recommends azure-functions-core-tools-3 10 | 11 | -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/azure-functions-node 3 | { 4 | "name": "Azure Functions & Node.js", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | // Update 'VARIANT' to pick a Node.js version: 10, 12 8 | "args": { "VARIANT": "12" } 9 | }, 10 | "forwardPorts": [ 7071 ], 11 | 12 | // Set *default* container specific settings.json values on container create. 13 | "settings": { 14 | "terminal.integrated.shell.linux": "/bin/bash" 15 | }, 16 | 17 | // Add the IDs of extensions you want installed when the container is created. 18 | "extensions": [ 19 | "ms-azuretools.vscode-azurefunctions", 20 | "dbaeumer.vscode-eslint" 21 | ], 22 | 23 | // Use 'postCreateCommand' to run commands after the container is created. 24 | "postCreateCommand": "npm install", 25 | 26 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 27 | "remoteUser": "node" 28 | } -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/.funcignore: -------------------------------------------------------------------------------- 1 | *.js.map 2 | *.ts 3 | .git* 4 | .vscode 5 | local.settings.json 6 | test 7 | tsconfig.json -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | .env.test 64 | 65 | # parcel-bundler cache (https://parceljs.org/) 66 | .cache 67 | 68 | # next.js build output 69 | .next 70 | 71 | # nuxt.js build output 72 | .nuxt 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless/ 79 | 80 | # FuseBox cache 81 | .fusebox/ 82 | 83 | # DynamoDB Local files 84 | .dynamodb/ 85 | 86 | # TypeScript output 87 | dist 88 | out 89 | 90 | # Azure Functions artifacts 91 | bin 92 | obj 93 | appsettings.json 94 | local.settings.json -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions", 4 | "ms-vscode-remote.remote-containers" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run Functions", 6 | "type": "node", 7 | "request": "attach", 8 | "port": 9229, 9 | "preLaunchTask": "func: host start" 10 | },{ 11 | "type": "node", 12 | "request": "launch", 13 | "name": "Run Tests", 14 | "disableOptimisticBPs": true, 15 | "program": "${workspaceRoot}/node_modules/jest/bin/jest.js", 16 | "args": [ 17 | "-i" 18 | ], 19 | "internalConsoleOptions": "openOnSessionStart" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "azureFunctions.deploySubpath": ".", 3 | "azureFunctions.postDeployTask": "npm install", 4 | "azureFunctions.projectLanguage": "TypeScript", 5 | "azureFunctions.projectRuntime": "~3", 6 | "debug.internalConsoleOptions": "neverOpen", 7 | "azureFunctions.preDeployTask": "npm prune" 8 | } -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "func", 6 | "command": "host start", 7 | "problemMatcher": "$func-node-watch", 8 | "isBackground": true, 9 | "dependsOn": "npm build" 10 | }, 11 | { 12 | "type": "shell", 13 | "label": "npm build", 14 | "command": "npm run build", 15 | "dependsOn": "npm install", 16 | "problemMatcher": "$tsc" 17 | }, 18 | { 19 | "type": "shell", 20 | "label": "npm install", 21 | "command": "npm install" 22 | }, 23 | { 24 | "type": "shell", 25 | "label": "npm prune", 26 | "command": "npm prune --production", 27 | "dependsOn": "npm build", 28 | "problemMatcher": [] 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = api => { 2 | const isTest = api.env('test'); 3 | // You can use isTest to determine what presets and plugins to use. 4 | 5 | return { 6 | presets: [ 7 | ['@babel/preset-env', 8 | { 9 | targets: 10 | { node: 'current' } 11 | } 12 | ], 13 | '@babel/preset-typescript', 14 | ] 15 | }; 16 | }; -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | }, 11 | "extensionBundle": { 12 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 13 | "version": "[1.*, 2.0.0)" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twitterfunction", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "build": "tsc", 7 | "watch": "tsc -w", 8 | "prestart": "npm run clean && npm run build", 9 | "start": "func start --watch", 10 | "test": "npm run prestart && jest /dist --verbose --coverage", 11 | "clean": "rm -rf dist" 12 | }, 13 | "dependencies": { 14 | "jest": "^26.6.3", 15 | "twitter-text": "^3.1.0" 16 | }, 17 | "devDependencies": { 18 | "@azure/functions": "^1.2.3", 19 | "@babel/core": "^7.13.8", 20 | "@babel/preset-env": "^7.13.8", 21 | "@babel/preset-typescript": "^7.13.0", 22 | "@types/jest": "^26.0.20", 23 | "babel-jest": "^26.6.3", 24 | "typescript": "^3.3.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/proxies.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/proxies", 3 | "proxies": {} 4 | } 5 | -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/readme.md: -------------------------------------------------------------------------------- 1 | # Azure Functions with TypeScript 2 | 3 | ## Visual Studio Code extensions 4 | 5 | * Azure Functions - ms-azuretools.vscode-azurefunctions 6 | * Remote - Containers - ms-vscode-remote.remote-containers 7 | 8 | ## Azure resources 9 | 10 | * Run this app locally in the container provided: 11 | * all environment and JS dependencies are installed 12 | * there isn't a problem with the version of Node.js you have locally installed. This allows you to use the same version of Node.js as the Azure Function runtime environment. 13 | 14 | ## Azure Functions 15 | 16 | * 200 - Post with valid body value 17 | * 404 - Post without body or without body.tweetText 18 | 19 | ## HTTP Request 20 | 21 | Function is configured to use HTTP Post and expects the POST Body to look like: 22 | 23 | ```JSON 24 | { tweetText: "This is my tweet" } 25 | ``` 26 | 27 | You can see this used in the test. 28 | 29 | ## HTTP Response 30 | 31 | ```JSON 32 | { 33 | "textReturn": "16 / 280", 34 | "isValid": true 35 | } 36 | ``` 37 | ## Local Function runtime 38 | 39 | Use [Azure Function core tools](https://www.npmjs.com/package/azure-functions-core-tools) to run local function environment. If you run the project in the Docker container, core tools is already installed with the `./devcontainer.json/DockerFile`. 40 | 41 | ## CORS 42 | 43 | ## Function authentication 44 | 45 | Create the function (not the function app) with function-level authentication. This requires the requestor to pass an `x-functions-key` header with the key value (found in the Azure portal) to validate the requestor has proper auth to call the function. 46 | 47 | ## Test local functions 48 | 49 | The boilerplate for the project was created with the Azure Functions VSCode extension. It didn't include the Jest integration, which was added afterward. 50 | 51 | ```bash 52 | curl -i \ 53 | --header "Accept: application/json" \ 54 | --request POST \ 55 | --data '{"tweetText":"This is my tweet"}' \ 56 | http://localhost:7071/api/validateTweet 57 | ``` 58 | 59 | ## Test remote functions 60 | 61 | ```bash 62 | curl -i \ 63 | --header "Accept: application/json" \ 64 | --header "x-functions-key: SEE-VALUE-IN-AZURE-PORTAL-FOR-FUNCTION" \ 65 | --request POST \ 66 | --data '{"tweetText":"This is my tweet"}' \ 67 | https://YOUR-RESOURCE-NAME.azurewebsites.net/api/validateTweet 68 | ``` 69 | 70 | -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "dist", 6 | "rootDir": ".", 7 | "sourceMap": true, 8 | "strict": false 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/validateTweet/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "function", 5 | "type": "httpTrigger", 6 | "direction": "in", 7 | "name": "req", 8 | "methods": [ 9 | "post" 10 | ] 11 | }, 12 | { 13 | "type": "http", 14 | "direction": "out", 15 | "name": "res" 16 | } 17 | ], 18 | "scriptFile": "../dist/validateTweet/index.js" 19 | } 20 | -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/validateTweet/index.test.ts: -------------------------------------------------------------------------------- 1 | import httpTrigger from "./index"; 2 | import { Context } from "@azure/functions"; 3 | 4 | describe("Test for validateTweet Function", () => { 5 | let context: Context; 6 | 7 | beforeEach(() => { 8 | context = ({ log: jest.fn() } as unknown) as Context; 9 | }); 10 | 11 | test('404 - Http trigger request missing required params', async () => { 12 | 13 | const request = { 14 | body: null 15 | }; 16 | 17 | await httpTrigger(context, request); 18 | 19 | expect(JSON.stringify(context.res.status)).toEqual("404"); 20 | }); 21 | 22 | test('200 - Http trigger returns success object', async () => { 23 | 24 | const request = { 25 | body: { tweetText: "This is my tweet" }, 26 | headers: { 27 | Accept: 'application/json' 28 | } 29 | }; 30 | 31 | await httpTrigger(context, request); 32 | 33 | expect(JSON.stringify(context.res.headers["Content-Type"]).includes('application/json')).toEqual(true) 34 | expect(JSON.stringify(context.res.body)).toEqual(JSON.stringify({ 35 | "textReturn": "16 / 280", 36 | "isValid": true 37 | })); 38 | }); 39 | }); -------------------------------------------------------------------------------- /apps/azure-functions-http-trigger-typescript/validateTweet/index.ts: -------------------------------------------------------------------------------- 1 | import { AzureFunction, Context, HttpRequest } from "@azure/functions" 2 | 3 | const twttr = require('twitter-text'); 4 | 5 | // Call twitter's SDK to validate tweet 6 | const validateTweetBody = (text): any => { 7 | const result = twttr.parseTweet(text); 8 | const maxChars = 280; 9 | if (result) { 10 | let textReturn = `${result.weightedLength} / ${maxChars}`; 11 | let isValid = result.weightedLength <= maxChars ? true : false; 12 | return { 13 | textReturn: textReturn, 14 | isValid: isValid 15 | } 16 | } 17 | } 18 | 19 | /* 20 | To test locally: 21 | 22 | curl -i \ 23 | --header "Accept: application/json" \ 24 | --request POST \ 25 | --data '{"tweetText":"This is my tweet"}' \ 26 | http://localhost:7071/api/validateTweet 27 | 28 | To test after deployed to Azure functions: 29 | 30 | curl -i \ 31 | --header "Accept: application/json" \ 32 | --header "x-functions-key: YOUR-FUNCTION-KEY" \ 33 | --request POST \ 34 | --data '{"tweetText":"This is my tweet"}' \ 35 | https://YOUR-RESOURCE-NAME.azurewebsites.net/api/validateTweet 36 | 37 | */ 38 | 39 | const httpTrigger: AzureFunction = async (context: Context, req: HttpRequest): Promise => { 40 | 41 | const tweet = req.body; 42 | 43 | if (!tweet || !tweet.tweetText) { 44 | context.res = { 45 | status: 404, 46 | body: "Invalid function params - expected Post to include tweet object {tweetText}" 47 | }; 48 | return; 49 | } 50 | 51 | const validation:any = validateTweetBody(tweet.tweetText); 52 | 53 | /* 54 | { 55 | "textReturn": "16 / 280", 56 | "isValid": true 57 | } 58 | */ 59 | context.res = { 60 | body: validation, 61 | headers: { 62 | "Content-Type":"application/json" 63 | } 64 | }; 65 | 66 | }; 67 | 68 | export default httpTrigger; -------------------------------------------------------------------------------- /apps/nodejs-redirect/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | var debug = require("debug")("node_blog:server"); 4 | 5 | const port = process.env.PORT || "8080"; 6 | const redirLocation = "https://abc.com"; 7 | 8 | app.get("*", (req, res) => { 9 | res.redirect(redirLocation); 10 | }); 11 | 12 | app.listen(port, () => { 13 | console.log(`App listening on port ${port}`); 14 | }); 15 | -------------------------------------------------------------------------------- /apps/nodejs-redirect/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-redirect", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.18.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cognitive-services/face.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Use Cognitive Services Face resource to detect faces in a list of images. 4 | 5 | Requires: 6 | 7 | Azure subscription, Azure resource group, Azure Face resource already exists. 8 | 9 | Add your Face resource key and name: 10 | * "REPLACE-WITH-YOUR-FACE-RESOURCE-KEY" 11 | * "REPLACE-WITH-YOUR-FACE-RESOURCE-NAME" 12 | 13 | References: 14 | * [Azure SDK Ref Docs for Face detectWithUrl](https://docs.microsoft.com/en-us/javascript/api/@azure/cognitiveservices-face/face?view=azure-node-latest#detectWithUrl_string__Models_FaceDetectWithUrlOptionalParams_) 15 | * [Azure Service documentation for Face](https://docs.microsoft.com/en-us/azure/cognitive-services/face/) 16 | 17 | */ 18 | 19 | const msRest = require("@azure/ms-rest-js"); 20 | const Face = require("@azure/cognitiveservices-face"); 21 | 22 | const azureResourceKey = process.env["FACE-RESOURCE-KEY"] || "REPLACE-WITH-YOUR-FACE-RESOURCE-KEY"; 23 | const azureResourceName = process.env["FACE-RESOURCE-NAME"] || "REPLACE-WITH-YOUR-FACE-RESOURCE-NAME"; 24 | 25 | endpoint = `https://${azureResourceName}.cognitiveservices.azure.com/` 26 | 27 | 28 | const credentials = new msRest.ApiKeyCredentials({ inHeader: { 'Ocp-Apim-Subscription-Key': azureResourceKey } }); 29 | const client = new Face.FaceClient(credentials, endpoint); 30 | 31 | 32 | const image_base_url = "https://csdx.blob.core.windows.net/resources/Face/Images/"; 33 | 34 | const detectImage = async (imageUrl) => { 35 | 36 | const options = { 37 | returnFaceAttributes: ["Accessories", "Age", "Blur", "Emotion", "Exposure", "FacialHair", "Gender", "Glasses", "Hair", "HeadPose", "Makeup", "Noise", "Occlusion", "Smile"], 38 | 39 | // detection model 1 = retrieving attributes. 40 | detectionModel: "detection_01" 41 | } 42 | 43 | return await client.face.detectWithUrl(imageUrl, options); 44 | }; 45 | 46 | const processImageList = async () => { 47 | 48 | // Create a list of images 49 | const image_file_names = [ 50 | "detection1.jpg", // single female with glasses 51 | "detection5.jpg", // family, woman child man 52 | "detection6.jpg" // elderly couple, male female 53 | ]; 54 | 55 | let results = []; 56 | 57 | for (let i = 0; i < image_file_names.length; i++) { 58 | 59 | const imageUrl = `${image_base_url}${image_file_names[i]}`; 60 | console.log(imageUrl); 61 | 62 | const detectImageResult = await detectImage(imageUrl); 63 | console.log("pushing " + i); 64 | results.push(detectImageResult); 65 | } 66 | 67 | return results; 68 | } 69 | 70 | processImageList().then(results => { 71 | console.log(JSON.stringify(results)); 72 | }).catch(err => { 73 | console.log(err); 74 | }) -------------------------------------------------------------------------------- /cognitive-services/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cognitive-services", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "face.js", 6 | "scripts": { 7 | "start": "node face.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/cognitiveservices-face": "^4.2.0", 14 | "@azure/ms-rest-js": "^2.3.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /database/cassandra/index.js: -------------------------------------------------------------------------------- 1 | // install cassandra-driver SDK 2 | // run at command line 3 | // npm install cassandra-driver 4 | 5 | const cassandra = require('cassandra-driver'); 6 | 7 | const config = { 8 | username: 'YOUR-USERNAME', // Your Cassandra user name is the resource name 9 | password: 10 | 'YOUR-PASSWORD', 11 | contactPoint: 'YOUR-RESOURCE-NAME.cassandra.cosmos.azure.com', 12 | }; 13 | 14 | let client = null; 15 | 16 | const callCassandra = async () => { 17 | 18 | // authentication 19 | const authProvider = new cassandra.auth.PlainTextAuthProvider( 20 | config.username, 21 | config.password 22 | ); 23 | 24 | // create client 25 | client = new cassandra.Client({ 26 | contactPoints: [`${config.contactPoint}:10350`], 27 | authProvider: authProvider, 28 | localDataCenter: 'Central US', 29 | sslOptions: { 30 | secureProtocol: 'TLSv1_2_method', 31 | rejectUnauthorized: false, 32 | }, 33 | }); 34 | 35 | await client.connect(); 36 | console.log("connected"); 37 | 38 | // create keyspace 39 | let query = 40 | "CREATE KEYSPACE IF NOT EXISTS uprofile WITH replication = {\'class\': \'NetworkTopologyStrategy\', \'datacenter\' : \'1\' }"; 41 | await client.execute(query); 42 | console.log('created keyspace'); 43 | 44 | // create table 45 | query = 46 | 'CREATE TABLE IF NOT EXISTS uprofile.user (name text, alias text, region text Primary Key)'; 47 | await client.execute(query); 48 | console.log('created table'); 49 | 50 | // insert 3 rows 51 | console.log('insert'); 52 | const arr = [ 53 | "INSERT INTO uprofile.user (name, alias , region) VALUES ('Tim Jones', 'TJones', 'centralus')", 54 | "INSERT INTO uprofile.user (name, alias , region) VALUES ('Joan Smith', 'JSmith', 'northus')", 55 | "INSERT INTO uprofile.user (name, alias , region) VALUES ('Bob Wright', 'BWright', 'westus')" 56 | ]; 57 | for (const element of arr) { 58 | await client.execute(element); 59 | } 60 | 61 | // get all rows 62 | query = 'SELECT * FROM uprofile.user'; 63 | const resultSelect = await client.execute(query); 64 | 65 | for (const row of resultSelect.rows) { 66 | console.log( 67 | 'Obtained row: %s | %s | %s ', 68 | row.name, 69 | row.alias, 70 | row.region 71 | ); 72 | } 73 | 74 | // get filtered row 75 | console.log('Getting by region'); 76 | query = 'SELECT * FROM uprofile.user where region=\'westus\''; 77 | const resultSelectWhere = await client.execute(query); 78 | 79 | for (const row of resultSelectWhere.rows) { 80 | console.log( 81 | 'Obtained row: %s | %s | %s ', 82 | row.name, 83 | row.alias, 84 | row.region 85 | ); 86 | } 87 | 88 | client.shutdown(); 89 | }; 90 | 91 | callCassandra() 92 | .then(() => { 93 | console.log('done'); 94 | }) 95 | .catch((err) => { 96 | if (client) { 97 | client.shutdown(); 98 | } 99 | console.log(err); 100 | }); -------------------------------------------------------------------------------- /database/cassandra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cassandra", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cassandra-driver": "^4.6.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /database/cosmos-db-sql-core-api/find_product_by_query_with_param_like_keyword.js: -------------------------------------------------------------------------------- 1 | // Prereq: data already exists in container 2 | 3 | // Get environment variables from .env 4 | import * as path from 'path'; 5 | import { promises as fs } from 'fs'; 6 | import { fileURLToPath } from 'url'; 7 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 8 | 9 | 10 | /* 11 | 12 | Data like 13 | 14 | { 15 | "id": "794ACC61-01E9-49BF-B150-1D02EE01DABC", 16 | "categoryId": "3E4CEACD-D007-46EB-82D7-31F6141752B2", 17 | "categoryName": "Components, Road Frames", 18 | "sku": "FR-R38B-38", 19 | "name": "LL Road Frame - Blue, 38", 20 | "description": "The product called \"LL Road Frame - Blue, 38\"", 21 | "price": 337.22000000000003, 22 | "tags": [ 23 | { 24 | "_id": "A2443B36-76AE-4963-9E21-368868F9C514", 25 | "name": "Tag-6" 26 | }, 27 | { 28 | "_id": "F07885AF-BD6C-4B71-88B1-F04295992176", 29 | "name": "Tag-149" 30 | } 31 | ] 32 | } 33 | 34 | */ 35 | 36 | 37 | import * as dotenv from "dotenv"; 38 | dotenv.config(); 39 | 40 | // Get Cosmos Client 41 | import { CosmosClient } from "@azure/cosmos"; 42 | 43 | // Provide required connection from environment variables 44 | const key = process.env.COSMOS_KEY; 45 | // Endpoint format: https://YOUR-RESOURCE-NAME.documents.azure.com:443/ 46 | const endpoint = process.env.COSMOS_ENDPOINT; 47 | // Authenticate to Azure Cosmos DB 48 | const cosmosClient = new CosmosClient({ endpoint, key }); 49 | 50 | // Set Database name and container name 51 | const databaseName = `contoso_1663344094228`; 52 | const containerName = `products_1663344094228`; 53 | const partitionKeyPath = ["/categoryName"]; 54 | 55 | const { database } = await cosmosClient.databases.createIfNotExists({ 56 | id: databaseName, 57 | }); 58 | const { container } = await database.containers.createIfNotExists({ 59 | id: containerName, 60 | partitionKey: { 61 | paths: partitionKeyPath, 62 | }, 63 | }); 64 | 65 | async function executeSql(property, value) { 66 | 67 | // value example string: "%value%" 68 | const querySpec = { 69 | query: `select * from products p where p.${property} LIKE @propertyValue`, 70 | parameters: [ 71 | { 72 | name: "@propertyValue", 73 | value: `${value}`, 74 | }, 75 | ], 76 | }; 77 | 78 | console.log(querySpec); 79 | 80 | const { resources } = await container.items.query(querySpec).fetchAll(); 81 | 82 | let i=0; 83 | 84 | for (const item of resources) { 85 | console.log(`${++i}: ${item.id}: ${item.name}, ${item.sku}`); 86 | } 87 | } 88 | 89 | executeSql('name', '%Blue%'); 90 | -------------------------------------------------------------------------------- /database/cosmos-db-sql-core-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contosoretail", 3 | "version": "1.0.0", 4 | "description": "", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "start:products": "node products.js categoryName 'Bikes, Road Bikes'", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@azure/cosmos": "^3.17.1", 16 | "dotenv": "^16.0.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /database/cosmos-db-sql-core-api/update-products.js: -------------------------------------------------------------------------------- 1 | // Get environment variables from .env 2 | import * as path from "path"; 3 | import { promises as fs } from "fs"; 4 | import { fileURLToPath } from "url"; 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 6 | 7 | import * as dotenv from "dotenv"; 8 | dotenv.config(); 9 | 10 | // Get Cosmos Client 11 | import { CosmosClient } from "@azure/cosmos"; 12 | 13 | // Provide required connection from environment variables 14 | const key = process.env.COSMOS_KEY; 15 | // Endpoint format: https://YOUR-RESOURCE-NAME.documents.azure.com:443/ 16 | const endpoint = process.env.COSMOS_ENDPOINT; 17 | // Authenticate to Azure Cosmos DB 18 | const cosmosClient = new CosmosClient({ endpoint, key }); 19 | 20 | // Set Database name and container name 21 | const databaseName = `contoso_1663344094228`; 22 | const containerName = `products_storedproc-16`; 23 | const partitionKeyPath = ["/categoryName"]; 24 | 25 | const { database } = await cosmosClient.databases.createIfNotExists({ 26 | id: databaseName, 27 | }); 28 | const { container } = await database.containers.createIfNotExists({ 29 | id: containerName, 30 | partitionKey: { 31 | paths: partitionKeyPath, 32 | }, 33 | }); 34 | 35 | const updatePrices = async(priceChanges)=>{ 36 | const returnStatus = { 37 | success: true, 38 | status: { 39 | updated: [], 40 | failed: [], 41 | }}; 42 | 43 | // Nothing to do 44 | if (!priceChanges || priceChanges.length == 0) { 45 | return returnStatus; 46 | } 47 | 48 | for (let priceChangeRequest of priceChanges) { 49 | try { 50 | // Get product 51 | const { resource } = await container.item(priceChangeRequest.id, priceChangeRequest.categoryName).read(); 52 | 53 | if(!resource) { 54 | returnStatus.status.failed.push({...priceChangeRequest, status: "not found"}); 55 | continue; 56 | } 57 | 58 | // update price 59 | resource.price += priceChangeRequest.priceChange; 60 | 61 | // update product in container 62 | const updateResult = await container.items.upsert(resource); 63 | 64 | // update status based on HTTP result code 65 | if ( (updateResult.statusCode > 199) && updateResult.statusCode < 300){ 66 | returnStatus.status.updated.push(priceChangeRequest); 67 | } else { 68 | returnStatus.status.failed.push(priceChangeRequest); 69 | } 70 | } catch (err) { 71 | // update status 72 | returnStatus.status.failed.push(priceChangeRequest); 73 | } 74 | } 75 | 76 | return { 77 | success: returnStatus.status.updated.length === priceChanges.length ? true : false, 78 | status: returnStatus.status 79 | }; 80 | }; 81 | 82 | 83 | 84 | const initialData = [ 85 | { 86 | "id": "027D0B9A-F9D9-4C96-8213-C8546C4AAE71", 87 | "categoryId": "26C74104-40BC-4541-8EF5-9892F7F03D72", 88 | "categoryName": "Components, Saddles", 89 | "sku": "SE-R581", 90 | "name": "LL Road Seat/Saddle", 91 | "description": "The product called \"LL Road Seat/Saddle\"", 92 | "price": 27.120000000000001, 93 | "tags": [ 94 | { 95 | "id": "0573D684-9140-4DEE-89AF-4E4A90E65666", 96 | "name": "Tag-113" 97 | }, 98 | { 99 | "id": "6C2F05C8-1E61-4912-BE1A-C67A378429BB", 100 | "name": "Tag-5" 101 | }, 102 | { 103 | "id": "B48D6572-67EB-4630-A1DB-AFD4AD7041C9", 104 | "name": "Tag-100" 105 | }, 106 | { 107 | "id": "D70F215D-A8AC-483A-9ABD-4A008D2B72B2", 108 | "name": "Tag-85" 109 | }, 110 | { 111 | "id": "DCF66D9A-E2BF-4C70-8AC1-AD55E5988E9D", 112 | "name": "Tag-37" 113 | } 114 | ] 115 | }, 116 | { 117 | "id": "08225A9E-F2B3-4FA3-AB08-8C70ADD6C3C2", 118 | "categoryId": "75BF1ACB-168D-469C-9AA3-1FD26BB4EA4C", 119 | "categoryName": "Bikes, Touring Bikes", 120 | "sku": "BK-T79U-50", 121 | "name": "Touring-1000 Blue, 50", 122 | "description": "The product called \"Touring-1000 Blue, 50\"", 123 | "price": 2384.0700000000002, 124 | "tags": [ 125 | { 126 | "id": "27B7F8D5-1009-45B8-88F5-41008A0F0393", 127 | "name": "Tag-61" 128 | } 129 | ] 130 | }, 131 | { 132 | "id": "0A7E57DA-C73F-467F-954F-17B7AFD6227E", 133 | "categoryId": "4F34E180-384D-42FC-AC10-FEC30227577F", 134 | "categoryName": "Components, Pedals", 135 | "sku": "PD-R563", 136 | "name": "ML Road Pedal", 137 | "description": "The product called \"ML Road Pedal\"", 138 | "price": 62.090000000000003, 139 | "tags": [ 140 | { 141 | "id": "14CFF1D6-7749-4A57-85B3-783F47731F32", 142 | "name": "Tag-7" 143 | }, 144 | { 145 | "id": "319E277F-6B7A-483D-81BA-1EC34CC700EB", 146 | "name": "Tag-163" 147 | } 148 | ] 149 | } 150 | ]; 151 | 152 | const priceUpdates = [ 153 | { 154 | "id": "027D0B9A-F9D9-4C96-8213-C8546C4AAE71", 155 | "categoryName": "Components, Saddles", 156 | "priceChange": 3 157 | }, 158 | { 159 | "id": "08225A9E-F2B3-4FA3-AB08-8C70ADD6C3C2", 160 | "categoryName": "Bikes, Touring Bikes", 161 | "priceChange": 616 162 | }, 163 | { 164 | "id": "0A7E57DA-C73F-467F-954F-17B7AFD6227E", 165 | "categoryName": "Components, Pedals", 166 | "price": -32 167 | }, 168 | { 169 | "id": "not-in-data-set", 170 | "categoryName": "Components, Pedals", 171 | "price": -32 172 | } 173 | ]; 174 | 175 | for (const item of initialData) { 176 | 177 | const { resource } = await container.items.create(item); 178 | console.log(`'${resource.name}' inserted`); 179 | } 180 | const updateResult = await updatePrices(priceUpdates); 181 | console.log(JSON.stringify(updateResult)); 182 | 183 | /* 184 | 185 | 'LL Road Seat/Saddle' inserted 186 | 'Touring-1000 Blue, 50' inserted 187 | 'ML Road Pedal' inserted 188 | {"success":false,"status":{"updated":[{"id":"027D0B9A-F9D9-4C96-8213-C8546C4AAE71","categoryName":"Components, Saddles","priceChange":3},{"id":"08225A9E-F2B3-4FA3-AB08-8C70ADD6C3C2","categoryName":"Bikes, Touring Bikes","priceChange":616},{"id":"0A7E57DA-C73F-467F-954F-17B7AFD6227E","categoryName":"Components, Pedals","price":-32}],"failed":[{"id":"not-in-data-set","categoryName":"Components, Pedals","price":-32,"status":"not found"}]}} 189 | 190 | */ 191 | -------------------------------------------------------------------------------- /database/cosmos-db-sql-core-api/upsert_products.js: -------------------------------------------------------------------------------- 1 | // Get environment variables from .env 2 | import * as path from 'path'; 3 | import { promises as fs } from 'fs'; 4 | import { fileURLToPath } from 'url'; 5 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 6 | 7 | import * as dotenv from "dotenv"; 8 | dotenv.config(); 9 | 10 | // Get Cosmos Client 11 | import { CosmosClient } from "@azure/cosmos"; 12 | 13 | 14 | // Provide required connection from environment variables 15 | const key = process.env.COSMOS_KEY; 16 | // Endpoint format: https://YOUR-RESOURCE-NAME.documents.azure.com:443/ 17 | const endpoint = process.env.COSMOS_ENDPOINT; 18 | // Authenticate to Azure Cosmos DB 19 | const cosmosClient = new CosmosClient({ endpoint, key }); 20 | 21 | // Set Database name and container name 22 | const databaseName = `contoso_1663344094228`; 23 | const containerName = `products_upsert-3`; 24 | const partitionKeyPath = ["/categoryName"]; 25 | 26 | const { database } = await cosmosClient.databases.createIfNotExists({ 27 | id: databaseName, 28 | }); 29 | const { container } = await database.containers.createIfNotExists({ 30 | id: containerName, 31 | partitionKey: { 32 | paths: partitionKeyPath, 33 | }, 34 | }); 35 | 36 | /* 37 | 38 | Rules: 39 | If item with same "id" is present, update 40 | If item with same "id" is not present, insert 41 | 42 | 43 | */ 44 | async function upsert(fileAndPathToJson, encoding='utf-8') { 45 | 46 | console.log(fileAndPathToJson); 47 | 48 | const data = JSON.parse(await fs.readFile(path.join(__dirname, fileAndPathToJson), encoding)); 49 | console.log(data); 50 | 51 | const { resource } = await container.items.upsert(data); 52 | console.log(`'${resource.name}' inserted`); 53 | } 54 | 55 | await upsert('./new_item.json'); 56 | await upsert('./update_item.json'); 57 | await upsert('./update_partition_key.json'); 58 | 59 | const { resources } = await container.items.readAll().fetchAll(); 60 | 61 | for (const item of resources) { 62 | console.log(`${item.id}: ${item.name}, ${item.sku}`); 63 | } 64 | -------------------------------------------------------------------------------- /database/mariadb/index.js: -------------------------------------------------------------------------------- 1 | // To install npm package, 2 | // run following command at terminal 3 | // npm install mariadb 4 | 5 | // get mariadb SDK 6 | const mariadb = require('mariadb'); 7 | 8 | // query server and close connection 9 | const query = async (config) => { 10 | // creation connection 11 | const connection = await mariadb.createConnection(config); 12 | 13 | // show databases on server 14 | const databases = await connection.query('SHOW DATABASES;'); 15 | console.log(databases); 16 | 17 | // show tables in the mysql database 18 | const tables = await connection.query('SHOW TABLES FROM mysql;'); 19 | console.log(tables); 20 | 21 | // show users configured for the server 22 | const rows = await connection.query('select User from mysql.user;'); 23 | console.log(rows); 24 | 25 | // close connection 26 | connection.end(); 27 | }; 28 | 29 | const config = { 30 | host: 'YOUR-RESOURCE_NAME.mariadb.database.azure.com', 31 | user: 'YOUR-ADMIN-NAME@YOUR-RESOURCE_NAME', 32 | password: 'YOUR-ADMIN-PASSWORD', 33 | port: 3306, 34 | }; 35 | 36 | query(config) 37 | .then(() => console.log('done')) 38 | .catch((err) => console.log(err)); -------------------------------------------------------------------------------- /database/mariadb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mariadb", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "mariadb": "^2.5.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /database/mongodb/.env: -------------------------------------------------------------------------------- 1 | YOUR_RESOURCE_PRIMARY_CONNECTION_STRING= 2 | DATABASE_NAME=BooksDB 3 | DATABASE_COLLECTION_NAME=GoodBooks 4 | -------------------------------------------------------------------------------- /database/mongodb/azure-cosmosdb-mongodb.ts: -------------------------------------------------------------------------------- 1 | import { Schema, model, connect } from "mongoose"; 2 | 3 | const CategorySchema = new Schema( 4 | { categoryName: String }, 5 | { timestamps: true } 6 | ); 7 | const CategoryModel = model("Category", CategorySchema, "Bookstore"); 8 | 9 | export const init = async () => { 10 | await connect(process.env["CosmosDbConnectionString"]); 11 | }; 12 | export const addItem = async (doc) => { 13 | const modelToInsert = new CategoryModel(); 14 | modelToInsert["categoryName"] = doc.name; 15 | 16 | return await modelToInsert.save(); 17 | }; 18 | export const findItemById = async (id) => { 19 | return await CategoryModel.findById(id); 20 | }; 21 | export const findItems = async (query = {}) => { 22 | return await CategoryModel.find({}); 23 | }; 24 | export const deleteItemById = async (id) => { 25 | return await CategoryModel.findByIdAndDelete(id); 26 | }; 27 | -------------------------------------------------------------------------------- /database/mongodb/bulk_insert_mongodb.js: -------------------------------------------------------------------------------- 1 | const { MongoClient } = require('mongodb'); 2 | const ObjectId = require('mongodb').ObjectID; 3 | require('dotenv').config(); 4 | 5 | const fs = require('fs'); 6 | const parse = require('csv-parser') 7 | const { finished } = require('stream/promises'); 8 | 9 | const DATABASE_URL = process.env.YOUR_RESOURCE_PRIMARY_CONNECTION_STRING 10 | ? process.env.YOUR_RESOURCE_PRIMARY_CONNECTION_STRING 11 | : 'mongodb://localhost:27017'; 12 | const DATABASE_NAME = process.env.DATABASE_NAME || 'my-tutorial-db'; 13 | const DATABASE_COLLECTION_NAME = 14 | process.env.DATABASE_COLLECTION_NAME || 'my-collection'; 15 | 16 | const csvFile = './books.csv' 17 | 18 | let mongoConnection = null; 19 | let db = null; 20 | let collection = null; 21 | 22 | 23 | // insert each row into MongoDB 24 | const insertData = async (readable) =>{ 25 | 26 | let i = 0; 27 | 28 | for await (const row of readable) { 29 | console.log(`${i++} = ${JSON.stringify(row.goodreads_book_id)}`); 30 | await collection.insertOne(row); 31 | } 32 | } 33 | const bulkInsert = async () => { 34 | 35 | mongoConnection = await MongoClient.connect(DATABASE_URL, { useUnifiedTopology: true }); 36 | db = mongoConnection.db(DATABASE_NAME); 37 | collection = await db.collection(DATABASE_COLLECTION_NAME); 38 | 39 | // read file, parse CSV, each row is a chunk 40 | const readable = fs 41 | .createReadStream(csvFile) 42 | .pipe(parse()); 43 | 44 | // Pipe rows to insert function 45 | await insertData(readable) 46 | await mongoConnection.close(); 47 | } 48 | 49 | bulkInsert().then(() => { 50 | console.log('done'); 51 | 52 | }).catch(err => { 53 | console.log(`done + failed ${err}`) 54 | }) 55 | -------------------------------------------------------------------------------- /database/mongodb/index_mongodb.js: -------------------------------------------------------------------------------- 1 | const { MongoClient } = require('mongodb'); 2 | const ObjectId = require('mongodb').ObjectID; 3 | 4 | // read .env file 5 | require('dotenv').config(); 6 | 7 | /* eslint no-return-await: 0 */ 8 | 9 | const DATABASE_URL = process.env."YOUR_RESOURCE_PRIMARY_CONNECTION_STRING" 10 | ? process.env."YOUR_RESOURCE_PRIMARY_CONNECTION_STRING" 11 | : 'mongodb://localhost:27017'; 12 | const DATABASE_NAME = process.env.DATABASE_NAME || 'my-tutorial-db'; 13 | const DATABASE_COLLECTION_NAME = 14 | process.env.DATABASE_COLLECTION_NAME || 'my-collection'; 15 | 16 | let mongoConnection = null; 17 | let db = null; 18 | 19 | const insertDocuments = async ( 20 | documents = [{ a: 1 }, { a: 2 }, { a: 3 }] 21 | ) => { 22 | // check params 23 | if (!db || !documents) 24 | throw Error('insertDocuments::missing required params'); 25 | 26 | // Get the collection 27 | const collection = await db.collection(DATABASE_COLLECTION_NAME); 28 | 29 | // Insert some documents 30 | return await collection.insertMany(documents); 31 | }; 32 | const findDocuments = async ( 33 | query = { a: 3 } 34 | ) => { 35 | 36 | // check params 37 | if (!db) 38 | throw Error('findDocuments::missing required params'); 39 | 40 | // Get the collection 41 | const collection = await db.collection(DATABASE_COLLECTION_NAME ); 42 | 43 | // find documents 44 | return await collection.find(query).toArray(); 45 | }; 46 | 47 | const removeDocuments = async ( 48 | docFilter = {} 49 | ) => { 50 | 51 | // check params 52 | if (!db ) 53 | throw Error('removeDocuments::missing required params'); 54 | 55 | // Get the documents collection 56 | const collection = await db.collection(DATABASE_COLLECTION_NAME); 57 | 58 | // Delete document 59 | return await collection.deleteMany(docFilter); 60 | }; 61 | 62 | const connect = async (url) => { 63 | 64 | // check params 65 | if (!url) throw Error('connect::missing required params'); 66 | 67 | return MongoClient.connect(url, { useUnifiedTopology: true }); 68 | }; 69 | /* 70 | eslint consistent-return: [0, { "treatUndefinedAsUnspecified": false }] 71 | */ 72 | const connectToDatabase = async () => { 73 | try { 74 | if (!DATABASE_URL || !DATABASE_NAME) { 75 | console.log('DB required params are missing'); 76 | console.log(`DB required params DATABASE_URL = ${DATABASE_URL}`); 77 | console.log(`DB required params DATABASE_NAME = ${DATABASE_NAME}`); 78 | } 79 | 80 | mongoConnection = await connect(DATABASE_URL); 81 | db = mongoConnection.db(DATABASE_NAME); 82 | 83 | console.log(`DB connected = ${!!db}`); 84 | 85 | return !!db; 86 | 87 | } catch (err) { 88 | console.log('DB not connected - err'); 89 | console.log(err); 90 | } 91 | }; 92 | module.exports = { 93 | insertDocuments, 94 | findDocuments, 95 | removeDocuments, 96 | ObjectId, 97 | connectToDatabase 98 | }; -------------------------------------------------------------------------------- /database/mongodb/index_mongoose.js: -------------------------------------------------------------------------------- 1 | // get mongoose SDK 2 | const mongoose = require("mongoose"); 3 | 4 | const run = async () => { 5 | // connect to mongoose 6 | await mongoose.connect( 7 | "YOUR_RESOURCE_PRIMARY_CONNECTION_STRING", 8 | { 9 | useNewUrlParser: true, 10 | useUnifiedTopology: true, 11 | useFindAndModify: false, 12 | useCreateIndex: true, 13 | } 14 | ); 15 | 16 | // define a schema 17 | const Schema = mongoose.Schema; 18 | const ObjectId = Schema.ObjectId; 19 | 20 | const JobSchema = new Schema({ 21 | id: ObjectId, 22 | name: String, 23 | job: String, 24 | }); 25 | 26 | // Create model for database collection `Job` 27 | const JobModel = mongoose.model("Job", JobSchema); 28 | 29 | // Add data to doc and save 30 | const doc1 = new JobModel(); 31 | doc1.name = "Joan Smith"; 32 | doc1.job = "Developer"; 33 | await doc1.save(); 34 | 35 | const doc2 = new JobModel(); 36 | doc2.name = "Bob Jones"; 37 | doc2.job = "Quality Assurance"; 38 | await doc2.save(); 39 | 40 | const doc3 = new JobModel(); 41 | doc3.name = "Michelle Roberts"; 42 | doc3.job = "Program Manager"; 43 | await doc3.save(); 44 | 45 | // find all docs in collection 46 | console.log("find all"); 47 | const jobs = await JobModel.find({}); 48 | 49 | //iterate over docs 50 | for (var job of jobs) { 51 | console.log(`loop ` + JSON.stringify(job)); 52 | } 53 | 54 | // close connection 55 | mongoose.connection.close(); 56 | 57 | return "succeeded"; 58 | }; 59 | 60 | run() 61 | .then((result) => { 62 | console.log(result); 63 | }) 64 | .catch((err) => { 65 | console.log(err); 66 | }); 67 | -------------------------------------------------------------------------------- /database/mongodb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mongodb", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "bulk_insert.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "csv-parser": "^3.0.0", 13 | "dotenv": "^8.2.0", 14 | "mongodb": "^3.6.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /database/mongodb/readme.md: -------------------------------------------------------------------------------- 1 | # MongoDB on Cosmos DB 2 | 3 | Code found here is used as part of the Azure JavaScript developer center. 4 | 5 | ## Bulk insert 6 | 7 | The bulk insert is derived from this Cognitive Search [blog post](https://devblogs.microsoft.com/azure-sdk/search-app-with-cognitive-search/) and [source code](https://github.com/dereklegenzoff/azure-search-react-template). This script replaces the blog's python bulk insert script. The bulk inserts use a `.csv` file found in the [zygmuntz 8 | / 9 | goodbooks-10k](https://github.com/zygmuntz/goodbooks-10k) dataset. -------------------------------------------------------------------------------- /database/mssql/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Create Azure SQL with sample database to use this query 4 | 5 | */ 6 | 7 | const sql = require('mssql') 8 | 9 | const connectionString = "Driver={ODBC Driver 18 for SQL Server};Server=tcp:YOUR-SERVER.database.windows.net,1433;Database=YOUR-DATABASE;Uid=YOUR-USER;Pwd=YOUR-PASSWORD;Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;Authentication=ActiveDirectoryPassword"; 10 | 11 | const main = async() =>{ 12 | await sql.connect(connectionString) 13 | const results = await sql.query`SELECT TOP (10) * FROM [SalesLT].[Product]` 14 | console.dir(results.recordsets[0]) 15 | 16 | } 17 | main().then(()=>console.log('done')).catch((err)=> console.log(err)) 18 | -------------------------------------------------------------------------------- /database/mysql/index.js: -------------------------------------------------------------------------------- 1 | // To install npm package, 2 | // run following command at terminal 3 | // npm install promise-mysql 4 | 5 | // get MySQL SDK 6 | const MySQL = require('promise-mysql'); 7 | 8 | // query server and close connection 9 | const query = async (config) => { 10 | // creation connection 11 | const connection = await MySQL.createConnection(config); 12 | 13 | // show databases on server 14 | const databases = await connection.query('SHOW DATABASES;'); 15 | console.log(databases); 16 | 17 | // show tables in the mysql database 18 | const tables = await connection.query('SHOW TABLES FROM mysql;'); 19 | console.log(tables); 20 | 21 | // show users configured for the server 22 | const rows = await connection.query('select User from mysql.user;'); 23 | console.log(rows); 24 | 25 | // close connection 26 | connection.end(); 27 | }; 28 | 29 | const config = { 30 | host: 'YOUR-RESOURCE_NAME.mysql.database.azure.com', 31 | user: 'YOUR-ADMIN-NAME@YOUR-RESOURCE_NAME', 32 | password: 'YOUR-ADMIN-PASSWORD', 33 | port: 3306, 34 | }; 35 | 36 | query(config) 37 | .then(() => console.log('done')) 38 | .catch((err) => console.log(err)); -------------------------------------------------------------------------------- /database/mysql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mysql", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "promise-mysql": "^5.0.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /database/postgresql/index.js: -------------------------------------------------------------------------------- 1 | const { Client } = require('pg') 2 | 3 | const query = async (connectionString) => { 4 | 5 | // create connection 6 | const connection = new Client(connectionString); 7 | connection.connect(); 8 | 9 | // show tables in the postgres database 10 | const tables = await connection.query('SELECT table_name FROM information_schema.tables where table_type=\'BASE TABLE\';'); 11 | console.log(tables.rows); 12 | 13 | // show users configured for the server 14 | const users = await connection.query('select pg_user.usename FROM pg_catalog.pg_user;'); 15 | console.log(users.rows); 16 | 17 | // close connection 18 | connection.end(); 19 | } 20 | 21 | const server='YOURRESOURCENAME'; 22 | const user='YOUR-ADMIN-USER'; 23 | const password='YOUR-PASSWORD'; 24 | const database='postgres'; 25 | 26 | const connectionString = `postgres://${user}@${server}:${password}@${server}.postgres.database.azure.com:5432/${database}`; 27 | 28 | query(connectionString) 29 | .then(() => console.log('done')) 30 | .catch((err) => console.log(err)); -------------------------------------------------------------------------------- /database/postgresql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgresql", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "pg": "^8.6.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /database/redis/.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node 3 | { 4 | "name": "Node.js", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/javascript-node:0-18" 7 | 8 | // Features to add to the dev container. More info: https://containers.dev/features. 9 | // "features": {}, 10 | 11 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 12 | // "forwardPorts": [], 13 | 14 | // Use 'postCreateCommand' to run commands after the container is created. 15 | // "postCreateCommand": "yarn install", 16 | 17 | // Configure tool-specific properties. 18 | // "customizations": {}, 19 | 20 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 21 | // "remoteUser": "root" 22 | } 23 | -------------------------------------------------------------------------------- /database/redis/.env.sample: -------------------------------------------------------------------------------- 1 | AZURE_CACHE_FOR_REDIS_RESOURCE_NAME= 2 | AZURE_CACHE_FOR_REDIS_RESOURCE_KEY= -------------------------------------------------------------------------------- /database/redis/MOCK_DATA.csv: -------------------------------------------------------------------------------- 1 | id,first_name,last_name,email,gender,ip_address 2 | 1,Rodrigo,Lock,rlock0@eventbrite.com,Agender,73.93.61.37 3 | 2,Nikos,Gutierrez,ngutierrez1@yahoo.com,Genderfluid,213.54.40.210 4 | 3,Eada,Sotham,esotham2@yellowpages.com,Bigender,28.236.183.89 5 | 4,Ana,Brazil,abrazil3@who.int,Polygender,142.30.140.225 6 | 5,Roman,Rohmer,rrohmer4@admin.ch,Genderqueer,235.197.52.85 7 | 6,Elyn,Sute,esute5@ftc.gov,Genderqueer,171.151.109.188 8 | 7,Omero,Childers,ochilders6@stanford.edu,Bigender,133.21.192.66 9 | 8,Stephana,Pipet,spipet7@parallels.com,Genderfluid,177.48.129.213 10 | 9,Anthiathia,Ulster,aulster8@weebly.com,Genderfluid,175.1.59.106 11 | 10,Yard,Pyson,ypyson9@jalbum.net,Non-binary,0.8.135.151 12 | -------------------------------------------------------------------------------- /database/redis/bulk_insert.js: -------------------------------------------------------------------------------- 1 | const Redis = require('ioredis'); 2 | const fs = require('fs'); 3 | const parse = require('csv-parser') 4 | const { finished } = require('stream/promises'); 5 | const { config } = require('./config') 6 | 7 | // Create Redis config object 8 | const configuration = { 9 | host: config.HOST, 10 | port: 6380, 11 | password: config.KEY, 12 | tls: { 13 | servername: config.HOST 14 | }, 15 | database: 0, 16 | keyPrefix: config.KEY_PREFIX 17 | } 18 | var redis = new Redis(configuration); 19 | 20 | // insert each row into Redis 21 | async function insertData(readable) { 22 | for await (const row of readable) { 23 | await redis.set(`bar2:${row.id}`, JSON.stringify(row)) 24 | } 25 | } 26 | 27 | /* 28 | id,first_name,last_name,email,gender,ip_address 29 | 1,Rodrigo,Lock,rlock0@eventbrite.com,Agender,73.93.61.37 30 | 2,Nikos,Gutierrez,ngutierrez1@yahoo.com,Genderfluid,213.54.40.210 31 | 3,Eada,Sotham,esotham2@yellowpages.com,Bigender,28.236.183.89 32 | 4,Ana,Brazil,abrazil3@who.int,Polygender,142.30.140.225 33 | 5,Roman,Rohmer,rrohmer4@admin.ch,Genderqueer,235.197.52.85 34 | 6,Elyn,Sute,esute5@ftc.gov,Genderqueer,171.151.109.188 35 | 7,Omero,Childers,ochilders6@stanford.edu,Bigender,133.21.192.66 36 | 8,Stephana,Pipet,spipet7@parallels.com,Genderfluid,177.48.129.213 37 | 9,Anthiathia,Ulster,aulster8@weebly.com,Genderfluid,175.1.59.106 38 | 10,Yard,Pyson,ypyson9@jalbum.net,Non-binary,0.8.135.151 39 | */ 40 | 41 | // read file, parse CSV, each row is a chunck 42 | const readable = fs 43 | .createReadStream('./MOCK_DATA.csv') 44 | .pipe(parse()); 45 | 46 | // Pipe rows to insert function 47 | insertData(readable) 48 | .then(() => { 49 | console.log('succeeded'); 50 | redis.disconnect(); 51 | }) 52 | .catch(console.error); 53 | -------------------------------------------------------------------------------- /database/redis/config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | const resourceName = process.env.AZURE_CACHE_FOR_REDIS_RESOURCE_NAME 4 | const resourceKey = process.env.AZURE_CACHE_FOR_REDIS_RESOURCE_KEY 5 | 6 | const config = { 7 | "HOST": `${resourceName}.redis.cache.windows.net`, 8 | "KEY": `${resourceKey}`, 9 | "TIMEOUT": 300, 10 | "KEY_PREFIX": "demoExample:" 11 | } 12 | 13 | module.exports = {config} -------------------------------------------------------------------------------- /database/redis/get-set.js: -------------------------------------------------------------------------------- 1 | const redis = require('ioredis'); 2 | const { config } = require('./config') 3 | 4 | // Create Redis config object 5 | const configuration = { 6 | host: config.HOST, 7 | port: 6380, 8 | password: config.KEY, 9 | timeout: config.TIMEOUT, 10 | tls: { 11 | servername: config.HOST 12 | }, 13 | database: 0, 14 | keyPrefix: config.KEY_PREFIX 15 | } 16 | 17 | const connect = () => { 18 | return redis.createClient(configuration); 19 | } 20 | 21 | const set = async (client, key, expiresInSeconds=configuration.timeout, stringify=true, data) => { 22 | return await client.setex(key, expiresInSeconds, stringify? JSON.stringify(data): data); 23 | } 24 | 25 | const get = async (client, key, stringParse=true) => { 26 | const value = await client.get(key); 27 | return stringParse ? JSON.parse(value) : value; 28 | } 29 | 30 | const remove = async (client, key) => { 31 | return await client.del(key); 32 | } 33 | 34 | const disconnect = (client) => { 35 | client.disconnect(); 36 | } 37 | 38 | const test = async () => { 39 | 40 | // connect 41 | const dbConnection = await connect(); 42 | 43 | // set 44 | const setResult1 = await set(dbConnection, "r1", "1000000", false, "record 1"); 45 | const setResult2 = await set(dbConnection, "r2", "1000000", false, "record 2"); 46 | const setResult3 = await set(dbConnection, "r3", "1000000", false, "record 3"); 47 | 48 | // get 49 | const val2 = await get(dbConnection, "r2", false); 50 | console.log(val2); 51 | 52 | // delete 53 | const remove2 = await remove(dbConnection, "r2"); 54 | 55 | // get again = won't be there 56 | const val2Again = await get(dbConnection, "r2", false); 57 | console.log(val2Again); 58 | 59 | // done 60 | disconnect(dbConnection) 61 | } 62 | 63 | test() 64 | .then(() => console.log("done")) 65 | .catch(err => console.log(err)) 66 | -------------------------------------------------------------------------------- /database/redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azure-cache-for-redis", 3 | "version": "1.0.0", 4 | "description": "JavaScript to Azure Cache for Redis", 5 | "main": "index.js", 6 | "scripts": { 7 | "start:ui": "redis-commander --redis-password --redis-port 6379 --redis-host gh-redis-cache.redis.cache.windows.net --redis-tls" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "csv-parser": "^3.0.0", 13 | "dotenv": "^16.0.3", 14 | "ioredis": "^4.22.0", 15 | "redis": "^4.6.4" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /database/redis/quickstart.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | const { config } = require('./config') 3 | 4 | async function testCache() { 5 | 6 | // Connect to the Azure Cache for Redis over the TLS port using the key. 7 | var cacheHostName = config.HOST; 8 | var cachePassword = config.KEY; 9 | var cacheConnection = redis.createClient({ 10 | // rediss for TLS 11 | url: "rediss://" + cacheHostName + ":6380", 12 | password: cachePassword, 13 | }); 14 | await cacheConnection.connect(); 15 | 16 | // Perform cache operations using the cache connection object... 17 | 18 | // Simple PING command 19 | console.log("\nCache command: PING"); 20 | console.log("Cache response : " + await cacheConnection.ping()); 21 | 22 | // Simple get and put of integral data types into the cache 23 | console.log("\nCache command: GET Message"); 24 | console.log("Cache response : " + await cacheConnection.get("Message")); 25 | 26 | console.log("\nCache command: SET Message"); 27 | console.log("Cache response : " + await cacheConnection.set("Message", 28 | "Hello! The cache is working from Node.js!")); 29 | 30 | // Demonstrate "SET Message" executed as expected... 31 | console.log("\nCache command: GET Message"); 32 | console.log("Cache response : " + await cacheConnection.get("Message")); 33 | 34 | // Get the client list, useful to see if connection list is growing... 35 | console.log("\nCache command: CLIENT LIST"); 36 | console.log("Cache response : " + await cacheConnection.sendCommand(["CLIENT", "LIST"])); 37 | 38 | console.log("\nDone"); 39 | process.exit(); 40 | } 41 | 42 | testCache(); -------------------------------------------------------------------------------- /database/redis/readme.md: -------------------------------------------------------------------------------- 1 | # Azure Cache for Redis sample code for JavaScript 2 | 3 | ## Create Azure Cache for Redis resource 4 | 5 | * [Azure portal](https://ms.portal.azure.com/#create/Microsoft.Cache) 6 | 7 | ## First party resources 8 | 9 | * [Azure Cache for Redis docs](https://docs.microsoft.com/en-us/azure/azure-cache-for-redis/) 10 | * JavaScript sample code 11 | * [Bulk insert](bulk_insert.js) 12 | * [Set key, get key, delete key](get-set.js) 13 | 14 | ## 3rd party resources 15 | 16 | The following 3rd party samples and resources may be helpful: 17 | 18 | * Express/Redis [example](https://github.com/aenesgur/Nodejs-RedisCache_MovieApp) 19 | * Express CLI [cheatsheet](https://gist.github.com/LeCoupa/1596b8f359ad8812c7271b5322c30946) 20 | * [Node/Redis series](https://medium.com/@stockholmux/the-node-redis-series-e812085c917f) 21 | -------------------------------------------------------------------------------- /database/sql/.env.sample: -------------------------------------------------------------------------------- 1 | COSMOS_DB_ENDPOINT= 2 | COSMOS_DB_KEY= -------------------------------------------------------------------------------- /database/sql/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\${file}" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /database/sql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sql", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node index.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@azure/cosmos": "^3.17.0", 16 | "dotenv": "^16.0.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /database/sql/quickstart.js: -------------------------------------------------------------------------------- 1 | // Get environment variables from .env 2 | import * as dotenv from 'dotenv'; 3 | dotenv.config(); 4 | 5 | // Get Cosmos Client 6 | import { CosmosClient } from "@azure/cosmos"; 7 | 8 | // Provide required connection from environment variables 9 | const key = process.env.COSMOS_KEY; 10 | // Endpoint format: https://YOUR-RESOURCE-NAME.documents.azure.com:443/ 11 | const endpoint = process.env.COSMOS_ENDPOINT; 12 | 13 | // Uniqueness for database and container 14 | const timeStamp = + new Date(); 15 | 16 | // Set Database name and container name with unique timestamp 17 | const databaseName = `contoso_${timeStamp}`; 18 | const containerName = `products_${timeStamp}`; 19 | const partitionKeyPath = ["/categoryName"]; 20 | 21 | // Authenticate to Azure Cosmos DB 22 | const cosmosClient = new CosmosClient({ endpoint, key }); 23 | 24 | // Create database if it doesn't exist 25 | const { database } = await cosmosClient.databases.createIfNotExists({ id: databaseName }); 26 | console.log(`${database.id} database ready`); 27 | 28 | // Create container if it doesn't exist 29 | const { container } = await database.containers.createIfNotExists({ 30 | id: containerName, 31 | partitionKey: { 32 | paths: partitionKeyPath 33 | } 34 | }); 35 | console.log(`${container.id} container ready`); 36 | 37 | // Data items 38 | const items = [ 39 | { 40 | "id": "08225A9E-F2B3-4FA3-AB08-8C70ADD6C3C2", 41 | "categoryId": "75BF1ACB-168D-469C-9AA3-1FD26BB4EA4C", 42 | "categoryName": "Bikes, Touring Bikes", 43 | "sku": "BK-T79U-50", 44 | "name": "Touring-1000 Blue, 50", 45 | "description": "The product called \"Touring-1000 Blue, 50\"", 46 | "price": 2384.0700000000002, 47 | "tags": [ 48 | { 49 | "_id": "27B7F8D5-1009-45B8-88F5-41008A0F0393", 50 | "name": "Tag-61" 51 | } 52 | ] 53 | }, 54 | { 55 | "id": "2C981511-AC73-4A65-9DA3-A0577E386394", 56 | "categoryId": "75BF1ACB-168D-469C-9AA3-1FD26BB4EA4C", 57 | "categoryName": "Bikes, Touring Bikes", 58 | "sku": "BK-T79U-46", 59 | "name": "Touring-1000 Blue, 46", 60 | "description": "The product called \"Touring-1000 Blue, 46\"", 61 | "price": 2384.0700000000002, 62 | "tags": [ 63 | { 64 | "_id": "4E102F3F-7D57-4CD7-88F4-AC5076A42C59", 65 | "name": "Tag-91" 66 | } 67 | ] 68 | }, 69 | { 70 | "id": "0F124781-C991-48A9-ACF2-249771D44029", 71 | "categoryId": "56400CF3-446D-4C3F-B9B2-68286DA3BB99", 72 | "categoryName": "Bikes, Mountain Bikes", 73 | "sku": "BK-M68B-42", 74 | "name": "Mountain-200 Black, 42", 75 | "description": "The product called \"Mountain-200 Black, 42\"", 76 | "price": 2294.9899999999998, 77 | "tags": [ 78 | { 79 | "_id": "4F67013C-3B5E-4A3D-B4B0-8C597A491EB6", 80 | "name": "Tag-82" 81 | } 82 | ] 83 | } 84 | ]; 85 | 86 | // Create all items 87 | for (const item of items) { 88 | 89 | const { resource } = await container.items.create(item); 90 | console.log(`'${resource.name}' inserted`); 91 | } 92 | 93 | // Read item by id and partitionKey - least expensive `find` 94 | const { resource } = await container.item(items[0].id, items[0].categoryName).read(); 95 | console.log(`${resource.name} read`); 96 | 97 | // Query by SQL - more expensive `find` 98 | // find all items with same categoryName (partitionKey) 99 | const querySpec = { 100 | query: "select * from products p where p.categoryName=@categoryName", 101 | parameters: [ 102 | { 103 | name: "@categoryName", 104 | value: items[2].categoryName 105 | } 106 | ] 107 | }; 108 | 109 | // Get iterator for query 110 | const queryIterator = container.items.query(querySpec); 111 | 112 | let count = 0; 113 | 114 | // Artificially low value for quickstart 115 | const pageSize = 10; 116 | 117 | // Get pages 118 | while (queryIterator.hasMoreResults() && count <= pageSize) { 119 | 120 | // Get items in page 121 | const { resources: items } = await queryIterator.fetchNext(); 122 | 123 | // loop through items in page 124 | for(let item of items){ 125 | console.log(`${item.id}: ${item.name}, ${item.sku}`); 126 | } 127 | } 128 | 129 | // Delete item 130 | const { statusCode } = await container.item(items[2].id, items[2].categoryName).delete(); 131 | console.log(`${items[2].id} ${statusCode==204 ? `Item deleted` : `Item not deleted`}`); 132 | 133 | -------------------------------------------------------------------------------- /docs/templates/end-to-end-scenario.md: -------------------------------------------------------------------------------- 1 | 14 | 15 | # E2E scenario template 16 | 17 | This **enter a name for scenario** implements ... in order to solve ... 18 | 19 | ## Use Case 20 | 21 | This scenario was designed to solve the problem of ... 22 | 23 | This scenario is intended to be implemented by (finish with roles such as Dev Manager, FE/BE/FS engineer, QA engineer, etc). 24 | 25 | ## Architecture 26 | 27 | This architecture shows how to implement ... 28 | 29 | Include an architecture diagram 30 | 31 | The list of Azure resources includes: 32 | 33 | The list of tools to implement this scenario includes: 34 | 35 | What cloud planes are involved and how: 36 | * Management plane 37 | * Control plane 38 | * Data plane 39 | 40 | Some SDKs combine control and data plane together. If you know the scenario uses an SDK that does this, please state that. 41 | 42 | ## Implementation summary 43 | 44 | This implementation can be broken down into the following groups of tasks: 45 | 46 | * Group 1 47 | * Task 1 48 | * List of files, tools, websites that need to be shown/explained to understand the steps 49 | * Task 2 50 | * Task 3 51 | * Group 2 52 | * Group 3 53 | 54 | Link to any videos that would show procedures/steps to complete 55 | 56 | ## Implementation steps 57 | 58 | Each group or task needs to be broken down into steps you expect a customer to complete. Don't focus on grammar, spelling. Focus on the tool used and steps. If the tool isn't common, you may want to add a few more steps. 59 | 60 | Please be aware that the resources and configuration of this scenario needs to be automated in order for future-proofing of the scenario. For example, if a bug comes in against the scenario in a year, and someone new to the team has to verify and fix the content, this script helps them spend time on the issue and not the steps to spin up the scenario. 61 | 62 | ## What was your development environment when you engineered this scenario? 63 | 64 | I ran through this procedure on a (remove whatever doesn't apply - no expectation that you did this on all options): 65 | 66 | * Win 67 | * Mac 68 | * Linux 69 | * container 70 | * VM 71 | 72 | You generally interact with the environment with: 73 | 74 | * keyboard short cuts - please use those when describing a task - just once - not exhaustively 75 | * Command palatte in VSCode 76 | * Bash commands that you developed to short-circuit longer commands 77 | * etc 78 | 79 | If the person writing up the steps doesn't understand how to get from A to C because your B was not obvious - that is what I'm looking for here. 80 | 81 | ## Security/Identity considerations 82 | 83 | Any information about what is provided for security/identity and what is not. 84 | 85 | * RBAC 86 | * Easy auth 87 | * MSAL 88 | * VNet 89 | * Firewalls 90 | * CORS 91 | * AAD apps 92 | * 3rd party auth (GitHub, Stripe, etc) 93 | 94 | ## Pipeline/automation considerations 95 | 96 | What automation is used to complete this scenario? 97 | 98 | * Resource management scripts (Az CLI, AZD cli, SWA CLI, Func CLI, PowerShell) 99 | * GitHub Actions, Azure DevOps Pipelines 100 | * AAD automation 101 | * Custom or 3rd party automation 102 | * GH CLI 103 | * Stripe CLI 104 | * Mongo CLI 105 | * Any `hidden` or poorly known areas such as https://resources.azure.com - explain why you needed it so that can translate into content. 106 | 107 | All automation must include cleanup/teardown scripts 108 | 109 | ## Verify scenario 110 | 111 | This scenario must have steps so that a customer can know they have completed it successfully, so an end state that can be validated. 112 | 113 | Examples could include: 114 | 115 | * an API response 116 | * a web site image 117 | * a set of test files generated as part of playwright 118 | 119 | ## Troubleshooting 120 | 121 | What issues did you come across that the customer may also hit? 122 | 123 | Examples include: 124 | 125 | * You forgot a config step and that resulted in an error in a certain task 126 | * The SDK or CLI returned an confusing error. 127 | * The SDK reference docs example would have been more helpful it it included ... please include exact link to ref doc. 128 | * The pricing tier (free or basic) didn't support your scenario 129 | * The portal or UI was confusing and would have been more helpful if the button/textbox/etc was boxed and had an error. 130 | 131 | ## What surprised you about the scenario? 132 | 133 | It was hard, easy, you loved the code but hated the .... 134 | 135 | Have an opinion that I could invoke with a tone or point where a customer needs to be more careful at certain steps. 136 | 137 | ## What terminology restrictions should be used? 138 | 139 | If there are specific constraints on terminology dos/don'ts, please call them out. 140 | 141 | ## References 142 | 143 | What websites did you use to complete this work? What were you looking for when you went to these websites? I usually keep this as a running list in my dev diary. 144 | 145 | They don't have to be microsoft properties, but it would be helpful to know if/when MS content was incomplete and how it was incomplete. 146 | -------------------------------------------------------------------------------- /events/event-hubs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "event-hubs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "send.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@azure/event-hubs": "^5.8.0", 13 | "@azure/eventhubs-checkpointstore-blob": "^1.0.1", 14 | "@azure/identity": "^3.1.2", 15 | "@azure/storage-blob": "^12.12.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /events/event-hubs/receive-passwordless.js: -------------------------------------------------------------------------------- 1 | const { DefaultAzureCredential } = require("@azure/identity"); 2 | const { EventHubConsumerClient, earliestEventPosition } = require("@azure/event-hubs"); 3 | const { ContainerClient } = require("@azure/storage-blob"); 4 | const { BlobCheckpointStore } = require("@azure/eventhubs-checkpointstore-blob"); 5 | 6 | // Event hubs 7 | const eventHubsResourceName = "YOUR EVENT HUBS RESOURCE NAME"; 8 | const fullyQualifiedNamespace = `${eventHubsResourceName}.servicebus.windows.net`; 9 | const eventHubName = "YOUR EVENT HUB NAME"; 10 | const consumerGroup = "$Default"; // name of the default consumer group 11 | 12 | // Azure Storage 13 | const storageAccountName = "YOUR STORAGE ACCOUNT NAME"; 14 | const storageContainerName = "YOUR BLOB CONTAINER NAME"; 15 | const baseUrl = `https://${storageAccountName}.blob.core.windows.net`; 16 | 17 | // Azure Identity - passwordless authentication 18 | const credential = new DefaultAzureCredential(); 19 | 20 | async function main() { 21 | 22 | // Create a blob container client and a blob checkpoint store using the client. 23 | const containerClient = new ContainerClient( 24 | `${baseUrl}/${storageContainerName}`, 25 | credential 26 | ); 27 | const checkpointStore = new BlobCheckpointStore(containerClient); 28 | 29 | // Create a consumer client for the event hub by specifying the checkpoint store. 30 | const consumerClient = new EventHubConsumerClient(consumerGroup, fullyQualifiedNamespace, eventHubName, credential, checkpointStore); 31 | 32 | // Subscribe to the events, and specify handlers for processing the events and errors. 33 | const subscription = consumerClient.subscribe({ 34 | processEvents: async (events, context) => { 35 | if (events.length === 0) { 36 | console.log(`No events received within wait time. Waiting for next interval`); 37 | return; 38 | } 39 | 40 | for (const event of events) { 41 | console.log(`Received event: '${event.body}' from partition: '${context.partitionId}' and consumer group: '${context.consumerGroup}'`); 42 | } 43 | // Update the checkpoint. 44 | await context.updateCheckpoint(events[events.length - 1]); 45 | }, 46 | 47 | processError: async (err, context) => { 48 | console.log(`Error : ${err}`); 49 | } 50 | }, 51 | { startPosition: earliestEventPosition } 52 | ); 53 | 54 | // After 30 seconds, stop processing. 55 | await new Promise((resolve) => { 56 | setTimeout(async () => { 57 | await subscription.close(); 58 | await consumerClient.close(); 59 | resolve(); 60 | }, 30000); 61 | }); 62 | } 63 | 64 | main().catch((err) => { 65 | console.log("Error occurred: ", err); 66 | }); -------------------------------------------------------------------------------- /events/event-hubs/receive.js: -------------------------------------------------------------------------------- 1 | const { EventHubConsumerClient, earliestEventPosition } = require("@azure/event-hubs"); 2 | const { ContainerClient } = require("@azure/storage-blob"); 3 | const { BlobCheckpointStore } = require("@azure/eventhubs-checkpointstore-blob"); 4 | 5 | const connectionString = "EVENT HUBS NAMESPACE CONNECTION STRING"; 6 | const eventHubName = "YOUR EVENT HUB NAME"; 7 | const consumerGroup = "$Default"; // name of the default consumer group 8 | const storageConnectionString = "AZURE STORAGE CONNECTION STRING"; 9 | const containerName = "BLOB CONTAINER NAME"; 10 | 11 | async function main() { 12 | // Create a blob container client and a blob checkpoint store using the client. 13 | const containerClient = new ContainerClient(storageConnectionString, containerName); 14 | const checkpointStore = new BlobCheckpointStore(containerClient); 15 | 16 | // Create a consumer client for the event hub by specifying the checkpoint store. 17 | const consumerClient = new EventHubConsumerClient(consumerGroup, connectionString, eventHubName, checkpointStore); 18 | 19 | // Subscribe to the events, and specify handlers for processing the events and errors. 20 | const subscription = consumerClient.subscribe({ 21 | processEvents: async (events, context) => { 22 | if (events.length === 0) { 23 | console.log(`No events received within wait time. Waiting for next interval`); 24 | return; 25 | } 26 | 27 | for (const event of events) { 28 | console.log(`Received event: '${event.body}' from partition: '${context.partitionId}' and consumer group: '${context.consumerGroup}'`); 29 | } 30 | // Update the checkpoint. 31 | await context.updateCheckpoint(events[events.length - 1]); 32 | }, 33 | 34 | processError: async (err, context) => { 35 | console.log(`Error : ${err}`); 36 | } 37 | }, 38 | { startPosition: earliestEventPosition } 39 | ); 40 | 41 | // After 30 seconds, stop processing. 42 | await new Promise((resolve) => { 43 | setTimeout(async () => { 44 | await subscription.close(); 45 | await consumerClient.close(); 46 | resolve(); 47 | }, 30000); 48 | }); 49 | } 50 | 51 | main().catch((err) => { 52 | console.log("Error occurred: ", err); 53 | }); -------------------------------------------------------------------------------- /events/event-hubs/send-passwordless.js: -------------------------------------------------------------------------------- 1 | const { EventHubProducerClient } = require("@azure/event-hubs"); 2 | const { DefaultAzureCredential } = require("@azure/identity"); 3 | 4 | // Event hubs 5 | const eventHubsResourceName = "YOUR EVENT HUBS RESOURCE NAME"; 6 | const fullyQualifiedNamespace = `${eventHubsResourceName}.servicebus.windows.net`; 7 | const eventHubName = "YOUR EVENT HUB NAME"; 8 | 9 | // Azure Identity - passwordless authentication 10 | const credential = new DefaultAzureCredential(); 11 | 12 | async function main() { 13 | 14 | // Create a producer client to send messages to the event hub. 15 | const producer = new EventHubProducerClient(fullyQualifiedNamespace, eventHubName, credential); 16 | 17 | // Prepare a batch of three events. 18 | const batch = await producer.createBatch(); 19 | batch.tryAdd({ body: "passwordless First event" }); 20 | batch.tryAdd({ body: "passwordless Second event" }); 21 | batch.tryAdd({ body: "passwordless Third event" }); 22 | 23 | // Send the batch to the event hub. 24 | await producer.sendBatch(batch); 25 | 26 | // Close the producer client. 27 | await producer.close(); 28 | 29 | console.log("A batch of three events have been sent to the event hub"); 30 | } 31 | 32 | main().catch((err) => { 33 | console.log("Error occurred: ", err); 34 | }); -------------------------------------------------------------------------------- /events/event-hubs/send.js: -------------------------------------------------------------------------------- 1 | const { EventHubProducerClient } = require("@azure/event-hubs"); 2 | 3 | const connectionString = "EVENT HUBS NAMESPACE CONNECTION STRING"; 4 | const eventHubName = "EVENT HUB NAME"; 5 | 6 | async function main() { 7 | 8 | // Create a producer client to send messages to the event hub. 9 | const producer = new EventHubProducerClient(connectionString, eventHubName); 10 | 11 | // Prepare a batch of three events. 12 | const batch = await producer.createBatch(); 13 | batch.tryAdd({ body: "First event" }); 14 | batch.tryAdd({ body: "Second event" }); 15 | batch.tryAdd({ body: "Third event" }); 16 | 17 | // Send the batch to the event hub. 18 | await producer.sendBatch(batch); 19 | 20 | // Close the producer client. 21 | await producer.close(); 22 | 23 | console.log("A batch of three events have been sent to the event hub"); 24 | } 25 | 26 | main().catch((err) => { 27 | console.log("Error occurred: ", err); 28 | }); -------------------------------------------------------------------------------- /graph/my-profile-from-rest-api.js: -------------------------------------------------------------------------------- 1 | // package.json - type: "module" 2 | 3 | import axios from 'axios'; 4 | 5 | 6 | // https://developer.microsoft.com/en-us/graph/graph-explorer 7 | // https://jwt.ms/ 8 | 9 | 10 | 11 | const main = async (accessToken) => { 12 | 13 | 14 | try { 15 | 16 | const url = 'https://graph.microsoft.com/v1.0/me'; 17 | 18 | const options = { 19 | method: 'GET', 20 | headers: { 21 | Authorization: 'Bearer ' + accessToken, 22 | 'Content-type': 'application/json', 23 | }, 24 | }; 25 | 26 | const graphResponse = await axios.get(url, options); 27 | 28 | const { data } = await graphResponse; 29 | return data; 30 | 31 | } catch (err) { 32 | throw err; 33 | } 34 | } 35 | 36 | const accessToken = "... replace with your access token ..."; 37 | 38 | main(accessToken).then((userData)=>{ 39 | console.log(userData); 40 | }).catch((err)=>{ 41 | console.log(err); 42 | }) 43 | -------------------------------------------------------------------------------- /graph/my-profile-from-sdk.js: -------------------------------------------------------------------------------- 1 | // package.json - type: "module" 2 | 3 | import graph from '@microsoft/microsoft-graph-client'; 4 | import 'isomorphic-fetch'; 5 | 6 | const getAuthenticatedClient = (accessToken) => { 7 | // Initialize Graph client 8 | const client = graph.Client.init({ 9 | // Use the provided access token to authenticate requests 10 | authProvider: (done) => { 11 | done(null, accessToken); 12 | } 13 | }); 14 | 15 | return client; 16 | } 17 | 18 | // https://developer.microsoft.com/en-us/graph/graph-explorer 19 | // https://jwt.ms/ 20 | // https://github.com/Azure-Samples/ms-identity-easyauth-nodejs-storage-graphapi/blob/main/2-WebApp-graphapi-on-behalf/controllers/graphController.js 21 | 22 | const main = async (accessToken) => { 23 | 24 | 25 | try { 26 | const graphClient = getAuthenticatedClient(accessToken); 27 | 28 | const profile = await graphClient 29 | .api('/me') 30 | .get(); 31 | 32 | return profile; 33 | 34 | } catch (err) { 35 | throw err; 36 | } 37 | } 38 | 39 | const accessToken = "... replace with your access token ..."; 40 | 41 | main(accessToken).then((userData)=>{ 42 | console.log(userData); 43 | }).catch((err)=>{ 44 | console.log(err); 45 | }) 46 | -------------------------------------------------------------------------------- /keyvault/azure-keyvault-secrets.js: -------------------------------------------------------------------------------- 1 | const { DefaultAzureCredential } = require("@azure/identity"); 2 | const { SecretClient } = require("@azure/keyvault-secrets"); 3 | 4 | const getSecret = async (secretName, keyVaultName) => { 5 | 6 | if (!secretName || !keyVaultName) { 7 | throw Error("getSecret: Required params missing") 8 | } 9 | 10 | /* 11 | * 12 | * 1. Create a resource group for all Azure resources in app 13 | * or project - skip if you have already completed for 14 | * this sample app 15 | * 16 | * 2. Create an Azure Key Vault resource in Azure CLI 17 | * 18 | * az keyvault create \ 19 | * --subscription REPLACE-WITH-YOUR-SUBSCRIPTION-NAME-OR-ID \ 20 | * --resource-group REPLACE-WITH-YOUR-RESOURCE-GROUP-NAME \ 21 | * --name REPLACE-WITH-YOUR-KEY-VAULT-NAME 22 | * 23 | * 3. Create Service Principal (LOGICAL-APP-NAME) for new App 24 | * registration with the following Azure CLI command 25 | * in a bash terminal: 26 | * 27 | * az ad sp create-for-rbac \ 28 | * --name REPLACE-WITH-YOUR-NEW-APP-LOGICAL-NAME 29 | * --skip-assignment 30 | * 31 | * ServicePrincipalOutput = 32 | * 33 | * { 34 | * "appId": "123456", 35 | * "displayName": "REPLACE-WITH-YOUR-NEW-APP-LOGICAL-NAME", 36 | * "name": "http://REPLACE-WITH-YOUR-NEW-APP-LOGICAL-NAME", 37 | * "password": "!@#$%", 38 | * "tenant": "987654" 39 | * } 40 | * 41 | * 4. Set these environment variables to create the REQUIRED 42 | * context to use DefaultAzureCredential. 43 | * 44 | * AZURE_TENANT_ID: The `tenant` in the JSON response above. 45 | * AZURE_CLIENT_ID: The `appId` in the JSON response above. 46 | * AZURE_CLIENT_SECRET: The `password` in the JSON response above. 47 | * 48 | * 5. Give Service Principal (LOGICAL-APP-NAME) access to 49 | * Key Vault with Azure CLI command. The value for --spn is 50 | * your `appId`. 51 | * 52 | * az keyvault set-policy \ 53 | * --subscription REPLACE-WITH-YOUR-SUBSCRIPTION-NAME-OR-ID \ 54 | * --name "REPLACE-WITH-YOUR-KEY-VAULT-NAME" \ 55 | * --spn REPLACE-WITH-YOUR-SERVICE-PRINCIPAL-APP-ID \ 56 | * --secret-permissions get list 57 | * 58 | * 6. Add database connection string as secret named `DATABASEURL`. 59 | * 60 | * az keyvault secret set \ 61 | * --subscription REPLACE-WITH-YOUR-SUBSCRIPTION-NAME-OR-ID \ 62 | * --vault-name "REPLACE-WITH-YOUR-KEY-VAULT-NAME" \ 63 | * --name "DATABASEURL" \ 64 | * --value "mongodb://my-cosmos-mongodb" 65 | * 66 | * 7. Call the getSecret function as 67 | * 68 | * const { getSecret } = require("./azure/azure-keyvault"); 69 | * const KEY_VAULT_CONNECTION_STRING_SECRET_NAME = "DATABASEURL"; 70 | * const KEY_VAULT_NAME = "my-keyvault"; 71 | * let DATABASE_URL = await getSecret(KEY_VAULT_CONNECTION_STRING_SECRET_NAME, KEY_VAULT_NAME); 72 | */ 73 | 74 | if (!process.env.AZURE_TENANT_ID || 75 | !process.env.AZURE_CLIENT_ID || 76 | !process.env.AZURE_CLIENT_SECRET) { 77 | throw Error("KeyVault can't use DefaultAzureCredential"); 78 | } 79 | 80 | const credential = new DefaultAzureCredential(); 81 | 82 | // Build the URL to reach your key vault 83 | const url = `https://${keyVaultName}.vault.azure.net`; 84 | 85 | try { 86 | // Create client to connect to service 87 | const client = new SecretClient(url, credential); 88 | 89 | // Get secret Obj 90 | const latestSecret = await client.getSecret(secretName); 91 | 92 | // Return value 93 | return latestSecret.value; 94 | } catch (ex) { 95 | console.log(ex) 96 | throw ex; 97 | } 98 | } 99 | 100 | module.exports = { 101 | getSecret 102 | }; 103 | 104 | -------------------------------------------------------------------------------- /keyvault/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "keyvault", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/identity": "^1.2.5", 14 | "@azure/keyvault-secrets": "^4.1.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /resources/.env: -------------------------------------------------------------------------------- 1 | AZURE_TENANT_ID= 2 | AZURE_CLIENT_ID= 3 | AZURE_CLIENT_SECRET= 4 | AZURE_SUBSCRIPTION_ID= 5 | AZURE-RESOURCE-GROUP-NAME= 6 | EMAIL-ALIAS= 7 | APP-NAME= -------------------------------------------------------------------------------- /resources/authorization/list.js: -------------------------------------------------------------------------------- 1 | const { DefaultAzureCredential } = require("@azure/identity"); 2 | const { AuthorizationManagementClient } = require("@azure/arm-authorization"); 3 | const subscriptionId = process.env["AZURE_SUBSCRIPTION_ID"]; 4 | 5 | // Use `DefaultAzureCredential` or any other credential of your choice based on https://aka.ms/azsdk/js/identity/examples 6 | // Please note that you can also use credentials from the `@azure/ms-rest-nodeauth` package instead. 7 | const creds = new DefaultAzureCredential(); 8 | const client = new AuthorizationManagementClient(creds, subscriptionId); 9 | 10 | async function listOfSubscriptions() { 11 | const ListResult = new Array(); 12 | for await (const item of client.classicAdministrators.list()) { 13 | ListResult.push(item); 14 | } 15 | return ListResult; 16 | } 17 | 18 | listOfSubscriptions() 19 | .then((result) => { 20 | console.log("The result is:"); 21 | console.log(result); 22 | }) 23 | .catch((err) => { 24 | console.log("An error occurred:"); 25 | console.error(err); 26 | }); 27 | -------------------------------------------------------------------------------- /resources/authorization/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "authorization", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@azure/abort-controller": { 8 | "version": "1.0.4", 9 | "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.4.tgz", 10 | "integrity": "sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==", 11 | "requires": { 12 | "tslib": "^2.0.0" 13 | }, 14 | "dependencies": { 15 | "tslib": { 16 | "version": "2.3.1", 17 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", 18 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" 19 | } 20 | } 21 | }, 22 | "@azure/arm-authorization": { 23 | "version": "9.0.0-beta.1", 24 | "resolved": "https://registry.npmjs.org/@azure/arm-authorization/-/arm-authorization-9.0.0-beta.1.tgz", 25 | "integrity": "sha512-S3qzT28SFRnOoYFwydcbXHrN0Fc+aiETHe7pNIxA+sZOISmnYQWPIG4zEvsTOhpw1w4Quk3IcNHNSXxMRt91hA==", 26 | "requires": { 27 | "@azure/core-auth": "^1.1.4", 28 | "@azure/ms-rest-azure-js": "^2.1.0", 29 | "@azure/ms-rest-js": "^2.2.0", 30 | "tslib": "^1.9.3" 31 | } 32 | }, 33 | "@azure/core-auth": { 34 | "version": "1.3.2", 35 | "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.3.2.tgz", 36 | "integrity": "sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA==", 37 | "requires": { 38 | "@azure/abort-controller": "^1.0.0", 39 | "tslib": "^2.2.0" 40 | }, 41 | "dependencies": { 42 | "tslib": { 43 | "version": "2.3.1", 44 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", 45 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" 46 | } 47 | } 48 | }, 49 | "@azure/ms-rest-azure-js": { 50 | "version": "2.1.0", 51 | "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-js/-/ms-rest-azure-js-2.1.0.tgz", 52 | "integrity": "sha512-CjZjB8apvXl5h97Ck6SbeeCmU0sk56YPozPtTyGudPp1RGoHXNjFNtoOvwOG76EdpmMpxbK10DqcygI16Lu60Q==", 53 | "requires": { 54 | "@azure/core-auth": "^1.1.4", 55 | "@azure/ms-rest-js": "^2.2.0", 56 | "tslib": "^1.10.0" 57 | } 58 | }, 59 | "@azure/ms-rest-js": { 60 | "version": "2.6.0", 61 | "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.6.0.tgz", 62 | "integrity": "sha512-4C5FCtvEzWudblB+h92/TYYPiq7tuElX8icVYToxOdggnYqeec4Se14mjse5miInKtZahiFHdl8lZA/jziEc5g==", 63 | "requires": { 64 | "@azure/core-auth": "^1.1.4", 65 | "abort-controller": "^3.0.0", 66 | "form-data": "^2.5.0", 67 | "node-fetch": "^2.6.0", 68 | "tough-cookie": "^3.0.1", 69 | "tslib": "^1.10.0", 70 | "tunnel": "0.0.6", 71 | "uuid": "^8.3.2", 72 | "xml2js": "^0.4.19" 73 | } 74 | }, 75 | "abort-controller": { 76 | "version": "3.0.0", 77 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 78 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 79 | "requires": { 80 | "event-target-shim": "^5.0.0" 81 | } 82 | }, 83 | "asynckit": { 84 | "version": "0.4.0", 85 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 86 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 87 | }, 88 | "combined-stream": { 89 | "version": "1.0.8", 90 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 91 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 92 | "requires": { 93 | "delayed-stream": "~1.0.0" 94 | } 95 | }, 96 | "delayed-stream": { 97 | "version": "1.0.0", 98 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 99 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 100 | }, 101 | "event-target-shim": { 102 | "version": "5.0.1", 103 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 104 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" 105 | }, 106 | "form-data": { 107 | "version": "2.5.1", 108 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", 109 | "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", 110 | "requires": { 111 | "asynckit": "^0.4.0", 112 | "combined-stream": "^1.0.6", 113 | "mime-types": "^2.1.12" 114 | } 115 | }, 116 | "ip-regex": { 117 | "version": "2.1.0", 118 | "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", 119 | "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" 120 | }, 121 | "mime-db": { 122 | "version": "1.49.0", 123 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", 124 | "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==" 125 | }, 126 | "mime-types": { 127 | "version": "2.1.32", 128 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", 129 | "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", 130 | "requires": { 131 | "mime-db": "1.49.0" 132 | } 133 | }, 134 | "node-fetch": { 135 | "version": "2.6.2", 136 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.2.tgz", 137 | "integrity": "sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA==" 138 | }, 139 | "psl": { 140 | "version": "1.8.0", 141 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", 142 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" 143 | }, 144 | "punycode": { 145 | "version": "2.1.1", 146 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 147 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 148 | }, 149 | "sax": { 150 | "version": "1.2.4", 151 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 152 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 153 | }, 154 | "tough-cookie": { 155 | "version": "3.0.1", 156 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", 157 | "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", 158 | "requires": { 159 | "ip-regex": "^2.1.0", 160 | "psl": "^1.1.28", 161 | "punycode": "^2.1.1" 162 | } 163 | }, 164 | "tslib": { 165 | "version": "1.14.1", 166 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 167 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 168 | }, 169 | "tunnel": { 170 | "version": "0.0.6", 171 | "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", 172 | "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" 173 | }, 174 | "uuid": { 175 | "version": "8.3.2", 176 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 177 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" 178 | }, 179 | "xml2js": { 180 | "version": "0.4.23", 181 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", 182 | "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", 183 | "requires": { 184 | "sax": ">=0.6.0", 185 | "xmlbuilder": "~11.0.0" 186 | } 187 | }, 188 | "xmlbuilder": { 189 | "version": "11.0.1", 190 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 191 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /resources/authorization/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "authorization", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@azure/arm-authorization": "^9.0.0-beta.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /resources/billing/billing.js: -------------------------------------------------------------------------------- 1 | const { DefaultAzureCredential } = require("@azure/identity"); 2 | const { BillingManagementClient } = require("@azure/arm-billing"); 3 | 4 | const subscriptionId = process.env["AZURE_SUBSCRIPTION_ID"]; 5 | const creds = new DefaultAzureCredential(); 6 | 7 | // Use `DefaultAzureCredential` or any other credential of your choice based on https://aka.ms/azsdk/js/identity/examples 8 | // Please note that you can also use credentials from the `@azure/ms-rest-nodeauth` package instead. 9 | try { 10 | const client = new BillingManagementClient(creds, subscriptionId); 11 | const result = new Array(); 12 | const expand = "testexpand"; 13 | for await (const item of client.billingAccounts.list(expand)) { 14 | result.push(item); 15 | } 16 | console.log("The result is:"); 17 | console.log(JSON.stringify(result)); 18 | } catch (error) { 19 | console.log("An error occurred:"); 20 | console.error(JSON.stringify(err)); 21 | } 22 | -------------------------------------------------------------------------------- /resources/billing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "billing", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@azure/arm-billing": "^4.0.0", 13 | "@azure/identity": "^1.5.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /resources/create-resource-default-credential.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Create an Azure Cognitive Services Face resource. 4 | 5 | Requires: 6 | 7 | 1. Create .env file with key/value: 8 | * AZURE_SUBSCRIPTION_ID 9 | * AZURE-RESOURCE-GROUP-NAME 10 | * EMAIL-ALIAS 11 | * APP-NAME 12 | 13 | 2. Install npm packages. 14 | 15 | ``` 16 | npm install dotenv @azure/identity @azure/arm-resources 17 | ``` 18 | 19 | 3. Run code. 20 | 21 | ``` 22 | node create-resource-default-credential.js 23 | ``` 24 | 25 | References: 26 | * [Azure SDK Ref Docs for resource creation](https://docs.microsoft.com/en-us/javascript/api/@azure/arm-resources/resources?view=azure-node-latest) 27 | * [Resource provider names](https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/azure-services-resource-providers) 28 | * [Get versions](https://github.com/Azure/azure-rest-api-specs) - versions are used as subfolders in the REST API repo 29 | 30 | 31 | */ 32 | require("dotenv").config(); 33 | const subscriptionId = process.env["AZURE_SUBSCRIPTION_ID"]; 34 | const resourceGroupName = process.env["AZURE-RESOURCE-GROUP-NAME"]; 35 | const emailAlias = process.env["EMAIL-ALIAS"]; 36 | const appName = process.env["APP-NAME"]; 37 | 38 | const { DefaultAzureCredential } = require("@azure/identity"); 39 | const { ResourceManagementClient } = require("@azure/arm-resources"); 40 | 41 | // Use Azure Identity Default Credential 42 | const credentials = new DefaultAzureCredential(); 43 | 44 | async function createAzureFaceResource(credentials) { 45 | try { 46 | // Use Azure SDK for Resource Management 47 | const resourceManagementClient = new ResourceManagementClient( 48 | credentials, 49 | subscriptionId 50 | ); 51 | const resources = resourceManagementClient.resources; 52 | 53 | // These are specific to the Azure Cognitive Services Face API 54 | const resourceProviderNamespace = "Microsoft.CognitiveServices"; 55 | const parentResourcePath = ""; 56 | const apiVersion = "2017-04-18"; 57 | 58 | let parameters = { 59 | type: "Microsoft.CognitiveServices/accounts", 60 | location: "eastus", 61 | tags: { 62 | alias: emailAlias, 63 | app: appName, 64 | }, 65 | sku: { 66 | name: "F0", 67 | }, 68 | kind: "Face", 69 | properties: { 70 | networkAcls: { 71 | defaultAction: "Allow", 72 | virtualNetworkRules: [], 73 | ipRules: [], 74 | }, 75 | privateEndpointConnections: [], 76 | publicNetworkAccess: "Enabled", 77 | }, 78 | }; 79 | 80 | // Use Date as part of the resource name convention 81 | const date = new Date(); 82 | const createdDate = date.toJSON().slice(0, 10); 83 | 84 | const resourceType = "accounts"; 85 | const resourceName = `${parameters.tags.alias}-${resourceGroupName}-${resourceType}-${parameters.sku.name}-${createdDate}`; 86 | const longRunningOperationResult = await resources.beginCreateOrUpdate( 87 | resourceGroupName, 88 | resourceProviderNamespace, 89 | parentResourcePath, 90 | resourceType, 91 | resourceName, 92 | apiVersion, 93 | parameters 94 | ); 95 | console.log(longRunningOperationResult); 96 | return longRunningOperationResult; 97 | } catch (err) { 98 | console.log(err); 99 | } 100 | } 101 | 102 | createAzureFaceResource() 103 | .then(() => { 104 | console.log("done"); 105 | }) 106 | .catch((err) => { 107 | console.error(err); 108 | }); 109 | -------------------------------------------------------------------------------- /resources/create-resource.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Create an Azure Cognitive Services Face resource. 4 | 5 | Requires: 6 | 7 | Azure subscription and Azure resource group already exists. Both are required in the code: 8 | * "REPLACE-WITH-YOUR-SUBSCRIPTION-ID" 9 | * "REPLACE-WITH-YOUR-RESOURCE-GROUP-NAME" 10 | 11 | Add your email alias, the part before the `@` symbol, to your naming convention: 12 | * "REPLACE-WITH-YOUR-EMAIL-ALIAS" 13 | * "REPLACE-WITH-YOUR-APP-NAME" 14 | 15 | References: 16 | * [Azure SDK Ref Docs for resource creation](https://docs.microsoft.com/en-us/javascript/api/@azure/arm-resources/resources?view=azure-node-latest) 17 | * [Resource provider names](https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/azure-services-resource-providers) 18 | * [Get versions](https://github.com/Azure/azure-rest-api-specs) - versions are used as subfolders in the REST API repo 19 | 20 | 21 | */ 22 | 23 | const { InteractiveBrowserCredential } = require("@azure/identity"); 24 | const { ResourceManagementClient } = require("@azure/arm-resources"); 25 | 26 | // Use Azure Identity Default Credential 27 | const credentials = new InteractiveBrowserCredential(); 28 | 29 | async function createAzureFaceResource(credentials) { 30 | // Use Azure SDK for Resource Management 31 | const client = new ResourceManagementClient(credential, subscriptionId); 32 | const resources = client.resources; 33 | 34 | // REPLACE WITH YOUR VALUES 35 | const subscriptionId = "REPLACE-WITH-YOUR-SUBSCRIPTION-ID"; 36 | const resourceGroupName = "REPLACE-WITH-YOUR-RESOURCE-GROUP-NAME"; 37 | 38 | // These are specific to the Azure Cognitive Services Face API 39 | const resourceProviderNamespace = "Microsoft.CognitiveServices"; 40 | const parentResourcePath = ""; 41 | const apiVersion = "2017-04-18"; 42 | 43 | // REPLACE WITH YOUR VALUES 44 | let parameters = { 45 | type: "Microsoft.CognitiveServices/accounts", 46 | location: "eastus", 47 | tags: { 48 | alias: process.env["EMAIL-ALIAS"] || "REPLACE-WITH-YOUR-EMAIL-ALIAS", 49 | app: process.env["APP-NAME"] || "REPLACE-WITH-YOUR-APP-NAME", 50 | }, 51 | sku: { 52 | name: "F0", 53 | }, 54 | kind: "Face", 55 | properties: { 56 | networkAcls: { 57 | defaultAction: "Allow", 58 | virtualNetworkRules: [], 59 | ipRules: [], 60 | }, 61 | privateEndpointConnections: [], 62 | publicNetworkAccess: "Enabled", 63 | }, 64 | }; 65 | 66 | // Use Date as part of the resource name convention 67 | const date = new Date(); 68 | const createdDate = date.toJSON().slice(0, 10); 69 | 70 | const resourceType = "accounts"; 71 | const resourceName = `${parameters.tags.alias}-${resourceGroupName}-${resourceType}-${parameters.sku.name}-${createdDate}`; 72 | const longRunningOperationResult = await resources.beginCreateOrUpdate( 73 | resourceGroupName, 74 | resourceProviderNamespace, 75 | parentResourcePath, 76 | resourceType, 77 | resourceName, 78 | apiVersion, 79 | parameters 80 | ); 81 | return longRunningOperationResult; 82 | } 83 | 84 | createAzureFaceResource(credentials) 85 | .then((res) => { 86 | console.log(JSON.stringify(res)); 87 | }) 88 | .catch((err) => { 89 | console.log(err); 90 | }); 91 | -------------------------------------------------------------------------------- /resources/monitor/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\${file}" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /resources/monitor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monitor", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/arm-monitor": "^7.0.0", 14 | "@azure/identity": "^1.5.2", 15 | "@azure/monitor-query": "^1.0.0-beta.5", 16 | "@base2/pretty-print-object": "^1.0.1", 17 | "dayjs": "^1.10.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /resources/monitor/resource-creation-history.js: -------------------------------------------------------------------------------- 1 | const { 2 | ClientSecretCredential, 3 | DefaultAzureCredential, 4 | } = require("@azure/identity"); 5 | const { MonitorManagementClient } = require("@azure/arm-monitor"); 6 | const dayjs = require("dayjs"); 7 | const { prettyPrint } = require("@base2/pretty-print-object"); 8 | 9 | // resource group - returns all resource groups if not specified 10 | const resourceGroupName = ""; 11 | 12 | // days needs to be less than or equal to 90 13 | const daysAgo = 10; 14 | 15 | // filter 16 | // https://docs.microsoft.com/en-us/javascript/api/@azure/arm-monitor/activitylogs?view=azure-node-latest#list_string__ActivityLogsListOptionalParams__ServiceCallback_EventDataCollection__ 17 | const greaterThanIsoTime = dayjs().subtract(daysAgo, "day").toISOString(); 18 | const lessThanIsoTime = new Date().toISOString(); 19 | let filter = `eventTimestamp ge '${greaterThanIsoTime}' and eventTimestamp le '${lessThanIsoTime}'`; 20 | filter += resourceGroupName 21 | ? ` and resourceGroupName eq '${resourceGroupName}'` 22 | : null; 23 | 24 | // Azure authentication in environment variables for DefaultAzureCredential 25 | let credentials = null; 26 | const tenantId = 27 | process.env["AZURE_TENANT_ID"] || "REPLACE-WITH-YOUR-TENANT-ID"; 28 | const clientId = 29 | process.env["AZURE_CLIENT_ID"] || "REPLACE-WITH-YOUR-CLIENT-ID"; 30 | const secret = 31 | process.env["AZURE_CLIENT_SECRET"] || "REPLACE-WITH-YOUR-CLIENT-SECRET"; 32 | const subscriptionId = 33 | process.env["AZURE_SUBSCRIPTION_ID"] || "REPLACE-WITH-YOUR-SUBSCRIPTION_ID"; 34 | 35 | if (process.env.production) { 36 | // production 37 | credentials = new DefaultAzureCredential(); 38 | } else { 39 | // development 40 | credentials = new ClientSecretCredential(tenantId, clientId, secret); 41 | console.log("development"); 42 | } 43 | 44 | try { 45 | // use credential to authenticate with Azure SDKs 46 | const client = new MonitorManagementClient(credentials, subscriptionId); 47 | 48 | const arrObjects = new Array(); 49 | for await (const element of client.activityLogs.list(filter)) { 50 | arrObjects.push({ 51 | resourceGroupName: element?.resourceGroupName, 52 | action: element?.authorization?.action, 53 | user: 54 | element?.claims?.[ 55 | "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" 56 | ], 57 | resourceProviderName: element?.resourceProviderName, 58 | resourceType: element?.resourceType, 59 | operationName: element.operationName, 60 | status: element.status, 61 | eventTimestamp: element.eventTimestamp, 62 | }); 63 | } 64 | console.log(prettyPrint(arrObjects)); 65 | } catch (err) { 66 | console.log("An error occurred:"); 67 | console.log(err); 68 | } 69 | 70 | /* 71 | 72 | Example element: 73 | 74 | { 75 | resourceGroupName: 'johnsmith-temp', 76 | action: 'Microsoft.DocumentDB/databaseAccounts/listConnectionStrings/action', 77 | user: 'johnsmith@contoso.com', 78 | resourceProviderName: { 79 | value: 'Microsoft.DocumentDB', 80 | localizedValue: 'Microsoft.DocumentDB' 81 | }, 82 | resourceType: { 83 | value: 'Microsoft.DocumentDB/databaseAccounts', 84 | localizedValue: 'Microsoft.DocumentDB/databaseAccounts' 85 | }, 86 | operationName: { 87 | value: 'Microsoft.DocumentDB/databaseAccounts/listConnectionStrings/action', 88 | localizedValue: 'Get Connection Strings' 89 | }, 90 | status: { value: 'Succeeded', localizedValue: 'Succeeded' }, 91 | eventTimestamp: 2021-09-21T17:27:22.727Z 92 | }, 93 | 94 | */ 95 | -------------------------------------------------------------------------------- /resources/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azure-resources", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "resource-group.js", 6 | "scripts": { 7 | "start": "node resource-group.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "@azure/arm-resources": "^5.0.0", 14 | "@azure/identity": "^1.3.0", 15 | "@azure/ms-rest-nodeauth": "^3.0.9", 16 | "dotenv": "^8.2.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /resources/resource-group-create/README.md: -------------------------------------------------------------------------------- 1 | # Create resource group in subscription with Azure SDK for JavaScript 2 | 3 | 1. Sign in to Azure CLI in terminal. 4 | 5 | ```bash 6 | az login 7 | ``` 8 | 9 | 10 | 1. Create service principal with Azure CLI, replace `YOUR-SERVICE-PRINCIPAL-NAME` with a name 11 | such as `jsmith-quickstart-azure`: 12 | 13 | ```bash 14 | az ad sp create-for-rbac --name YOUR-SERVICE-PRINCIPAL-NAME 15 | ``` 16 | 17 | 1. Save the output in a secure location such as Azure Key vault 18 | 19 | ```json 20 | { 21 | "appId": "717...", 22 | "displayName": "jsmith-quickstart-azure", 23 | "name": "http://jsmith-quickstart-azure", 24 | "password": "PEG...", 25 | "tenant": "72f..." 26 | } 27 | ``` 28 | 29 | 1. Create new environment variables. These environment variables are REQUIRED for the context to use DefaultAzureCredential. 30 | 31 | ``` 32 | AZURE_TENANT_ID: `tenant` from the service principal output above. 33 | AZURE_CLIENT_ID: `appId` from the service principal output above. 34 | AZURE_CLIENT_SECRET: `password` from the service principal output above. 35 | AZURE_SUBSCRIPTION_ID: Your default subscription containing your resource groups. 36 | ``` 37 | 38 | 1. Complete the following commands from a bash terminal to install package dependencies: 39 | 40 | ```bash 41 | npm install @azure/identity @azure/arm-resources --save 42 | ``` 43 | 44 | 1. Run this script from a bash terminal to see a list of resource groups in your default subscription: 45 | 46 | ```bash 47 | npm start 48 | ``` 49 | 50 | 1. Output looks like 51 | 52 | ```JSON 53 | { 54 | "id": "/subscriptions/12345/resourceGroups/jsmith-ResourceGroup", 55 | "name": "jsmith-ResourceGroup", 56 | "type": "Microsoft.Resources/resourceGroups", 57 | "properties": { "provisioningState": "Succeeded" }, 58 | "location": "westus", 59 | "tags": { "createdBy": "jsmith" } 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /resources/resource-group-create/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resource-group-create", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@azure/arm-resources": "^5.0.0", 13 | "@azure/identity": "^1.5.2", 14 | "stringify-object": "^4.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /resources/resource-group-create/resource-group-create.js: -------------------------------------------------------------------------------- 1 | // Include npm dependencies 2 | const { DefaultAzureCredential } = require("@azure/identity"); 3 | const { ResourceManagementClient } = require("@azure/arm-resources"); 4 | 5 | // Get subscription from environment variables 6 | const subscriptionId = process.env["AZURE_SUBSCRIPTION_ID"]; 7 | if (!subscriptionId) 8 | throw Error("Azure Subscription is missing from environment variables."); 9 | 10 | // The following code is only used to check you have environment 11 | // variables configured. The DefaultAzureCredential reads your 12 | // environment - it doesn't read these variables. 13 | const tenantId = process.env["AZURE_TENANT_ID"]; 14 | if (!tenantId) 15 | throw Error("AZURE_TENANT_ID is missing from environment variables."); 16 | const clientId = process.env["AZURE_CLIENT_ID"]; 17 | if (!clientId) 18 | throw Error("AZURE_CLIENT_ID is missing from environment variables."); 19 | const secret = process.env["AZURE_CLIENT_SECRET"]; 20 | if (!secret) 21 | throw Error("AZURE_CLIENT_SECRET is missing from environment variables."); 22 | 23 | // Create Azure authentication credentials 24 | const credentials = new DefaultAzureCredential(); 25 | 26 | try { 27 | // Create Azure SDK client for Resource Management such as resource groups 28 | const resourceManagement = new ResourceManagementClient( 29 | credentials, 30 | subscriptionId 31 | ); 32 | 33 | const ownerAlias = "jsmith"; 34 | const location = "westus"; 35 | 36 | // Resource group definition 37 | const resourceGroupName = `${ownerAlias}-ResourceGroup`; 38 | const resourceGroupParameters = { 39 | location: location, 40 | tags: { createdBy: ownerAlias }, 41 | }; 42 | 43 | // Create resource groups in subscription 44 | await resourceManagement.resourceGroups 45 | .createOrUpdate(resourceGroupName, resourceGroupParameters) 46 | .then((result) => { 47 | console.log(result); 48 | }); 49 | 50 | /* 51 | { 52 | id: '/subscriptions/12345/resourceGroups/jsmith-ResourceGroup', 53 | name: 'jsmith-ResourceGroup', 54 | type: 'Microsoft.Resources/resourceGroups', 55 | properties: { provisioningState: 'Succeeded' }, 56 | location: 'westus', 57 | tags: { createdBy: 'jsmith' } 58 | } 59 | */ 60 | } catch (err) { 61 | console.log(err); 62 | } 63 | -------------------------------------------------------------------------------- /resources/resource-group-default-credential.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Create an Azure resource group. 4 | 5 | Requires: 6 | 7 | Azure subscription already exists. Value is required in the code: 8 | * "REPLACE-WITH-YOUR-SUBSCRIPTION-ID" 9 | 10 | Add your email alias, the part before the `@` symbol, to your naming convention: 11 | * "REPLACE-WITH-YOUR-EMAIL-ALIAS" 12 | * "REPLACE-WITH-YOUR-APP-NAME" 13 | 14 | References: 15 | * [Azure SDK Ref Docs for Resources](https://docs.microsoft.com/en-us/javascript/api/overview/azure/resources) 16 | 17 | */ 18 | 19 | require("dotenv").config(); 20 | const subscriptionId = process.env["AZURE_SUBSCRIPTION_ID"]; 21 | const myEmailAlias = process.env["EMAIL-ALIAS"]; 22 | const myAppName = process.env["APP-NAME"]; 23 | 24 | const { DefaultAzureCredential } = require("@azure/identity"); 25 | const { ResourceManagementClient } = require("@azure/arm-resources"); 26 | 27 | const resourceCreatedDate = new Date().toISOString(); 28 | const resourceGroupName = `${myAppName}-resource-group`; 29 | const resourceGroupLocation = "eastus"; 30 | 31 | async function createResourceGroup() { 32 | try { 33 | // Use Azure Identity Default Credential 34 | const credentials = new DefaultAzureCredential(); 35 | 36 | // Use Azure SDK for Resource Management 37 | const resourceManagement = new ResourceManagementClient( 38 | credentials, 39 | subscriptionId 40 | ); 41 | 42 | // Create 43 | const parameters = { 44 | location: resourceGroupLocation, 45 | tags: { 46 | owner: myEmailAlias, 47 | created: resourceCreatedDate, 48 | }, 49 | }; 50 | console.log("Creating..."); 51 | const createResult = await resourceManagement.resourceGroups.createOrUpdate( 52 | resourceGroupName, 53 | parameters 54 | ); 55 | console.log(JSON.stringify(createResult)); 56 | 57 | // Check existence - returns boolean 58 | console.log("Exists..."); 59 | const checkExistenceResult = await resourceManagement.resourceGroups.checkExistence( 60 | resourceGroupName 61 | ); 62 | console.log(JSON.stringify(checkExistenceResult)); 63 | } catch (err) { 64 | console.log(err); 65 | } 66 | } 67 | 68 | createResourceGroup() 69 | .then(() => { 70 | console.log("done"); 71 | }) 72 | .catch((err) => { 73 | console.log(err); 74 | }); 75 | -------------------------------------------------------------------------------- /resources/resource-group.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Create an Azure resource group. 4 | 5 | Requires: 6 | 7 | Azure subscription already exists. Value is equired in the code: 8 | * "REPLACE-WITH-YOUR-SUBSCRIPTION-ID" 9 | 10 | Add your email alias, the part before the `@` symbol, to your naming convention: 11 | * "REPLACE-WITH-YOUR-EMAIL-ALIAS" 12 | * "REPLACE-WITH-YOUR-APP-NAME" 13 | 14 | References: 15 | * [Azure SDK Ref Docs for Resources](https://docs.microsoft.com/en-us/javascript/api/overview/azure/resources) 16 | 17 | */ 18 | 19 | const { InteractiveBrowserCredential } = require("@azure/identity"); 20 | const { ResourceManagementClient } = require("@azure/arm-resources"); 21 | 22 | const myEmailAlias = 23 | process.env["EMAIL-ALIAS"] || "REPLACE-WITH-YOUR-EMAIL-ALIAS"; 24 | const myAppName = process.env["APP-NAME"] || "REPLACE-WITH-YOUR-APP-NAME"; 25 | 26 | const subscriptionId = 27 | process.env["SUBSCRIPTION-ID"] || "REPLACE-WITH-YOUR-SUBSCRIPTION-ID"; 28 | 29 | const resourceCreatedDate = new Date().toISOString(); 30 | const resourceGroupName = `${myAppName}-resource-group`; 31 | const resourceGroupLocation = "eastus"; 32 | const resourceGroupFilter = `tagName eq 'owner' and tagValue eq '${myEmailAlias}'`; 33 | const resourceGroupTop = 10; 34 | 35 | const credential = new InteractiveBrowserCredential(); 36 | 37 | async function resourceGroupActions(credential) { 38 | try { 39 | const resourceManagement = new ResourceManagementClient( 40 | credential, 41 | subscriptionId 42 | ); 43 | 44 | // Create 45 | const parameters = { 46 | location: resourceGroupLocation, 47 | tags: { 48 | owner: myEmailAlias, 49 | created: resourceCreatedDate, 50 | }, 51 | }; 52 | console.log("Creating..."); 53 | const createResult = await resourceManagement.resourceGroups.createOrUpdate( 54 | resourceGroupName, 55 | parameters 56 | ); 57 | console.log(JSON.stringify(createResult)); 58 | 59 | // Check existence - returns boolean 60 | console.log("Exists..."); 61 | const checkExistenceResult = await resourceManagement.resourceGroups.checkExistence( 62 | resourceGroupName 63 | ); 64 | console.log(JSON.stringify(checkExistenceResult)); 65 | 66 | // List filtered by tag name and value 67 | const properties = { 68 | filter: resourceGroupFilter, 69 | top: resourceGroupTop, 70 | }; 71 | console.log("Filtered..."); 72 | const filteredListReturn = new Array(); 73 | for await (const item of resourceManagement.resourceGroups.list( 74 | properties 75 | )) { 76 | filteredListReturn.push(item); 77 | } 78 | console.log(JSON.stringify(filteredListReturn)); 79 | 80 | // List all 81 | console.log("All..."); 82 | const allListResult = new Array(); 83 | for await (const item of resourceManagement.resourceGroups.list()) { 84 | allListResult.push(item); 85 | } 86 | console.log(JSON.stringify(allListResult)); 87 | 88 | // Delete - HTTP status 200 on success, no body 89 | console.log("Deleting..."); 90 | const deleteResult = await resourceManagement.resourceGroups.deleteMethod( 91 | resourceGroupName 92 | ); 93 | console.log(JSON.stringify(deleteResult)); 94 | } catch (err) { 95 | console.log(err); 96 | } 97 | } 98 | 99 | resourceGroupActions(credential) 100 | .then((res) => { 101 | console.log(JSON.stringify(res)); 102 | }) 103 | .catch((err) => { 104 | console.log(err); 105 | }); -------------------------------------------------------------------------------- /resources/resource-groups-list/README.md: -------------------------------------------------------------------------------- 1 | # View resource groups in subscription 2 | 3 | 1. Sign in to Azure CLI in terminal. 4 | 5 | ```bash 6 | az login 7 | ``` 8 | 9 | 10 | 1. Create service principal with Azure CLI, replace `YOUR-SERVICE-PRINCIPAL-NAME` with a name 11 | such as `joesmith-quickstart-azure`: 12 | 13 | ```bash 14 | az ad sp create-for-rbac --name YOUR-SERVICE-PRINCIPAL-NAME 15 | ``` 16 | 17 | 1. Save the output in a secure location such as Azure Key vault 18 | 19 | ```json 20 | { 21 | "appId": "717...", 22 | "displayName": "joesmith-quickstart-azure", 23 | "name": "http://joesmith-quickstart-azure", 24 | "password": "PEG...", 25 | "tenant": "72f..." 26 | } 27 | ``` 28 | 29 | 1. Create new environment variables. These environment variables are REQUIRED for the context to use DefaultAzureCredential. 30 | 31 | ``` 32 | AZURE_TENANT_ID: `tenant` from the service principal output above. 33 | AZURE_CLIENT_ID: `appId` from the service principal output above. 34 | AZURE_CLIENT_SECRET: `password` from the service principal output above. 35 | AZURE_SUBSCRIPTION: Your default subscription containing your resource groups. 36 | ``` 37 | 38 | 1. Complete the following commands from a bash terminal to install package dependencies: 39 | 40 | ```bash 41 | npm install @azure/identity @azure/arm-resources stringify-object 42 | ``` 43 | 44 | Note: `stringify-object` is only used to provide readable JSON. It is 45 | not required to use Azure SDKs. 46 | 47 | 1. Run this script from a bash terminal to see a list of resource groups in your default subscription: 48 | 49 | ```bash 50 | npm start 51 | ``` 52 | 53 | 1. Pretty output looks like 54 | 55 | ```JSON 56 | [ 57 | { 58 | id: "/subscriptions/bb8.../resourceGroups/DefaultResourceGroup-WUS2", 59 | name: "DefaultResourceGroup-WUS2", 60 | type: "Microsoft.Resources/resourceGroups", 61 | properties: { 62 | provisioningState: "Succeeded" 63 | }, 64 | location: "westus2", 65 | tags: {} 66 | }, 67 | { 68 | ... 69 | } 70 | ] 71 | ``` -------------------------------------------------------------------------------- /resources/resource-groups-list/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resource-groups-list", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "resource-groups-list.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node resource-groups-list.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/arm-resources": "^5.0.0", 14 | "@azure/identity": "^1.5.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /resources/resource-groups-list/resource-groups-list.js: -------------------------------------------------------------------------------- 1 | // Include npm dependencies 2 | const { DefaultAzureCredential } = require("@azure/identity"); 3 | const { ResourceManagementClient } = require("@azure/arm-resources"); 4 | 5 | // Get subscription from environment variables 6 | const subscriptionId = process.env["AZURE_SUBSCRIPTION"]; 7 | if (!subscriptionId) 8 | throw Error("Azure Subscription is missing from environment variables."); 9 | 10 | // The following code is only used to check you have environment 11 | // variables configured. The DefaultAzureCredential reads your 12 | // environment - it doesn't read these variables. 13 | const tenantId = process.env["AZURE_TENANT_ID"]; 14 | if (!tenantId) 15 | throw Error("AZURE_TENANT_ID is missing from environment variables."); 16 | const clientId = process.env["AZURE_CLIENT_ID"]; 17 | if (!clientId) 18 | throw Error("AZURE_CLIENT_ID is missing from environment variables."); 19 | const secret = process.env["AZURE_CLIENT_SECRET"]; 20 | if (!secret) 21 | throw Error("AZURE_CLIENT_SECRET is missing from environment variables."); 22 | 23 | // Create Azure authentication credentials 24 | const credentials = new DefaultAzureCredential(); 25 | 26 | try { 27 | // Create Azure SDK client for Resource Management such as resource groups 28 | const resourceManagement = new ResourceManagementClient( 29 | credentials, 30 | subscriptionId 31 | ); 32 | 33 | // List resource groups in subscription 34 | const result = new Array(); 35 | for await (const item of resourceManagement.resourceGroups.list()) { 36 | result.push(item); 37 | } 38 | console.log(JSON.stringify(result)); 39 | } catch (err) { 40 | console.log(err); 41 | } 42 | -------------------------------------------------------------------------------- /resources/resources-list-in-subscription/list-resources-in-subscription.js: -------------------------------------------------------------------------------- 1 | // Include npm dependencies 2 | const { DefaultAzureCredential } = require("@azure/identity"); 3 | const { ResourceManagementClient } = require("@azure/arm-resources"); 4 | 5 | // Get subscription from environment variables 6 | const subscriptionId = process.env["AZURE_SUBSCRIPTION"]; 7 | if (!subscriptionId) 8 | throw Error("Azure Subscription is missing from environment variables."); 9 | 10 | // The following code is only used to check you have environment 11 | // variables configured. The DefaultAzureCredential reads your 12 | // environment - it doesn't read these variables. 13 | const tenantId = process.env["AZURE_TENANT_ID"]; 14 | if (!tenantId) 15 | throw Error("AZURE_TENANT_ID is missing from environment variables."); 16 | const clientId = process.env["AZURE_CLIENT_ID"]; 17 | if (!clientId) 18 | throw Error("AZURE_CLIENT_ID is missing from environment variables."); 19 | const secret = process.env["AZURE_CLIENT_SECRET"]; 20 | if (!secret) 21 | throw Error("AZURE_CLIENT_SECRET is missing from environment variables."); 22 | 23 | // Create Azure authentication credentials 24 | const credentials = new DefaultAzureCredential(); 25 | 26 | try { 27 | // Create Azure SDK client for Resource Management such as resource groups 28 | const resourceManagement = new ResourceManagementClient( 29 | credentials, 30 | subscriptionId 31 | ); 32 | 33 | // List resource groups in subscription 34 | const result = new Array(); 35 | for await (const item of resourceManagement.resources.list()) { 36 | result.push(item); 37 | } 38 | console.log(JSON.stringify(result)); 39 | } catch (err) { 40 | console.log(err); 41 | } 42 | -------------------------------------------------------------------------------- /resources/subscriptions/list-locations.js: -------------------------------------------------------------------------------- 1 | const { DefaultAzureCredential } = require("@azure/identity"); 2 | const { SubscriptionClient } = require("@azure/arm-subscriptions"); 3 | 4 | // Get subscription from environment variables 5 | const subscriptionId = process.env["AZURE_SUBSCRIPTION"]; 6 | if (!subscriptionId) 7 | throw Error("Azure Subscription is missing from environment variables."); 8 | 9 | // The following code is only used to check you have environment 10 | // variables configured. The DefaultAzureCredential reads your 11 | // environment - it doesn't read these variables. 12 | const tenantId = process.env["AZURE_TENANT_ID"]; 13 | if (!tenantId) 14 | throw Error("AZURE_TENANT_ID is missing from environment variables."); 15 | const clientId = process.env["AZURE_CLIENT_ID"]; 16 | if (!clientId) 17 | throw Error("AZURE_CLIENT_ID is missing from environment variables."); 18 | const secret = process.env["AZURE_CLIENT_SECRET"]; 19 | if (!secret) 20 | throw Error("AZURE_CLIENT_SECRET is missing from environment variables."); 21 | 22 | if (!subscriptionId || !tenantId || !clientId || !secret) return; 23 | 24 | try { 25 | const credentials = new DefaultAzureCredential(); 26 | 27 | // Create Azure SDK client for Resource Management such as resource groups 28 | const client = new SubscriptionClient(credentials, subscriptionId); 29 | 30 | // List resource groups in subscription 31 | const result = new Array(); 32 | for await (const item of client.subscriptions.listLocations(subscriptionId)) { 33 | result.push(item); 34 | } 35 | console.log("The result is"); 36 | console.log(JSON.stringify(result)); 37 | } catch (err) { 38 | console.log("An error occurred:"); 39 | console.error(err); 40 | } 41 | -------------------------------------------------------------------------------- /resources/subscriptions/list.js: -------------------------------------------------------------------------------- 1 | const { 2 | ClientSecretCredential, 3 | DefaultAzureCredential, 4 | } = require("@azure/identity"); 5 | const { SubscriptionClient } = require("@azure/arm-subscriptions"); 6 | require("dotenv").config(); 7 | 8 | let credentials = null; 9 | 10 | const tenantId = process.env["AZURE_TENANT_ID"]; 11 | const clientId = process.env["AZURE_CLIENT_ID"]; 12 | const secret = process.env["AZURE_CLIENT_SECRET"]; 13 | 14 | if (process.env.NODE_ENV && process.env.NODE_ENV === "production") { 15 | // production 16 | credentials = new DefaultAzureCredential(); 17 | } else { 18 | // development 19 | if (tenantId && clientId && secret) { 20 | console.log("development"); 21 | credentials = new ClientSecretCredential(tenantId, clientId, secret); 22 | } else { 23 | credentials = new DefaultAzureCredential(); 24 | } 25 | } 26 | 27 | async function listSubscriptions() { 28 | try { 29 | // use credential to authenticate with Azure SDKs 30 | const client = new SubscriptionClient(credentials); 31 | 32 | // get details of each subscription 33 | for await (const item of client.subscriptions.list()) { 34 | const subscriptionDetails = await client.subscriptions.get( 35 | item.subscriptionId 36 | ); 37 | /* 38 | Each item looks like: 39 | 40 | { 41 | id: '/subscriptions/123456', 42 | subscriptionId: '123456', 43 | displayName: 'YOUR-SUBSCRIPTION-NAME', 44 | state: 'Enabled', 45 | subscriptionPolicies: { 46 | locationPlacementId: 'Internal_2014-09-01', 47 | quotaId: 'Internal_2014-09-01', 48 | spendingLimit: 'Off' 49 | }, 50 | authorizationSource: 'RoleBased' 51 | }, 52 | */ 53 | console.log(subscriptionDetails); 54 | } 55 | } catch (err) { 56 | console.error(JSON.stringify(err)); 57 | } 58 | } 59 | 60 | listSubscriptions() 61 | .then(() => { 62 | console.log("done"); 63 | }) 64 | .catch((ex) => { 65 | console.log(ex); 66 | }); 67 | -------------------------------------------------------------------------------- /resources/subscriptions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subscriptions", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "list-locations.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@azure/arm-subscriptions": "^5.0.0", 13 | "@azure/identity": "^1.5.2", 14 | "dotenv": "^16.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /resources/virtual-machines/README.md: -------------------------------------------------------------------------------- 1 | # Create, manage, and delete a virtual machine 2 | 3 | A virtual machine is a collection of Azure resources. To use these scripts: 4 | 5 | * Create a service principal 6 | * Add Azure authentication environment variables or change variables in the code files. 7 | * Install dependences 8 | 9 | ```bash 10 | npm install 11 | ``` 12 | 13 | * Change any variables specific to your use. For the VM creation script, the two variables are: 14 | 15 | ```javascript 16 | // CHANGE THIS - used as prefix for naming resources 17 | const yourAlias = "johnsmith"; 18 | 19 | // CHANGE THIS - used to add tags to resources 20 | const projectName = "azure-samples-create-vm"; 21 | ``` 22 | 23 | ## Create a VM 24 | 25 | Use the [create vm](create-vm.js) file to create resources associated with a VM. Review the file to change any preset values such as pricing tier, user name and password. 26 | 27 | ## Manage a VM 28 | 29 | Use the management scripts to: 30 | 31 | * [Start a VM](start-vm.js) 32 | * [Stop a VM](stop-vm.js) 33 | * [Get VM information](vm-info.js) 34 | * [List VMs in subscription](list-vms.js) 35 | 36 | ## Delete a VM 37 | 38 | Because there are several resources associated with a VM, the easiest way to delete a VM is to delete the resource group. Use the [delete vm](delete-resources.js) file to delete the resource group. 39 | -------------------------------------------------------------------------------- /resources/virtual-machines/delete-resources.js: -------------------------------------------------------------------------------- 1 | const { 2 | ClientSecretCredential, 3 | DefaultAzureCredential, 4 | } = require("@azure/identity"); 5 | const { ResourceManagementClient } = require("@azure/arm-resources"); 6 | 7 | // Azure authentication in environment variables for DefaultAzureCredential 8 | let credentials = null; 9 | const tenantId = 10 | process.env["AZURE_TENANT_ID"] || "REPLACE-WITH-YOUR-TENANT-ID"; 11 | const clientId = 12 | process.env["AZURE_CLIENT_ID"] || "REPLACE-WITH-YOUR-CLIENT-ID"; 13 | const secret = 14 | process.env["AZURE_CLIENT_SECRET"] || "REPLACE-WITH-YOUR-CLIENT-SECRET"; 15 | const subscriptionId = 16 | process.env["AZURE_SUBSCRIPTION_ID"] || "REPLACE-WITH-YOUR-SUBSCRIPTION_ID"; 17 | 18 | const resourceGroupName = "REPLACE-WITH-YOUR-RESOURCE_GROUP-NAME"; 19 | 20 | if (process.env.production) { 21 | // production 22 | credentials = new DefaultAzureCredential(); 23 | } else { 24 | // development 25 | credentials = new ClientSecretCredential(tenantId, clientId, secret); 26 | console.log("development"); 27 | } 28 | 29 | async function deleteResourceGroup() { 30 | // Create Azure SDK client for Resource Management such as resource groups 31 | const resourceClient = new ResourceManagementClient( 32 | credentials, 33 | subscriptionId 34 | ); 35 | 36 | const result = await resourceClient.resourceGroups.deleteMethod( 37 | resourceGroupName 38 | ); 39 | console.log(JSON.stringify(result)); 40 | } 41 | 42 | deleteResourceGroup() 43 | .then((result) => { 44 | console.log(result); 45 | }) 46 | .catch((ex) => { 47 | console.log(ex); 48 | }); 49 | -------------------------------------------------------------------------------- /resources/virtual-machines/list-vms.js: -------------------------------------------------------------------------------- 1 | const { 2 | ClientSecretCredential, 3 | DefaultAzureCredential, 4 | } = require("@azure/identity"); 5 | const { ComputeManagementClient } = require("@azure/arm-compute"); 6 | 7 | // Azure authentication in environment variables for DefaultAzureCredential 8 | let credentials = null; 9 | const tenantId = 10 | process.env["AZURE_TENANT_ID"] || "REPLACE-WITH-YOUR-TENANT-ID"; 11 | const clientId = 12 | process.env["AZURE_CLIENT_ID"] || "REPLACE-WITH-YOUR-CLIENT-ID"; 13 | const secret = 14 | process.env["AZURE_CLIENT_SECRET"] || "REPLACE-WITH-YOUR-CLIENT-SECRET"; 15 | const subscriptionId = 16 | process.env["AZURE_SUBSCRIPTION_ID"] || "REPLACE-WITH-YOUR-SUBSCRIPTION_ID"; 17 | 18 | if (process.env.production) { 19 | // production 20 | credentials = new DefaultAzureCredential(); 21 | } else { 22 | // development 23 | credentials = new ClientSecretCredential(tenantId, clientId, secret); 24 | console.log("development"); 25 | } 26 | 27 | async function listVMs() { 28 | // use credential to authenticate with Azure SDKs 29 | const client = new ComputeManagementClient(credentials, subscriptionId); 30 | 31 | // get details of each subscription 32 | const listResult = new Array(); 33 | for await (const item of client.virtualMachines.listAll()) { 34 | listResult.push(item); 35 | } 36 | return listResult; 37 | 38 | /* 39 | Result is an array of items. Each item looks something like: 40 | 41 | { 42 | "id": "/subscriptions/123456/resourceGroups/johnsmith-TESTRG3215/providers/Microsoft.Compute/virtualMachines/johnsmithvm6859", 43 | "name": "johnsmithvm6859", 44 | "type": "Microsoft.Compute/virtualMachines", 45 | "location": "eastus", 46 | "hardwareProfile": { "vmSize": "Standard_B1ls" }, 47 | "storageProfile": { 48 | "imageReference": { 49 | "publisher": "Canonical", 50 | "offer": "UbuntuServer", 51 | "sku": "14.04.3-LTS", 52 | "version": "14.04.201805220", 53 | "exactVersion": "14.04.201805220" 54 | }, 55 | "osDisk": { 56 | "osType": "Linux", 57 | "name": "johnsmithosdisk9293", 58 | "vhd": { 59 | "uri": "https://johnsmithac1195.blob.core.windows.net/nodejscontainer/osnodejslinux.vhd" 60 | }, 61 | "caching": "None", 62 | "createOption": "FromImage", 63 | "diskSizeGB": 30, 64 | "deleteOption": "Detach" 65 | }, 66 | "dataDisks": [] 67 | }, 68 | "osProfile": { 69 | "computerName": "johnsmithvm6859", 70 | "adminUsername": "notadmin", 71 | "linuxConfiguration": { 72 | "disablePasswordAuthentication": false, 73 | "provisionVMAgent": true, 74 | "patchSettings": { 75 | "patchMode": "ImageDefault", 76 | "assessmentMode": "ImageDefault" 77 | } 78 | }, 79 | "secrets": [], 80 | "allowExtensionOperations": true, 81 | "requireGuestProvisionSignal": true 82 | }, 83 | "networkProfile": { 84 | "networkInterfaces": [ 85 | { 86 | "id": "/subscriptions/123456/resourceGroups/johnsmith-testrg3215/providers/Microsoft.Network/networkInterfaces/johnsmithnic7962", 87 | "primary": true 88 | } 89 | ] 90 | }, 91 | "provisioningState": "Succeeded", 92 | "vmId": "987654" 93 | } 94 | 95 | */ 96 | } 97 | 98 | listVMs() 99 | .then((result) => { 100 | console.log(result); 101 | }) 102 | .catch((err) => { 103 | console.log(err); 104 | }); 105 | -------------------------------------------------------------------------------- /resources/virtual-machines/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "virtual-machines", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "create-vm.js", 6 | "dependencies": { 7 | "@azure/arm-compute": "^17.2.0", 8 | "@azure/arm-network": "^26.0.0", 9 | "@azure/arm-resources": "^5.0.0", 10 | "@azure/arm-storage": "^17.1.0", 11 | "@azure/identity": "^2.0.0" 12 | }, 13 | "scripts": { 14 | "test": "echo \"Error: no test specified\" && exit 1" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "ISC" 19 | } 20 | -------------------------------------------------------------------------------- /resources/virtual-machines/start-vm.js: -------------------------------------------------------------------------------- 1 | const { 2 | ClientSecretCredential, 3 | DefaultAzureCredential, 4 | } = require("@azure/identity"); 5 | const { ComputeManagementClient } = require("@azure/arm-compute"); 6 | 7 | // Azure authentication in environment variables for DefaultAzureCredential 8 | let credentials = null; 9 | const tenantId = 10 | process.env["AZURE_TENANT_ID"] || "REPLACE-WITH-YOUR-TENANT-ID"; 11 | const clientId = 12 | process.env["AZURE_CLIENT_ID"] || "REPLACE-WITH-YOUR-CLIENT-ID"; 13 | const secret = 14 | process.env["AZURE_CLIENT_SECRET"] || "REPLACE-WITH-YOUR-CLIENT-SECRET"; 15 | const subscriptionId = 16 | process.env["AZURE_SUBSCRIPTION_ID"] || "REPLACE-WITH-YOUR-SUBSCRIPTION_ID"; 17 | 18 | const resourceGroupName = "REPLACE-WITHYOUR-RESOURCE_GROUP-NAME"; 19 | const vmResourceName = "REPLACE-WITHYOUR-RESOURCE-NAME"; 20 | 21 | if (process.env.production) { 22 | // production 23 | credentials = new DefaultAzureCredential(); 24 | } else { 25 | // development 26 | credentials = new ClientSecretCredential(tenantId, clientId, secret); 27 | console.log("development"); 28 | } 29 | 30 | async function startVM() { 31 | const computeClient = new ComputeManagementClient( 32 | credentials, 33 | subscriptionId 34 | ); 35 | const result = await computeClient.virtualMachines.start( 36 | resourceGroupName, 37 | vmResourceName 38 | ); 39 | return result; 40 | } 41 | 42 | startVM() 43 | .then((result) => { 44 | console.log(JSON.stringify(result)); 45 | }) 46 | .catch((err) => { 47 | console.log(err); 48 | }); 49 | 50 | /* 51 | 52 | Start operation results: 53 | 54 | { 55 | "startTime":"2021-10-27T16:35:59.6006484+00:00", 56 | "endTime":"2021-10-27T16:35:59.850632+00:00", 57 | "status":"Succeeded", 58 | "name":"1773c5e7-d904-4f98-b2a6-6e2f2465407f" 59 | } 60 | 61 | */ 62 | -------------------------------------------------------------------------------- /resources/virtual-machines/status-vms-all.js: -------------------------------------------------------------------------------- 1 | const { 2 | ClientSecretCredential, 3 | DefaultAzureCredential, 4 | } = require("@azure/identity"); 5 | const { ComputeManagementClient } = require("@azure/arm-compute"); 6 | 7 | // Azure authentication in environment variables for DefaultAzureCredential 8 | let credentials = null; 9 | const tenantId = 10 | process.env["AZURE_TENANT_ID"] || "REPLACE-WITH-YOUR-TENANT-ID"; 11 | const clientId = 12 | process.env["AZURE_CLIENT_ID"] || "REPLACE-WITH-YOUR-CLIENT-ID"; 13 | const secret = 14 | process.env["AZURE_CLIENT_SECRET"] || "REPLACE-WITH-YOUR-CLIENT-SECRET"; 15 | const subscriptionId = 16 | process.env["AZURE_SUBSCRIPTION_ID"] || "REPLACE-WITH-YOUR-SUBSCRIPTION_ID"; 17 | 18 | if (process.env.production) { 19 | // production 20 | credentials = new DefaultAzureCredential(); 21 | } else { 22 | // development 23 | credentials = new ClientSecretCredential(tenantId, clientId, secret); 24 | } 25 | 26 | const listVMsStatus = async () => { 27 | 28 | // Set params to only ask for status 29 | const virtualMachinesListAllOptionalParams = { 30 | statusOnly: "true", 31 | }; 32 | 33 | const computeClient = new ComputeManagementClient( 34 | credentials, 35 | subscriptionId 36 | ); 37 | const result = await computeClient.virtualMachines.listAll( 38 | virtualMachinesListAllOptionalParams 39 | ); 40 | result.map((vm) => { 41 | console.log(`${vm.name}`); 42 | vm.instanceView.statuses.map((status) => { 43 | console.log( 44 | `---${status.displayStatus}${status.time && status.time.length > 0 ? status.time : ""}` 45 | ); 46 | }); 47 | }); 48 | }; 49 | 50 | listVMsStatus() 51 | .then((result) => { 52 | console.log(result); 53 | }) 54 | .catch((ex) => { 55 | console.log(ex); 56 | }); 57 | 58 | /* 59 | Result is an array of VMs with only status. Each item looks something like: 60 | 61 | { 62 | id: "/subscriptions/123456/resourceGroups/JohnSmith-TESTRG3215/providers/Microsoft.Compute/virtualMachines/JohnSmithvm6859", 63 | name: "JohnSmithvm6859", 64 | type: "Microsoft.Compute/virtualMachines", 65 | location: "eastus", 66 | hardwareProfile: {}, 67 | provisioningState: "Succeeded", 68 | instanceView: { 69 | statuses: [ 70 | { 71 | code: "ProvisioningState/succeeded", 72 | level: "Info", 73 | displayStatus: "Provisioning succeeded", 74 | time: "2021-10-28T17:41:03.453Z", 75 | }, 76 | { 77 | code: "PowerState/running", 78 | level: "Info", 79 | displayStatus: "VM running", 80 | }, 81 | ], 82 | }, 83 | vmId: "987654", 84 | } 85 | 86 | */ 87 | -------------------------------------------------------------------------------- /resources/virtual-machines/status.js: -------------------------------------------------------------------------------- 1 | const { 2 | ClientSecretCredential, 3 | DefaultAzureCredential, 4 | } = require("@azure/identity"); 5 | const { ComputeManagementClient } = require("@azure/arm-compute"); 6 | 7 | // Azure authentication in environment variables for DefaultAzureCredential 8 | let credentials = null; 9 | const tenantId = 10 | process.env["AZURE_TENANT_ID"] || "REPLACE-WITH-YOUR-TENANT-ID"; 11 | const clientId = 12 | process.env["AZURE_CLIENT_ID"] || "REPLACE-WITH-YOUR-CLIENT-ID"; 13 | const secret = 14 | process.env["AZURE_CLIENT_SECRET"] || "REPLACE-WITH-YOUR-CLIENT-SECRET"; 15 | const subscriptionId = 16 | process.env["AZURE_SUBSCRIPTION_ID"] || "REPLACE-WITH-YOUR-SUBSCRIPTION_ID"; 17 | 18 | if (process.env.production) { 19 | // production 20 | credentials = new DefaultAzureCredential(); 21 | } else { 22 | // development 23 | credentials = new ClientSecretCredential(tenantId, clientId, secret); 24 | } 25 | 26 | async function listVMsStatus() { 27 | // Set params to only ask for status 28 | const virtualMachinesListAllOptionalParams = { statusOnly: "true" }; 29 | 30 | const computeClient = new ComputeManagementClient( 31 | credentials, 32 | subscriptionId 33 | ); 34 | 35 | const result = new Array(); 36 | for await (const item of computeClient.virtualMachines.listAll( 37 | virtualMachinesListAllOptionalParams 38 | )) { 39 | result.push(item); 40 | } 41 | result.map((vm) => { 42 | console.log(`${vm.name}`); 43 | vm.instanceView.statuses.map((status) => { 44 | console.log( 45 | `---${status.displayStatus} ${status.time ? status.time : ""}` 46 | 47 | /* 48 | Example: 49 | johnsmithvm6859 50 | ---Provisioning succeeded Thu Oct 28 2021 10:41:03 GMT-0700 (Pacific Daylight Time) 51 | ---VM running 52 | */ 53 | ); 54 | }); 55 | }); 56 | } 57 | 58 | listVMsStatus() 59 | .then((result) => { 60 | console.log("done"); 61 | }) 62 | .catch((ex) => { 63 | console.log(ex); 64 | }); 65 | 66 | /* 67 | Result is an array of VMs with only status. Each item looks something like: 68 | 69 | { 70 | id: "/subscriptions/123456/resourceGroups/JohnSmith-TESTRG3215/providers/Microsoft.Compute/virtualMachines/JohnSmithvm6859", 71 | name: "JohnSmithvm6859", 72 | type: "Microsoft.Compute/virtualMachines", 73 | location: "eastus", 74 | hardwareProfile: {}, 75 | provisioningState: "Succeeded", 76 | instanceView: { 77 | statuses: [ 78 | { 79 | code: "ProvisioningState/succeeded", 80 | level: "Info", 81 | displayStatus: "Provisioning succeeded", 82 | time: "2021-10-28T17:41:03.453Z", 83 | }, 84 | { 85 | code: "PowerState/running", 86 | level: "Info", 87 | displayStatus: "VM running", 88 | }, 89 | ], 90 | }, 91 | vmId: "987654", 92 | } 93 | 94 | */ 95 | -------------------------------------------------------------------------------- /resources/virtual-machines/stop-vm.js: -------------------------------------------------------------------------------- 1 | const { 2 | ClientSecretCredential, 3 | DefaultAzureCredential, 4 | } = require("@azure/identity"); 5 | const { ComputeManagementClient } = require("@azure/arm-compute"); 6 | 7 | // Azure authentication in environment variables for DefaultAzureCredential 8 | let credentials = null; 9 | const tenantId = 10 | process.env["AZURE_TENANT_ID"] || "REPLACE-WITH-YOUR-TENANT-ID"; 11 | const clientId = 12 | process.env["AZURE_CLIENT_ID"] || "REPLACE-WITH-YOUR-CLIENT-ID"; 13 | const secret = 14 | process.env["AZURE_CLIENT_SECRET"] || "REPLACE-WITH-YOUR-CLIENT-SECRET"; 15 | const subscriptionId = 16 | process.env["AZURE_SUBSCRIPTION_ID"] || "REPLACE-WITH-YOUR-SUBSCRIPTION_ID"; 17 | 18 | const resourceGroupName = "REPLACE-WITHYOUR-RESOURCE_GROUP-NAME"; 19 | const vmResourceName = "REPLACE-WITHYOUR-RESOURCE-NAME"; 20 | 21 | if (process.env.production) { 22 | // production 23 | credentials = new DefaultAzureCredential(); 24 | } else { 25 | // development 26 | credentials = new ClientSecretCredential(tenantId, clientId, secret); 27 | console.log("development"); 28 | } 29 | 30 | async function stopVM() { 31 | const computeClient = new ComputeManagementClient( 32 | credentials, 33 | subscriptionId 34 | ); 35 | const result = await computeClient.virtualMachines.powerOff( 36 | resourceGroupName, 37 | vmResourceName 38 | ); 39 | return result; 40 | } 41 | 42 | stopVM() 43 | .then((result) => { 44 | console.log(JSON.stringify(result)); 45 | }) 46 | .catch((err) => { 47 | console.log(err); 48 | }); 49 | /* 50 | 51 | Stop operation results: 52 | 53 | { 54 | "startTime":"2021-10-27T16:35:59.6006484+00:00", 55 | "endTime":"2021-10-27T16:35:59.850632+00:00", 56 | "status":"Succeeded", 57 | "name":"1773c5e7-d904-4f98-b2a6-6e2f2465407f" 58 | } 59 | 60 | */ 61 | -------------------------------------------------------------------------------- /resources/virtual-machines/vm-info.js: -------------------------------------------------------------------------------- 1 | const { 2 | ClientSecretCredential, 3 | DefaultAzureCredential, 4 | } = require("@azure/identity"); 5 | const { ComputeManagementClient } = require("@azure/arm-compute"); 6 | 7 | // Azure authentication in environment variables for DefaultAzureCredential 8 | let credentials = null; 9 | const tenantId = 10 | process.env["AZURE_TENANT_ID"] || "REPLACE-WITH-YOUR-TENANT-ID"; 11 | const clientId = 12 | process.env["AZURE_CLIENT_ID"] || "REPLACE-WITH-YOUR-CLIENT-ID"; 13 | const secret = 14 | process.env["AZURE_CLIENT_SECRET"] || "REPLACE-WITH-YOUR-CLIENT-SECRET"; 15 | const subscriptionId = 16 | process.env["AZURE_SUBSCRIPTION_ID"] || "REPLACE-WITH-YOUR-SUBSCRIPTION_ID"; 17 | 18 | const resourceGroupName = "REPLACE-WITHYOUR-RESOURCE_GROUP-NAME"; 19 | const vmResourceName = "REPLACE-WITHYOUR-RESOURCE-NAME"; 20 | 21 | if (process.env.production) { 22 | // production 23 | credentials = new DefaultAzureCredential(); 24 | } else { 25 | // development 26 | credentials = new ClientSecretCredential(tenantId, clientId, secret); 27 | console.log("development"); 28 | } 29 | 30 | async function getVmInfo() { 31 | const computeClient = new ComputeManagementClient( 32 | credentials, 33 | subscriptionId 34 | ); 35 | const result = await computeClient.virtualMachines.get( 36 | resourceGroupName, 37 | vmResourceName 38 | ); 39 | return result; 40 | } 41 | 42 | getVmInfo() 43 | .then((result) => { 44 | console.log(JSON.stringify(result)); 45 | }) 46 | .catch((err) => { 47 | console.log(err); 48 | }); 49 | 50 | /* 51 | { 52 | "id": "/subscriptions/123456/resourceGroups/johnsmith-testrg3215/providers/Microsoft.Compute/virtualMachines/johnsmithvm6859", 53 | "name": "johnsmithvm6859", 54 | "type": "Microsoft.Compute/virtualMachines", 55 | "location": "eastus", 56 | "hardwareProfile": { "vmSize": "Standard_B1ls" }, 57 | "storageProfile": { 58 | "imageReference": { 59 | "publisher": "Canonical", 60 | "offer": "UbuntuServer", 61 | "sku": "14.04.3-LTS", 62 | "version": "14.04.201805220", 63 | "exactVersion": "14.04.201805220" 64 | }, 65 | "osDisk": { 66 | "osType": "Linux", 67 | "name": "johnsmithosdisk9293", 68 | "vhd": { 69 | "uri": "https://johnsmithac1195.blob.core.windows.net/nodejscontainer/osnodejslinux.vhd" 70 | }, 71 | "caching": "None", 72 | "createOption": "FromImage", 73 | "diskSizeGB": 30, 74 | "deleteOption": "Detach" 75 | }, 76 | "dataDisks": [] 77 | }, 78 | "osProfile": { 79 | "computerName": "johnsmithvm6859", 80 | "adminUsername": "notadmin", 81 | "linuxConfiguration": { 82 | "disablePasswordAuthentication": false, 83 | "provisionVMAgent": true, 84 | "patchSettings": { 85 | "patchMode": "ImageDefault", 86 | "assessmentMode": "ImageDefault" 87 | } 88 | }, 89 | "secrets": [], 90 | "allowExtensionOperations": true, 91 | "requireGuestProvisionSignal": true 92 | }, 93 | "networkProfile": { 94 | "networkInterfaces": [ 95 | { 96 | "id": "/subscriptions/123456/resourceGroups/johnsmith-testrg3215/providers/Microsoft.Network/networkInterfaces/johnsmithnic7962", 97 | "primary": true 98 | } 99 | ] 100 | }, 101 | "provisioningState": "Succeeded", 102 | "vmId": "987654" 103 | } 104 | */ 105 | -------------------------------------------------------------------------------- /search/bulk-insert-books-from-csv/books.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": [ 3 | { 4 | "key": true, 5 | "name": "id", 6 | "type": "Edm.String", 7 | "retrievable": true 8 | }, 9 | { 10 | "name": "goodreads_book_id", 11 | "type": "Edm.String", 12 | "retrievable": true 13 | }, 14 | { 15 | "name": "best_book_id", 16 | "type": "Edm.String", 17 | "retrievable": true 18 | }, 19 | { 20 | "name": "work_id", 21 | "type": "Edm.String", 22 | "retrievable": true 23 | }, 24 | { 25 | "name": "books_count", 26 | "type": "Edm.Double", 27 | "retrievable": true 28 | }, 29 | { 30 | "name": "isbn", 31 | "type": "Edm.String", 32 | "retrievable": true, 33 | "searchable": true, 34 | "analyzer": "standard.lucene" 35 | }, 36 | { 37 | "name": "isbn13", 38 | "type": "Edm.String", 39 | "retrievable": true 40 | }, 41 | { 42 | "name": "authors", 43 | "type": "Collection(Edm.String)", 44 | "retrievable": true, 45 | "filterable": true, 46 | "searchable": true, 47 | "facetable":true, 48 | "analyzer": "en.lucene" 49 | }, 50 | { 51 | "name": "original_publication_year", 52 | "type": "Edm.Int32", 53 | "retrievable": true 54 | }, 55 | { 56 | "name": "original_title", 57 | "type": "Edm.String", 58 | "retrievable": true, 59 | "searchable": true, 60 | "analyzer": "en.lucene" 61 | }, 62 | { 63 | "name": "title", 64 | "type": "Edm.String", 65 | "retrievable": true, 66 | "searchable": true, 67 | "analyzer": "en.lucene" 68 | }, 69 | { 70 | "name": "language_code", 71 | "type": "Edm.String", 72 | "retrievable": true, 73 | "filterable": true, 74 | "facetable": true 75 | }, 76 | { 77 | "name": "average_rating", 78 | "type": "Edm.Int32", 79 | "retrievable": true, 80 | "filterable": true, 81 | "sortable": true 82 | }, 83 | { 84 | "name": "ratings_count", 85 | "type": "Edm.Int32", 86 | "retrievable": true, 87 | "filterable": true, 88 | "facetable": true 89 | }, 90 | { 91 | "name": "work_ratings_count", 92 | "type": "Edm.Int32", 93 | "retrievable": true 94 | }, 95 | { 96 | "name": "work_text_reviews_count", 97 | "type": "Edm.Int32", 98 | "retrievable": true 99 | }, 100 | { 101 | "name": "ratings_1", 102 | "type": "Edm.Int32", 103 | "retrievable": true 104 | }, 105 | { 106 | "name": "ratings_2", 107 | "type": "Edm.Int32", 108 | "retrievable": true 109 | }, 110 | { 111 | "name": "ratings_3", 112 | "type": "Edm.Int32", 113 | "retrievable": true 114 | }, 115 | { 116 | "name": "ratings_4", 117 | "type": "Edm.Int32", 118 | "retrievable": true 119 | }, 120 | { 121 | "name": "ratings_5", 122 | "type": "Edm.Int32", 123 | "retrievable": true 124 | }, 125 | { 126 | "name": "image_url", 127 | "type": "Edm.String", 128 | "retrievable": true 129 | }, 130 | { 131 | "name": "small_image_url", 132 | "type": "Edm.String", 133 | "retrievable": true 134 | } 135 | ], 136 | "suggesters": [ 137 | { 138 | "name": "sg", 139 | "searchMode": "analyzingInfixMatching", 140 | "sourceFields": [ 141 | "authors", 142 | "original_title" 143 | ] 144 | } 145 | ] 146 | } -------------------------------------------------------------------------------- /search/bulk-insert-books-from-csv/bulk_insert_books.js: -------------------------------------------------------------------------------- 1 | // Works with Node.js 15+ 2 | 3 | const fs = require('fs'); 4 | const parse = require('csv-parser') 5 | const { finished } = require('stream/promises'); 6 | const { SearchClient, SearchIndexClient, AzureKeyCredential } = require("@azure/search-documents"); 7 | 8 | const SEARCH_ENDPOINT = "https://YOUR-RESOURCE-NAME.search.windows.net"; 9 | const SEARCH_KEY = "YOUR-RESOURCE-KEY"; 10 | 11 | const SEARCH_INDEX_NAME = "good-books"; 12 | const csvFile = './books.csv' 13 | const schema = require("./books.schema.json"); 14 | 15 | const client = new SearchClient( 16 | SEARCH_ENDPOINT, 17 | SEARCH_INDEX_NAME, 18 | new AzureKeyCredential(SEARCH_KEY) 19 | ); 20 | const clientIndex = new SearchIndexClient( 21 | SEARCH_ENDPOINT, 22 | new AzureKeyCredential(SEARCH_KEY) 23 | ); 24 | 25 | 26 | // insert each row into ... 27 | const insertData = async (readable) => { 28 | 29 | let i = 0; 30 | 31 | for await (const row of readable) { 32 | console.log(`${i++} = ${JSON.stringify(row)}`); 33 | 34 | const indexItem = { 35 | "id": row.book_id, 36 | "goodreads_book_id": row.goodreads_book_id, 37 | "best_book_id": row.best_book_id, 38 | "work_id": row.work_id, 39 | "books_count": !row.books_count ? 0 : parseInt(row.books_count), 40 | "isbn": row.isbn, 41 | "isbn13": row.isbn13, 42 | "authors": row.authors.split(",").map(name => name.trim()), 43 | "original_publication_year": !row.original_publication_year ? 0 : parseInt(row.original_publication_year), 44 | "original_title": row.original_title, 45 | "title": row.title, 46 | "language_code": row.language_code, 47 | "average_rating": !row.average_rating ? 0 : parseInt(row.average_rating), 48 | "ratings_count": !row.ratings_count ? 0 : parseInt(row.ratings_count), 49 | "work_ratings_count": !row.work_ratings_count ? 0 : parseInt(row.work_ratings_count), 50 | "work_text_reviews_count": !row.work_text_reviews_count ? 0 : parseInt(row.work_text_reviews_count), 51 | "ratings_1": !row.ratings_1 ? 0 : parseInt(row.ratings_1), 52 | "ratings_2": !row.ratings_2 ? 0 : parseInt(row.ratings_2), 53 | "ratings_3": !row.ratings_3 ? 0 : parseInt(row.ratings_3), 54 | "ratings_4": !row.ratings_4 ? 0 : parseInt(row.ratings_4), 55 | "ratings_5": !row.ratings_5 ? 0 : parseInt(row.ratings_5), 56 | "image_url": row.image_url, 57 | "small_image_url": row.small_image_url 58 | } 59 | 60 | 61 | const uploadResult = await client.uploadDocuments([indexItem]); 62 | } 63 | 64 | } 65 | const bulkInsert = async () => { 66 | 67 | 68 | // read file, parse CSV, each row is a chunk 69 | const readable = fs 70 | .createReadStream(csvFile) 71 | .pipe(parse()); 72 | 73 | // Pipe rows to insert function 74 | await insertData(readable) 75 | } 76 | async function createIndex() { 77 | 78 | schema.name = SEARCH_INDEX_NAME; 79 | const result = await clientIndex.createIndex(schema); 80 | 81 | console.log(result); 82 | } 83 | 84 | 85 | const main = async () => { 86 | 87 | await createIndex(); 88 | console.log("index created"); 89 | 90 | await bulkInsert(); 91 | 92 | } 93 | 94 | 95 | main() 96 | .then(() => console.log('done')) 97 | .catch((err) => { 98 | console.log(`done + failed ${err}`) 99 | }); 100 | -------------------------------------------------------------------------------- /search/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "search", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/search-documents": "^11.1.0", 14 | "csv-parser": "^3.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /search/readme.md: -------------------------------------------------------------------------------- 1 | # Azure Cognitive Search 2 | 3 | ## Create Search resource 4 | 5 | ```azurecli 6 | 7 | ``` 8 | 9 | 10 | ## Bulk insert from CSV to Search 11 | 12 | Use `./bulk-insert-books-from-csv/bulk_insert_books.js` to bulk insert from `./bulk-insert-books-from-csv/books.csv` into a new Search index: 13 | 14 | * Get your resource name and key 15 | * Add/change in `./bulk-insert-books-from-csv/bulk_insert_books.js` 16 | 17 | ## Use Search in sample app 18 | 19 | * Use [azure-search-react-template](https://github.com/dereklegenzoff/azure-search-react-template) sample web site to use Search 20 | 21 | -------------------------------------------------------------------------------- /storage/blob-paging/blob-paging.js: -------------------------------------------------------------------------------- 1 | const { BlobServiceClient } = require("@azure/storage-blob"); 2 | 3 | const blobAccountConnectionString = "REPLACE-WITH-YOUR-STORAGE-CONNECTION-STRING"; 4 | const blobAccountContainerName = "REPLACE-WITH-YOUR-STORAGE-CONTAINER-NAME"; 5 | 6 | const pageSize = 2; 7 | 8 | const list = async () => { 9 | 10 | console.log(`List`); 11 | 12 | let continuationToken = ""; 13 | let currentPage = 1; 14 | let containerClient=null; 15 | let currentItem = 1; 16 | 17 | // Get Blob Container - need to have items in container before running this code 18 | const blobServiceClient = BlobServiceClient.fromConnectionString(blobAccountConnectionString); 19 | containerClient = blobServiceClient.getContainerClient(blobAccountContainerName); 20 | 21 | do { 22 | 23 | // Get Page of Blobs 24 | iterator = (continuationToken != "") 25 | ? containerClient.listBlobsFlat().byPage({ maxPageSize: pageSize, continuationToken }) 26 | : containerClient.listBlobsFlat().byPage({ maxPageSize: pageSize }); 27 | 28 | page = (await iterator.next()).value; 29 | 30 | // Display list 31 | if (page.segment?.blobItems) { 32 | console.log(`\tPage [${currentPage}] `); 33 | for (const blob of page.segment.blobItems) { 34 | console.log(`\t\tItem [${currentItem++}] ${blob.name}`); 35 | } 36 | }; 37 | 38 | // Move to next page 39 | continuationToken = page.continuationToken; 40 | if (continuationToken) { 41 | currentPage++; 42 | } 43 | 44 | } while (continuationToken != "") 45 | } 46 | 47 | list(() => { 48 | console.log("done"); 49 | }).catch((ex) => 50 | console.log(ex) 51 | ); 52 | -------------------------------------------------------------------------------- /storage/blob-paging/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blob-paging", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/storage-blob": "^12.8.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /storage/file-paging/README.md: -------------------------------------------------------------------------------- 1 | # Storage samples 2 | 3 | ## page-through-files 4 | 5 | ### Create resource 6 | 7 | 1. Create an Azure Storage resource. 8 | 1. Create a file share and directory, then upload at least 5 files to the directory. 9 | 10 | ### Set up environment variables 11 | 12 | 1. Copy the following from the Azure portal for your Azure Storage resource, then set in the corresponding environment variables: 13 | 14 | |Object Type|Storage setting|Environment variable| 15 | |--|--|--| 16 | |Files| Storage Account Name|AzureStorageResourceNameForFiles| 17 | |Files| Storage Account Key|AzureStorageResourceKeyForFiles| 18 | |Files| Storage Account Share Name|AzureStorageResourceShareNameForFiles| 19 | |Files| Storage Account Directory Name (under the share)|AzureStorageResourceDirectoryNameForFiles| 20 | 21 | 22 | ### Run code 23 | 24 | 1. Install and run code: 25 | 26 | ```javascript 27 | npm install && node page-through-files.js 28 | ``` -------------------------------------------------------------------------------- /storage/file-paging/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "page-through-files", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/storage-file-share": "^12.8.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /storage/file-paging/page-through-files.js: -------------------------------------------------------------------------------- 1 | const { 2 | ShareClient, 3 | StorageSharedKeyCredential 4 | } = require("@azure/storage-file-share"); 5 | 6 | // Page of Files 7 | const pageSize = 2; 8 | 9 | // RESOURCE 10 | const fileAccountName = process.env["AzureStorageResourceNameForFiles"] || ""; 11 | const fileAccountKey = process.env["AzureStorageResourceKeyForFiles"] || ""; 12 | const fileAccountShareName = process.env["AzureStorageResourceShareNameForFiles"] || ""; 13 | const fileAccountDirectoryName = process.env["AzureStorageResourceDirectoryNameForFiles"] || ""; 14 | 15 | // Create client 16 | const getFileDirectoryAccountClient = () => { 17 | 18 | // create account sas token for file service 19 | const fileCreds = new StorageSharedKeyCredential( 20 | fileAccountName, 21 | fileAccountKey 22 | ); 23 | 24 | //get file share client 25 | const shareClient = new ShareClient( 26 | `https://${fileAccountName}.file.core.windows.net/${fileAccountShareName}`, 27 | fileCreds 28 | ); 29 | const fileDirectoryClient = shareClient.getDirectoryClient(fileAccountDirectoryName); 30 | return { fileDirectoryClient }; 31 | } 32 | 33 | const getPage = async (directoryClient, continuationToken) => { 34 | 35 | let pageIterator, pageResponse; 36 | 37 | if (continuationToken) { 38 | 39 | // Get next page 40 | pageIterator = directoryClient 41 | .listFilesAndDirectories() 42 | .byPage({ continuationToken, maxPageSize: pageSize }); 43 | 44 | } else { 45 | 46 | // Get 1st page 47 | pageIterator = directoryClient 48 | .listFilesAndDirectories() 49 | .byPage({ maxPageSize: pageSize }); 50 | } 51 | 52 | pageResponse = (await pageIterator.next()).value; 53 | return pageResponse; 54 | } 55 | const list = async () =>{ 56 | 57 | // Get Azure SDK Storage clients for this task 58 | const { fileDirectoryClient } = getFileDirectoryAccountClient(); 59 | 60 | // Keep continuation token for next page 61 | let continuationToken = null; 62 | 63 | // Track file count 64 | let i = 1; 65 | let pageCount = 1; 66 | 67 | do { 68 | 69 | // Get 1 page of data 70 | const listFiles = await getPage(fileDirectoryClient, continuationToken); 71 | 72 | // Store returned continuationToken for next Page 73 | continuationToken = listFiles.continuationToken; 74 | 75 | console.log(`Page ${pageCount++}\t\tContinuation token:${continuationToken}`); 76 | 77 | // List files in Page 78 | for (const fileItem of listFiles.segment.fileItems) { 79 | console.log(`${i++} - file\t: ${fileItem.name}`); 80 | } 81 | 82 | // stop when there are no more directories and no more pages 83 | } while (continuationToken !== ""); 84 | } 85 | 86 | list() 87 | .then(() => 88 | console.log(`done`) 89 | ).catch((ex) => 90 | console.log(ex) 91 | ); -------------------------------------------------------------------------------- /storage/queue/convert-into-and-out-of-string.js: -------------------------------------------------------------------------------- 1 | // Message queue messages are strings. All data must eventuall 2 | // turn into a string. These functions are meant to move JSON 3 | // data into and out of base 64 format. 4 | 5 | function jsonToBase64(jsonObj) { 6 | const jsonString = JSON.stringify(jsonObj) 7 | return Buffer.from(jsonString).toString('base64') 8 | } 9 | function encodeBase64ToJson(base64String) { 10 | const jsonString = Buffer.from(base64String,'base64').toString() 11 | return JSON.parse(jsonString) 12 | } 13 | -------------------------------------------------------------------------------- /storage/upload-files-from-url/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\${file}" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /storage/upload-files-from-url/README.md: -------------------------------------------------------------------------------- 1 | # Storage samples 2 | 3 | ## copy-from-files-to-blobs.js 4 | 5 | ### Create resources 6 | 7 | While this sample should work with two separate Azure Storage resources, it was tested with the same, single resource. 8 | 9 | 1. Create an Azure Storage resource. 10 | 1. Create a file share and directory, then upload at least 5 files to the directory. 11 | 1. Create a container directory. It can be the same name as the file directory. 12 | 13 | ### Set up environment variables 14 | 15 | 1. Copy the following from the Azure portal for your Azure Storage resource, then set in the corresponding environment variables: 16 | 17 | |Object Type|Storage setting|Environment variable| 18 | |--|--|--| 19 | |Files| Storage Account Name|AzureStorageResourceNameForFiles| 20 | |Files| Storage Account Key|AzureStorageResourceKeyForFiles| 21 | |Files| Storage Account Share Name|AzureStorageResourceShareNameForFiles| 22 | |Files| Storage Account Directory Name (under the share)|AzureStorageResourceDirectoryNameForFiles| 23 | |Blobs| Storage Account Connection String|AzureStorageResourceConnectionString| 24 | |Blobs| Storage Account Directory Name|AzureStorageResourceDirectoryNameForBlobs| 25 | 26 | ### Run code 27 | 28 | 1. Install and run code: 29 | 30 | ```javascript 31 | npm install && node copy-from-files-to-blobs.js 32 | ``` -------------------------------------------------------------------------------- /storage/upload-files-from-url/copy-from-files-to-blobs.js: -------------------------------------------------------------------------------- 1 | const { BlobServiceClient } = require("@azure/storage-blob"); 2 | const { 3 | ShareClient, 4 | generateAccountSASQueryParameters, 5 | StorageSharedKeyCredential, 6 | AccountSASResourceTypes, 7 | AccountSASPermissions, 8 | AccountSASServices, 9 | } = require("@azure/storage-file-share"); 10 | 11 | // Page of Files 12 | const pageSize = 2; 13 | 14 | // COPY FROM THIS RESOURCE 15 | const fileAccountName = process.env["AzureStorageResourceNameForFiles"] || ""; 16 | const fileAccountKey = process.env["AzureStorageResourceKeyForFiles"] || ""; 17 | const fileAccountShareName = process.env["AzureStorageResourceShareNameForFiles"] || ""; 18 | const fileAccountDirectoryName = process.env["AzureStorageResourceDirectoryNameForFiles"] || ""; 19 | 20 | // COPY TO THIS RESOURCE 21 | const blobAccountConnectionString = process.env["AzureStorageResourceConnectionString"] || ""; 22 | const blobAccountDirectoryName = process.env["AzureStorageResourceDirectoryNameForBlobs"] || ""; 23 | 24 | // Create From client 25 | const getFileDirectoryAccountClient = () => { 26 | 27 | // create account sas token for file service 28 | const fileCreds = new StorageSharedKeyCredential( 29 | fileAccountName, 30 | fileAccountKey 31 | ); 32 | const accountSas = generateAccountSASQueryParameters( 33 | { 34 | startsOn: new Date(new Date().valueOf() - 8640), 35 | expiresOn: new Date(new Date().valueOf() + 86400000), 36 | resourceTypes: AccountSASResourceTypes.parse("sco").toString(), 37 | permissions: AccountSASPermissions.parse("rwdlc").toString(), 38 | services: AccountSASServices.parse("f").toString(), 39 | }, 40 | fileCreds 41 | ).toString(); 42 | 43 | console.log(`accountSas = ${accountSas}`) 44 | 45 | //get file share client 46 | const shareClient = new ShareClient( 47 | `https://${fileAccountName}.file.core.windows.net/${fileAccountShareName}`, 48 | fileCreds 49 | ); 50 | const fileDirectoryClient = shareClient.getDirectoryClient(fileAccountDirectoryName); 51 | return { accountSas, fileDirectoryClient }; 52 | } 53 | 54 | // Create To client 55 | const getBlobAccountClient = async () => { 56 | 57 | // get container client 58 | const blobServiceClient = BlobServiceClient.fromConnectionString(blobAccountConnectionString); 59 | 60 | // get container's directory client 61 | var containerClient = blobServiceClient.getContainerClient(blobAccountDirectoryName); 62 | 63 | // create container if it doesn't already exist 64 | await containerClient.createIfNotExists(); 65 | 66 | return containerClient; 67 | } 68 | 69 | const copyFileToBlob = async (blobClient, toBlobName, fromSourceUrl) => { 70 | 71 | try { 72 | 73 | 74 | const res = await ( 75 | 76 | // copy file to blob from URL 77 | await blobClient 78 | .getBlobClient(toBlobName) 79 | .beginCopyFromURL(fromSourceUrl) 80 | 81 | ).pollUntilDone(); // wait until finished with pollUntilDone 82 | 83 | console.log(res.copyStatus); 84 | 85 | } catch (error) { 86 | 87 | // Copy into Blob failed 88 | throw error; 89 | } 90 | } 91 | 92 | const getFilesPage = async (directoryClient, continuationToken) => { 93 | 94 | let pageIterator, pageResponse; 95 | 96 | if (continuationToken) { 97 | 98 | // Get next page 99 | pageIterator = directoryClient 100 | .listFilesAndDirectories() 101 | .byPage({ continuationToken, maxPageSize: pageSize }); 102 | 103 | } else { 104 | 105 | // Get page 106 | pageIterator = directoryClient 107 | .listFilesAndDirectories() 108 | .byPage({ maxPageSize: pageSize }); 109 | } 110 | 111 | pageResponse = (await pageIterator.next()).value; 112 | return pageResponse; 113 | } 114 | async function copy() { 115 | 116 | // Get Azure SDK Storage clients for this task 117 | const { accountSas, fileDirectoryClient } = getFileDirectoryAccountClient(); 118 | const blobContainerClient = await getBlobAccountClient(); 119 | 120 | // Keep a list of directories 121 | let arrFolders = []; 122 | 123 | // Keep continuation token for next page 124 | let continuationToken = null; 125 | 126 | // Track file count 127 | let i = 1; 128 | 129 | arrFolders.push(fileAccountShareName); 130 | do { 131 | 132 | let fileAccountDirectoryName = arrFolders.pop(); 133 | 134 | console.log(`List directories and files under directory ${fileAccountDirectoryName}`); 135 | 136 | // Get 1 page of data 137 | const listFilesAndDirectoriesResponse = await getFilesPage(fileDirectoryClient, continuationToken); 138 | 139 | // Store returned continuationToken for next Page 140 | continuationToken = listFilesAndDirectoriesResponse.continuationToken; 141 | 142 | // Add returned directories to array to process 143 | for (const dirItem of listFilesAndDirectoriesResponse.segment.directoryItems) { 144 | console.log(`${i++} - directory\t: ${dirItem.name}`); 145 | arrFolders.push( 146 | fileAccountDirectoryName == "" ? dirItem.name : fileAccountDirectoryName + "\\" + dirItem.name 147 | ); 148 | } 149 | 150 | // Copy files to blobs 151 | for (const fileItem of listFilesAndDirectoriesResponse.segment.fileItems) { 152 | 153 | console.log(`${i++} - file\t: ${fileItem.name}`); 154 | 155 | const fileClient = fileDirectoryClient.getFileClient(fileItem.name); 156 | const sourceURL = fileClient.url + "?" + accountSas; 157 | await copyFileToBlob(blobContainerClient, fileItem.name, sourceURL); 158 | 159 | } 160 | 161 | // stop when there are no more directories and no more pages 162 | } while (arrFolders.length > 0 || continuationToken !== ""); 163 | } 164 | 165 | copy() 166 | .then(() => 167 | console.log(`done`) 168 | ).catch((ex) => 169 | console.log(ex) 170 | ); -------------------------------------------------------------------------------- /storage/upload-files-from-url/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upload-files-from-url", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/storage-blob": "^12.8.0", 14 | "@azure/storage-file-share": "^12.8.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /storage/upload-string-to-file/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upload-string-to-file", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@azure/storage-file-share": "^12.8.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /storage/upload-string-to-file/upload-string-to-file.js: -------------------------------------------------------------------------------- 1 | const { ShareServiceClient, StorageSharedKeyCredential } = require("@azure/storage-file-share"); 2 | 3 | // Enter your storage account name and shared key 4 | const account = process.env["AzureStorageResourceNameForFiles"] || ""; 5 | const accountKey = process.env["AzureStorageResourceKeyForFiles"] || ""; 6 | 7 | const main = async () => { 8 | 9 | // Use SharedKeyCredential with storage account and account key 10 | // SharedKeyCredential is only avaiable in Node.js runtime, not in browsers 11 | const sharedKeyCredential = new StorageSharedKeyCredential(account, accountKey); 12 | const serviceClient = new ShareServiceClient( 13 | // When using AnonymousCredential, following url should include a valid SAS 14 | `https://${account}.file.core.windows.net`, 15 | sharedKeyCredential 16 | ); 17 | 18 | // Create new share and directory 19 | const shareName = `newshare${new Date().getTime()}`; 20 | const shareClient = serviceClient.getShareClient(shareName); 21 | await shareClient.create(); 22 | console.log(`Create share ${shareName} successfully`); 23 | 24 | const directoryName = `newdirectory${new Date().getTime()}`; 25 | const directoryClient = shareClient.getDirectoryClient(directoryName); 26 | await directoryClient.create(); 27 | console.log(`Create directory ${directoryName} successfully`); 28 | 29 | // Create an azure file then upload to it 30 | const content = "Hello World!"; 31 | const fileName = "newfile" + new Date().getTime(); 32 | const fileClient = directoryClient.getFileClient(fileName); 33 | await fileClient.create(content.length); 34 | console.log(`Create file ${fileName} successfully`); 35 | 36 | // Upload file range 37 | await fileClient.uploadRange(content, 0, content.length); 38 | console.log(`Upload file range "${content}" to ${fileName} successfully`); 39 | 40 | // List files and directories under a directory 41 | // Use DirectoryClient.listFilesAndDirectories() to iterator over files and directories, with the new for-await-of syntax. The kind property can be used to identify whether a iterm is a directory or a file. 42 | 43 | let dirIter1 = directoryClient.listFilesAndDirectories(); 44 | let i = 1; 45 | for await (const item of dirIter1) { 46 | if (item.kind === "directory") { 47 | console.log(`${i} - directory\t: ${item.name}`); 48 | } else { 49 | console.log(`${i} - file\t: ${item.name}`); 50 | } 51 | i++; 52 | } 53 | } 54 | 55 | 56 | main() 57 | .then(() => 58 | console.log(`done`) 59 | ).catch((ex) => 60 | console.log(ex) 61 | ); 62 | -------------------------------------------------------------------------------- /storage/upload-url-to-blob-poll-until-done/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\${file}" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /storage/upload-url-to-blob-poll-until-done/README.md: -------------------------------------------------------------------------------- 1 | # Storage samples 2 | 3 | ## upload-url-to-blob-poll-until-done.js 4 | 5 | ### Create resources 6 | 7 | 1. Create an Azure Storage resource. 8 | 1. Copy connection string into variable. 9 | 10 | ### Run code 11 | 12 | 1. Install and run code: 13 | 14 | ```javascript 15 | npm install && node upload-url-to-blob-poll-until-done.js 16 | ``` -------------------------------------------------------------------------------- /storage/upload-url-to-blob-poll-until-done/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upload-url-to-blob-poll-until-done", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@azure/storage-blob": "^12.8.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /storage/upload-url-to-blob-poll-until-done/upload-url-to-blob-poll-until-done-by-page.js: -------------------------------------------------------------------------------- 1 | const { BlobServiceClient } = require("@azure/storage-blob"); 2 | 3 | const blobAccountConnectionString = process.env["AzureStorageResourceConnectionString"] || ""; 4 | const blobAccountDirectoryName = process.env["AzureStorageResourceDirectoryNameForBlobs"] || `test-${Date.now().toString()}`; 5 | 6 | const pageSize = 2; 7 | 8 | let containerClient=null; 9 | let currentItem = 1; 10 | 11 | // publicly accessible files 12 | const files = [ 13 | { 14 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/README.md", 15 | "fileName": "README.md" 16 | }, 17 | { 18 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/gulpfile.ts", 19 | "fileName": "gulpfile.ts" 20 | }, 21 | { 22 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/rush.json", 23 | "fileName": "rush.json" 24 | }, 25 | { 26 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/package.json", 27 | "fileName": "package.json" 28 | }, 29 | { 30 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/tsdoc.json", 31 | "fileName": "tsdoc.json" 32 | }, 33 | ]; 34 | 35 | const awaitTimeout = delay => new Promise(resolve => setTimeout(resolve, delay)); 36 | 37 | const init = async() =>{ 38 | 39 | // get container client 40 | const blobServiceClient = BlobServiceClient.fromConnectionString(blobAccountConnectionString); 41 | 42 | // get container's directory client 43 | containerClient = blobServiceClient.getContainerClient(blobAccountDirectoryName); 44 | 45 | // create container if it doesn't already exist 46 | await containerClient.createIfNotExists(); 47 | 48 | } 49 | const upload = async () => { 50 | 51 | console.log(`Upload to ${blobAccountDirectoryName}`); 52 | 53 | files.forEach(async (file) => { 54 | 55 | console.log(`\t\t${currentItem++} - ${file.fileName}`); 56 | 57 | await ( 58 | 59 | await containerClient 60 | .getBlobClient(file.fileName) 61 | .beginCopyFromURL(file.url) 62 | 63 | ).pollUntilDone(); 64 | }) 65 | } 66 | 67 | const list = async () => { 68 | 69 | console.log(`List`); 70 | 71 | let continuationToken = ""; 72 | 73 | let currentPage = 1; 74 | 75 | do { 76 | 77 | // Get Page of Blobs 78 | iterator = (continuationToken != "") ? containerClient.listBlobsFlat().byPage({ maxPageSize: pageSize, continuationToken }) : containerClient.listBlobsFlat().byPage({ maxPageSize: pageSize }); 79 | page = (await iterator.next()).value; 80 | 81 | // Display list 82 | if (page.segment?.blobItems) { 83 | console.log(`\tPage [${currentPage}] `); 84 | for (const blob of page.segment.blobItems) { 85 | console.log(`\t\tItem [${currentItem++}] ${blob.name}`); 86 | } 87 | }; 88 | 89 | // Move to next page 90 | continuationToken = page.continuationToken; 91 | if (continuationToken) { 92 | currentPage++; 93 | } 94 | 95 | } while (continuationToken != "") 96 | 97 | } 98 | 99 | const main = async () =>{ 100 | 101 | await init(); 102 | await upload(); 103 | 104 | currentItem=1; 105 | 106 | //awaitTimeout(5000).then(async() =>{ 107 | await list(); 108 | //}); 109 | 110 | } 111 | 112 | main(() => { 113 | console.log("done"); 114 | }).catch((ex) => 115 | console.log(ex) 116 | ); 117 | 118 | -------------------------------------------------------------------------------- /storage/upload-url-to-blob-poll-until-done/upload-url-to-blob-poll-until-done.js: -------------------------------------------------------------------------------- 1 | const { BlobServiceClient } = require("@azure/storage-blob"); 2 | 3 | const blobAccountConnectionString = "REPLACE-WITH-YOUR-STORAGE-CONNECTION-STRING"; 4 | const blobAccountContainerName = `test-${Date.now().toString()}`; 5 | 6 | const files = [ 7 | { 8 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/README.md", 9 | "fileName": "README.md" 10 | }, 11 | { 12 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/gulpfile.ts", 13 | "fileName": "gulpfile.ts" 14 | }, 15 | { 16 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/rush.json", 17 | "fileName": "rush.json" 18 | }, 19 | { 20 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/package.json", 21 | "fileName": "package.json" 22 | }, 23 | { 24 | "url": "https://github.com/Azure/azure-sdk-for-js/blob/main/tsdoc.json", 25 | "fileName": "tsdoc.json" 26 | }, 27 | ]; 28 | 29 | const upload = async() => { 30 | 31 | // get container client 32 | const blobServiceClient = BlobServiceClient.fromConnectionString(blobAccountConnectionString); 33 | 34 | // get container's directory client 35 | const containerClient = blobServiceClient.getContainerClient(blobAccountContainerName); 36 | 37 | files.forEach(async(file) =>{ 38 | await ( 39 | 40 | await containerClient 41 | .getBlobClient(file.fileName) 42 | .beginCopyFromURL(file.url) 43 | 44 | ).pollUntilDone(); 45 | }) 46 | } 47 | 48 | upload(() => { 49 | console.log("done"); 50 | }).catch((ex) => 51 | console.log(ex) 52 | ); 53 | -------------------------------------------------------------------------------- /vscode-docs-linter/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint":{ 3 | "plugins": [ 4 | "prettier" 5 | ], 6 | "root": true, 7 | "extends": [ 8 | "eslint:recommended", 9 | "airbnb-base", 10 | "prettier" 11 | ], 12 | "env": { 13 | "commonjs": true, 14 | "es2020": true, 15 | "node": true, 16 | "jest": true 17 | }, 18 | "parserOptions": { 19 | "ecmaVersion": 11 20 | }, 21 | "ignorePatterns": [ 22 | ".vscode", 23 | ".devcontainer", 24 | "dist" 25 | ], 26 | "rules": { 27 | "semi": [ 28 | "warn", 29 | "always" 30 | ], 31 | "quotes": [ 32 | "error", 33 | "single" 34 | ], 35 | "no-console": [ 36 | 0 37 | ], 38 | "no-return-await": [ 39 | "warn" 40 | ] 41 | } 42 | }, 43 | "prettier":{ 44 | "trailingComma": "all", 45 | "tabWidth": 2, 46 | "semi": true, 47 | "singleQuote": false, 48 | "bracketSpacing": true, 49 | "printWidth": 120, 50 | "arrowParens": "avoid" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /windows-terminal/readme.md: -------------------------------------------------------------------------------- 1 | # Windows Terminal 2 | 3 | ## Set default terminal to bash 4 | 5 | 1. Right-click on Settings 6 | 1. In the `settings.json` file that opens, change the `defaultProfile` value to the bash Id. 7 | 8 | ## Set default directory 9 | 10 | 1. Right-click on Settings 11 | 1. In the `settings.json` file that opens, add a property to the profile item named `startingDirectory` with the new value. 12 | 13 | ```json 14 | "startingDirectory": "C:\\repos\\", 15 | ``` 16 | --------------------------------------------------------------------------------