Apply an Azure Policy to a management group using ARM - azure-resource-manager

Goal: Deploy an Azure Policy to a management group so when certain tags are missing from a resource within its remit, apply the specified Tag from the resource group
Problem: Deploying this template to the management group results in "'The template function 'RESOURCEGROUP' is not expected at this location."
There is a fairly plain structure similar to:
<Management Group> - <Subscription 1> - <Resource Group 1> - <Resource A>
- <Resource Group 2> - <Resource B>
- <Subscription 2> - <Resource Group 3> - <Resource C>
- <Resource D>
There is a fairly simple template using a nested policy definition:
......
"resources": [
{
"type": "Microsoft.Authorization/policyDefinitions",
"apiVersion": "2019-09-01",
"name": ".",
"properties": {
"policyType": "Custom",
"mode": "Indexed",
"displayName": ".",
"description": ".",
"metadata": {
"category": "Tags"
},
"policyRule": {
"if": {
"anyOf": [
{
"field": "tags['costCenter']",
"exists": "false"
},
{
"field": "tags['CostCenter']",
"notin": "[parameters('allowedCostCenter')]"
}
]
},
"then": {
"effect": "modify",
"details": {
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f"
],
"operations": [
{
"operation": "add",
"field": "tags['CostCenter']",
"value": "[resourcegroup().tags['CostCenter']]"
}
]
}
}
}
}
}
]
I realise that you can not use "resourcegroup()" on items that are not within a resource group, but the guides suggested using this within the nested template and on "indexed" resources should work.
I'm fairly sure the pipeline is correct as I already have several audit policies deploying
From experimenting in the portal, this looks like it should be possible
There is a decent amount of reading around, but I have not read (or at least understood) that seems to help with this
Is what I am trying to achieve possible? If so, can you see what I am doing wrong?
Thanks for your help!

You need to add escape character if you want resourcegroup() function to be executed as a part of the Azure Policy, not the MG-scope ARM template:
"value": "[[resourcegroup().tags['CostCenter']]"

Related

Can't get the desired properties via JsonPath evaluate method

I have a json schema that marks special properties in need of processing and I want to query those via JsonPath.Evaluate.
Here's a part of the schema to illustrate the issue
{
"type": "object",
"properties": {
"period": {
"description": "The period in which the rule applies",
"type": "object",
"properties": {
"start": {
"type": "string",
"format": "date-time"
},
"end": {
"type": "string",
"format": "date-time"
}
},
"required": [
"start"
],
"x-updateIndicatorProperties": [
"start"
]
},
"productType": {
"type": "string"
},
"x-updateIndicatorProperties": [
"productType"
]
}
}
I want to get the the JsonPath of the "x-updateIndicatorProperties" properties, so that I can then query the actual properties to process.
For this example, the expected result would be
[
"$['properties']['x-updateIndicatorProperties']",
"$['properties']['period']['x-updateIndicatorProperties']"
]
I've been trying for a while to get a JsonPath expression that would query these properties.
Currently I'm just iterating all properties and filter them manually :
"$..*"
I've also tried using :
$..['x-updateIndicatorProperties']
This works. But it returns a lot of duplicates. For the example above, I get 5 results instead of the expected 2. Can be demonstrated here : https://json-everything.net/json-path
Assuming I can't influence the schema itself, only the code that traverses it,
can anybody help with an expression to get the expected results or any other way to achieve the same outcome?
The stack is JsonPath 0.2.0, .net 6 and system.text.json.
This was a bug in the library when parsing paths that use a recursive descent (..) into a quoted-property-name selector (['foo']). So it would happen for any path in the form $..['foo'].
I've fixed the issue and released version 0.2.1.

Artifactory: how can I obtain a complete dump of all fields for an artifact?

I'm interested in the value of one field (Module ID) but there seems to be no way of obtaining this specifically. A complete dump of all field values would also suffice but I haven't succeeded in finding a way to do that either. I've looked at and tried the searches available within the documentation here: https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-SEARCHES
If it helps, I'm trying to query an on-premise installation of Artifactory.
More fields can be added to the AQL using the "include" element.
For example - to list all artifacts under "libs-release-local" repository, including their module names, run the following query:
items.find(
{
"repo":{"$eq":"libs-release-local"}
}
).include("artifact.module")
Response example:
{
"results": [
{
"repo": "libs-release-local",
"path": "org/jfrog/test/multi2/2.17.0",
"name": "multi2-2.17.0.jar",
"type": "file",
"size": 1022,
"created": "2021-09-11T13:51:33.878Z",
"created_by": "deployer",
"modified": "2021-09-11T13:51:33.631Z",
"modified_by": "deployer",
"updated": "2021-09-11T13:51:33.881Z",
"artifacts": [
{
"modules": [
{
"module.name": "org.jfrog.test:multi2:2.17.0"
}
]
}
]
}
]
}
You can find all of the required information under AQL documentation.

How to "dependsOn" all copies of a resource?

How can I set up a dependsOn to depend on all copies of a certain resource? Hypothetically, I deploy 0..N number of websites and I need them all to complete before I deploy my traffic manager because the TM needs resource IDs.
Currently I'm only deploying 2 and so I'm just enumerating two items in the dependsOn array, but if I decide I want to deploy more copies (as determined by
[variables('tdfConfiguration')] array), it would be nice for dependsOn to dynamically figure this out.
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Resources/deployments",
"name": "[concat(resourceGroup().name, '-', variables('tdfConfiguration')[0]['roleName'], '-tmprofile')]",
"dependsOn": [
"[concat(resourceGroup().Name, '-', variables('tdfConfiguration')[0]['roleName'], '-website')]",
"[concat(resourceGroup().Name, '-', variables('tdfConfiguration')[1]['roleName'], '-website')]"
],
fairly easy, use copy name. suppose you have a resource like so:
"name": xxx,
"type": zzz,
...
"copy": {
"name": "myCopy",
"count": 0..N
}
you can use the followin dependsOn to depend on all copies:
"dependsOn": [ "myCopy" ]
Reading: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#depend-on-resources-in-a-loop

How to I prevent Microsoft.Automation/automationAccounts/Compilationjobs to always run in ARM deployment?

My ARM template is below which is nested template in bigger ARM template. For some reason DSC Compilation job always run on each deployment. I expected it not be run if it was already run before. How do I control this behavior? I tried using "incrementNodeConfigurationBuild": "false" but it did not do the trick.
{
"name": "WorkerNodeDscConfiguration",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"resourceGroup": "[parameters('automationAccountRGName')]",
"dependsOn": [],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.1",
"resources": [
{
"apiversion": "2015-10-31",
"location": "[reference(variables('automationAccountResourceId'), '2018-01-15','Full').location]",
"name": "[parameters('automationAccountName')]",
"type": "Microsoft.Automation/automationAccounts",
"properties": {
"sku": {
"name": "Basic"
}
},
"tags": {},
"resources": [
{
"name": "workernode",
"type": "configurations",
"apiVersion": "2018-01-15",
"location": "[reference(variables('automationAccountResourceId'), '2018-01-15','Full').location]",
"dependsOn": [
"[concat('Microsoft.Automation/automationAccounts/', parameters('AutomationAccountName'))]"
],
"properties": {
"state": "Published",
"overwrite": "false",
"incrementNodeConfigurationBuild": "false",
"Source": {
"Version": "1.2",
"type": "uri",
"value": "[parameters('WorkerNodeDSCConfigURL')]"
}
}
},
{
"name": "[guid(resourceGroup().id, deployment().name)]",
"type": "Compilationjobs",
"apiVersion": "2018-01-15",
"tags": {},
"dependsOn": [
"[concat('Microsoft.Automation/automationAccounts/', parameters('AutomationAccountName'))]",
"[concat('Microsoft.Automation/automationAccounts/', parameters('AutomationAccountName'),'/Configurations/workernode')]"
],
"properties": {
"configuration": {
"name": "workernode"
},
"incrementNodeConfigurationBuild": "false",
"parameters": {
"WebServerContentURL": "[parameters('WebServerContentURL')]"
}
}
}
]
}
]
}
}
}
In short, AFAIK you should be able to control this behaviour with 'condition'.
To explain it in detail, the DSC compilation jobs resource always run on each deployment because when we use the DSC compilation jobs resource (i.e., Microsoft.Automation/automationAccounts/compilationjobs) in the ARM template, IMHO what it does in the behind is, basically clicks on 'Compile' button of the DSC configuration.
If you click on that 'Compile' button, the compilation of job happens for sure even if it already compiled the job. You may check the same part manually as well.
So AFAIK that was the reason for compilation job always running on each deployment.
What you could do is, update your ARM template with 'condition' (For more information, refer https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-templates-resources#condition and https://learn.microsoft.com/en-us/azure/architecture/building-blocks/extending-templates/conditional-deploy) and then wrap your template with below sample piece of PowerShell code that would determine if the Compilation of job for particular DSC configuration is done already and then deploy the template by passing inline parameter value or by updating condition parameter in parameters template file with new or existing value accordingly. (For more information, refer https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-deploy#pass-parameter-values)
$DscCompilationJob = Get-AzAutomationDscCompilationJob -AutomationAccountName AUTOMATIONACCOUNTNAME -ResourceGroupName RESOURCEGROUPNAME|Sort-Object -Descending -Property CreationTime|Select -First 1| Select Status
$DscCompilationJobStatus = $DscCompilationJob.Status
if ($DscCompilationJobStatus -ne "Completed"){
$DscCompilationJobStatusInlineParameter = "new"
New-AzResourceGroupDeployment -Name ExampleDeployment -ResourceGroupName testgroup -TemplateFile TEMPLATEFILEPATH\demotemplate.json -exampleString $DscCompilationJobStatusInlineParameter
#or update condition parameter in parameters template file with new value accordingly and use below command to deploy the template
New-AzResourceGroupDeployment -Name ExampleDeployment -ResourceGroupName ExampleResourceGroup -TemplateFile TEMPLATEFILEPATH\demotemplate.json -TemplateParameterFile TEMPLATEFILEPATH\demotemplate.parameters.json
}else{
$DscCompilationJobStatusInlineParameter = "existing"
New-AzResourceGroupDeployment -Name ExampleDeployment -ResourceGroupName testgroup -TemplateFile TEMPLATEFILEPATH\demotemplate.json -exampleString $DscCompilationJobStatusInlineParameter
#or update condition parameter in parameters template file with existing value accordingly and use below command to deploy the template
New-AzResourceGroupDeployment -Name ExampleDeployment -ResourceGroupName ExampleResourceGroup -TemplateFile TEMPLATEFILEPATH\demotemplate.json -TemplateParameterFile TEMPLATEFILEPATH\demotemplate.parameters.json
}
And regarding incrementNodeConfigurationBuild property, IMHO this property is just with regards to creation of a new build version of Node Configuration is required or not i.e., when incremental node configuration build is set to false, it does not override the earlier existing Node Configuration by creating a new Node Configuration with the name CONFIGNAME[<2>] (the version number is incremented based on the existing version number already present).
Hope this helps!! Cheers!! :)

FHIR : adding a custom extension

I would like to add to add a custom extension to my Schedule resource.
In my app, Schedule have visit motives (reasons). I know there's a list of classified appointments / encounter reasons but I would like to use mine.
I have something like this :
{
"resourceType":"Schedule",
"identifier":"logical_id",
"type":"schedule_speciality",
"actor":{
"practioner_id":"identifier",
"practioner_name":"practioner name"
},
"external_id":{
"extension":[
{
"url":"http://api.test.com/fhir/schedule/external_id",
"valueIdentifier":"external_id"
}
]
},
"visit_motives":{
"extension":[
{
"url":"https://api.test.com/fhir/ValueSet/schedule#visit_motives",
"valueString":"vist_motive1"
},
{
"url":"https://api.test.com/fhir/ValueSet/schedule#visit_motives",
"valueString":"vist_motive2"
},
{
"url":"https://api.test.com/fhir/ValueSet/schedule#visit_motives",
"valueString":"vist_motive3"
}
]
},
"practice_id":{
"extension":[
{
"url":"https://api.test.com/fhir/schedule/practice_id",
"valueIdentifier":"practice_id"
}
]
}
}
I'm not sure about this part :
"visit_motives":{
"extension":[
{
"url":"https://api.test.com/fhir/ValueSet/schedule#visit_motives",
"valueString":"vist_motive1"
},
{
"url":"https://api.test.com/fhir/ValueSet/schedule#visit_motives",
"valueString":"vist_motive2"
},
{
"url":"https://api.test.com/fhir/ValueSet/schedule#visit_motives",
"valueString":"vist_motive3"
}
]
}
Is it correct to add an extension this way ? There are always multiple visit motives for a specific schedule so I have to list them.
I also have seen this kind of things :
"visit_motives": {
"coding": [
{
"system": "https://api.test.com/fhir/ValueSet/schedule#visit_motives",
"code": "visit_motive1"
}
]
}
Which one is the correct one or am I wrong ?
There are several issues here:
It seems odd to capture a "reason" on a schedule. A schedule says when a particular clinician or clinic or other resource is available. E.g. "Dr. Smith takes appointments Mon/Wed/Fri from 1pm-4pm". So if you were to capture a reason on the resource, it would reflect "Why does Dr. Smith have a schedule?" Typically reasons are captured for an individual Appointment. That's the resource that reserves a particular slot for a planned visit. And Appointment already has an element for reason where you're free to use your own codes or just send text.
You have extensions to convey identifiers, but Schedule already has an element for identifiers. Why would you use extensions instead of the standard element? Note that you can use the "system" and/or "type" components to differentiate different kinds of identifiers.
You're sending "identifier", "type", "name", etc. as simple strings - but they're complex data types, so you need to communicate the child elements
actor is of type Reference - that means you need to point to the Practitioner resource. You can't send the properties in-line. (If the Practitioner only exists in the context of the Schedule, you could use the "contained" approach which would use an internal reference, but containment doesn't seem to make sense in this use-case.
The URL for your extension contains ValueSet, which isn't correct - extensions are all structure definitions. Also, there shouldn't be a # symbol in the URL.
Your syntax for extensions is incorrect. You can't introduce new properties in FHIR. The property name for all extensions is just "extension". You differentiate by the URL. So your syntax should be:
{
"resourceType":"Schedule",
"id":"logical_id",
"extension": [
{
"url":"https://api.test.com/fhir/StructureDefinition/schedule-visit_motive",
"valueString":"vist_motive1"
},
{
"url":"https://api.test.com/fhir/StructureDefinition/schedule-visit_motive",
"valueString":"vist_motive2"
},
{
"url":"https://api.test.com/fhir/StructureDefinition/schedule-visit_motives",
"valueString":"vist_motive3"
}
],
"identifier": [
{
"system": http://api.test.com/fhir/NamingSystem/external_id",
"value": "external_id"
}
{
"system": http://api.test.com/fhir/NamingSystem/practice_id",
"value": "practice_id"
}
]
"type": {
"coding": {
"system": "http://somewhere.org/fhir/CodeSystem/specialties",
"code": "schedule_speciality"
},
"text": "Some text description of specialty"
},
"actor":{
"reference": "http://myserver.org/fhir/Practitioner/12345"
"display": "Dr. smith"
}
}

Resources