1
votes

I am currently trying to deploy a container group inside Azure Container Instances via ARM template all called from Azure Devops Yaml build pipeline. I have found out that I can use copy sentence to create multiple Resource Groups and/or properties.

Here is my ARM template

{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
    "containerGroupName": {
        "type": "string",
        "metadata": {
            "description": "Emulators ACR name."
        }
    },
    "copies":{
        "type": "int",
        "defaultValue": 2,
        "metadata": {
            "description": "Defines the number of container's copies."
        }
    },
    "server":{
        "type": "string",
        "metadata": {
            "description": "Defines the ACR server url."
        }
    },
    "serverUser":{
        "type": "string",
        "metadata": {
            "description": "Defines the ACR user."
        }
    },
    "serverPassword":{
        "type": "string",
        "metadata": {
            "description": "Defines the ACR password."
        }
    },
    "imageName":{
        "type": "string",
        "metadata": {
            "description": "Defines the ACR repository hosting the image."
        }
    },
    "imageVersion":{
        "type": "string",
        "metadata": {
            "description": "Defines the ACR image version."
        }
    },
    "containerName":{
        "type": "string",
        "metadata": {
            "description": "Defines the ACI containers name. This will be suffixed with the copy index."
        }
    }
},
"variables": {
    "emulatorImage": "[concat(parameters('server'), '/', parameters('imageName'))]",
    "emulatorImageVersion": "[parameters('imageVersion')]"
},
"resources": [
    {
        "name": "[concat(parameters('containerGroupName'), '-', copyIndex(1))]",
        "type": "Microsoft.ContainerInstance/containerGroups",
        "apiVersion": "2018-10-01",
        "location": "[resourceGroup().location]",
        "copy": {
            "name": "acicopy",
            "count": "[if(equals(mod(parameters('copies'), 60), 0), div(parameters('copies'), 60), add(div(parameters('copies'), 60), 1))]"
        },
        "properties": {
            "copy": [
                {
                    "name": "containers",
                    "count": "[if(equals(div(sub(parameters('copies'), mul(60, copyIndex())), 60), 0), if(equals(mod(sub(parameters('copies'), mul(60, copyIndex())), 60), 0), 60, mod(sub(parameters('copies'), mul(60, copyIndex())), 60)), 0)]",
                    "input": {
                        "name": "[concat(parameters('containerName'), '-', copyIndex(1), copyIndex('containers', 1))]",
                        "properties": {
                            "image": "[concat(variables('emulatorImage'), ':' ,variables('emulatorImageVersion'))]",
                            "resources": {
                                "requests": {
                                    "cpu": 0.01,
                                    "memoryInGB": 0.1
                                }
                            }
                        }
                    }
                }
            ],
            "imageRegistryCredentials": [
                {
                    "server": "[parameters('server')]",
                    "username": "[parameters('serverUser')]",
                    "password": "[parameters('serverPassword')]"
                }
            ],
            "osType": "Linux"
        }
    }
]

}

As you can see I have two copy iterations. The first one on the RG to generate enough Container Instances (as I am limited to 60 containers per ACI) and the second one on properties, to generate multiple containers (max 60).

So, if I need 100 containers, I should create 2 ACI, the first one will contain 60 containers the second one 40.

The conditions on count properties of copy might look a little complicated to read, so here is the C# equivalence.

public static void DefineNumber(int number)
{
    Console.WriteLine("Number : " + number);
    int mainLoop = number % 60 == 0 ? (int)(number / 60) : (int)(number / 60) + 1;
    Console.WriteLine("MainLoop : " + mainLoop);

    for(int i = 0; i < mainLoop; i++)
    {
        Console.WriteLine("----");

        int div = (number - (60 * i)) / 60;
        Console.WriteLine("Div : " + div);

        int mod = (number - (60 * i)) % 60;
        Console.WriteLine("Mod : " + mod);  

        int iteration = div == 0 ? (mod == 0 ? 60 : mod) : 60;
        Console.WriteLine("Number of containers for main loop n°" + (i+1) + " will be : " + iteration);
    }
}

mainloop is for the first copy iteration

iteration is for the second one

The problem I am currently facing is that when I ask the template to create 100 containers, I have the following build error

enter image description here

The message is clear enough, but I don't understand where the problem is. imageRegistryCredentials property is defined once for each copy iteration, and located on the same level as the containers property, so why do it succeed on first iteration, then fails ?

2

2 Answers

0
votes

According to what I know ( I didnt revisit this particular scenario in a very long time ) - you cannot use copy and properties copy on the same resource. your workaround would be - create linked deployments (1 for each 60 containers) and then you would only need properties copy inside each of those.

but given this error, I'm not sure that this scenario is not possible at this point in time. Because previously it would complain about copyIndex() not being expected at that place.

0
votes

I have figured out what was going wrong in my ARM template. It was a stupid mistyping error .... In my "properties" copy iteration functions at the end I have put a 0 value instead of 60

"count": "[if(equals(div(sub(parameters('copies'), mul(60, copyIndex())), 60), 0), if(equals(mod(sub(parameters('copies'), mul(60, copyIndex())), 60), 0), 60, mod(sub(parameters('copies'), mul(60, copyIndex())), 60)), 0)]",

If I replace it by C# equivalence,

What I should have put:

 int iteration = div == 0 ? (mod == 0 ? 60 : mod) : 60;

What I put:

 int iteration = div == 0 ? (mod == 0 ? 60 : mod) : 0;

The difference is at the end...

Anyway thank you 4c74356b41 for your help !