├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── EventGridTrigger ├── function.json └── run.ps1 ├── LICENSE ├── Negotiate ├── function.json └── run.ps1 ├── README.md ├── extensions.csproj ├── host.json ├── powershell-func-resource-monitor.gif ├── profile.ps1 ├── requirements.psd1 └── www └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | csx 4 | .vs 5 | edge 6 | Publish 7 | 8 | *.user 9 | *.suo 10 | *.cscfg 11 | *.Cache 12 | project.lock.json 13 | 14 | /packages 15 | /TestResults 16 | 17 | /tools/NuGet.exe 18 | /App_Data 19 | /secrets 20 | /data 21 | .secrets 22 | appsettings.json 23 | local.settings.json 24 | 25 | node_modules 26 | dist 27 | 28 | # Local python packages 29 | .python_packages/ 30 | 31 | # Python Environments 32 | .env 33 | .venv 34 | env/ 35 | venv/ 36 | ENV/ 37 | env.bak/ 38 | venv.bak/ 39 | 40 | # Byte-compiled / optimized / DLL files 41 | __pycache__/ 42 | *.py[cod] 43 | *$py.class -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions", 4 | "ms-vscode.PowerShell" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Attach to PowerShell Functions", 6 | "type": "PowerShell", 7 | "request": "attach", 8 | "customPipeName": "AzureFunctionsPSWorker", 9 | "runspaceId": 1, 10 | "preLaunchTask": "func: host start" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "azureFunctions.projectRuntime": "~2", 3 | "azureFunctions.projectLanguage": "PowerShell", 4 | "azureFunctions.deploySubpath": ".", 5 | "azureFunctions.preDeployTask": "func: extensions install", 6 | "files.exclude": { 7 | "obj": true, 8 | "bin": true 9 | }, 10 | "debug.internalConsoleOptions": "neverOpen" 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "func", 6 | "command": "host start", 7 | "problemMatcher": "$func-watch", 8 | "dependsOn": "func: extensions install", 9 | "isBackground": true 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /EventGridTrigger/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "type": "eventGridTrigger", 5 | "name": "eventGridEvent", 6 | "direction": "in" 7 | }, 8 | { 9 | "type": "signalR", 10 | "direction": "out", 11 | "name": "SignalRMessages", 12 | "hubName": "resources" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /EventGridTrigger/run.ps1: -------------------------------------------------------------------------------- 1 | param($eventGridEvent, $TriggerMetadata) 2 | 3 | $resourceId = $eventGridEvent.subject 4 | $eventType = $eventGridEvent.eventType 5 | 6 | if ($eventType -ne "Microsoft.Resources.ResourceDeleteSuccess") { 7 | $resource = Get-AzResource -ResourceId $resourceId 8 | } 9 | 10 | Push-OutputBinding -Name SignalRMessages -Value (@{ 11 | Target = 'newResourceEvent' 12 | Arguments = , @{ 13 | resourceGroupName = $resource.ResourceGroupName 14 | resourceName = $resource.ResourceName 15 | resourceId = $resourceId 16 | resourceProvider = $eventGridEvent.data.resourceProvider 17 | location = $resource.Location 18 | timestamp = $eventGridEvent.eventTime 19 | operationName = $eventGridEvent.data.operationName 20 | username = $eventGridEvent.data.claims["name"] 21 | eventType = $eventGridEvent.eventType 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Anthony Chu 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. 22 | -------------------------------------------------------------------------------- /Negotiate/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "anonymous", 5 | "type": "httpTrigger", 6 | "direction": "in", 7 | "name": "Request", 8 | "methods": [ 9 | "post" 10 | ] 11 | }, 12 | { 13 | "type": "http", 14 | "direction": "out", 15 | "name": "Response" 16 | }, 17 | { 18 | "type": "signalRConnectionInfo", 19 | "direction": "in", 20 | "name": "ConnectionInfo", 21 | "hubName": "resources" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /Negotiate/run.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Net 2 | 3 | param($Request, $TriggerMetadata, $ConnectionInfo) 4 | 5 | Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ 6 | StatusCode = [HttpStatusCode]::OK 7 | ContentType = "application/json" 8 | Body = $ConnectionInfo 9 | }) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Real-time Azure Resource Monitor 2 | 3 | Powered by PowerShell [Azure Functions](https://docs.microsoft.com/azure/azure-functions/?WT.mc_id=functionsresourcemonitor-github-antchu), [Azure Event Grid](https://docs.microsoft.com/azure/event-grid/?WT.mc_id=functionsresourcemonitor-github-antchu), and [Azure SignalR Service](https://docs.microsoft.com/azure/azure-signalr/?WT.mc_id=functionsresourcemonitor-github-antchu) 4 | 5 | ## How does this work? 6 | 7 | 1. Resources are changed in an Azure subscription 8 | 1. Azure Event Grid reacts to event and triggers an Azure Function 9 | 1. PowerShell Azure Function retrieves more information about the resource 10 | 1. Function uses Azure SignalR Service output binding to send messages 11 | 1. Web client receives messages over WebSocket 12 | 13 | **Check out [PowerShell for Azure Functions](https://docs.microsoft.com/azure/azure-functions/functions-create-first-function-powershell?WT.mc_id=functionsresourcemonitor-github-antchu) for more information** 14 | 15 | ![](powershell-func-resource-monitor.gif) -------------------------------------------------------------------------------- /extensions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | 5 | ** 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "managedDependency": { 4 | "enabled": true 5 | } 6 | } -------------------------------------------------------------------------------- /powershell-func-resource-monitor.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonychu/functions-resource-monitor/0e921840d6f57b88bf8df7565f2d849ea1e660d0/powershell-func-resource-monitor.gif -------------------------------------------------------------------------------- /profile.ps1: -------------------------------------------------------------------------------- 1 | # Azure Functions profile.ps1 2 | # 3 | # This profile.ps1 will get executed every "cold start" of your Function App. 4 | # "cold start" occurs when: 5 | # 6 | # * A Function App starts up for the very first time 7 | # * A Function App starts up after being de-allocated due to inactivity 8 | # 9 | # You can define helper functions, run commands, or specify environment variables 10 | # NOTE: any variables defined that are not environment variables will get reset after the first execution 11 | 12 | # Authenticate with Azure PowerShell using MSI. 13 | # Remove this if you are not planning on using MSI or Azure PowerShell. 14 | if ($env:MSI_SECRET -and (Get-Module -ListAvailable Az.Accounts)) { 15 | Connect-AzAccount -Identity 16 | } 17 | 18 | # Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell. 19 | # Enable-AzureRmAlias 20 | 21 | # You can also define functions or aliases that can be referenced in any of your PowerShell functions. -------------------------------------------------------------------------------- /requirements.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | 'Az' = '1.*' 3 | } -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cloud Resource Monitor 7 | 9 | 27 | 28 | 29 |
30 |

Cloud Resource Monitor

31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
EventTypeResource GroupResourceLocationUsernameTimestamp
{{ resource.eventType | shortResourceType }}{{ resource.resourceProvider }}{{ resource.resourceGroupName }}{{ resource.resourceName }}{{ resource.location }}{{ resource.username }}{{ resource.timestamp }}
53 |
54 | 55 | 56 | 57 | 100 | 101 | --------------------------------------------------------------------------------