Overview
Azure Event Grid is an event handler service that subscribes to certain event sources which then call handlers to perform a task, or tasks, based on the event that occurred. Azure Event Grid documentation is provided here. Azure Functions is an event driven, compute-on-demand experience that extends the existing Azure application platform with capabilities to implement code triggered by events occurring in Azure or third party service as well as on-premises systems.
With Azure Event Grid and Azure Functions working together, Event Grid is configured to handle a specific event, and if that event occurs, can call an Azure Function to perform specific tasks.
We’ll be taking a look at doing the following:
- If a Virtual Machine is created, Event Grid will trigger a Serverless Azure Function written in PowerShell which will then modify the Virtual Machine.
- If a Storage Account is created, Event Grid will trigger a Serverless Azure Function written in PowerShell which will then modify the Storage Account. In this example, I will provide additional code where if the Storage Account is created without the setting requiring HTTPs Encryption Only (EnableHttpsTrafficOnly flag set to $true), the Function will automatically configure the Storage Account to require HTTPs.
Note
In cases where your organization’s compliance requirements demand that EnableHttpsTrafficOnly is always set to $true and it is unacceptable to even allow Storage Account creation where EnableHttpsTraffic is set to $false, it is better to leverage Azure Policy to deny Storage Account creation when the EnableHttpsTrafficOnly flag is set to $false.
These are just two examples. You can configure an endless amount of scenarios in which a resource is provisioned and there is a requirement to customize the deployment. In this case, you can leverage Azure Event Grid with Azure Functions to satisfy these scenarios.
The question may arise, why not use Azure Policy? Azure Policy may be beneficial for certain things such as Appending information or Denying to enforce your compliance settings. There may be times where you can’t Append or you may not want to Deny but have the settings that you want changed at the time of Resource Creation. With this model of leveraging Azure Event Grid and Azure Functions, the configuration change is not immediate and will take several minutes for Azure Event Grid to call the Azure Function, the Azure Function to start, the change be made, and optionally logged.
Note
This blog article will be leveraging the new Azure Functions Preview Interface which is a newly released preview capability at the time of this writing.
This is a two-part series.
- Creating our Azure Function
- Creating our Azure Event Grid Subscription
- Linking the Azure Function to Event Grid Subscription
- Verifying Azure Event Grid Subscription is successfully linked to our Azure Function
Part 2 (Current)
- Viewing Event Grid Data in our Azure Function leveraging Application Insights
- Modify our output into Application Insights to use JSON to get better insight into how to create Event Grid Subscription Filters
- Writing your PowerShell Function to Differentiate Between Resource Types obtained from the Event Grid Subscription
- Executing Actual Code and Logging for a new Storage Account to modify HTTPS Encryption Required Setting from Not Required to Required
- Create a Managed Identity for the Function with Storage Account Contributor
- Creating a Storage Account that is configured to not Require HTTPS Encryption to require HTTPS Encrypting and verify Application Insights Logging as well as verifying Resource modification.
Viewing Event Grid Data in our Azure Function
Now that we have our Azure Function created and it is hooked into our Event Grid Subscription, let’s take a look at how we can see Event Grid Data in our Azure Function.
In our Azure Function, click on Code/Test and take a look at the initial code. Remember in Part 1 we specified our Event Grid parameter name as eventGridEvent? We use that as a variable in our code which stores data on the event that Event Grid sent to our function. This is unique per event. The starter code for an Azure Function that is using PowerShell and integrated with Event Grid is as follows:
The default code will take the event, convert it to string format, and do a Write-Host. How do we see this information? We click on Logs at the bottom or Monitor just under Code/Test.. This connects to Application Insights to see logging data such as Write-Host, Write-Information, Write-Warning, Write-Error, etc…
Note
It can take up to 5 minutes for data to show up in Application Insights.
Let’s generate an event by creating a Virtual Machine called VMFuncBlog01 running Windows Server 2019.
In Logs, we see a streaming log of Events that come in from Event Grid.
If we click Monitor, we can see multiple Events and we can go find the event we are looking for.
In the event we are looking for, we see the following:
The problem is, since this data has been converted to String format, there are certain things that are cut off by default and we don’t really get the full picture with all the details. We can change this with Out-String -Width. But, I’d rather see this data in JSON first and then have that JSON be converted to String format. Therefore, let’s modify our Function Code to be as such:
param($eventGridEvent, $TriggerMetadata)
# Make sure to pass hashtables to Out-String so they're logged correctly
$eventGridEvent | ConvertTo-JSON -Depth 5 | Out-String -Width 200 | Write-Host
We want to convert the event into a JSON Format using ConvertTo-JSON with a depth of 5 to make sure nothing gets cut off. Then we want to convert it to String format using a Width of 200 to ensure we see more horizontal data.
Now let’s create another Virtual Machine. This time, we’ll create VMFuncBlog02 also running Windows Server 2019. And we’ll use the Monitor within the Azure Function going forward.
Viewing Event Grid JSON Data to Create Event Grid Subscription Filters
After the VM has been created and at least 5 minutes have passed for the data to appear in Application Insights, let’s go into our Function’s Monitor to go find information about our VM Creation.
We successfully see our new Event with JSON output.
Now let’s take a look at how we can take our JSON data into Visual Studio Code and view the JSON as a whole to find filters we can use in Event Grid.
Important
Why do we want to create filters in Event Grid? Can’t we just send everything Event Grid gets for Resource Writes and use some filters in Azure Function PowerShell? You can, but Azure Functions using the Consumption Model cost money. If we were to do this, every time the Azure Function starts, you’d be incurring cost. Therefore, it is best to filter at Event Grid so the Function only gets started when it’s a Resource Event you intending for the Function to act upon.
In Visual Studio Code, do the following:
- Paste the JSON Code into a new file
- Set it to JSON which you can see at the bottom right
- Right-Click somewhere in the contents of the JSON data and choose Format Document
- Use the carrots to hide portions of the data you don’t care about
You will ultimately see we have operationName that tells us that part of the Event Grid Schema that comes back contains an OperationName of Microsoft.Compute/virtualMachines/write. And this is within the data section. Therefore, this would become data.operationName.
Now let’s navigate back to our Event Grid Subscription. Click on Filters and we can create a Key for data.operationName with an Operator of String contains with the Value being VirtualMachines. If you were wanting to filter on StorageAccount Creations, the Value for those would be StorageAccount.
Writing your PowerShell Function to Differentiate Between Resource Types
Now that we’ve configured out Event Grid Subscription to only send our Function Resource Creations for VirtualMachines and StorageAccount, the question is, how do we write our Function Code to differentiate between the two?
One of the other pieces of data in our JSON data from Event Grid is the subject. For our VM Creation, we see the following subject:
"subject": "/subscriptions/959bxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/VMFuncBlog02/providers/Microsoft.Compute/virtualMachines/VMFuncBlog02"
And because our Event Grid Data is stored in the $eventGridEvent variable (parameter name specified during creation), we can use $eventGridEvent.subject to do some PowerShell checks. Therefore, our code would look as such:
if ($eventGridEvent.subject -like "*VirtualMachines*")
{
<Code Here to do Cool Things with Virtual Machine Creations!>
}
elseif ($eventGridEvent.subject -like "*StorageAccount*")
{
<Code Here to do Cool Things with Storage Account Creations!>
}
For Virtual Machines, if you want to get the Virtual Machine Name and Resource Group Name, the following code can be used:
$EventGridSubjectSplit = $eventGridEvent.subject.split("/")
$EventGridSubjectSplitCount = [int]$EventGridSubjectSplit.count - 1
$VMName = $EventGridSubjectSplit[$EventGridSubjectSplitCount]
$RGIndex = [int]$EventGridSubjectSplit.indexof('resourcegroups') + 1
$RGName = $EventGridSubjectSplit[$RGIndex]
For Storage Accounts, if you want to get the Storage Account Name and Resource Group Name, the following code can be used:
$EventGridSubjectSplit = $eventGridEvent.subject.split("/")
$EventGridSubjectSplitCount = [int]$EventGridSubjectSplit.count - 1
$SAName = $EventGridSubjectSplit[$EventGridSubjectSplitCount]
$RGIndex = [int]$EventGridSubjectSplit.indexof('resourcegroups') + 1
$RGName = $EventGridSubjectSplit[$RGIndex]
Executing Actual Code And Logging
Let’s fill in some code for Storage Accounts. What we’ll do is modify our PowerShell Code to do the following:
- Create a Storage Account which will trigger Event Grid to call our Function
- Our Function will see that it is a Storage Account and then execute the portion of code within the Storage Account elseif statement.
- Our Function will login to Azure PowerShell which is automatically done by default for any Functions that are within a PowerShell Function App.
- Configure the Storage Account to require HTTPS Encryption
Managed Identity and Assigning RBAC Permissions
Now in order to allow our Azure Function to be able to read and configure data using Azure PowerShell, it needs RBAC Permissions to do so. Conforming to Principal of Least Privilege, we will create a Managed Identity for our Azure PowerShell Function and then give it Storage Account Contributor RBAC permission. To learn more about Managed Identities, click here.
In our Function, click Identity, ensure System assigned is selected, and flip the Status to On. That’s all there is to creating a Managed Identity for an Azure Function.
To assign it RBAC Storage Account Contributor Rights to the entire Subscription, go to All Services > Subscriptions > Access control (IAM) > Add > Add role assignment.
sdfsdaf
Now your Function will have permissions to both read as well as make modifications to Storage Accounts.
Adding the Storage Account Code
The following will be the elseif statement for the PowerShell Function Code.
elseif ($eventGridevent.subject -like "*StorageAccounts*")
{
# Obtain the Subject of the Event Grid Event which contains the Storage Account Name and split using /
# Subject for Storage Account looks as such: "subject": "/subscriptions/subscriptionID/resourcegroups/StorageAccounts/providers/Microsoft.Storage/storageAccounts/StorageaccountName",
$EventGridSubjectSplit = $eventGridEvent.subject.split("/")
# Take the array of split items and obtain the Storage Account Name and Resource Group Name
$EventGridSubjectSplitCount = [int]$EventGridSubjectSplit.count - 1
$SAName = $EventGridSubjectSplit[$EventGridSubjectSplitCount]
$RGIndex = [int]$EventGridSubjectSplit.indexof('resourcegroups') + 1
$RGName = $EventGridSubjectSplit[$RGIndex]
Write-Information "Name of the Storage Account is $SAName and name of Resource Group is $RGName"
try
{
# Get the Storage Account Details
$SAData = Get-AzStorageAccount -StorageAccountName $SAName -ResourceGroupName $RGName -ErrorAction Stop
# Obtain the Existing HTTPS Encryption Setting
$HTTPEncryptionSetting = $SAData.EnableHttpsTrafficOnly
# If HTTPS Encryption Setting is not enabled, configure Storage Account to Require HTTPS Traffic Only
if ($HTTPEncryptionSetting -ne $true)
{
Write-Information "HTTPs Encryption Only not configured on new Storage Account: $SAName. Attempting to Force HTTPS Encrytion Only."
try
{
Set-AzStorageaccount -StorageAccountName $SAName -ResourceGroupName $RGName -EnableHttpsTrafficOnly $true -ErrorAction Stop
Write-Information "Successfully configured Storage Account: $SAName for HTTPS Encryption Only."
}
catch
{
Write-Error "Error prevented Storage Account: $SAName from being enabled for HTTPS Encryption."
}
}
else
{
Write-Information "HTTPs Encryption Only already configured on Storage Account: $SAName. No Changes necessary."
}
}
catch
{
# Error. Exit Script.
#push-outputbinding -name outputBlob -value "There was an error getting the VM Data for the $VMName VM. Exiting Script."
Write-Information "There was an error getting the StorageAccount Data for the $SAName Storage Account. Exiting Script."
exit
}
}
You can see in here, there are several Write-Information and Write-Error statements. These statements when triggered will add logging data into Application Insights. Therefore, when an action occurs and you do some kind of Write-, you can look at your Function’s Monitor to view logged information.
Our entire Azure Function Code will be written as follows:
param($eventGridEvent, $TriggerMetadata)
if ($eventGridEvent.subject -like "*VirtualMachines*")
{
Write-Host "Not Doing Anything Yet"
}
elseif ($eventGridevent.subject -like "*StorageAccounts*")
{
# Obtain the Subject of the Event Grid Event which contains the Storage Account Name and split using /
# Subject for Storage Account looks as such: "subject": "/subscriptions/subscriptionID/resourcegroups/StorageAccounts/providers/Microsoft.Storage/storageAccounts/StorageaccountName",
$EventGridSubjectSplit = $eventGridEvent.subject.split("/")
# Take the array of split items and obtain the Storage Account Name and Resource Group Name
$EventGridSubjectSplitCount = [int]$EventGridSubjectSplit.count - 1
$SAName = $EventGridSubjectSplit[$EventGridSubjectSplitCount]
$RGIndex = [int]$EventGridSubjectSplit.indexof('resourcegroups') + 1
$RGName = $EventGridSubjectSplit[$RGIndex]
Write-Information "Name of the Storage Account is $SAName and name of Resource Group is $RGName"
try
{
# Get the Storage Account Details
$SAData = Get-AzStorageAccount -StorageAccountName $SAName -ResourceGroupName $RGName -ErrorAction Stop
# Obtain the Existing HTTPS Encryption Setting
$HTTPEncryptionSetting = $SAData.EnableHttpsTrafficOnly
# If HTTPS Encryption Setting is not enabled, configure Storage Account to Require HTTPS Traffic Only
if ($HTTPEncryptionSetting -ne $true)
{
Write-Information "HTTPs Encryption Only not configured on new Storage Account: $SAName. Attempting to Force HTTPS Encrytion Only."
try
{
Set-AzStorageaccount -StorageAccountName $SAName -ResourceGroupName $RGName -EnableHttpsTrafficOnly $true -ErrorAction Stop
Write-Information "Successfully configured Storage Account: $SAName for HTTPS Encryption Only."
}
catch
{
Write-Error "Error prevented Storage Account: $SAName from being enabled for HTTPS Encryption."
}
}
else
{
Write-Information "HTTPs Encryption Only already configured on Storage Account: $SAName. No Changes necessary."
}
}
catch
{
# Error. Exit Script.
#push-outputbinding -name outputBlob -value "There was an error getting the VM Data for the $VMName VM. Exiting Script."
Write-Information "There was an error getting the StorageAccount Data for the $SAName Storage Account. Exiting Script."
exit
}
}
# Make sure to pass hashtables to Out-String so they're logged correctly
$eventGridEvent | ConvertTo-JSON -Depth 5 | Out-String -Width 200 | Write-Host
Be sure to click Save.
Creating a Storage Account and Verifying Results
I’m creating a new Storage Account called shudfuncsa01 with the Secure transfer required setting set to disabled.
After the Storage Account is created, wait several minutes. Go into your Azure Function, go to Monitor, and view your Application Insights logs.
We see that Event Grid passed the Storage Account creation to our Azure Function and the following occurred with the Function:
- Used its Managed Identity to login to Azure PowerShell
- Used Azure PowerShell to check whether HTTPs Encryption Only was configured
- Determined HTTPs Encryption Only was not set to True
- Configured the HTTPs Encryption Only setting to True
Let’s go to the Storage Account in the Portal and validate whether the HTTPs Encryption Only setting is set to True.
Voila! Successful!
Thanks for reading all the way to the end. Hopefully you found this valuable. And if you have any questions, as always, feel free to comment.
Leave a Reply