Microsoft Secure Tech Accelerator
Apr 03 2024, 07:00 AM - 11:00 AM (PDT)
Microsoft Tech Community
Tagging Azure Resources with a Creator
Published Jun 21 2020 05:39 PM 33.3K Views
Microsoft

 

Update: Objects created by a Service Principal will tag the objects with a GUID instead of a name by default. You can fix this behavior by giving the Managed Identity the Application Developer role in Azure Active Directory. 

 

Hello everyone, my name is Anthony Watherston and I am a Senior Premier Field Engineer in Melbourne Australia. A question was recently raised by a colleague about how we can tag an Azure resource with the name of the person who created it. This would prove especially helpful in the case of resources that are older than the default activity log retention time where the owner cannot be identified from the initial creation event. Azure Policy would normally be used to update tags however policies can only act on the properties of the resource itself – the caller is only exposed through the activity log event which creates the resource.

 

Enter Event Grid Subscriptions and Azure Functions. Each time an activity occurs in an Azure subscription an event is generated, and we can subscribe to that event using an Event Grid Subscription – part of the information captured in this event will be the user who is making the call. The subscription will help me to identify that an event has occurred, and because it is only going to deploy a tag the code should be minimal so I can use an Azure Function.

 

So, my flow looks like this: -

Anthony_W_0-1592784724912.png

 

You can clone or download the project at https://github.com/anwather/TagWithCreator  and check out the code but let us go through the main parts.

 

I built my function using PowerShell because it is easiest for me – you can create a function by following the quick start guide at Quickstart: Create a function in Azure using Visual Studio Code but instead of selecting a HTTP trigger I chose an Event Grid trigger. The extension provided in Visual Studio Code will create all the necessary files and even provide you with a sample of what an Event Grid event looks like in a file called sample.dat.

 

The main script code is at /functions/TagWithCreator/run.ps1.The events are passed into the function as an object already so I can just get the fields I want out of the event and assign them to variables to use later.

 

 

 

 

$caller = $eventGridEvent.data.claims.name
Write-Host "Caller: $caller"
$resourceId = $eventGridEvent.data.resourceUri
Write-Host "ResourceId: $resourceId"

 

 

 

 

The script checks if a tag exists on the object before applying – if it needs to make change then I am using the Update-AzTag cmdlet to apply the tag.

 

 

 

 

if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags)) {
    $tag = @{
        Creator = $caller
    }
    Update-AzTag -ResourceId $resourceId -Operation Merge -Tag $tag
    Write-Host "Added creator tag with user: $caller"
}
else {
    Write-Host "Tag already exists"
}

 

 

 

 

As far as the code is concerned there is nothing else that is any different to a normal PowerShell script but be aware that it executes using PowerShell 6 so any Windows PowerShell commands that are specific to Windows will not work.

 

The Azure Function handles all the connections to Azure using a managed identity which will be created when we deploy it – I also assign permissions to that identity so it can make the changes to the resources.

 

By default, the scaffolded Azure Function will download the entire Az module on each cold start of the function which adds time to the execution, so to improve performance I specify each module and version required in requirements.psd1

 

 

 

 

# This file enables modules to be automatically managed by the Functions service.
# See https://aka.ms/functionsmanageddependency for additional information.
#
@{
    'Az.Accounts'  = '1.8.0'
    'Az.Resources' = '2.0.1'
}

 

 

 

 

Apart from writing the actual code in run.ps1 and changing the requirements.psd1 file there are no other changes to be made to the scaffolded code – easy!

 

Deploying the Azure Function requires the following resources in Azure – a Storage Account, App Service Plan, Function App, Application Insights, and the Event Grid Subscription. The first four objects are created using an Azure Resource Manager template and I have put instructions to create the Event Grid Subscription later.

 

The steps to deploy the ARM template and function code are as below: -

1. Ensure that you have the latest version of the Az PowerShell modules available.

2. Connect to Azure using Connect-AzAccount

3. Modify the /environment/deploy.ps1 script and change the values where indicated.

 

 

 

$resourceGroupName = "awfunctionsdev"            # <-- REPLACE the variable values with your own values.
$location = "australiasoutheast"                 # <-- Ensure that the location is a valid Azure location
$storageAccountName = "awfunctionsdev"           # <-- Ensure the storage account name is unique
$appServicePlanName = "AustraliaSoutheastPlan"   # <--
$appInsightsName = "awfunctionsdev"              # <--
$functionName = "awfunctionsdev"                 # <--

 

 

 

 

4. From a PowerShell prompt change into the /environment directory.

5. Run the deploy.ps1 script. The output should be like below.

 

2.gif

 

This script will create a resource group, deploy the ARM (Azure Resource Manager) template, change the permissions for the managed identity created for the app service and finally compress and publish the Azure Function. All that is left to do is create the Event Grid Subscription and start sending events.

 

To create the subscription, follow the steps below: -

  1. In the Azure Portal click in the search bar at the top and enter “event grid subscriptions” – select the item that appears with that name.
  2. Click the + button next to Event subscription
  3. Fill out the details as in the image below with a couple of points to note: -
    1. “Topic Type” should be subscription and select your subscription – you will have to do this for each subscription you want to monitor.
    2. “Filter To Event Types” – you should only select “Resource Write Success”
    3. Select “Azure Function” as the “Endpoint Type” and then follow the pop-out blade to select your function.
  4. Click on “Filters”
  5. In the “Advanced Filters” section add a key and value as below. This will stop the subscription from sending events for when a tag is written and retriggering the function app.

 

Anthony_W_2-1592784725026.png

 

Anthony_W_3-1592784725028.png

 

Note: There is also an ARM template to deploy the Event Grid subscription in the GitHub repository in the eventgrid-arm branch.

 

Time to test – I can create a simple virtual machine using the New-AzVM command. As resources are created, they will send events which are picked up by the Event Grid Subscription. Each event is then sent to the Azure Function endpoint which applies the tag if it does not already exist.

 

Anthony_W_4-1592784725031.png

 

So, there it is – a way to tag resources with the name of the creator, it is important to point out a few caveats around this solution.

  • It will not apply a tag if a creator tag already exists
  • If you deploy objects using a service principal, you may not see a name appear in the tag – instead, it will show the application Id for the service principal.
  • If an object is updated by any process this can cause the tag to be applied – it does not know if a resource is pre-existing before the function was implemented. If you wanted to deploy this in your environment, you could apply a policy to tag every object with a Creator tag and a default value before implementing the function. That way if someone makes changes to an already existing object the function will not alter the tag. You would then remediate all objects and disable the policy before creating the Event Grid subscription.
  • If you wanted to scope this to only tag virtual machines when they are created you could add another filter to the Event Grid subscription, like the image below.

 

Anthony_W_5-1592784725034.png

 

  • My testing cases are small and in no way should reflect your own testing.

 

Thanks for reading this post, if there are any issues or you would like to contribute to the code please submit a PR (pull request) or issue on the Github page.

 

Disclaimer:

The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.

 

75 Comments
Copper Contributor

Nice one @Anthony_W

Is it a good opportunity to add "created date" as another tag at the same time?

Microsoft

@JamesCave absolutely - very easy to do although I already have Azure Policy which does this for me. You could do it via the function though at the same time - just another line of code. 

Update-AzTag -ResourceId $resourceId -Tag @{CreatedDate=$(Get-Date)} -Operation Merge
Copper Contributor

@Anthony_W 

 

This is fantastic!  Thank you.  I have deployed this into our tenant.  The only issue I am having is that all osdisks, either deployed from the marketplace or Devops are being tagged with the function name.  Have you seen this issue?  

Microsoft

@lmpalermo - I haven't seen that, are they being deployed in the same deployment as a virtual machine? My disks are tagged with 05250873-b7a8-4392-b112-cf3c65d72ee1 which is the application Id for the Compute Resource Provider.

Copper Contributor

@Anthony_W sorry for the delay in responding, I was out of the office.

 

Yes, the disks are being deployed with the VM.  I also see it with SQL Servers and SQL Databases.  All other resources are tagged correctly.

 

lmpalermo_0-1603889563391.png

 

Iron Contributor

I suggest to remove auto download of dependencies and instead add them to Modules folder. In consumption plan this adds significant amount of time for function startup time and does not persistent between runs.

Copper Contributor

@Anthony_W 

 

Ran into an issue today where Frontdoor WAF Policies aren't being tagged.  The activity log shows a failure every 2 minutes.  Looks to be an issue with Frontdoor (see below).

 

Is there anyway to exclude this resource type from being tagged?

 

https://github.com/Azure/azure-powershell/issues/10104

 

"Apparently the Azure portal itself also makes use of the PATCH operation to add tags to a Front Door or WAF policy. Hence currently it's not possible to add tags to those resources via the Azure portal. I also tried adding tags to Front Door via an ARM template, which worked. But adding tags to a WAF policy via ARM didn't."

Microsoft

@lmpalermo - you would have to add a filter into the code to make it skip for the front door resources

Microsoft

@Gregory Suvalian Great idea!

Copper Contributor

@Anthony_W 

 

Yep, sorry, I meant to update my post.

 

I updated line 23 of run.ps1 to: 

$ignore = @("providers/Microsoft.Resources/deployments""providers/Microsoft.Resources/tags""providers/Microsoft.Network/frontdoor")
 
No more alerts on any frontdoor resources.
Copper Contributor

Hi @Anthony_W ,

 

Thanks for this article! I've been testing out this solution and everything was working great until today. Now I'm getting a lot of resource "not found" errors when the function tries to apply tags.

 

The function is able to print the ResourceID for the VM or resource on which the tag is being applied like so:

 

2020-11-12T22:39:54.070 [Information] INFORMATION: ResourceId: /subscriptions/blablabla/resourcegroups/test/providers/Microsoft.Compute/virtualMachines/Test1

 

But when the Update-AzTag command is run with that same ResourceId, the resource can't be found. This error spits out on the console:


2020-11-12T22:39:54.867 [Error] ERROR: The Resource 'Microsoft.Compute/virtualMachines/Test1' under resource group 'test' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix

 

I've spent most of my day troubleshooting this. According to the deployment logs the resources finish creating well before the function attempts to add the tags. I checked out the "more details" link provided with the error this morning but it wasn't very helpful. Interestingly, the link now redirects to Microsoft's home page.  (Edit: somehow I copy/pasted the link wrong, edited the link).

 

Am I the only one running into this? Or has something changed on the Azure side?

Microsoft

@biglyfellow  Try updating the runtime to version 3 as at https://github.com/Azure/azure-functions-powershell-worker/wiki/PowerShell-6-to-7-Migration-Guide as this fixed an issue for me today.

 

I haven't seen any issues with Update-AzTag, and I've been running it today as part of something else. Also can confirm the function is running ok in my environment.

 

Have you tried restarting the function app? Anything changed with the managed identity permissions?

Copper Contributor

Thanks @Anthony_W I am using version 3. I just deployed a VM and tried manually running the Update-AzTag command from PowerShell, and it took somewhere around 20 minutes after the deployment before a tag could be applied. Something is probably wrong with the account. I'll keep on digging into it; there was an account notification earlier that said the status of deleted VMs was incorrect. Maybe that's related somehow. Thanks for your help!

 

biglyfellow_1-1605227384575.png

biglyfellow_0-1605227337640.png

 

Brass Contributor

Would be great to support "StringNotContains" in the Advanced filters of Event Grid.

 

  • Use case 1 : filter on data.resourceUri where "StringNotContains" --> 
@("providers/Microsoft.Resources/deployments", "providers/Microsoft.Resources/tags", "providers/Microsoft.Network/frontdoor", "providers/microsoft.insights/autoscalesettings", "Microsoft.Compute/virtualMachines/extensions", "Microsoft.Compute/restorePointCollections", "Microsoft.Classic")
  • Use case 2 : filter on data.resourceUri where "StringNotContains" the Managed Resource Group of Databricks where no ressources can be tagged.
 
Did a feature request on the feedback.azure.com.
Jamesdld

 

Copper Contributor

In case anyone else is running into the issue where the Azure function fails to tag resources, and it takes 20+ minutes for the Update-AzTag command to work after deployment, I have an update from Azure support. The 20+ minute delay is how long it takes Azure to replicate the newly-deployed resource information to the region in which the Update-AzTag command is executed. Azure commands are routed to a single region, and if a resource is deployed in a resource group in another region, that resource's info has to replicate to the region in which the command is executed before the command will be successful. I'm told Azure is working on reducing the replication delay, but for now, I think that this solution is only guaranteed to work if the resources are deployed in a resource group in the same region as the Azure Function.

 

My problem is that I deployed the Azure Function in the US East region, but the VMs being deployed are in resource groups in different regions around the world. According to Azure support, the resource group's location that the VMs are deployed in must match the region in which the tagging command is executed to avoid replication delays. For instance, if I deployed a VM in the US East region but the resource group is assigned to US West 2, the VM's info would still need to replicate from the US West 2 region to the US East region.

 

As a workaround, I'm going to export the write events and use them to tag resources an hour after each event is generated. That should be enough time for resource info to replicate across all Azure regions, and then I wouldn't have to worry about what region the tagging command is executed in.

Copper Contributor

This is all working really well, and many thanks for such a great script. Unfortunately, I'm still getting a GUID when using a service principal to deploy resources via a pipeline, despite giving the managed ID the Application Dev role. It's not a big issue, but it would be nice to resolve, if you have any ideas

 

 

Copper Contributor

@nicktf 

 

Did you try this?  It worked for me.  "Objects created by a Service Principal will tag the objects with a GUID instead of a name by default. You can fix this behavior by giving the Managed Identity the Application Developer role in Azure Active Directory."

Copper Contributor

@nicktf

 

Sorry, I see you tried that.  I had the same issue the first time.  I removed the role on the ID and added it back.  Since then, the name instead of GUID is tagged on my resources deployed from Devops.

Copper Contributor

@lmpalermo Thanks! - I just gave that a try and no joy, I'm still seeing the Object ID - Hmm. 

Copper Contributor

@nicktf

 

I just checked the SP account that my pipeline uses to deploy, and I assigned Application administrator, not Application Developer.  Might want to give that a try. 

Copper Contributor

@lmpalermo - I'm an idiot - total user error - I was making my pipeline SPs accounts App Devs - it needs to be the SP which runs the tagging operation....

Copper Contributor

This was fantastic and super helpful. Thank you!

I assume this pulls the name from their profile, is there a list of other fields that could be used from their profile, such as department or GL?

Copper Contributor

I'm having the same problem as @biglyfellow where write events to resources in a different resource group are failing. Is there any fix for this? It seems like Azure's replication sync takes around 20 minutes still. @biglyfellow, how'd your solution work out?

Copper Contributor

@ell10706The solution is working well! On the event trigger, I'm writing all the relevant resource data (resource ID, creator name, group membership pulled from Azure AD, creation time, etc) to Azure Cosmos DB. Then every 10 minutes, another function gets triggered that tries to tag the resource with the data from the DB. If it fails due to the resource not found error, it will try again on the next trigger until it succeeds.

 

I use a variable named "tagged" in the DB for each resource to keep track of whether it's tagged or not, and it only gets set to true if Update-AzTag doesn't return an error. That prevents the script from trying to tag the resource again after it gets tagged once, and then I delete items in the DB after two weeks.

 

I did file a bug (https://github.com/Azure/azure-powershell/issues/13659) for the replication delay which is still not resolved. It's also not assigned, so I don't think anyone will update this particular bug. But Microsoft Support did tell me that it's a known issue and they're working on fixing it.

Copper Contributor

Hi @Anthony_W,

 

Is it still working? I'm checking one of the deployment and i see the resource id is not matching. Ex: If i deploy a storage account it's generating different value rather then the actual ID. 

 

Resource ID from the Activity Log: Actual storage name is: stgthirdlogan01

/subscriptions/SubID/resourcegroups/third-rg01/providers/microsoft.resources/deployments/stgthirdlogan01_1659025096474

 

Actual Resource ID: 

/subscriptions/SubID/resourceGroups/Third-RG01/providers/Microsoft.Storage/storageAccounts/stgthirdlogan01

 

It's randomly generating some numbers for storage account and VM resource also it's not working properly. Any help would be much appreciated! Thank you!

Microsoft

Hi @Logan-IN - if it is tagging with a GUID - it may be because you haven't given Application Developer permissions to the function Managed Identity. Function runs that are triggered by a write to a deployment object are ignored in the code. 

 

Try manually deploying a resource - it should attach your name correctly as the caller. The function also writes into Application Insights with all the logs so you can view in there to see what is happening - or post a sample of the logs as well.

Copper Contributor

Hi @Anthony_W ,

 

Thank you for the response. It's identifying the calller properly and the only issue i see is resource id is not correct. 

 

Btw, i'm not using above mentioned solution and I have used LogAnalytics with automation and it's partially working. I'll test this function based solution and let you know if it's not working.  

Copper Contributor

Hi @Anthony_W,

 

Your solution for tagging Azure resources with a creator is great but as far as I understand your described setup is being considered to run in a single Subscription.

Do you have any recommendations how to deal with several hundred Subscriptions by avoiding to create an Event Grid Subscription for each Subscription?

 

Microsoft

@JohnnyB85 - you can use Azure Policy to deploy event grid subscriptions and point them all to the function - need to make sure that the function managed identity has permissions in the other subscriptions.

 

You can use this link to see how the DINE effect is done to deploy the subscription topic - the rest doesn't apply but should get you started Event grid policy (github.com)

Copper Contributor

@Anthony_W 

I'm having a problem on line 33 of the script, this particular part. 

 

$tags.TagsProperty.ContainsKey('Creator')

 

On some resource types it doesn't seem to use the ContainsKey properly and fails with this exception pasted below.  It'll consistanly fail on VMs, NSGs, disks, VNETs, and NICs.

 

Though it does work on some resource types--whether or not creator is contains is correctly measured as true or false.  Examples of working resource types I've found are user-assigned managed IDs, resource groups, and app insights.

 

ERROR: You cannot call a method on a null-valued expression. Exception : Type : System.Management.Automation.RuntimeException ErrorRecord : Exception : Type : System.Management.Automation.ParentContainsErrorRecordException Message : You cannot call a method on a null-valued expression. HResult : -2146233087 CategoryInfo : InvalidOperation: (:) [], ParentContainsErrorRecordException FullyQualifiedErrorId : InvokeMethodOnNull InvocationInfo : ScriptLineNumber : 33 OffsetInLine : 5 HistoryId : -1 ScriptName : C:\home\site\wwwroot\TagWithCreator\run.ps1 Line : if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags)) { PositionMessage : At C:\home\site\wwwroot\TagWithCreator\run.ps1:33 char:5 + if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PSScriptRoot : C:\home\site\wwwroot\TagWithCreator PSCommandPath : C:\home\site\wwwroot\TagWithCreator\run.ps1 CommandOrigin : Internal ScriptStackTrace : at <ScriptBlock>, C:\home\site\wwwroot\TagWithCreator\run.ps1: line 33 TargetSite : System.Object CallSite.Target(System.Runtime.CompilerServices.Closure, System.Runtime.CompilerServices.CallSite, System.Object, System.String) StackTrace : at CallSite.Target(Closure , CallSite , Object , String ) at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame) Message : You cannot call a method on a null-valued expression. Data : System.Collections.ListDictionaryInternal Source : Anonymously Hosted DynamicMethods Assembly HResult : -2146233087 CategoryInfo : InvalidOperation: (:) [], RuntimeException FullyQualifiedErrorId : InvokeMethodOnNull InvocationInfo : ScriptLineNumber : 33 OffsetInLine : 5 HistoryId : -1 ScriptName : C:\home\site\wwwroot\TagWithCreator\run.ps1 Line : if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags)) { PositionMessage : At C:\home\site\wwwroot\TagWithCreator\run.ps1:33 char:5 + if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PSScriptRoot : C:\home\site\wwwroot\TagWithCreator PSCommandPath : C:\home\site\wwwroot\TagWithCreator\run.ps1 CommandOrigin : Internal ScriptStackTrace : at <ScriptBlock>, C:\home\site\wwwroot\TagWithCreator\run.ps1: line 33

Microsoft

@Colegrove  Strange I haven't seen this for a long time - can you confirm that the managed identity on the function has the correct permissions? If it can't find that key it could indicate that line 31 is failing to retrieve the resource - you can get better logging if you check the function traces in app insights and you can trace through the whole execution.

Copper Contributor

@Anthony_W  Thanks for taking a look.  I double-checked that the function has the reader role and the tag contributor role on the target subscription (which happens to be the same sub that contains the function itself).

 

To verify line 31, I added this bit on line 32:

 

Write-Host "is line 31 working? Tags: $tags"

 

It does seem to be working, or at least, the line 32 results are the same when the script executes successfully or unsuccessfully.  The output is always this in both cases:

 

2022-09-09T11:19:31.030 [Information] INFORMATION: is line 31 working? Tags: Microsoft.Azure.Commands.Tags.Model.PSTagsObject

 

I also suspected the resrouceID variable might not be getting created properly in line 14, but line 15 also consistently produces the correct ID whether or not the script fails.  Here's an example of the output from a failed run: 

 

2022-09-09T11:17:56.458 [Information] INFORMATION: ResourceId: /subscriptions/xxxxxxxxxxxxxxxxx/resourcegroups/testrg10/providers/Microsoft.Compute/disks/testdisk

 

Lastly, I've tried PS version 7.0 and 7.2 but it made no difference. 

 

I'm not familiar with how to directly interface with the function's app insights, but I'll look that up and check it next.

Copper Contributor

@Anthony_W 

I stripped down the PS code to only the essentials and have been testing it in VS Code (PS 7.2.6) and ISE (PS 5.1.14409.1027) .  In this case, a resource group (working type) and an NSG (non-working type), both with no tags at all.

 

$ResourceGroupId = '/subscriptions/xxxxx/resourceGroups/FridayTest'
$NsgId = '/subscriptions/xxxxx/resourceGroups/FridayTest/providers/Microsoft.Network/networkSecurityGroups/test40-nsg'
$RgTags = (Get-AzTag -ResourceId $ResourceGroupId).Properties
$NsgTags = (Get-AzTag -ResourceId $NsgId).Properties
 
When I check the variables that contain the Get-AzTag properties, I've noticed a slight difference in the working resource types vs non-working.
 
Here's the output of a working one, notice the braces

PS C:\> $RgTags

TagsProperty
------------
{}

 

And here's the output of a non-working one, no braces

PS C:\> $NsgTags

TagsProperty
------------

 

The $RgTags variable will handle the ContainsKey just fine but the $NsgTags variable will fail with the aforementioned error.   I'm starting to suspect there's a bug in the Get-AzTag cmdlet but then how are other not reporting this issue?  Very confusing.

Copper Contributor

I have a question. This configuration is failing the Micorosoft Defender for Clouds recommendations:

Encrypt data in transit > Function App should only be accessible over HTTPS. 

Can I modify the Function App to force only HTTPS traffic since this Function App is not using the HTTP trigger but should be using an Event Grid trigger? This will align with the latest Azure Security Benchmark and should not affect your ps1 correct? 

Microsoft

@AllenVisser - should be fine - I've updated the main code to set httpsOnly and the minTLS version to 1.2

Copper Contributor

I get an error when trying to deploy via the deploy.ps1 script.

VERBOSE: Performing the operation "Replacing resource group ..." on target "resourcegroup9876".
VERBOSE: 12:38:05 PM - Created resource group 'resourcegroup9876' in location 'westus'

ResourceGroupName : resourcegroup9876
Location          : westus
ProvisioningState : Succeeded
Tags              :
ResourceId        : /subscriptions/121434-243-234-234-234/resourceGroups/resourcegroup9876

VERBOSE: Performing the operation "Creating Deployment" on target "resourcegroup9876".
New-AzResourceGroupDeployment: /TagWithCreator/environment/deploy.ps1:17
Line |
  17 |  $output = New-AzResourceGroupDeployment -ResourceGroupName $resourceG …
     |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Invalid character after parsing property name. Expected ':' but got: s. Path 'resources[3].properties', line 70, position 17.

New-AzRoleAssignment: /TagWithCreator/environment/deploy.ps1:19
Line |
  19 |  … me "Reader" -ObjectId $output.Outputs.managedIdentityId.Value -ErrorA …
     |                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot validate argument on parameter 'ObjectId'. The argument is null or empty. Provide an argument that is not null or empty, and then try the
     | command again.

New-AzRoleAssignment: /TagWithCreator/environment/deploy.ps1:20
Line |
  20 |  … ontributor" -ObjectId $output.Outputs.managedIdentityId.Value -ErrorA …
     |                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot validate argument on parameter 'ObjectId'. The argument is null or empty. Provide an argument that is not null or empty, and then try the
     | command again.

VERBOSE: Preparing to compress...
VERBOSE: Performing the operation "Compress-Archive" on target "
/TagWithCreator/functions/TagWithCreator
/TagWithCreator/functions/host.json
/TagWithCreator/functions/local.settings.json
/TagWithCreator/functions/profile.ps1
/TagWithCreator/functions/proxies.json
/TagWithCreator/functions/requirements.psd1".
VERBOSE: Adding '/TagWithCreator/functions/TagWithCreator/function.json'.
VERBOSE: Adding '/TagWithCreator/functions/TagWithCreator/run.ps1'.
VERBOSE: Adding '/TagWithCreator/functions/TagWithCreator/sample.dat'.
VERBOSE: Adding '/TagWithCreator/functions/host.json'.
VERBOSE: Adding '/TagWithCreator/functions/local.settings.json'.
VERBOSE: Adding '/TagWithCreator/functions/profile.ps1'.
VERBOSE: Adding '/TagWithCreator/functions/proxies.json'.
VERBOSE: Adding '/TagWithCreator/functions/requirements.psd1'.
Publish-AzWebApp: /TagWithCreator/environment/deploy.ps1:29
Line |
  29 |  Publish-AzWebApp -ResourceGroupName $resourceGroupName -Name $functio …
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Operation returned an invalid status code 'NotFound'
Brass Contributor

@Anthony_W adding "httpsOnly: true" has broken the JSON file.

I rolled back the JSON file to the 2020 version and it is now working.

Microsoft

@NMLVS thanks for letting me know - I probably should have tested it before updating - will look at it next week. 

Copper Contributor

Definitely NOT WORKING with many errors.

 

Please either remove or update 

Copper Contributor

I am getting the same error as others - 

 

023-01-06T15:57:59.727 [Error] ERROR: You cannot call a method on a null-valued expression.Exception :Type : System.Management.Automation.RuntimeExceptionErrorRecord :Exception :Type : System.Management.Automation.ParentContainsErrorRecordExceptionMessage : You cannot call a method on a null-valued expression.HResult : -2146233087CategoryInfo : InvalidOperation: (:) [], ParentContainsErrorRecordExceptionFullyQualifiedErrorId : InvokeMethodOnNullInvocationInfo :ScriptLineNumber : 35OffsetInLine : 5HistoryId : -1ScriptName : C:\home\site\wwwroot\TagWithCreator\run.ps1Line : if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags)) {PositionMessage : At C:\home\site\wwwroot\TagWithCreator\run.ps1:35 char:5+ if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags)) …+

 

If I create a Resource Group (for example) on its own then it works. However, If (for example) a VM then it fails on every resource (VM, Resource Group, Disk etc.).

 

@Colegrove did you ever find a resolution?

Copper Contributor

I get the same error as @Steven Colgrove.

 

ERROR: You cannot call a method on a null-valued expression. Exception : Type : System.Management.Automation.RuntimeException ErrorRecord : Exception : Type : System.Management.Automation.ParentContainsErrorRecordException Message : You cannot call a method on a null-valued expression. HResult : -2146233087 CategoryInfo : InvalidOperation: (:) [], ParentContainsErrorRecordException FullyQualifiedErrorId : InvokeMethodOnNull InvocationInfo : ScriptLineNumber : 35 OffsetInLine : 5 HistoryId : -1 ScriptName : C:\home\site\wwwroot\TagWithCreator\run.ps1 Line : if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags)) { PositionMessage : At C:\home\site\wwwroot\TagWithCreator\run.ps1:35 char:5 + if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags)) … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PSScriptRoot : C:\home\site\wwwroot\TagWithCreator PSCommandPath : C:\home\site\wwwroot\TagWithCreator\run.ps1 CommandOrigin : Internal ScriptStackTrace : at <ScriptBlock>, C:\home\site\wwwroot\TagWithCreator\run.ps1: line 35 TargetSite : System.Object CallSite.Target(System.Runtime.CompilerServices.Closure, System.Runtime.CompilerServices.CallSite, System.Object, System.String) StackTrace : at CallSite.Target(Closure , CallSite , Object , String ) at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame) Message : You cannot call a method on a null-valued expression. Data : System.Collections.ListDictionaryInternal Source : Anonymously Hosted DynamicMethods Assembly HResult : -2146233087 CategoryInfo : InvalidOperation: (:) [], RuntimeException FullyQualifiedErrorId : InvokeMethodOnNull InvocationInfo : ScriptLineNumber : 35 OffsetInLine : 5 HistoryId : -1 ScriptName : C:\home\site\wwwroot\TagWithCreator\run.ps1 Line : if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags)) { PositionMessage : At C:\home\site\wwwroot\TagWithCreator\run.ps1:35 char:5 + if (!($tags.TagsProperty.ContainsKey('Creator')) -or ($null -eq $tags)) … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PSScriptRoot : C:\home\site\wwwroot\TagWithCreator PSCommandPath : C:\home\site\wwwroot\TagWithCreator\run.ps1 CommandOrigin : Internal ScriptStackTrace : at <ScriptBlock>, C:\home\site\wwwroot\TagWithCreator\run.ps1: line 35

 

If I create (e.g.) a Resource Group on its own then it works.

If I create (e.g.) a VM then it fails on every resource (VM, disk, NIC, Resource Group).

Copper Contributor

After some changes to the function code tagging works for me:

 

#//--snip---
$newTag = @{
    CreatedBy = $caller
}

$tags = (Get-AzTag -ResourceId $resourceId)

if ($tags){  # Tags supported?
    if ($tags.properties){ # if null no tags?
        if ($tags.properties.TagsProperty){
            if (!($tags.properties.TagsProperty.ContainsKey('CreatedBy')) ) {
                Update-AzTag -ResourceId $resourceId -Operation Merge -Tag $newTag | Out-Null
                Write-Host "Added CreatedBy tag with user: $caller"
            }
            else {
                Write-Host "CreatedBy tag already exists"
            }
        }else{
            Write-Host "Added CreatedBy tag with user: $caller"
            New-AzTag -ResourceId $resourceId -Tag $newTag | Out-Null
        }
    }else{
        Write-Host "WARNNG! Does $resourceId does not support tags? (`$tags.properties is null)"
    }
}
else{
    Write-Host "$resourceId does not support tags"
}
#//--snip---

 

@Anthony_W :

Can you please show how we can automatically create the event subscription via a azure policy?

Unfortunately the link has not helped me.

Copper Contributor

@bawe75 

I've updated code with your snippet code. Before it was working only for Resource Group tag, unfortunately it doesn't work for RG.

Storage account tagging is working fine from previous code but not for SQL database and Virtual Machine.

Add below filters.

rrawat75_0-1674135230657.png

Guy's I've automate whole process using terraform code, but looking for a way how to get tagging on Virtual machine.

Copper Contributor

@rrawat75 

Here my filters:

bawe75_0-1674141968247.png

With SQL I've not tested so far - but Virtual machines (vnet,vm,ip,nsg,networkinterface,ssh-key) works - only the disk was created by a uuid instand of my user....

Microsoft

@bawe75 do you want to add your changes to the main repo and do a PR so it can be integrated? 

 

I'll have another look at the event grid creation through Azure Policy and get back to you

Copper Contributor

@bawe75 Perfect!!!

It's working for me now, I've tested for below resources and tag is reflect on all.

  • Resource Group 
  • Storage Account
  • SQL Database/ SQL Server
  • Virtual Machines (and tag appear in all associated resources)

And it was working fine from my terraform code. The only thing is left I can't pass the requirements.psd1 app file though terraform.

For this I've to enable Az Module manually from console. Rest terraform does for me :)

Brass Contributor

@rrawat75 can you expand on what you are doing with this via Terraform please?

Copper Contributor

@NMLVS Creating Azure function app, function, Event grid System Topic, Role assignment (tag contributor) and Event grid on Subscription using terraform code. 

No manual work except adding Azure Modules in requirements.psd1 file. 

 

Snippet from my code:

rrawat75_0-1674237345661.png

 

Copper Contributor

FYI.  The new run.ps1 uses a different tag name, "CreatedBy".  The old run.ps1 and the tag name referenced in the main article use "Creator".  

Copper Contributor

Any suggestion on how to modify the PS script to process the events when they're piped through a Service Bus, storage queue, or Event Hub?  I have some networking requirements that can be met with the architecture described in this article.  

 

Deliver events using private link service - Azure Event Grid | Microsoft Learn

Version history
Last update:
‎Jun 22 2020 06:14 PM
Updated by: