UPDATE: I have just added installation instructions for deploying this solution within your Azure subscription. I have provided both a manual step-by-step set of instructions as well as a install script that will do most of the work for you with instructions on how to use it. This can be found within the README file within the Github repository linked to in this blog post. Please feel free to post any Issues that you might have or enhancement requests that you would like to see.
Over the last few weeks, I have been working on a solution to a common problem that I have heard from customers. Specifically, how do you easily track down orphan resources that are still sitting in one or more subscriptions and are either still costing money or may just be sitting around cluttering up the Azure subscription and corresponding reporting?
NOTE: An example of an orphan Resource Type would be a Public IP address, which falls under the Microsoft.Network resource provider. This resource does have an hourly cost associated with it, but if it is not attached to either a Load Balancer, Application Gateway, or NIC Card, then it is providing any useful function but is still costing you money.
I do realize that there are a number of third-party products out there that will help customers with this, but they are very expensive and they have a lot of features that you might not need. These third-party products can also be very expensive, so it might not make financial sense to look at them depending on where you are in your Azure journey. No matter the reason, I have been able to put together a pretty simple solution that uses some very inexpensive Azure services. These services primarily fall into the Serverless category which makes this solution very easy to extend or modify based on your own business requirements.
All of the source code for the solution along with all installation details can be found at the following GitHub Repo: https://github.com/brharr/Azure-Orphan-Audit
Solution Overview
Let’s start by talking about which Azure services are being leveraged to provide this solution and then we can walk-through exactly what the solution is doing behind the scenes. To make this as simple and light as possible, I wanted to make sure that I used services that would only charge per execution thereby keeping the cost down as low as possible. With respect to any data that is being created, I made sure that the data objects being passed around were very simple JSON objects which would not require a lot of storage space, thereby keeping the storage footprint as small as possible.
In the end, I put together a solution that leverages the following Azure services:
- Azure Automation Runbooks
- Azure Functions (Node JS)
- Azure Logic Apps
- Azure Storage Queues
I broke up this solution into each of these services to provide modularity for the future as well as because I completely believe in both Serverless and Microservices Architectures and this solution adheres to both of these paradigms. The entire solution could be performed easily enough in either Azure Automation or Azure Functions by themselves if you like, but this is my preferred method, please feel free to modify it for your own purposes.
Solution Walk-through
Let’s now do a quick walk-through of what the solution actually does. In its simplest form, the solution loops through one or more Resource Types within your subscription to see if they are disconnected or orphaned and therefore not performing any useful function. Once it finds a resource, the solution creates a JSON object containing the type of resource that it is in Resource Provider notation (i.e. Microsoft.Network/publicIPAddresses) and pops that JSON object onto a Storage Queue. The solution will then process all of these JSON objects and send an email to a person or group of your choosing with a complete Orphan Report to make them aware of all the orphans that are found.
A more detailed walk-through can be found below:
- One or more Azure Automation Runbooks will check for Orphan resource
- One Runbook for each resource type
- Runbook loops through all resources of a type and find ones that are no longer associated with expected resources (i.e. Public IP Addresses not associated with LB or NIC Cards)
- Each Runbook will need to be setup to run on a schedule
- Runbook calls HTTP Trigger based Azure Function
- Create JSON Object containing Resource ID and Resource Type
- Send JSON Object to Function within Body of POST Call
- Azure Function processes JSNON Object and sends to Azure Storage Queue
- Timer Trigger based Function runs on Schedule to create Orphan Report
- Loop through each of the Queue Messages and concatenate them together into a simple report format
- Send JSON Report to secondary Queue within same Storage Account
- Azure Logic App using a Queue based trigger processes all messages within the Report Queue and sends an Email to a Cloud Admininstrator
I am sure that you can see where there might be a lot of different configuration options not only for making the system work correctly, but also to really make the deployment your own based on your business requirements. For example, how often you run through your chosen Runbooks and how often you pull the messages off the queue is going to be completely up to you. I am using a default value of a weekly recurrence within the installation documentation and ARM Template, but you can very easily change that.
I am sure that you can see where there might be a lot of different configuration options not only for making the system work correctly, but also to really make the deployment your own based on your business requirements. For example, how often you run through your chosen Runbooks and how often you pull the messages off the queue is going to be completely up to you. I am using a default value of a weekly recurrence within the installation documentation and ARM Template, but you can very easily change that.
INSTALLATION: The installation of this solution will require you to clone the repository to your local system, but after you are done with that, you can leverage the install script within the install folder to deploy the entire solution. The entire set of installation steps can be found in the README file within the repository and this is also visible when you navigate to the repo on Github.
NOTE: Please keep in mind that the installation script is not quite ready yet because Azure Functions and Azure Automation Accounts are not easily installed through ARM and Automation does not have a module in Azure CLI. So I need to create all the necessary steps in Powershell for everything. The script will be done in the next few days.
Configuration Information
Unfortunately, there are a few set of steps that cannot be automated within the Installation process, so I wanted to point these out here just so that you are aware of them and know what you will need to do. Each of the items can be found in the list below and should be performed once the installation process is complete and they must be performed in the order listed below because of dependency concerns:
- Update all Modules – When a new Automation Account is created, the shared modules that are deployed are of the lowest version possible. Before the Runbooks will function properly or before the Network module below can be instaled, all of the AzureRm modules will need to be updated. This used to be available through a push button on the Modules section of your Automation Account, now you must import a PowerShell Runbook which can be found here: https://github.com/Microsoft/AzureAutomation-Account-Modules-Update. Clone the repo and then import the Runbook and immediately kick off a manual Job to update all existing modules to their latest version.
- AzureRM.Network module installed – within the Automation Account, go to the Modules section under Shared Resources and using the Browse Gallery functionality, add the specified module for Networking. Use the following Azure documentation for more information: https://docs.microsoft.com/en-us/azure/automation/automation-runbook-gallery#modules-in-powershell-gallery
- You will need to manually create the NodeJS based functions within your Function App or setup your own version of Continuous Delivery with the Function App to push the code once you clone my repo
- Create and Import the Azure Logic App that is available as a JSON document within the repo. You will also need to make sure to update the JSON to use a email address that is tied to your organization.
Assuming that you use the install script that will be available in the install folder, then you should have everything else setup and connected correctly.
Upcoming
As I mentioned at the top, this is still under development, but there should be a completed solution that you can start with by the end of the week including installation documentation which will be within the README on the GitHub repo. The first initial version will only have two Runbooks (Public IPs and Disks) and they will only be in PowerShell. I will also be including an ARM template for deployment and installation instructions for anything that cannot be deployed via ARM.
In the future, I plan to do the following:
- Complete PowerShell based Install script that will create all of the necessary Resources within Azure and setup all configurations that can be automated
- Create additional Runbooks for other potential Resource Types (SQL Servers, Load Balancers, Application Gateways, VPN Gateways, etc.)
- Provide Python versions of each Runbook
- Provide C# versions of Azure Functions
- Add in checks for Locked resources and keep them out of the reporting
- Add ability to process multiple subscriptions in a single runbook.
Conclusion
There really isn’t much more to say. I realize that I might be releasing this a little pre-maturely, but please know that I will be making updates over the next couple of weeks to make it more polished.
In addition, if you do find any bugs with the documentation or the code files themselves, please just file an Issue on the Github repository and I will get to work on it as quickly as can. Same is true if you should have any recommendations for new features or new modules to add.