# ARM Template object parameters: defaults and unions

This is a quick post on working with object parameters in ARM templates.

ARM templates have different [parameter types](https://docs.microsoft.com/azure/azure-resource-manager/templates/parameters#minimal-declaration): `string`, `int`, `bool`, `object`, and so on.

Parameters have various attributes: their type, some metadata, and a default value.

The [default value](https://docs.microsoft.com/azure/azure-resource-manager/templates/parameters#default-value) attribute is used to make a parameter optional: if you deploy the template and don't specify a value for a particular parameter, ARM will just use the default value configured on that parameter. If you don't specify a value **and** the parameter doesn't have a default value, you get a deployment error.

For scalar parameter types like `string` or `int`, specifying a default value is simple and intuitive: a `string` default value could just be "" (blank) or "foo" or whatever, including using some ARM functions. Same with `int`, `bool`, and so on. For example:

```json
"parameters": {
  "location": {
    "type": "string",
    "defaultValue": "[resourceGroup().location]"
  },
  "doSomething": {
    "type": "bool",
    "defaultValue": false
  },
  "thingCount": {
    "type": "int",
    "defaultValue": 0
  }
}
```

This is very straightforward. However: when you use a parameter of type `object` and want its default value to be null or empty if not specified, **this isn't documented** (that I could find). Various tricks like double quotes, `null()`, `json('null')` and so on all fail.

For example, adding a `tags` parameter to allow a caller to optionally specify a set of key-value tags, but if not specified, having the value of `tags` be an empty or null object:

```json
		"tags": {
			"type": "object",
			"defaultValue": "try various things here to default to an empty or null object..."
		}
```

results in an error like this:

```json
{'code': 'InvalidTemplate', 'message': "Deployment template validation failed: 'Template parameter 'tags' was provided an invalid value. Expected a value of type 'Object', but received a value of type 'Null'.
```

So how do you do it? [`createObject()`](https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions-object#createobject)!

Normally, [`createObject()`](https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions-object#createobject) requires an even set of arguments (key and value pairs), but if passed zero arguments, it emits an empty object.

```json
		"tags": {
			"type": "object",
			"defaultValue": "[createObject()]"
		}
```

This works! Now you can have ARM template parameters of type `object`, and make them optional for template callers, defaulting to empty objects.

In my case, I used such a `tags` parameter to allow a template deployment to optionally specify a set of key-value tags, then added an automatically-generated Timestamp tag so that any Azure resource deployed with this template would automatically have a Timestamp showing when the resource was deployed. (You could figure this out with the Activity log, of course, but a tag makes it easier and quicker.)

To do this, you'll first need a `Timestamp` parameter with its value automatically set to the current date and time. Here, I'm using UTC time for consistency across a multi-geo Azure estate. And for completeness, I'm showing both the `timestamp` template parameter as well as `tags`, which you already saw above.

```json
"parameters": {
	"timestamp": {
		"type": "string",
		"defaultValue": "[utcNow('u')]"
	},
	"tags": {
		"type": "object",
		"defaultValue": "[createObject()]"
	}
}
```

And then in template variables, I'm first creating a `Timestamp` tag (remember a tag has to be a key-value pair) variable, and then combining any tags the caller may have specified with this Timestamp tag, using the ARM [`union()`](https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions-object#union) function.

```json
"variables": {
	"timeStampTag": {
		"Timestamp": "[parameters('timestamp')]"
	},
	"tags": "[union(parameters('tags'), variables('timeStampTag'))]"
}
```

And then we set the `tags` variable onto the deployed resource via its `tags` attribute, as usual.

```json
"tags": "[variables('tags')]"
```

When I call this template and don't specify a value for the `tags` parameter at all, I wind up with a deployed resource with just the timestamp.

![Resource with just the Timestamp tag](//cdn.plzm.blog/media/images/2022/03/30/tags-timestamp-only.png "Resource with just the Timestamp tag")

But when I call this template and specify some tags relevant in my environment - here, I passed `AutoRefresh`, `OSDiskName`, and `Classification` tags plus values for each - I get all these tags plus the Timestamp tag.

![Resource with specified and Timestamp tags combined](//cdn.plzm.blog/media/images/2022/03/30/tags-combined.png "Resource with specified and Timestamp tags combined")

The above ARM JSON fragments are from my [Azure Linux VM template](https://github.com/plzm/azure-deploy/blob/main/template/vm.linux.json). The screenshots with just the Timestamp tag, or the combined set of tags, are from VMs deployed with this template.

This is one of the modular templates I maintain in my [plzm/azure-deploy GitHub repo](https://github.com/plzm/azure-deploy) and use in various IaC projects. See [my post on modular and reusable ARM templates](https://plzm.blog/202104-modular-reusable-arm-templates) for more.

To recap:
1. Use an empty `[createObject()]` to make an `object` template parameter optional and default it to an empty object if the template caller does not specify a value.
2. You can combine automatically created objects with defaulted or specified objects.

Happy templating and IACing!
