Pages

Thursday, September 28, 2023

Using an Azure Function App to automatically Start and Stop (Deallocate) Virtual Machines based on tags

One of the most common questions I’ve been asked in the past when it comes to cost management in Azure is what are the options for powering off and on virtual machines based on a schedule. A quick Google search on the internet for this would return a mix of Azure Automation Accounts, the Auto-Shutdown feature blade within the VM (only powers off but not power on), Logic Apps, the new Automation Tasks, and Azure Functions. Each of these options have its advantages and disadvantages, and the associated cost to execute them. Few of them have limitations on the capabilities available for this type of automation. As much as I like Logic Apps because of it’s visual and little to no code capabilities, I find it a bit cumbersome to configure each step via a GUI and the flow of the Logic App can quickly become difficult to follow when there are multiple branches of conditions. My preference with most automation are Function Apps because it allows me to write code to perform anything I needed. With the above described, it’s probably not a surprise that this post is going to demonstrate this type of automation with an Azure Function App.

The scenario I want to provide is an ask from a client who wanted the following:

  1. Auto Start virtual machines at a certain time
  2. Auto Deallocate virtual machines at a certain time
  3. Capability to set start and deallocate schedules for weekdays and weekends
  4. Capability to indicate virtual machines should either be powered on or deallocated over the weekend (they had some workloads that did not need to be on during the week but had to be on over the weekend
  5. Lastly, and the most important, they wanted to use Tags to define the schedule because they use Azure Policies to enforce tagging

There are plenty of scripts available on the internet that provides most of the functionality but I could not find one that allowed the type of control over the weekend so I spent some time to write one.

Before I begin, I would like to explain that the script I wrote uses the Azure Resource Graph to query the status of the virtual machines, their resource groups, and their tags because I find the time it takes for ARM to interact with the resource providers can take a very long time as compared to interacting with the Resource Graph, which is much faster. Those who have used the Resource Graph Explorer in the Azure portal will recognize the KQL query I used to retrieve information. What’s great about this approach is that we can test the query directly in the portal:

imageimage

The design of the tagging for the virtual machines to control the power on, deallocate, and scheduling are as follows:

Tag

Value

Example

Purpose

Behavior

WD-AutoStart

Time in 24 hour format

08:00

Defines the start of the time when the VM should be powered on during the weekday

This condition is met if the time is equal or past the value for Monday to Friday

WD-AutoDeallocate

Time in 24 hour format

17:00

Defines the start of the time when the VM should be powered off during the weekday

This condition is met if the time is equal or past the value for Monday to Friday

WD-AutoStart

Time in 24 hour format

09:00

Defines the start of the time when the VM should be powered on during the weekend

This condition is met if the time is equal or past the value for Saturday and Sunday

WD-AutoDeallocate

Time in 24 hour format

15:00

Defines the start of the time when the VM should be powered off during the weekend

This condition is met if the time is equal or past the value for Saturday and Sunday

Weekend

On or Off

On

Defines whether the VM should be on or off over the weekend

This condition should be set if a weekday schedule is configured and the VM needs to be on as it is the condition to turn the VM back on after the power off on a Friday

The following is an example of a virtual machine with tags applied:

image

With the explanation out of the way, let’s get started with the configuration.

Step #1 – Create Function App

Begin by creating a Function App with the Runtime stack PowerShell Core version 7.2. The hosting option can either be consumption, premium, or App Service Plan but for this example, we’ll use consumption:

image

Proceed to configure the rest of the properties of the Function App:

imageimage

I always recommend turning on Application Insights whenever possible as it helps with debugging but it is not necessary:

image

You can integration the Function App with a GitHub account for CI/CD but for this example we won’t be enabling it:

image

Proceed to create the Function App:

image

Step #2 – Turn on System Assigned Managed Identity and Assign Permissions

To avoid managing certificates and secrets, and enhance the security posture of your Azure environment, it is recommended to use managed identities wherever possible so proceed to turn on the System assigned managed identity in the Identity blade of the Function App so an Enterprise Application object is created in Azure AD / Entra ID, which we can then use to assign permissions to the resources in the subscription:

image

You’ll see an Object (principal) ID created for the Function App after successfully turning on the System assigned identity:

image

Browsing to the Enterprise Applications in Entra ID will display the identity of the Function App:

imageimage

With the system managed identity created for the Function App, we can now proceed to grant it permissions to the resources it needs access to. This example will assign the managed identity as a Virtual Machine Contributor to the subscription so it can perform start and deallocate operations on all the virtual machines. Navigate to the subscription’s Access control (IAM) blade and click on Role assignments:

image

Proceed to select the Virtual Machine Contributor role:

image

Locate the Function App for the managed identity and save the permissions:

image

Step #3 – Configure the Function App

It’s currently September 27, 2023 as I write this post and I noticed that the Function App page layout and blades have changed. The Functions blade under Functions option no longer appears to exist so create the application by selecting Overview, under the Functions tab, click on Create in Azure Portal:

image

The type of function we’ll be creating will be the Timer Trigger and the Schedule will be configured as the following CRON expression:

0 0 * * * 0-6

The above CRON expression allows the function to run at every hour on every day, every month, Sunday to Saturday (every day, every hour).

image

Once the Function is created, proceed to click on Code + Test:

image

The code for this the Function can be copied from my GitHub repo at the following URL: https://github.com/terenceluk/Azure/blob/main/Function%20App/Start-Stop-VM-Function-Based-On-Tags.ps1

Make sure you update the subscriptions list and the timezone you want this script to use for the Tags:

image

Save the code and navigate back out to the Function App, select App Files, then select the requirements.psd1 in the heading to load the file. Note that the default template of this file has everything commented out. We can simply remove the hash character in front of 'Az' = '10.* ' to load all Az modules but I’ve had terrible luck in doing so as the process of downloading the files would cause the Function App to timeout. What I like to do is indicate exactly what modules I need and specify them.

image

The following are the modules my PowerShell script uses so proceed to copy and paste module requirements into the requirements.psd1 file:

'Az.Accounts' = '2.*'

'Az.Compute' = '2.*'

'Az.ResourceGraph' = '0.*'

image

Save the file and then switch to the host.json file:

image

As specified in the following Microsoft documentation: https://learn.microsoft.com/en-us/azure/azure-functions/functions-host-json#functiontimeout, , we can increase the default timeout of a consumption based Function App by adding the following attribute and value into the file:

{

"functionTimeout": "00:10:00"

}

Proceed to add the value to handle large environments that may cause the Function App to exceed the 5-minute limit:

image

Save the file and navigate back to the Function, Code + Test blade, and proceed to test the Function:

image

The execution should return a 202 Accepted HTTP response code and the virtual machines should now be powered on and off at the scheduled time:

image

I hope this blog post helps anyone who might be looking for a script that can handle weekend scheduling of VM start and stop operations.

Wednesday, August 23, 2023

Using Azure Resource Graph Explorer to determine what resources are sending diagnostic data to a Log Analytics Workspace

One of the questions I am frequently asked is how we can effectively determine what resources are sending data to a particular Log Analytics Workspace. Those who are administrators of Azure will know that most subscriptions will eventually contain Log Analytics Workspaces as shown in the list and screenshot below:

  • DefaultWorkspace-d3f0e229-2fcd-45df-a791-614ba183e648-canadaea
  • DefaultWorkspace-d3f0e229-2fcd-45df-a791-614ba183e648-CCAN
  • DefaultWorkspace-d3f0e229-2fcd-45df-a791-614ba183e648-EUS
image

This isn’t the fault of poor management as many resources such as Insights would automatically default to these types of workspaces when they are enabled.

Attempting to browse the blades in these Log Analytics Workspaces will not allow us to easily determine what resources in Azure are sending data to the Log Analytics Workspace:

image

While it is possible to review the type of tables created and if the schema and data stored is known, then we could possibly query the data for the resources but this can be prone to errors causing resources to be missed:

image

Trying to search for how to achieve this lead me to the PowerShell cmdlet: Get-AzOperationalInsightsDataSource (https://learn.microsoft.com/en-us/powershell/module/az.operationalinsights/get-azoperationalinsightsdatasource?view=azps-10.2.0) but this did not allow me to obtain the information I needed.

What I ended up thinking of was whether it was possible to use Resource Graph Explorer to retrieve this information and after viewing the properties of a resource that I need was sending logs to a Logs Analytics Workspace, I was able to confirm that it could be done.

The following the is properties of a Function App:

image

If we scroll down the properties of the resource, we will find the following name/value pair:

Name: "WorkspaceResourceId"
Value: "/subscriptions/dxxxxxx9-2fcd-xxxx-a791-xxxxxxxxe648/resourceGroups/DefaultResourceGroup-CCAN/providers/Microsoft.OperationalInsights/workspaces/DefaultWorkspace-d3f0e229-2fcd-45df-a791-614ba183e648-CCAN",

image

Validating that a resource would have the Log Analytics Workspace defined in its properties, we can use the following query to list all resources that contain this property:

resources
| where properties.WorkspaceResourceId == "/subscriptions/d3xxxxx-2fcd-xxxx-xxxx-6xxxxxe648/resourceGroups/DefaultResourceGroup-CCAN/providers/Microsoft.OperationalInsights/workspaces/DefaultWorkspace-d3f0e229-2fcd-45df-a791-614ba183e648-CCAN"
| project name

image

Note that if you do not know of at least one resource that uses the Log Analytics Workspace, we can retrieve the WorkspaceResourceId of the workspace by navigating to the Log Analytics Workspace in portal.azure.com and copying the string from the URL:

image

I hope this helps anyone who may be looking for this information as I did but unable to find an easy way to achieve this.

Sunday, August 20, 2023

PowerShell script to bulk convert Azure Firewall logs in JSON Line format stored on a Storage Container to CSV format

This post serves as a follow up to my:

Converting Azure Firewall logs in JSON format created from Archive to a storage account diagnostic setting to CSV format
http://terenceluk.blogspot.com/2023/07/converting-azure-firewall-logs-in-json.html

… where I provided a script to convert a single JSON file that stored Azure Firewall Logs in a Storage Account container.

As noted in the previous post, I want to follow up with a script that would traverse through a folder reading JSON files in the sub directories and converting them to CSVs to avoid manually generating each CSV for every hour of the day.

Additional reasons for using this script are:

  1. Allow the retrieval of archived Azure Firewall Logs that are no longer stored in Log Analytics
  2. Bulk converting JSON Line files to CSVs with a specified start and end date
  3. A method for working around the 30,000 records return limit when using Log Analytics Workspaces to query data

The entries for Azure Firewall Log activities can get fairly large so this script will read through each JSON file that are broken up in each hour of the day and convert them to CSV files. I originally thought about combining the files but working out the math for days of longs meant file sizes can get into the GBs and attempting to work with CSV files that large won’t be pleasant.

The JSON script can be found at GitHub repository here: https://github.com/terenceluk/Azure/blob/main/Azure%20Firewall/Bulk-Convert-Az-Firewall-Logs-JSON-to-CSV.ps1

The output file list would look as such:

image

Hope this helps anyone who may be looking for such a script that will save them the time required to manually convert the logs.

Saturday, August 12, 2023

Creating Azure Firewall Policy Rule Collections in Network Collection Group with PowerShell and Excel reference files

Those who have configured Rule Collections for a Azure Firewall Policy whether via GUI or scripting will know how tedious the task can be due to the amount of time for any type of change to be applied and the non-parallel stream of updates you can push to the firewall. I’ve also noticed that attempting to use multiple browser windows to copy and apply changes can potentially overwrite changes to the configuration. Case in point, I had a negative experience where I had window #1 to copy similar rule collections to window #2, and everything went as planned as long as I only saved to window #2. However, if I were to make a change in window #1 where it had not been refreshed with the changes applied to window #2, the save operation would overwrite the changes I made in window #2. I lost quite a bit of configuration due to this scenario.

To minimize the mistakes and amount of time I spent staring at the Azure Firewall Policy window and slowly applying configuration updates one at a time, I decide to spend a bit of time to create PowerShell scripts to reference an Excel file with configuration parameters. The first script I created was one that read an Excel spreadsheet to create the list of Rule Collections that are placed under a predefined Rule Collection Group.

The PowerShell script can be found here in my GitHub repository: https://github.com/terenceluk/Azure/blob/main/Azure%20Firewall/Create-NetworkRuleCollection.ps1

The following is a sample spreadsheet for the PowerShell script to read from:

image

Here is a sample screenshot of the Rule Collections in the Azure management portal:

image

Hope this helps anyone who may be looking for such a script as the creation of Rule Collections can only be created one at a time.

Thursday, August 10, 2023

Attempting to create a folder on an Azure Data Lake Storage Account with Private Endpoint fails with: "Failed to add directory 'Test'. Error: AuthorizationFailure: This request is not authorized to perform this operation."

Problem

A colleague of mine recently asked me to help troubleshoot an issue with an Azure Storage Account that has Hierarchical Namespace enabled, which is essentially an Azure Data Lake, where any attempts to create a folder would fail:

image

The error message presented was generic and appears to suggest that it is caused by a permissions issue:

Failed to add directory

Failed to add directory 'Test'. Error: AuthorizationFailure: This request is not authorized to perform this operation. RequestId:da720a90-c01f-0053-5d3f-c61ef5000000 Time:2023-08-03T19:22:01.2257950Z

image

Creating containers or uploading blobs (files) to the storage account did not have any issues as those operations were successful as shown in the following screenshot:

image

This error has been one that I’ve come across frequently in the past and it is usually because the storage account is locked down with only a private endpoint for the blob service and not for the data lake service created. The following Microsoft documentation explains the reason:

Use private endpoints for Azure Storage

https://learn.microsoft.com/en-us/azure/storage/common/storage-private-endpoints#creating-a-private-endpoint

If you create a private endpoint for the Data Lake Storage Gen2 storage resource, then you should also create one for the Blob Storage resource. That's because operations that target the Data Lake Storage Gen2 endpoint might be redirected to the Blob endpoint. Similarly, if you add a private endpoint for Blob Storage only, and not for Data Lake Storage Gen2, some operations (such as Manage ACL, Create Directory, Delete Directory, etc.) will fail since the Gen2 APIs require a DFS private endpoint. By creating a private endpoint for both resources, you ensure that all operations can complete successfully.

image

The following are screenshots confirming the missing configuration.

Note that Hierarchical Namespace is enabled:

image

Note that Public network access is set to Disabled:

image

Note that there is only 1 private endpoint configured for the storage account:

image

… and the Target sub-resource of the private endpoint is blob:

image

Solution

To correct the issue, we’ll need to create an additional private endpoint that has the Target sub-resource configured as DFS (Data Lake Storage Gen2). Begin by navigating to the Networking blade for the storage account and create a new Private Endpoint:

image

Proceed to fill in the details for the private endpoint:

image

Select dfs as the Target sub-resource:

image

Complete the creation of the private endpoint:

image

Folder creation should now succeed:

image

Hope this provides anyone who might have ran into this issue and is looking for a solution. I’ve found that searching for the error message does not always return results to this solution.

Tuesday, July 25, 2023

Creating Azure Route Tables, UDRs, and IPGroups with PowerShell and Excel reference files

I recently worked with a colleague to complete a deployment and one of the laborious activities we had to complete were:

  1. Create Route Tables with UDRs (user defined routes)
  2. IP Groups

There are a significant amount of entries for both resources and while it was possible to create these manually in the portal, I felt that it was better to create a PowerShell script to accelerate the creation and minimize human typo and copy and paste errors. The 2 scripts I created for this are as follows.

Creating Route Tables and UDRs

The PowerShell script I created, which can be found here in my Github repo: https://github.com/terenceluk/Azure/blob/main/PowerShell/Create-Route-Tables-and-UDRs.ps1, will read an Excel file and create the route tables and the corresponding UDRs (all route tables should have the same UDRs). One of the conditions I’ve added in is an IF statement that checks to see if the UDR to be added is the same subnet as where the route table will be attached. If it is the same, then the script will skip the creation of the UDR additional so we don’t end up routing traffic from the same subnet up to the firewall. The naming convention designed allows me to compare the Route Table and UDR name to determine if it is a match but if your environment is different then you’ll need to adjust the check. Here are screenshots of the sample spreadsheet that is read:

imageimage

Create IPGroups

There were many IP Groups that needed to be created as well because the environment had an IP Group for each subnet. The script that will read an Excel file and create the list of IP Groups can be found here at my GitHub repo: https://github.com/terenceluk/Azure/blob/main/PowerShell/Create-IP-Groups.ps1

Here are sample screenshots of the Excel file:

image