Space separated values; how to provide a value containing a space
Asked Answered
I

5

7

I'm creating a bash script to provision multiple Azure resources via the Azure CLI. So far so good, however I'm having a problem tagging resources.

My goal is to store multiple tags in a variable and provide that variable to the --tags option of several az commands in the script. The problem however is that a space in the value will be interpreted as a new key.

If we take for example the command az group update (which will update a resource group) the docs state the following about the --tags option:

--tags Space-separated tags in 'key[=value]' format. Use "" to clear existing tags.

When a value (or key) contains spaces it must be enclosed in quotes. So when we provide the key-value pairs directly to the command including a value with spaces, like in the following example, the result will be as expected:

az group update --tags owner="FirstName LastName" application=coolapp --name resource-group-name

The result will be that two tags have been added to the resource group:

{
  "id": "/subscriptions/1e42c44c-bc55-4b8a-b35e-de1dfbcfe481/resourceGroups/resource-group-name",
  "location": "westeurope",
  "managedBy": null,
  "name": "resource-group-name",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": {
    "application": "coolapp",
    "owner": "FirstName LastName"
  }
}

However, when we store the same value we used in the previous step in a variable the problem occurs.

tag='owner="FirstName LastName" application=coolapp'

I use echo $tag to validate that the variable contains exactly the same value as we provided in the previous example to the --tags option:

owner="FirstName LastName" application=coolapp

But when we provide this tag variable to the tags option of the command as shown in the next line:

az group update --tags $tag --name resource-group-name

The result will be three tags instead of the expected two:

{
  "id": "/subscriptions/1e42c44c-bc55-4b8a-b35e-de1dfbcfe481/resourceGroups/resource-group-name",
  "location": "westeurope",
  "managedBy": null,
  "name": "resource-group-name",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": {
    "LastName\"": "",
    "application": "coolapp",
    "owner": "\"FirstName"
  }
}

I've already tried defining the variable in the following ways, but no luck so far:

tag="owner=FirstName LastName application=coolapp"
tag=owner="Firstname Lastname" application=cool-name
tag='`owner="Firstname Lastname" application=cool-name`'

I even tried defining the variable as an array and providing it to the command as shown on the next line, but also that didn't provide the correct result:

tag=(owner="Firstname Lastname" application=cool-name)

az group update --tags ${tag[*]}--name resource-group-name

I also tried putting quotes around the variable in the command, as was suggested by @socowi, but this leads to the following incorrect result of one tag instead of two:

az group update --tags "$tag" --name resource-group-name

{
  "id": "/subscriptions/1e42c44c-bc55-4b8a-b35e-de1dfbcfe481/resourceGroups/resource-group-name",
  "location": "westeurope",
  "managedBy": null,
  "name": "resource-group-name",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": {
    "owner": "Firstname Lastname application=cool-name"
  }
}

Does anybody know how to solve this?

Illboding answered 17/11, 2018 at 9:7 Comment(4)
Long story short, write --tags "$tag". Duplicate of this, this, and this.Recliner
Thanks @Socowi, but I forgot to mention in my original post that I already tried that, without any luck. Will update post accordingly.Illboding
did you find a satistactory solution to this? Tried myriads of combinations here but even the accepted answer isn't working for meSmoking
i suspect the solution (at least under bash) is to replace spaces in tag values with \ (a space escaped with a slash), but good luck finding a way to make that work in a script where there's already multiple quoting and escaping syntax messing with the strings.Vail
R
8

Define your tags as

tags=("owner=Firstname Lastname" "application=cool-name")

then use

--tags "${tags[@]}"
Recliner answered 17/11, 2018 at 10:56 Comment(0)
S
1

I've found the following works. It requires a resource group already be created.

I used the following template:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "resourceName": {
      "type": "string",
      "metadata": {
        "description": "Specifies the name of the resource"
      }
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]",
      "metadata": {
        "description": "Location for the resources."
      }
    },
    "resourceTags": {
      "type": "object",
      "defaultValue": {
        "Cost Center": "Admin"
      }
    }
  },
  "resources": [
    {
      "apiVersion": "2019-06-01",
      "kind": "StorageV2",
      "location": "[parameters('location')]",
      "name": "[parameters('resourceName')]",
      "properties": {
        "supportsHttpsTrafficOnly": true
      },
      "sku": {
        "name": "Standard_LRS"
      },
      "type": "Microsoft.Storage/storageAccounts",
      "tags": "[parameters('resourceTags')]"
    }
  ]
}

In the Azure CLI using Bash, you can pass in the tag as a JSON object. In the following example, a template file with a location requires two parameters, resourceName and the tags which is an ARM object named resourceTags:

az deployment group create --name addstorage  --resource-group myResourceGroup \
--template-file $templateFile \
--parameters resourceName=abcdef45216 resourceTags='{"owner":"bruce","Cost Cen":"2345-324"}'

If you want to pass it as an environment variable, use:

tags='{"owner":"bruce","Cost Center":"2345-324"}'
az deployment group create --name addstorage  --resource-group myResourceGroup \
--template-file $templateFile \
--parameters resourceName=abcdef4556 resourceTags="$tags"

The $tags must be in double quotes. (You are passing in a JSON object string)

The JSON string also works when you are passing in the tags into Azure DevOps pipeline. See https://github.com/MicrosoftDocs/azure-devops-docs/issues/9051

Sofia answered 13/8, 2020 at 23:48 Comment(0)
P
0

First, build your string like so and double quote all keys/values just in case of spaces in either: (Sorry this is PoSH just example)

[string] $tags = [string]::Empty;
97..99 |% {
  $tags += "&`"$([char]$_)`"=`"$($_)`"";
}

The results of this is a string "&"a"="97"&"b"="98"&"c"="99".

Now pass it as a string array using the split function of the base string class which results in a 4 element array, the first element is blank. The CLI command ignores the first empty element. Here I set the tags for a storage account.

$tag='application=coolapp&owner="FirstName LastName"&"business Unit"="Human Resources"'
az resource tag -g rg -n someResource --resource-type Microsoft.Storage/storageaccounts -tags $tag.split("&")

I also employed this approach when I wanted to override the parameters provided in a parameter file for a resource group deployment.

az group deployment create --resource-group $rgName --template-file $templatefile --parameters $parametersFile --parameters $($overrideParams.split("&"));
Plateau answered 9/1, 2020 at 2:14 Comment(0)
O
0

I used the following bash script to tag all resources in a subscription. It has worked for us.

# set subscription before running script
tags=('App=Datawarehouse', '[email protected]', 'Cost Center=1234', 'Division=IT', 'Support Tier=0')
resourceIds=$(az resource list --query "[*].[id]" -o tsv)

for resourceId in $resourceIds
do
    
    az tag update --resource-id $resourceId --operation replace --tags "${tags[@]}"

done
Oxeyed answered 17/8, 2023 at 17:8 Comment(1)
Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?Crenelation
P
0

For me, I wasn't able to find a way to do this in sh for a while, but finally came across this solution:

You have to set the variables as arguments using the set command and call them using "$@". Example:

#!/bin/sh

set 'var1=key with spaces 1' 'var2=key with spaces 2'

az group create -n SomeName -l SomeLocation --tags "$@"

Then, running this using sh script.sh works.

Hope this helps anybody in need.

Paperboard answered 17/7, 2024 at 12:49 Comment(1)
The question is explicitly tagged with bash.Unpriced

© 2022 - 2025 — McMap. All rights reserved.