├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── ConsumerFunction ├── ConsumerFunction │ ├── function.json │ └── run.py ├── README.md └── deploy_vscode.png ├── LICENSE.md ├── Pipfile ├── Pipfile.lock ├── README.md ├── create_arm_event_subscriptions.py ├── create_eg_topics_and_event_subscriptions.py ├── create_storage_event_subscriptions.py ├── env_template └── event_grid_publisher.py /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "azureFunctions.projectLanguage": "Python", 3 | "azureFunctions.projectRuntime": "~1" 4 | } -------------------------------------------------------------------------------- /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.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., label, 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 | -------------------------------------------------------------------------------- /ConsumerFunction/ConsumerFunction/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "anonymous", 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 | "disabled": false 19 | } -------------------------------------------------------------------------------- /ConsumerFunction/ConsumerFunction/run.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | # 5 | # This code is designed for Azure Function for Python v1. 6 | # 7 | # This could be copied/pasted directly into a Python function, 8 | # or to be deployed using VSCode 9 | # https://code.visualstudio.com/tutorials/functions-extension/getting-started 10 | # 11 | # Note that Azure Function and Python is considered experimental 12 | # 13 | 14 | # The validation event, see https://aka.ms/esvalidation for details 15 | SUBSCRIPTION_VALIDATION_EVENT = "Microsoft.EventGrid.SubscriptionValidationEvent" 16 | # Blob created in a storage account 17 | STORAGE_BLOB_CREATED_EVENT = "Microsoft.Storage.BlobCreated" 18 | # The one used in the "Publisher" sample 19 | CUSTOM_EVENT = "PersonalEventType" 20 | 21 | postreqdata = json.loads(open(os.environ['req']).read()) 22 | print("Received events: {}".format(postreqdata)) 23 | 24 | response = open(os.environ['res'], 'w') 25 | for event in postreqdata: 26 | event_data = event['data'] 27 | 28 | # Deserialize the event data into the appropriate type based on event type using if/elif/else 29 | 30 | if event['eventType'] == SUBSCRIPTION_VALIDATION_EVENT: 31 | validation_code = event_data['validationCode'] 32 | # If you don't use the preview version of EventGrid, this might no exist 33 | validation_url = event_data.get('validationUrl', None) 34 | print("Got a SubscriptionValidation event data, validation code is: {}, validation url is {}".format( 35 | validation_code, 36 | validation_url 37 | )) 38 | answer_payload = { 39 | "validationResponse": validation_code 40 | } 41 | response.write(json.dumps(answer_payload)) 42 | elif event['eventType'] == STORAGE_BLOB_CREATED_EVENT: 43 | print("Got BlobCreated event data, blob URI {}".format( 44 | event_data['url'])) 45 | elif event['eventType'] == CUSTOM_EVENT: 46 | print("Got a custom event {} and received {}".format( 47 | CUSTOM_EVENT, event_data)) 48 | 49 | response.close() 50 | -------------------------------------------------------------------------------- /ConsumerFunction/README.md: -------------------------------------------------------------------------------- 1 | # Python Azure Function for Event Grid 2 | 3 | You can directly deploy this folder using VSCode, by installing the [Azure Function extension for VSCode](https://aka.ms/vscode-azure-functions). 4 | 5 | Then you can deploy your app by just doing a right click and "Deploy to Function App": 6 | 7 | ![alt text](./deploy_vscode.png "Deploy Python with VSCode") 8 | 9 | This will create a Function called "ConsumerFunction" available at the url: 10 | 11 | `https://.azurewebsites.net/api/ConsumerFunction` -------------------------------------------------------------------------------- /ConsumerFunction/deploy_vscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/event-grid-python-public-consume-events/53c15453864cb5cbb93e74d7c22cf1c340a47e41/ConsumerFunction/deploy_vscode.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 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 -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | azure-eventgrid = "*" 8 | azure-mgmt-eventgrid = ">=2.0.0rc1" 9 | azure-mgmt-resource = "*" 10 | haikunator = "*" 11 | 12 | [dev-packages] 13 | pylint = "*" 14 | 15 | [pipenv] 16 | allow_prereleases = true 17 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "4c0a6cb7fd75c4b8eda702adca225c697bc7dc5bd9c2d7744373538f090a2bb4" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "adal": { 18 | "hashes": [ 19 | "sha256:7a15d22b1ee7ce1be92441199958748982feba6b7dec35fbf60f9b607bad1bc0", 20 | "sha256:b332316f54d947f39acd9628e7d61d90f6e54d413d6f97025a51482c96bac6bc" 21 | ], 22 | "version": "==1.2.4" 23 | }, 24 | "asn1crypto": { 25 | "hashes": [ 26 | "sha256:4bcdf33c861c7d40bdcd74d8e4dd7661aac320fcdf40b9a3f95b4ee12fde2fa8", 27 | "sha256:f4f6e119474e58e04a2b1af817eb585b4fd72bdd89b998624712b5c99be7641c" 28 | ], 29 | "version": "==1.4.0" 30 | }, 31 | "azure-common": { 32 | "hashes": [ 33 | "sha256:ce0f1013e6d0e9faebaf3188cc069f4892fc60a6ec552e3f817c1a2f92835054", 34 | "sha256:fd02e4256dc9cdd2d4422bc795bdca2ef302f7a86148b154fbf4ea1f09da400a" 35 | ], 36 | "version": "==1.1.25" 37 | }, 38 | "azure-eventgrid": { 39 | "hashes": [ 40 | "sha256:c55062f7f7f69791688d60efda84162b0bdccdcded2a90ed052a01a6d67fd51c", 41 | "sha256:fca3d830bf887fcc61fa71cb541531c9e155a4437d861149cbfb842d36fb272f" 42 | ], 43 | "index": "pypi", 44 | "version": "==1.1.0" 45 | }, 46 | "azure-mgmt-eventgrid": { 47 | "hashes": [ 48 | "sha256:5d125980af374d029a639d918c541a4042ceb195db17016ef247cec0d11bf3af", 49 | "sha256:7cf7d1fe33dd178a9514aaf3e5153fe75501621815ce323ba3594b00b2cb3180" 50 | ], 51 | "index": "pypi", 52 | "version": "==2.0.0rc1" 53 | }, 54 | "azure-mgmt-nspkg": { 55 | "hashes": [ 56 | "sha256:1c6f5134de78c8907e8b73a8ceaaf1f336a24193a543039994fe002bb5f7f39f", 57 | "sha256:8b2287f671529505b296005e6de9150b074344c2c7d1c805b3f053d081d58c52", 58 | "sha256:d638ea5fda3ed323db943feb29acaa200f5d8ff092078bf8d29d4a2f8ed16999" 59 | ], 60 | "version": "==3.0.2" 61 | }, 62 | "azure-mgmt-resource": { 63 | "hashes": [ 64 | "sha256:8b116e31be2e5895502f2088a592870b057441b3efe3e75d62efb624e13e8e32", 65 | "sha256:fce4e085c5517826c81ff9ff6dbee06da98f08750c68789cc279b3e3d040429a" 66 | ], 67 | "index": "pypi", 68 | "version": "==2.0.0rc1" 69 | }, 70 | "azure-nspkg": { 71 | "hashes": [ 72 | "sha256:1d0bbb2157cf57b1bef6c8c8e5b41133957364456c43b0a43599890023cca0a8", 73 | "sha256:31a060caca00ed1ebd369fc7fe01a56768c927e404ebc92268f4d9d636435e28", 74 | "sha256:e7d3cea6af63e667d87ba1ca4f8cd7cb4dfca678e4c55fc1cedb320760e39dd0" 75 | ], 76 | "version": "==3.0.2" 77 | }, 78 | "certifi": { 79 | "hashes": [ 80 | "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", 81 | "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" 82 | ], 83 | "version": "==2020.6.20" 84 | }, 85 | "cffi": { 86 | "hashes": [ 87 | "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d", 88 | "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b", 89 | "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4", 90 | "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f", 91 | "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3", 92 | "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579", 93 | "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537", 94 | "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e", 95 | "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05", 96 | "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171", 97 | "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca", 98 | "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522", 99 | "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c", 100 | "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc", 101 | "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d", 102 | "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808", 103 | "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828", 104 | "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869", 105 | "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d", 106 | "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9", 107 | "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0", 108 | "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc", 109 | "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15", 110 | "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c", 111 | "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a", 112 | "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3", 113 | "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1", 114 | "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768", 115 | "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d", 116 | "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b", 117 | "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e", 118 | "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d", 119 | "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730", 120 | "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394", 121 | "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1", 122 | "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591" 123 | ], 124 | "version": "==1.14.3" 125 | }, 126 | "chardet": { 127 | "hashes": [ 128 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 129 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 130 | ], 131 | "version": "==3.0.4" 132 | }, 133 | "cryptography": { 134 | "hashes": [ 135 | "sha256:21af753934f2f6d1a10fe8f4c0a64315af209ef6adeaee63ca349797d747d687", 136 | "sha256:27bb401a20a838d6d0ea380f08c6ead3ccd8c9d8a0232dc9adcc0e4994576a66", 137 | "sha256:29720c4253263cff9aea64585adbbe85013ba647f6e98367efff9db2d7193ded", 138 | "sha256:2a35b7570d8f247889784010aac8b384fd2e4a47b33e15c4a60b45a7c1944120", 139 | "sha256:42c531a6a354407f42ee07fda5c2c0dc822cf6d52744949c182f2b295fbd4183", 140 | "sha256:5eb86f03f9c4f0ac2336ac5431271072ddf7ecc76b338e26366732cfac58aa19", 141 | "sha256:67f7f57eae8dede577f3f7775957f5bec93edd6bdb6ce597bb5b28e1bdf3d4fb", 142 | "sha256:6ec84edcbc966ae460560a51a90046503ff0b5b66157a9efc61515c68059f6c8", 143 | "sha256:7ba834564daef87557e7fcd35c3c3183a4147b0b3a57314e53317360b9b201b3", 144 | "sha256:7d7f084cbe1fdb82be5a0545062b59b1ad3637bc5a48612ac2eb428ff31b31ea", 145 | "sha256:82409f5150e529d699e5c33fa8fd85e965104db03bc564f5f4b6a9199e591f7c", 146 | "sha256:87d092a7c2a44e5f7414ab02fb4145723ebba411425e1a99773531dd4c0e9b8d", 147 | "sha256:8c56ef989342e42b9fcaba7c74b446f0cc9bed546dd00034fa7ad66fc00307ef", 148 | "sha256:9449f5d4d7c516a6118fa9210c4a00f34384cb1d2028672100ee0c6cce49d7f6", 149 | "sha256:bc2301170986ad82d9349a91eb8884e0e191209c45f5541b16aa7c0cfb135978", 150 | "sha256:c132bab45d4bd0fff1d3fe294d92b0a6eb8404e93337b3127bdec9f21de117e6", 151 | "sha256:c3d945b7b577f07a477700f618f46cbc287af3a9222cd73035c6ef527ef2c363", 152 | "sha256:cee18beb4c807b5c0b178f4fa2fae03cef9d51821a358c6890f8b23465b7e5d2", 153 | "sha256:d01dfc5c2b3495184f683574e03c70022674ca9a7be88589c5aba130d835ea90" 154 | ], 155 | "index": "pypi", 156 | "version": "==2.3" 157 | }, 158 | "haikunator": { 159 | "hashes": [ 160 | "sha256:66f68b15345b279f78a5fffd4ab56cfb19a9dbb1f41b7f442472efd4cb83458e", 161 | "sha256:91ee3949a3a613cac037ddde0b16b17062e248376b11491436e49d5ddc75ff9b" 162 | ], 163 | "index": "pypi", 164 | "version": "==2.1.0" 165 | }, 166 | "idna": { 167 | "hashes": [ 168 | "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", 169 | "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" 170 | ], 171 | "version": "==2.10" 172 | }, 173 | "isodate": { 174 | "hashes": [ 175 | "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8", 176 | "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81" 177 | ], 178 | "version": "==0.6.0" 179 | }, 180 | "msrest": { 181 | "hashes": [ 182 | "sha256:55f8c3940bc5dc609f8cf9fcd639444716cc212a943606756272e0d0017bbb5b", 183 | "sha256:87aa64948c3ef3dbf6f6956d2240493e68d714e4621b92b65b3c4d5808297929" 184 | ], 185 | "version": "==0.6.19" 186 | }, 187 | "msrestazure": { 188 | "hashes": [ 189 | "sha256:3de50f56147ef529b31e099a982496690468ecef33f0544cb0fa0cfe1e1de5b9", 190 | "sha256:a06f0dabc9a6f5efe3b6add4bd8fb623aeadacf816b7a35b0f89107e0544d189" 191 | ], 192 | "version": "==0.6.4" 193 | }, 194 | "oauthlib": { 195 | "hashes": [ 196 | "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889", 197 | "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea" 198 | ], 199 | "version": "==3.1.0" 200 | }, 201 | "pycparser": { 202 | "hashes": [ 203 | "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", 204 | "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" 205 | ], 206 | "version": "==2.20" 207 | }, 208 | "pyjwt": { 209 | "hashes": [ 210 | "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e", 211 | "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96" 212 | ], 213 | "version": "==1.7.1" 214 | }, 215 | "python-dateutil": { 216 | "hashes": [ 217 | "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", 218 | "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" 219 | ], 220 | "version": "==2.8.1" 221 | }, 222 | "requests": { 223 | "hashes": [ 224 | "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", 225 | "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" 226 | ], 227 | "version": "==2.24.0" 228 | }, 229 | "requests-oauthlib": { 230 | "hashes": [ 231 | "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d", 232 | "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a" 233 | ], 234 | "version": "==1.3.0" 235 | }, 236 | "six": { 237 | "hashes": [ 238 | "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", 239 | "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" 240 | ], 241 | "version": "==1.15.0" 242 | }, 243 | "urllib3": { 244 | "hashes": [ 245 | "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", 246 | "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" 247 | ], 248 | "version": "==1.25.10" 249 | } 250 | }, 251 | "develop": { 252 | "astroid": { 253 | "hashes": [ 254 | "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703", 255 | "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386" 256 | ], 257 | "version": "==2.4.2" 258 | }, 259 | "isort": { 260 | "hashes": [ 261 | "sha256:2f510f34ae18a8d0958c53eec51ef84fd099f07c4c639676525acbcd7b5bd3ff", 262 | "sha256:dd3211f513f4a92ec1ec1876fc1dc3c686649c349d49523f5b5adbb0814e5960" 263 | ], 264 | "version": "==5.6.1" 265 | }, 266 | "lazy-object-proxy": { 267 | "hashes": [ 268 | "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d", 269 | "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449", 270 | "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08", 271 | "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a", 272 | "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50", 273 | "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd", 274 | "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239", 275 | "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb", 276 | "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea", 277 | "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e", 278 | "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156", 279 | "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142", 280 | "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442", 281 | "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62", 282 | "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db", 283 | "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531", 284 | "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383", 285 | "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a", 286 | "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357", 287 | "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", 288 | "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" 289 | ], 290 | "version": "==1.4.3" 291 | }, 292 | "mccabe": { 293 | "hashes": [ 294 | "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", 295 | "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" 296 | ], 297 | "version": "==0.6.1" 298 | }, 299 | "pylint": { 300 | "hashes": [ 301 | "sha256:0990347c0f605927fadb2a9366a0b3d40bd19eb44e4312f0a1ef729a389b2f40", 302 | "sha256:19b902f93f2dc3fa45565e54b88702b28379be40107f509a8516dde152460d1f" 303 | ], 304 | "index": "pypi", 305 | "version": "==2.0.0.dev1" 306 | }, 307 | "six": { 308 | "hashes": [ 309 | "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", 310 | "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" 311 | ], 312 | "version": "==1.15.0" 313 | }, 314 | "wrapt": { 315 | "hashes": [ 316 | "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" 317 | ], 318 | "version": "==1.12.1" 319 | } 320 | } 321 | } 322 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - python 5 | products: 6 | - azure 7 | description: "This contains Python samples for publishing events to Azure Event Grid and consuming events from Azure Event Grid." 8 | urlFragment: event-grid-python-public-consume-events 9 | --- 10 | 11 | ***DISCLAIMER: The data plane samples in this repo are for azure-eventgrid v1 (1.x). For the samples for v4 (4.x and above) please visit [here](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/eventgrid/azure-eventgrid/samples). This repo is archived since v4 has become stable. For management plane samples, please visit [here](https://github.com/Azure-Samples/azure-samples-python-management/tree/master/samples/eventgrid).*** 12 | 13 | ***DISCLAIMER: If you are looking to migrate from azure-eventgrid v1(1.x) to v4(4.x), we suggest getting started at the [migration guide](https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/eventgrid/azure-eventgrid/migration_guide.md).*** 14 | 15 | 16 | # Microsoft Azure Event Grid Publish/Consume Samples for Python 17 | 18 | This contains Python samples for publishing events to Azure Event Grid and consuming events from Azure Event Grid. It also contains a set of management samples that demonstrates how to manage topics and event subscriptions. 19 | 20 | ## Features 21 | 22 | These samples demonstrates the following features: 23 | 24 | Data Plane: 25 | 26 | * How to publish events to Azure Event Grid. 27 | * How to consume events delivered by Azure Event Grid. 28 | 29 | The above two samples use the Event Grid data plane SDK [azure-eventgrid](https://pypi.org/project/azure-eventgrid/). 30 | 31 | Management Plane: 32 | 33 | * How to create a topic and an event subscription to a topic. 34 | * How to create an event subscription to a Storage account. 35 | * How to create an event subscription to an Azure subscription / Resource Group. 36 | 37 | The above three samples use the Event Grid management plane SDK [azure-mgmt-eventgrid](https://pypi.org/project/azure-mgmt-eventgrid/) 38 | 39 | ## Getting Started 40 | 41 | ### Prerequisites 42 | 43 | - Python 2.7, 3.4 or higher. 44 | - [Pipenv](https://docs.pipenv.org/). If you don't have it, follow the [pipenv installation tutorial](https://docs.pipenv.org/#install-pipenv-today). 45 | 46 | 47 | ### Quickstart 48 | 49 | 1. git clone https://github.com/Azure-Samples/event-grid-python-public-consume-events.git 50 | 2. cd event-grid-python-public-consume-events 51 | 3. pipenv install 52 | 4. For Management only, rename the file `env_template` to `.env` and update the correct values inside with your 53 | subscription ID and [Azure Service Principal credentials](https://docs.microsoft.com/azure/azure-resource-manager/resource-group-create-service-principal-portal). 54 | 55 | ## Running the samples 56 | 57 | ### Management Plane 58 | 59 | 1. `create_eg_topics_and_event_subscriptions.py` creates a new event subscription for an Event Grid Topic, and send it to a webhook 60 | 61 | In order for this sample to work, you need to have a valid `.env` file (see previous section). 62 | 63 | **This sample requires a endpoint that supports EventGrid security validation. See https://aka.ms/esvalidation for details** 64 | 65 | Run the sample : `pipenv run python create_eg_topics_and_event_subscriptions.py` 66 | 67 | 2. `create_storage_event_subscriptions.py` creates a new event subscription for a storage account, filtering jpg creation, and send it to an Relay Hybrid Connection 68 | 69 | In order for this sample to work, you need to have a valid `.env` file (see previous section). 70 | 71 | Run the sample : `pipenv run python create_storage_event_subscriptions.py` 72 | 73 | 3. `create_arm_event_subscriptions.py` creates a new event subscription for an Azure Subscription and send it to a Storage Queue 74 | 75 | In order for this sample to work, you need to have a valid `.env` file (see previous section). 76 | 77 | Run the sample : `pipenv run python create_arm_event_subscriptions.py` 78 | 79 | ### Data plane 80 | 81 | 1. `event_grid_publisher.py` publishes a custom event to a Topic. Topic might be created by the initial sample `create_eg_topics_and_event_subscriptions.py` 82 | or [CLI](https://docs.microsoft.com/azure/event-grid/custom-event-quickstart#create-a-custom-topic). 83 | 84 | In order for this sample to work, you need an Event Grid Topic and a Key (replace global variables in the script) 85 | 86 | Run the sample : `pipenv run python event_grid_publisher.py` 87 | 88 | 2. `ConsumerFunction` is an Azure Function app that receive an event from EventGrid. You can deploy the `ConsumerFunction` folder to an Azure Function using [VSCode](https://aka.ms/vscode-azure-functions), 89 | this folder is ready to deploy. See the Readme in that directory for details. 90 | 91 | If you just want the code, look at `ConsumerFunction/ConsumerFunction/run.py`. 92 | 93 | Creating an Azure Function is out of the scope of this sample, you can follow [CLI tutorial](https://docs.microsoft.com/azure/azure-functions/functions-create-first-azure-function-azure-cli) 94 | or [Portal tutorial](https://docs.microsoft.com/azure/azure-functions/functions-create-first-azure-function) or using [VSCode](https://aka.ms/vscode-azure-functions). 95 | 96 | ## Resources 97 | 98 | - https://docs.microsoft.com/azure/event-grid/overview 99 | - https://docs.microsoft.com/python/api/overview/azure/event-grid 100 | -------------------------------------------------------------------------------- /create_arm_event_subscriptions.py: -------------------------------------------------------------------------------- 1 | import os 2 | from haikunator import Haikunator 3 | 4 | from azure.common.credentials import ServicePrincipalCredentials 5 | 6 | from azure.mgmt.resource import ResourceManagementClient 7 | from azure.mgmt.eventgrid import EventGridManagementClient 8 | from azure.mgmt.eventgrid.models import Topic, EventSubscriptionFilter, EventSubscription, StorageQueueEventSubscriptionDestination 9 | 10 | # If you wish to debug 11 | # import logging 12 | # logging.basicConfig(level=logging.DEBUG) 13 | 14 | _haikunator = Haikunator() 15 | 16 | # Resource 17 | 18 | LOCATION = 'westus' 19 | GROUP_NAME = 'event-grid-python-sample-rg' 20 | 21 | # Event grid 22 | 23 | # Using a random topic name. Optionally, replace this with a topic name of your choice. 24 | TOPIC_NAME = "topicsample-" + _haikunator.haikunate(delimiter='') 25 | 26 | # In this sample, we will be demonstrating using a storage queue as the destination for the event subscription. 27 | # This should be in the format /subscriptions/id/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/account1 28 | # More info on Storage queue as an event handler is at https://docs.microsoft.com/en-us/azure/event-grid/event-handlers#queue-storage 29 | STORAGE_ACOUNT_RESOURCE_ID = 'replace-with-your-storage-account-resource-id' 30 | QUEUE_NAME = 'replace-with-your-queue-name-under-the-above-storage-account' 31 | 32 | # To run the sample, you must first create an Azure service principal. To create the service principal, follow one of these guides: 33 | # Azure Portal: https://azure.microsoft.com/documentation/articles/resource-group-create-service-principal-portal/) 34 | # PowerShell: https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal/ 35 | # Azure CLI: https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal-cli/ 36 | # 37 | # This script expects that the following environment vars are set: 38 | # 39 | # AZURE_TENANT_ID: with your Azure Active Directory tenant id or domain 40 | # AZURE_CLIENT_ID: with your Azure Active Directory Application Client ID 41 | # AZURE_CLIENT_SECRET: with your Azure Active Directory Application Secret 42 | # AZURE_SUBSCRIPTION_ID: with your Azure Subscription Id 43 | # 44 | 45 | 46 | def run_example(): 47 | """Resource Group management example.""" 48 | # 49 | # Create the Resource Manager Client with an Application (service principal) token provider 50 | # 51 | subscription_id = os.environ.get( 52 | 'AZURE_SUBSCRIPTION_ID', 53 | '11111111-1111-1111-1111-111111111111') # your Azure Subscription Id 54 | credentials = ServicePrincipalCredentials( 55 | client_id=os.environ['AZURE_CLIENT_ID'], 56 | secret=os.environ['AZURE_CLIENT_SECRET'], 57 | tenant=os.environ['AZURE_TENANT_ID'] 58 | ) 59 | resource_client = ResourceManagementClient(credentials, subscription_id) 60 | event_grid_client = EventGridManagementClient(credentials, subscription_id) 61 | 62 | # Create Resource group 63 | print('\nCreating a Resource Group...') 64 | resource_group = resource_client.resource_groups.create_or_update( 65 | GROUP_NAME, 66 | {'location': LOCATION} 67 | ) 68 | print_item(resource_group) 69 | 70 | # Creating an event subscription to storage account {StorageAccountResourceId} with destination as {HybridConnectionResourceId} 71 | print('\nCreating an event subscription to storage account {} with destination as {}'.format( 72 | STORAGE_ACOUNT_RESOURCE_ID, QUEUE_NAME)) 73 | 74 | # Scope could be any ARM resource ID that supports EventGrid 75 | # https://docs.microsoft.com/azure/event-grid/event-sources 76 | scope = "/subscriptions/{}".format(subscription_id) 77 | event_subscription_name = 'EventSubscription1' 78 | destination = StorageQueueEventSubscriptionDestination( 79 | resource_id=STORAGE_ACOUNT_RESOURCE_ID, 80 | queue_name=QUEUE_NAME 81 | ) 82 | filter = EventSubscriptionFilter( 83 | # By default, "All" event types are included 84 | is_subject_case_sensitive=False, 85 | subject_begins_with='', 86 | subject_ends_with='' 87 | ) 88 | 89 | event_subscription_info = EventSubscription( 90 | destination=destination, filter=filter) 91 | 92 | event_subscription_async_poller = event_grid_client.event_subscriptions.create_or_update( 93 | scope, 94 | event_subscription_name, 95 | event_subscription_info, 96 | ) 97 | # Blocking call for the EventSubscription to be created 98 | event_subscription = event_subscription_async_poller.result() # type: EventSubscription 99 | print_item(event_subscription) 100 | 101 | input("Press enter to delete all created resources.") 102 | 103 | # Delete the EventSubscription 104 | print('\nDeleting the event subscription') 105 | delete_async_operation = event_grid_client.event_subscriptions.delete( 106 | scope, 107 | event_subscription_name 108 | ) 109 | delete_async_operation.wait() 110 | print("\nDeleted: {}".format(event_subscription_name)) 111 | 112 | # Delete Resource group and everything in it 113 | print('\nDelete Resource Group') 114 | delete_async_operation = resource_client.resource_groups.delete(GROUP_NAME) 115 | delete_async_operation.wait() 116 | print("\nDeleted: {}".format(GROUP_NAME)) 117 | 118 | 119 | def print_item(group): 120 | """Print a ResourceGroup instance.""" 121 | print("\tName: {}".format(group.name)) 122 | print("\tId: {}".format(group.id)) 123 | if hasattr(group, 'location'): 124 | print("\tLocation: {}".format(group.location)) 125 | print_properties(getattr(group, 'properties', None)) 126 | 127 | 128 | def print_properties(props): 129 | """Print a ResourceGroup propertyies instance.""" 130 | if props and hasattr(props, 'provisioning_state'): 131 | print("\tProperties:") 132 | print("\t\tProvisioning State: {}".format(props.provisioning_state)) 133 | print("\n\n") 134 | 135 | 136 | if __name__ == "__main__": 137 | run_example() 138 | -------------------------------------------------------------------------------- /create_eg_topics_and_event_subscriptions.py: -------------------------------------------------------------------------------- 1 | import os 2 | from haikunator import Haikunator 3 | 4 | from azure.common.credentials import ServicePrincipalCredentials 5 | 6 | from azure.mgmt.resource import ResourceManagementClient 7 | from azure.mgmt.eventgrid import EventGridManagementClient 8 | from azure.mgmt.eventgrid.models import Topic, EventSubscriptionFilter, EventSubscription, WebHookEventSubscriptionDestination 9 | 10 | # If you wish to debug 11 | # import logging 12 | # logging.basicConfig(level=logging.DEBUG) 13 | 14 | _haikunator = Haikunator() 15 | 16 | # Resource 17 | 18 | LOCATION = 'westus' 19 | GROUP_NAME = 'event-grid-python-sample-rg' 20 | 21 | # Event grid 22 | 23 | # Using a random topic name. Optionally, replace this with a topic name of your choice. 24 | TOPIC_NAME = "topicsample-" + _haikunator.haikunate(delimiter='') 25 | 26 | # Replace the endpoint URL with the URL of your Azure function, or whatever endpoint you want to sent the event. 27 | # See the EventGridConsumer sample for a sample of an Azure function that can handle EventGridEvents 28 | # Publish the ConsumerFunction sample as an Azure function and use the URL of that function for the below. 29 | # 30 | # Your endpoint will be validated, see https://aka.ms/esvalidation for details 31 | ENDPOINT_URL = "replace with your Azure function-URL that support validation" 32 | 33 | 34 | # To run the sample, you must first create an Azure service principal. To create the service principal, follow one of these guides: 35 | # Azure Portal: https://azure.microsoft.com/documentation/articles/resource-group-create-service-principal-portal/) 36 | # PowerShell: https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal/ 37 | # Azure CLI: https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal-cli/ 38 | # 39 | # This script expects that the following environment vars are set: 40 | # 41 | # AZURE_TENANT_ID: with your Azure Active Directory tenant id or domain 42 | # AZURE_CLIENT_ID: with your Azure Active Directory Application Client ID 43 | # AZURE_CLIENT_SECRET: with your Azure Active Directory Application Secret 44 | # AZURE_SUBSCRIPTION_ID: with your Azure Subscription Id 45 | # 46 | def run_example(): 47 | """Resource Group management example.""" 48 | # 49 | # Create the Resource Manager Client with an Application (service principal) token provider 50 | # 51 | subscription_id = os.environ.get( 52 | 'AZURE_SUBSCRIPTION_ID', 53 | '11111111-1111-1111-1111-111111111111') # your Azure Subscription Id 54 | credentials = ServicePrincipalCredentials( 55 | client_id=os.environ['AZURE_CLIENT_ID'], 56 | secret=os.environ['AZURE_CLIENT_SECRET'], 57 | tenant=os.environ['AZURE_TENANT_ID'] 58 | ) 59 | resource_client = ResourceManagementClient(credentials, subscription_id) 60 | event_grid_client = EventGridManagementClient(credentials, subscription_id) 61 | 62 | # Create Resource group 63 | print('\nCreating a Resource Group...') 64 | resource_group = resource_client.resource_groups.create_or_update( 65 | GROUP_NAME, 66 | {'location': LOCATION} 67 | ) 68 | print_item(resource_group) 69 | 70 | # Create EventGrid topic 71 | print('\nCreating an EventGrid topic...') 72 | topic_result_async_poller = event_grid_client.topics.create_or_update( 73 | resource_group.name, 74 | TOPIC_NAME, 75 | Topic( 76 | location=resource_group.location, 77 | tags={'key1': 'value1', 'key2': 'value2'} 78 | ) 79 | ) 80 | # Blocking call for the Topic to be created 81 | topic = topic_result_async_poller.result() # type: Topic 82 | print_item(topic) 83 | 84 | # Get the keys for the topic 85 | print('\nGetting the topic keys...') 86 | keys = event_grid_client.topics.list_shared_access_keys( 87 | resource_group.name, 88 | topic.name 89 | ) # type: TopicSharedAccessKeys 90 | print('The key1 value of topic {} is: {}'.format(topic.name, keys.key1)) 91 | 92 | # Create an event subscription 93 | print('\nCreating an event subscription') 94 | event_subscription_name = 'EventSubscription1' 95 | destination = WebHookEventSubscriptionDestination( 96 | endpoint_url=ENDPOINT_URL 97 | ) 98 | filter = EventSubscriptionFilter( 99 | # By default, "All" event types are included 100 | is_subject_case_sensitive=False, 101 | subject_begins_with='', 102 | subject_ends_with='' 103 | ) 104 | 105 | event_subscription_info = EventSubscription( 106 | destination=destination, filter=filter) 107 | 108 | event_subscription_async_poller = event_grid_client.event_subscriptions.create_or_update( 109 | topic.id, 110 | event_subscription_name, 111 | event_subscription_info, 112 | ) 113 | # Blocking call for the EventSubscription to be created 114 | event_subscription = event_subscription_async_poller.result() # type: EventSubscription 115 | print_item(event_subscription) 116 | 117 | input("Press enter to delete all created resources.") 118 | 119 | # Delete the EventSubscription 120 | print('\nDeleting the event subscription') 121 | delete_async_operation = event_grid_client.event_subscriptions.delete( 122 | topic.id, 123 | event_subscription_name 124 | ) 125 | delete_async_operation.wait() 126 | print("\nDeleted: {}".format(event_subscription_name)) 127 | 128 | # Delete the topic 129 | print('\nDeleting the topic') 130 | delete_async_operation = event_grid_client.topics.delete( 131 | resource_group.name, 132 | topic.name 133 | ) 134 | delete_async_operation.wait() 135 | print("\nDeleted: {}".format(topic.name)) 136 | 137 | # Delete Resource group and everything in it 138 | print('\nDelete Resource Group') 139 | delete_async_operation = resource_client.resource_groups.delete(GROUP_NAME) 140 | delete_async_operation.wait() 141 | print("\nDeleted: {}".format(GROUP_NAME)) 142 | 143 | 144 | def print_item(group): 145 | """Print a ResourceGroup instance.""" 146 | print("\tName: {}".format(group.name)) 147 | print("\tId: {}".format(group.id)) 148 | if hasattr(group, 'location'): 149 | print("\tLocation: {}".format(group.location)) 150 | print_properties(getattr(group, 'properties', None)) 151 | 152 | 153 | def print_properties(props): 154 | """Print a ResourceGroup propertyies instance.""" 155 | if props and hasattr(props, 'provisioning_state'): 156 | print("\tProperties:") 157 | print("\t\tProvisioning State: {}".format(props.provisioning_state)) 158 | print("\n\n") 159 | 160 | 161 | if __name__ == "__main__": 162 | run_example() 163 | -------------------------------------------------------------------------------- /create_storage_event_subscriptions.py: -------------------------------------------------------------------------------- 1 | import os 2 | from haikunator import Haikunator 3 | 4 | from azure.common.credentials import ServicePrincipalCredentials 5 | 6 | from azure.mgmt.resource import ResourceManagementClient 7 | from azure.mgmt.eventgrid import EventGridManagementClient 8 | from azure.mgmt.eventgrid.models import Topic, EventSubscriptionFilter, EventSubscription, HybridConnectionEventSubscriptionDestination 9 | 10 | # If you wish to debug 11 | # import logging 12 | # logging.basicConfig(level=logging.DEBUG) 13 | 14 | _haikunator = Haikunator() 15 | 16 | # Resource 17 | 18 | LOCATION = 'westus' 19 | GROUP_NAME = 'event-grid-python-sample-rg' 20 | 21 | # Event grid 22 | 23 | # Using a random topic name. Optionally, replace this with a topic name of your choice. 24 | TOPIC_NAME = "topicsample-" + _haikunator.haikunate(delimiter='') 25 | 26 | # In this sample, we will be creating an event subscription to this storage account. 27 | # Specify the Azure resource ID of an already existing storage account. The account must be 28 | # a General Purpose V2 Storage account or a Blob Storage account. 29 | # This should be in the format /subscriptions/id/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/account1 30 | # More info on Storage as an event source is at https://docs.microsoft.com/en-us/azure/event-grid/event-sources#storage 31 | STORAGE_ACOUNT_RESOURCE_ID = 'replace-with-your-storage-account-resource-id' 32 | 33 | # In this sample, we will be demonstrating using a hybridconnection as the destination for the event subscription. 34 | # This should be in the format /subscriptions/id/resourceGroups/rg/providers/Microsoft.Relay/namespaces/namespace1/hybridConnections/test 35 | # More info on HybridConnection as an event handler is at https://docs.microsoft.com/en-us/azure/event-grid/event-handlers#hybrid-connections 36 | HYBRID_CONNECTION_RESOURCE_ID = 'replace-with-your-hybrid-connection-resource-id' 37 | 38 | # To run the sample, you must first create an Azure service principal. To create the service principal, follow one of these guides: 39 | # Azure Portal: https://azure.microsoft.com/documentation/articles/resource-group-create-service-principal-portal/) 40 | # PowerShell: https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal/ 41 | # Azure CLI: https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal-cli/ 42 | # 43 | # This script expects that the following environment vars are set: 44 | # 45 | # AZURE_TENANT_ID: with your Azure Active Directory tenant id or domain 46 | # AZURE_CLIENT_ID: with your Azure Active Directory Application Client ID 47 | # AZURE_CLIENT_SECRET: with your Azure Active Directory Application Secret 48 | # AZURE_SUBSCRIPTION_ID: with your Azure Subscription Id 49 | # 50 | 51 | 52 | def run_example(): 53 | """Resource Group management example.""" 54 | # 55 | # Create the Resource Manager Client with an Application (service principal) token provider 56 | # 57 | subscription_id = os.environ.get( 58 | 'AZURE_SUBSCRIPTION_ID', 59 | '11111111-1111-1111-1111-111111111111') # your Azure Subscription Id 60 | credentials = ServicePrincipalCredentials( 61 | client_id=os.environ['AZURE_CLIENT_ID'], 62 | secret=os.environ['AZURE_CLIENT_SECRET'], 63 | tenant=os.environ['AZURE_TENANT_ID'] 64 | ) 65 | resource_client = ResourceManagementClient(credentials, subscription_id) 66 | event_grid_client = EventGridManagementClient(credentials, subscription_id) 67 | 68 | # Create Resource group 69 | print('\nCreating a Resource Group...') 70 | resource_group = resource_client.resource_groups.create_or_update( 71 | GROUP_NAME, 72 | {'location': LOCATION} 73 | ) 74 | print_item(resource_group) 75 | 76 | # Creating an event subscription to storage account {StorageAccountResourceId} with destination as {HybridConnectionResourceId} 77 | print('\nCreating an event subscription to storage account {} with destination as {}'.format( 78 | STORAGE_ACOUNT_RESOURCE_ID, HYBRID_CONNECTION_RESOURCE_ID)) 79 | event_subscription_name = 'EventSubscription1' 80 | destination = HybridConnectionEventSubscriptionDestination( 81 | resource_id=HYBRID_CONNECTION_RESOURCE_ID 82 | ) 83 | filter = EventSubscriptionFilter( 84 | included_event_types=['Microsoft.Storage.BlobCreatedEvent'], 85 | is_subject_case_sensitive=False, 86 | subject_begins_with='/blobServices/default/containers/container1', 87 | subject_ends_with='.jpg' 88 | ) 89 | 90 | event_subscription_info = EventSubscription( 91 | destination=destination, filter=filter) 92 | 93 | event_subscription_async_poller = event_grid_client.event_subscriptions.create_or_update( 94 | STORAGE_ACOUNT_RESOURCE_ID, 95 | event_subscription_name, 96 | event_subscription_info, 97 | ) 98 | # Blocking call for the EventSubscription to be created 99 | event_subscription = event_subscription_async_poller.result() # type: EventSubscription 100 | print_item(event_subscription) 101 | 102 | input("Press enter to delete all created resources.") 103 | 104 | # Delete the EventSubscription 105 | print('\nDeleting the event subscription') 106 | delete_async_operation = event_grid_client.event_subscriptions.delete( 107 | STORAGE_ACOUNT_RESOURCE_ID, 108 | event_subscription_name 109 | ) 110 | delete_async_operation.wait() 111 | print("\nDeleted: {}".format(event_subscription_name)) 112 | 113 | # Delete Resource group and everything in it 114 | print('\nDelete Resource Group') 115 | delete_async_operation = resource_client.resource_groups.delete(GROUP_NAME) 116 | delete_async_operation.wait() 117 | print("\nDeleted: {}".format(GROUP_NAME)) 118 | 119 | 120 | def print_item(group): 121 | """Print a ResourceGroup instance.""" 122 | print("\tName: {}".format(group.name)) 123 | print("\tId: {}".format(group.id)) 124 | if hasattr(group, 'location'): 125 | print("\tLocation: {}".format(group.location)) 126 | print_properties(getattr(group, 'properties', None)) 127 | 128 | 129 | def print_properties(props): 130 | """Print a ResourceGroup propertyies instance.""" 131 | if props and hasattr(props, 'provisioning_state'): 132 | print("\tProperties:") 133 | print("\t\tProvisioning State: {}".format(props.provisioning_state)) 134 | print("\n\n") 135 | 136 | 137 | if __name__ == "__main__": 138 | run_example() 139 | -------------------------------------------------------------------------------- /env_template: -------------------------------------------------------------------------------- 1 | # Rename this file to ".env" to be auto-loaded by Pipenv and update the values 2 | # 3 | # To run the sample, you must first create an Azure service principal. To create the service principal, follow one of these guides: 4 | # Azure Portal: https://azure.microsoft.com/documentation/articles/resource-group-create-service-principal-portal/) 5 | # PowerShell: https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal/ 6 | # Azure CLI: https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal-cli/ 7 | AZURE_SUBSCRIPTION_ID=11111111-1111-1111-1111-111111111111 8 | AZURE_CLIENT_ID=11111111-1111-1111-1111-111111111111 9 | AZURE_CLIENT_SECRET=AZURE_CLIENT_SECRET 10 | AZURE_TENANT_ID=common -------------------------------------------------------------------------------- /event_grid_publisher.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import uuid 3 | 4 | from msrest.authentication import TopicCredentials 5 | from azure.eventgrid import EventGridClient 6 | from azure.eventgrid.models import EventGridEvent 7 | 8 | # If you wish to debug 9 | # import logging 10 | # logging.basicConfig(level=logging.DEBUG) 11 | 12 | # Enter values for and 13 | TOPIC_ENDPOINT = ".-1.eventgrid.azure.net" 14 | 15 | # Enter value for 16 | EVENT_GRID_KEY = '' 17 | 18 | 19 | def build_events_list(): 20 | # type: () -> List[EventGridEvent] 21 | result = [] 22 | for i in range(1): 23 | result.append(EventGridEvent( 24 | id=uuid.uuid4(), 25 | subject="My subject {}".format(i), 26 | data={ 27 | 'key': 'I accept any kind of data here, this is a free dictionary' 28 | }, 29 | event_type='PersonalEventType', 30 | event_time=datetime.datetime.now(), 31 | data_version=2.0 32 | )) 33 | return result 34 | 35 | 36 | def run_example(): 37 | 38 | credentials = TopicCredentials( 39 | EVENT_GRID_KEY 40 | ) 41 | event_grid_client = EventGridClient(credentials) 42 | event_grid_client.publish_events( 43 | TOPIC_ENDPOINT, 44 | events=build_events_list() 45 | ) 46 | print("Published events to Event Grid.") 47 | 48 | 49 | if __name__ == "__main__": 50 | run_example() 51 | () 52 | --------------------------------------------------------------------------------