From afe9764543f23501c2809d58bac917dc19fa83b1 Mon Sep 17 00:00:00 2001 From: Lenny Van de Winkel Date: Wed, 15 Jul 2026 15:45:23 +0200 Subject: [PATCH 01/15] Add Azure Compute Gallery image and verification options Add support for provisioning Azure Batch pool nodes from a custom VM image in an Azure Compute Gallery via the new 'azure.batch.pools..virtualMachineImageId' option, and add the 'azure.batch.pools..verification' option to choose the image verification type ('verified', 'unverified' or 'any'). Signed-off-by: Lenny Van de Winkel --- docs/azure.mdx | 37 ++++++++++++++ docs/reference/config.mdx | 8 +++ .../cloud/azure/batch/AzBatchService.groovy | 28 ++++++++-- .../cloud/azure/config/AzPoolOpts.groovy | 31 ++++++++++- .../azure/batch/AzBatchServiceTest.groovy | 2 +- .../cloud/azure/config/AzPoolOptsTest.groovy | 51 +++++++++++++++++++ 6 files changed, 150 insertions(+), 7 deletions(-) diff --git a/docs/azure.mdx b/docs/azure.mdx index d931b16205..10357a38d9 100644 --- a/docs/azure.mdx +++ b/docs/azure.mdx @@ -597,6 +597,43 @@ azure { } ``` +**Custom images from an Azure Compute Gallery** + +You can provision pool nodes from a custom VM image published in an [Azure Compute Gallery](https://learn.microsoft.com/en-us/azure/virtual-machines/azure-compute-gallery) by setting `virtualMachineImageId` to the image version resource ID. When set, `publisher` and `offer` are ignored, and `sku` must be set to the Batch node agent SKU id that matches the image operating system. + +```groovy +azure { + batch { + pools { + { + virtualMachineImageId = '/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/' + sku = 'batch.node.ubuntu 24.04' + } + } + } +} +``` + +:::warning +Custom images require Microsoft Entra authentication (service principal or managed identity). The **Azure Batch account identity** must have read access to the gallery image, and the pool typically requires `virtualNetwork` to be set. +::: + +**Image verification** + +By default, Nextflow only selects images that Azure Batch has formally verified. Set `verification` to `unverified` to also allow images that Azure Batch lists but has not verified, or to `any` to accept both. This setting is ignored when `virtualMachineImageId` is set. + +```groovy +azure { + batch { + pools { + { + verification = 'unverified' // 'verified' (default), 'unverified' or 'any' + } + } + } +} +``` + ### Advanced features **Virtual networks** diff --git a/docs/reference/config.mdx b/docs/reference/config.mdx index 5add615d87..93f0303ed2 100644 --- a/docs/reference/config.mdx +++ b/docs/reference/config.mdx @@ -577,6 +577,14 @@ Enable the `startTask` to run with elevated access (default`false`). The `startTask` that is executed as the node joins the Azure Batch node pool. +##### `azure.batch.pools..verification` + +The image verification type to match when resolving the VM image from the Batch supported-images list. Can be `verified`, `unverified`, or `any` (default`verified`). Ignored when `virtualMachineImageId` is set. + +##### `azure.batch.pools..virtualMachineImageId` + +The resource ID of a custom VM image from an Azure Compute Gallery to use for the pool nodes (e.g. `/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/`). When set, `publisher` and `offer` are ignored, and `sku` must be set to the Batch node agent SKU id that matches the image OS (e.g. `batch.node.ubuntu 24.04`). + ##### `azure.batch.pools..virtualNetwork` diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy index 2c3aa54e19..780c62f5d4 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy @@ -51,6 +51,7 @@ import com.azure.compute.batch.models.ContainerConfiguration import com.azure.compute.batch.models.ContainerRegistryReference import com.azure.compute.batch.models.ContainerType import com.azure.compute.batch.models.ElevationLevel +import com.azure.compute.batch.models.ImageReference import com.azure.compute.batch.models.MetadataItem import com.azure.compute.batch.models.MountConfiguration import com.azure.compute.batch.models.NetworkConfiguration @@ -704,7 +705,7 @@ class AzBatchService implements Closeable { continue if( it.osType != opts.osType ) continue - if( it.verificationType != opts.verification ) + if( opts.verification != null && it.verificationType != opts.verification ) continue if( !it.imageReference.publisher.equalsIgnoreCase(opts.publisher) ) continue @@ -712,8 +713,14 @@ class AzBatchService implements Closeable { return it } - log.debug "[AZURE BATCH] No VM image matching sku=$opts.sku; publisher=$opts.publisher; offer=$opts.offer; OS type=$opts.osType; verification type=$opts.verification - supported images: $available" - throw new IllegalStateException("Cannot find a matching VM image with publisher=$opts.publisher; offer=$opts.offer; OS type=$opts.osType; verification type=$opts.verification") + log.debug "[AZURE BATCH] No VM image matching sku=$opts.sku; publisher=$opts.publisher; offer=$opts.offer; OS type=$opts.osType; verification type=${opts.verification ?: 'any'} - supported images: $available" + throw new IllegalStateException("Cannot find a matching VM image with publisher=$opts.publisher; offer=$opts.offer; OS type=$opts.osType; verification type=${opts.verification ?: 'any'}") + } + + protected ImageReference customImageReference(AzPoolOpts opts) { + if( !opts.sku ) + throw new IllegalArgumentException("Azure Batch pool option 'sku' is required when 'virtualMachineImageId' is set - it must be a valid node agent SKU id (e.g. 'batch.node.ubuntu 24.04')") + return new ImageReference().setVirtualMachineImageId(opts.virtualMachineImageId) } protected AzVmPoolSpec specFromPoolConfig(String poolId) { @@ -883,9 +890,20 @@ class AzBatchService implements Closeable { log.debug "[AZURE BATCH] Connecting Azure Batch pool to Container Registry '$registryOpts.server'" } - final image = getImage(opts) + final ImageReference imageRef + final String nodeAgentSkuId + if( opts.virtualMachineImageId ) { + imageRef = customImageReference(opts) + nodeAgentSkuId = opts.sku + log.debug "[AZURE BATCH] Using custom VM image from Compute Gallery: $opts.virtualMachineImageId (node agent SKU: $nodeAgentSkuId)" + } + else { + final image = getImage(opts) + imageRef = image.imageReference + nodeAgentSkuId = image.nodeAgentSkuId + } - new VirtualMachineConfiguration(image.imageReference, image.nodeAgentSkuId) + new VirtualMachineConfiguration(imageRef, nodeAgentSkuId) .setContainerConfiguration(containerConfig) } diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy index 9714374481..d80b1b271b 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy @@ -144,8 +144,21 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { """) final String vmType + @ConfigOption + @Description(""" + The resource ID of a custom VM image from an Azure Compute Gallery to use for the pool nodes + (e.g. `/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/`). + When set, `publisher` and `offer` are ignored, and `sku` must be set to the Batch node agent SKU id that matches the image OS (e.g. `batch.node.ubuntu 24.04`). + """) + final String virtualMachineImageId + + @ConfigOption + @Description(""" + The image verification type to match when resolving the VM image from the Batch supported-images list. Can be `verified`, `unverified`, or `any` (default: `verified`). Ignored when `virtualMachineImageId` is set. + """) + final ImageVerificationType verification + OSType osType = DEFAULT_OS_TYPE - ImageVerificationType verification = ImageVerificationType.VERIFIED String registry String userName @@ -175,6 +188,20 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { this.password = opts.password this.virtualNetwork = opts.virtualNetwork this.lowPriority = opts.lowPriority as boolean + this.virtualMachineImageId = opts.virtualMachineImageId ?: null + this.verification = parseVerification(opts.verification) + } + + protected static ImageVerificationType parseVerification(value) { + if( value == null ) + return ImageVerificationType.VERIFIED + if( value instanceof ImageVerificationType ) + return value + final str = value.toString().toLowerCase() + if( str == 'verified' ) return ImageVerificationType.VERIFIED + if( str == 'unverified' ) return ImageVerificationType.UNVERIFIED + if( str == 'any' ) return null + throw new IllegalArgumentException("Invalid azure.batch.pools..verification value: '$value' - expected 'verified', 'unverified' or 'any'") } @Override @@ -195,6 +222,8 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { hasher.putUnencodedChars(schedulePolicy ?: '') hasher.putUnencodedChars(virtualNetwork ?: '') hasher.putBoolean(lowPriority) + hasher.putUnencodedChars(virtualMachineImageId ?: '') + hasher.putUnencodedChars(verification?.toString() ?: 'any') hasher.putUnencodedChars(startTask.script ?: '') hasher.putBoolean(startTask.privileged) return hasher diff --git a/plugins/nf-azure/src/test/nextflow/cloud/azure/batch/AzBatchServiceTest.groovy b/plugins/nf-azure/src/test/nextflow/cloud/azure/batch/AzBatchServiceTest.groovy index d5d732a108..91b73fe7c7 100644 --- a/plugins/nf-azure/src/test/nextflow/cloud/azure/batch/AzBatchServiceTest.groovy +++ b/plugins/nf-azure/src/test/nextflow/cloud/azure/batch/AzBatchServiceTest.groovy @@ -519,7 +519,7 @@ class AzBatchServiceTest extends Specification { then: 1 * svc.guessBestVm(LOC, CPUS, MEM, null, TYPE) >> VM and: - spec.poolId == 'nf-pool-42f3635f3fb8b71160900efa959f7809-Standard_X1' + spec.poolId == 'nf-pool-7483c5b1874eb7b96e8cb13e0b3781da-Standard_X1' spec.metadata == [foo: 'bar'] } diff --git a/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy b/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy index f47d8d673a..24cbb5fe7b 100644 --- a/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy +++ b/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy @@ -16,6 +16,9 @@ package nextflow.cloud.azure.config +import com.azure.compute.batch.models.ImageVerificationType +import com.google.common.hash.Hashing +import nextflow.util.CacheHelper import nextflow.util.Duration import spock.lang.Specification /** @@ -48,6 +51,54 @@ class AzPoolOptsTest extends Specification { !opts.lowPriority !opts.startTask.script !opts.startTask.privileged + !opts.virtualMachineImageId + opts.verification == ImageVerificationType.VERIFIED + } + + def 'should configure a custom compute gallery image' () { + when: + def opts = new AzPoolOpts([ + virtualMachineImageId: '/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1.0.0', + sku: 'batch.node.ubuntu 24.04', + verification: 'unverified', + ]) + then: + opts.virtualMachineImageId == '/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1.0.0' + opts.sku == 'batch.node.ubuntu 24.04' + opts.verification == ImageVerificationType.UNVERIFIED + } + + def 'should parse the verification value' () { + expect: + new AzPoolOpts([verification: VALUE]).verification == EXPECTED + where: + VALUE | EXPECTED + null | ImageVerificationType.VERIFIED + 'verified' | ImageVerificationType.VERIFIED + 'unverified' | ImageVerificationType.UNVERIFIED + 'any' | null + } + + def 'should reject an invalid verification value' () { + when: + new AzPoolOpts([verification: 'bogus']) + then: + def e = thrown(IllegalArgumentException) + e.message.contains('verification') + } + + private static String hash(AzPoolOpts opts) { + opts.funnel(Hashing.murmur3_128().newHasher(), CacheHelper.HashMode.STANDARD).hash().toString() + } + + def 'pool hash should differ when image config differs' () { + given: + def base = new AzPoolOpts() + def gallery = new AzPoolOpts([virtualMachineImageId: '/subscriptions/x/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1']) + def unverified = new AzPoolOpts([verification: 'unverified']) + expect: + hash(base) != hash(gallery) + hash(base) != hash(unverified) } def 'should create pool with custom options' () { From 8fd38fd57d5a7e33c791217aaf9384ba205f3818 Mon Sep 17 00:00:00 2001 From: Lenny Van de Winkel Date: Mon, 27 Jul 2026 10:08:16 +0200 Subject: [PATCH 02/15] Remove stale MetadataItem and replace ImageReference with BatchVmImageReference to comply with SDK changes in beta.6 --- .../nextflow/cloud/azure/batch/AzBatchService.groovy | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy index f3c537cdeb..8c95b79091 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy @@ -51,8 +51,7 @@ import com.azure.compute.batch.models.BatchContainerConfiguration import com.azure.compute.batch.models.ContainerRegistryReference import com.azure.compute.batch.models.ContainerType import com.azure.compute.batch.models.ElevationLevel -import com.azure.compute.batch.models.ImageReference -import com.azure.compute.batch.models.MetadataItem +import com.azure.compute.batch.models.BatchVmImageReference import com.azure.compute.batch.models.BatchMetadataItem import com.azure.compute.batch.models.MountConfiguration import com.azure.compute.batch.models.NetworkConfiguration @@ -718,10 +717,10 @@ class AzBatchService implements Closeable { throw new IllegalStateException("Cannot find a matching VM image with publisher=$opts.publisher; offer=$opts.offer; OS type=$opts.osType; verification type=${opts.verification ?: 'any'}") } - protected ImageReference customImageReference(AzPoolOpts opts) { + protected BatchVmImageReference customImageReference(AzPoolOpts opts) { if( !opts.sku ) throw new IllegalArgumentException("Azure Batch pool option 'sku' is required when 'virtualMachineImageId' is set - it must be a valid node agent SKU id (e.g. 'batch.node.ubuntu 24.04')") - return new ImageReference().setVirtualMachineImageId(opts.virtualMachineImageId) + return new BatchVmImageReference().setVirtualMachineImageId(opts.virtualMachineImageId) } protected AzVmPoolSpec specFromPoolConfig(String poolId) { @@ -891,7 +890,7 @@ class AzBatchService implements Closeable { log.debug "[AZURE BATCH] Connecting Azure Batch pool to Container Registry '$registryOpts.server'" } - final ImageReference imageRef + final BatchVmImageReference imageRef final String nodeAgentSkuId if( opts.virtualMachineImageId ) { imageRef = customImageReference(opts) From 5a8fbe2bb3e14e2dc3578286989f24e63d68dc57 Mon Sep 17 00:00:00 2001 From: Lenny Van de Winkel Date: Mon, 27 Jul 2026 10:31:15 +0200 Subject: [PATCH 03/15] Remove 'verification' property and replace with 'allowUnverifiedImages' boolean. Move to advanced features in documentation --- docs/azure.mdx | 8 +++--- docs/reference/config.mdx | 8 +++--- .../cloud/azure/batch/AzBatchService.groovy | 7 +++-- .../cloud/azure/config/AzPoolOpts.groovy | 21 +++----------- .../cloud/azure/config/AzPoolOptsTest.groovy | 28 +++---------------- 5 files changed, 20 insertions(+), 52 deletions(-) diff --git a/docs/azure.mdx b/docs/azure.mdx index 10357a38d9..01c2d8a9e8 100644 --- a/docs/azure.mdx +++ b/docs/azure.mdx @@ -618,24 +618,24 @@ azure { Custom images require Microsoft Entra authentication (service principal or managed identity). The **Azure Batch account identity** must have read access to the gallery image, and the pool typically requires `virtualNetwork` to be set. ::: +### Advanced features + **Image verification** -By default, Nextflow only selects images that Azure Batch has formally verified. Set `verification` to `unverified` to also allow images that Azure Batch lists but has not verified, or to `any` to accept both. This setting is ignored when `virtualMachineImageId` is set. +By default, Nextflow only selects images that Azure Batch has formally verified. Set `allowUnverifiedImages` to `true` to also allow images that Azure Batch lists but has not verified. This setting is ignored when `virtualMachineImageId` is set. ```groovy azure { batch { pools { { - verification = 'unverified' // 'verified' (default), 'unverified' or 'any' + allowUnverifiedImages = true } } } } ``` -### Advanced features - **Virtual networks** Pools can be configured to use virtual networks to connect to your existing network infrastructure. diff --git a/docs/reference/config.mdx b/docs/reference/config.mdx index 6906b12615..33cc2aad67 100644 --- a/docs/reference/config.mdx +++ b/docs/reference/config.mdx @@ -509,6 +509,10 @@ The name of the batch service region, e.g. `westeurope` or `eastus2`. Not needed The client ID for an Azure [managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) that is available on all Azure Batch node pools. This identity is used by Fusion to authenticate to Azure storage. If set to `'auto'`, Fusion will use the first available managed identity. +##### `azure.batch.pools..allowUnverifiedImages` + +Allow the use of unverified VM images when resolving the image from the Batch supported-images list (default: `false`). Ignored when `virtualMachineImageId` is set. + ##### `azure.batch.pools..autoScale` Enable autoscaling feature for the pool identified with ``. @@ -577,10 +581,6 @@ Enable the `startTask` to run with elevated access (default`false`). The `startTask` that is executed as the node joins the Azure Batch node pool. -##### `azure.batch.pools..verification` - -The image verification type to match when resolving the VM image from the Batch supported-images list. Can be `verified`, `unverified`, or `any` (default`verified`). Ignored when `virtualMachineImageId` is set. - ##### `azure.batch.pools..virtualMachineImageId` The resource ID of a custom VM image from an Azure Compute Gallery to use for the pool nodes (e.g. `/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/`). When set, `publisher` and `offer` are ignored, and `sku` must be set to the Batch node agent SKU id that matches the image OS (e.g. `batch.node.ubuntu 24.04`). diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy index 8c95b79091..8d57bdc6da 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy @@ -51,6 +51,7 @@ import com.azure.compute.batch.models.BatchContainerConfiguration import com.azure.compute.batch.models.ContainerRegistryReference import com.azure.compute.batch.models.ContainerType import com.azure.compute.batch.models.ElevationLevel +import com.azure.compute.batch.models.ImageVerificationType import com.azure.compute.batch.models.BatchVmImageReference import com.azure.compute.batch.models.BatchMetadataItem import com.azure.compute.batch.models.MountConfiguration @@ -705,7 +706,7 @@ class AzBatchService implements Closeable { continue if( it.osType != opts.osType ) continue - if( opts.verification != null && it.verificationType != opts.verification ) + if( !opts.allowUnverifiedImages && it.verificationType != ImageVerificationType.VERIFIED ) continue if( !it.imageReference.publisher.equalsIgnoreCase(opts.publisher) ) continue @@ -713,8 +714,8 @@ class AzBatchService implements Closeable { return it } - log.debug "[AZURE BATCH] No VM image matching sku=$opts.sku; publisher=$opts.publisher; offer=$opts.offer; OS type=$opts.osType; verification type=${opts.verification ?: 'any'} - supported images: $available" - throw new IllegalStateException("Cannot find a matching VM image with publisher=$opts.publisher; offer=$opts.offer; OS type=$opts.osType; verification type=${opts.verification ?: 'any'}") + log.debug "[AZURE BATCH] No VM image matching sku=$opts.sku; publisher=$opts.publisher; offer=$opts.offer; OS type=$opts.osType; allow unverified images=${opts.allowUnverifiedImages} - supported images: $available" + throw new IllegalStateException("Cannot find a matching VM image with publisher=$opts.publisher; offer=$opts.offer; OS type=$opts.osType; allow unverified images=${opts.allowUnverifiedImages}") } protected BatchVmImageReference customImageReference(AzPoolOpts opts) { diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy index d80b1b271b..7733772785 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy @@ -16,7 +16,6 @@ package nextflow.cloud.azure.config -import com.azure.compute.batch.models.ImageVerificationType import com.azure.compute.batch.models.OSType import com.google.common.hash.Hasher import groovy.transform.CompileStatic @@ -154,9 +153,9 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { @ConfigOption @Description(""" - The image verification type to match when resolving the VM image from the Batch supported-images list. Can be `verified`, `unverified`, or `any` (default: `verified`). Ignored when `virtualMachineImageId` is set. + Allow the use of unverified VM images when resolving the image from the Batch supported-images list (default: `false`). Ignored when `virtualMachineImageId` is set. """) - final ImageVerificationType verification + final boolean allowUnverifiedImages OSType osType = DEFAULT_OS_TYPE @@ -189,19 +188,7 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { this.virtualNetwork = opts.virtualNetwork this.lowPriority = opts.lowPriority as boolean this.virtualMachineImageId = opts.virtualMachineImageId ?: null - this.verification = parseVerification(opts.verification) - } - - protected static ImageVerificationType parseVerification(value) { - if( value == null ) - return ImageVerificationType.VERIFIED - if( value instanceof ImageVerificationType ) - return value - final str = value.toString().toLowerCase() - if( str == 'verified' ) return ImageVerificationType.VERIFIED - if( str == 'unverified' ) return ImageVerificationType.UNVERIFIED - if( str == 'any' ) return null - throw new IllegalArgumentException("Invalid azure.batch.pools..verification value: '$value' - expected 'verified', 'unverified' or 'any'") + this.allowUnverifiedImages = opts.allowUnverifiedImages as boolean } @Override @@ -223,7 +210,7 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { hasher.putUnencodedChars(virtualNetwork ?: '') hasher.putBoolean(lowPriority) hasher.putUnencodedChars(virtualMachineImageId ?: '') - hasher.putUnencodedChars(verification?.toString() ?: 'any') + hasher.putBoolean(allowUnverifiedImages) hasher.putUnencodedChars(startTask.script ?: '') hasher.putBoolean(startTask.privileged) return hasher diff --git a/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy b/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy index 24cbb5fe7b..ef392a1057 100644 --- a/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy +++ b/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy @@ -16,7 +16,6 @@ package nextflow.cloud.azure.config -import com.azure.compute.batch.models.ImageVerificationType import com.google.common.hash.Hashing import nextflow.util.CacheHelper import nextflow.util.Duration @@ -52,7 +51,7 @@ class AzPoolOptsTest extends Specification { !opts.startTask.script !opts.startTask.privileged !opts.virtualMachineImageId - opts.verification == ImageVerificationType.VERIFIED + !opts.allowUnverifiedImages } def 'should configure a custom compute gallery image' () { @@ -60,31 +59,12 @@ class AzPoolOptsTest extends Specification { def opts = new AzPoolOpts([ virtualMachineImageId: '/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1.0.0', sku: 'batch.node.ubuntu 24.04', - verification: 'unverified', + allowUnverifiedImages: true, ]) then: opts.virtualMachineImageId == '/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1.0.0' opts.sku == 'batch.node.ubuntu 24.04' - opts.verification == ImageVerificationType.UNVERIFIED - } - - def 'should parse the verification value' () { - expect: - new AzPoolOpts([verification: VALUE]).verification == EXPECTED - where: - VALUE | EXPECTED - null | ImageVerificationType.VERIFIED - 'verified' | ImageVerificationType.VERIFIED - 'unverified' | ImageVerificationType.UNVERIFIED - 'any' | null - } - - def 'should reject an invalid verification value' () { - when: - new AzPoolOpts([verification: 'bogus']) - then: - def e = thrown(IllegalArgumentException) - e.message.contains('verification') + opts.allowUnverifiedImages } private static String hash(AzPoolOpts opts) { @@ -95,7 +75,7 @@ class AzPoolOptsTest extends Specification { given: def base = new AzPoolOpts() def gallery = new AzPoolOpts([virtualMachineImageId: '/subscriptions/x/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1']) - def unverified = new AzPoolOpts([verification: 'unverified']) + def unverified = new AzPoolOpts([allowUnverifiedImages: true]) expect: hash(base) != hash(gallery) hash(base) != hash(unverified) From 93ec4814be1d075dd149232547c8d1c92b40794f Mon Sep 17 00:00:00 2001 From: Lenny Van de Winkel Date: Mon, 27 Jul 2026 10:34:41 +0200 Subject: [PATCH 04/15] Order opts more logically Signed-off-by: Lenny Van de Winkel --- .../src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy index 7733772785..c154efe6b3 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy @@ -172,6 +172,8 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { this.privileged = opts.privileged ?: false this.publisher = opts.publisher ?: DEFAULT_PUBLISHER this.offer = opts.offer ?: DEFAULT_OFFER + this.virtualMachineImageId = opts.virtualMachineImageId ?: null + this.allowUnverifiedImages = opts.allowUnverifiedImages as boolean this.sku = opts.sku ?: DEFAULT_SKU this.vmType = opts.vmType ?: DEFAULT_VM_TYPE this.fileShareRootPath = opts.fileShareRootPath ?: buildFileShareRootPath() @@ -187,8 +189,6 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { this.password = opts.password this.virtualNetwork = opts.virtualNetwork this.lowPriority = opts.lowPriority as boolean - this.virtualMachineImageId = opts.virtualMachineImageId ?: null - this.allowUnverifiedImages = opts.allowUnverifiedImages as boolean } @Override From ad21c265c1650144298e1e36fb41ba5c1f8e7cb8 Mon Sep 17 00:00:00 2001 From: Lenny Van de Winkel Date: Mon, 27 Jul 2026 10:41:26 +0200 Subject: [PATCH 05/15] Place guard around hashing 'allowUnverifiedImages' to ensure backwards compatibility with existing pools pre-upgrade Signed-off-by: Lenny Van de Winkel --- .../src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy | 3 ++- .../test/nextflow/cloud/azure/batch/AzBatchServiceTest.groovy | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy index c154efe6b3..60bf44c716 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy @@ -210,7 +210,8 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { hasher.putUnencodedChars(virtualNetwork ?: '') hasher.putBoolean(lowPriority) hasher.putUnencodedChars(virtualMachineImageId ?: '') - hasher.putBoolean(allowUnverifiedImages) + if( allowUnverifiedImages ) + hasher.putBoolean(allowUnverifiedImages) hasher.putUnencodedChars(startTask.script ?: '') hasher.putBoolean(startTask.privileged) return hasher diff --git a/plugins/nf-azure/src/test/nextflow/cloud/azure/batch/AzBatchServiceTest.groovy b/plugins/nf-azure/src/test/nextflow/cloud/azure/batch/AzBatchServiceTest.groovy index ce6b1d6ec8..57bedf9ac8 100644 --- a/plugins/nf-azure/src/test/nextflow/cloud/azure/batch/AzBatchServiceTest.groovy +++ b/plugins/nf-azure/src/test/nextflow/cloud/azure/batch/AzBatchServiceTest.groovy @@ -520,7 +520,7 @@ class AzBatchServiceTest extends Specification { then: 1 * svc.guessBestVm(LOC, CPUS, MEM, null, TYPE) >> VM and: - spec.poolId == 'nf-pool-7483c5b1874eb7b96e8cb13e0b3781da-Standard_X1' + spec.poolId == 'nf-pool-42f3635f3fb8b71160900efa959f7809-Standard_X1' spec.metadata == [foo: 'bar'] } From 9ff93ba73e5428a02eac190c080473c1f52dacca Mon Sep 17 00:00:00 2001 From: Lenny Van de Winkel Date: Mon, 27 Jul 2026 10:47:24 +0200 Subject: [PATCH 06/15] Add check on sku and virtualMachineImageId requirement properly Signed-off-by: Lenny Van de Winkel --- .../nextflow/cloud/azure/batch/AzBatchService.groovy | 2 -- .../main/nextflow/cloud/azure/config/AzPoolOpts.groovy | 2 ++ .../nextflow/cloud/azure/config/AzPoolOptsTest.groovy | 10 +++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy index 8d57bdc6da..554b9f1f3a 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/batch/AzBatchService.groovy @@ -719,8 +719,6 @@ class AzBatchService implements Closeable { } protected BatchVmImageReference customImageReference(AzPoolOpts opts) { - if( !opts.sku ) - throw new IllegalArgumentException("Azure Batch pool option 'sku' is required when 'virtualMachineImageId' is set - it must be a valid node agent SKU id (e.g. 'batch.node.ubuntu 24.04')") return new BatchVmImageReference().setVirtualMachineImageId(opts.virtualMachineImageId) } diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy index 60bf44c716..3bbd21eed0 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy @@ -174,6 +174,8 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { this.offer = opts.offer ?: DEFAULT_OFFER this.virtualMachineImageId = opts.virtualMachineImageId ?: null this.allowUnverifiedImages = opts.allowUnverifiedImages as boolean + if( this.virtualMachineImageId && !opts.sku ) + throw new IllegalArgumentException("Azure Batch pool option 'sku' is required when 'virtualMachineImageId' is set - it must be set to the Batch node agent SKU id that matches the image OS (e.g. 'batch.node.ubuntu 24.04')") this.sku = opts.sku ?: DEFAULT_SKU this.vmType = opts.vmType ?: DEFAULT_VM_TYPE this.fileShareRootPath = opts.fileShareRootPath ?: buildFileShareRootPath() diff --git a/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy b/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy index ef392a1057..4dc7b598b7 100644 --- a/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy +++ b/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy @@ -67,6 +67,14 @@ class AzPoolOptsTest extends Specification { opts.allowUnverifiedImages } + def 'should require sku for a compute gallery image' () { + when: + new AzPoolOpts([virtualMachineImageId: '/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1.0.0']) + then: + def e = thrown(IllegalArgumentException) + e.message.contains('sku') + } + private static String hash(AzPoolOpts opts) { opts.funnel(Hashing.murmur3_128().newHasher(), CacheHelper.HashMode.STANDARD).hash().toString() } @@ -74,7 +82,7 @@ class AzPoolOptsTest extends Specification { def 'pool hash should differ when image config differs' () { given: def base = new AzPoolOpts() - def gallery = new AzPoolOpts([virtualMachineImageId: '/subscriptions/x/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1']) + def gallery = new AzPoolOpts([virtualMachineImageId: '/subscriptions/x/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1', sku: 'batch.node.ubuntu 24.04']) def unverified = new AzPoolOpts([allowUnverifiedImages: true]) expect: hash(base) != hash(gallery) From ca37100f0888faff3eee4cd032ac03a7d1277d5b Mon Sep 17 00:00:00 2001 From: Lenny Van de Winkel Date: Mon, 27 Jul 2026 10:50:27 +0200 Subject: [PATCH 07/15] Ignore allowunverifiedimages property in hashing if virtualMachineImageId is set Signed-off-by: Lenny Van de Winkel --- .../main/nextflow/cloud/azure/config/AzPoolOpts.groovy | 3 ++- .../nextflow/cloud/azure/config/AzPoolOptsTest.groovy | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy index 3bbd21eed0..135d09c6b4 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy @@ -212,7 +212,8 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { hasher.putUnencodedChars(virtualNetwork ?: '') hasher.putBoolean(lowPriority) hasher.putUnencodedChars(virtualMachineImageId ?: '') - if( allowUnverifiedImages ) + // 'allowUnverifiedImages' only affects marketplace image resolution; it's ignored when a gallery image is set + if( !virtualMachineImageId && allowUnverifiedImages ) hasher.putBoolean(allowUnverifiedImages) hasher.putUnencodedChars(startTask.script ?: '') hasher.putBoolean(startTask.privileged) diff --git a/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy b/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy index 4dc7b598b7..6abec7eabd 100644 --- a/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy +++ b/plugins/nf-azure/src/test/nextflow/cloud/azure/config/AzPoolOptsTest.groovy @@ -89,6 +89,15 @@ class AzPoolOptsTest extends Specification { hash(base) != hash(unverified) } + def 'pool hash should ignore allowUnverifiedImages for a gallery image' () { + given: + def opts = [virtualMachineImageId: '/subscriptions/x/resourceGroups/rg/providers/Microsoft.Compute/galleries/g/images/d/versions/1', sku: 'batch.node.ubuntu 24.04'] + def a = new AzPoolOpts(opts) + def b = new AzPoolOpts(opts + [allowUnverifiedImages: true]) + expect: + hash(a) == hash(b) + } + def 'should create pool with custom options' () { when: def opts = new AzPoolOpts([ From fb30feb28e1f0518d34d032638d7261ed2c52fcc Mon Sep 17 00:00:00 2001 From: Lenny Date: Mon, 27 Jul 2026 10:52:28 +0200 Subject: [PATCH 08/15] Update docs/reference/config.mdx Co-authored-by: Chris Hakkaart Signed-off-by: Lenny --- docs/reference/config.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/reference/config.mdx b/docs/reference/config.mdx index 33cc2aad67..924c61a080 100644 --- a/docs/reference/config.mdx +++ b/docs/reference/config.mdx @@ -583,7 +583,8 @@ The `startTask` that is executed as the node joins the Azure Batch node pool. ##### `azure.batch.pools..virtualMachineImageId` -The resource ID of a custom VM image from an Azure Compute Gallery to use for the pool nodes (e.g. `/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/`). When set, `publisher` and `offer` are ignored, and `sku` must be set to the Batch node agent SKU id that matches the image OS (e.g. `batch.node.ubuntu 24.04`). +The resource ID of a custom VM image from an Azure Compute Gallery for the pool nodes (e.g., `/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/`). When set, `publisher` and `offer` are ignored, and `sku` must be the Batch node agent SKU id matching the image OS (for example, `batch.node.ubuntu 24.04`). + ##### `azure.batch.pools..virtualNetwork` From 48f230cb7111912ad9bed9b1afd3e8afb99b654a Mon Sep 17 00:00:00 2001 From: Lenny Date: Mon, 27 Jul 2026 10:52:36 +0200 Subject: [PATCH 09/15] Update docs/azure.mdx Co-authored-by: Chris Hakkaart Signed-off-by: Lenny --- docs/azure.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/azure.mdx b/docs/azure.mdx index 01c2d8a9e8..244e5d92f0 100644 --- a/docs/azure.mdx +++ b/docs/azure.mdx @@ -599,14 +599,14 @@ azure { **Custom images from an Azure Compute Gallery** -You can provision pool nodes from a custom VM image published in an [Azure Compute Gallery](https://learn.microsoft.com/en-us/azure/virtual-machines/azure-compute-gallery) by setting `virtualMachineImageId` to the image version resource ID. When set, `publisher` and `offer` are ignored, and `sku` must be set to the Batch node agent SKU id that matches the image operating system. +To provision pool nodes from a custom VM image published in an [Azure Compute Gallery](https://learn.microsoft.com/en-us/azure/virtual-machines/azure-compute-gallery), set `virtualMachineImageId` to the image version resource ID. When set, `publisher` and `offer` are ignored, and `sku` must be set to the Batch node agent SKU ID that matches the image operating system. ```groovy azure { batch { pools { - { - virtualMachineImageId = '/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/' + { + virtualMachineImageId = '/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/' sku = 'batch.node.ubuntu 24.04' } } From 7cd6de0edec4bff9e101d6197041859cd8b7a3c1 Mon Sep 17 00:00:00 2001 From: Lenny Date: Mon, 27 Jul 2026 10:53:19 +0200 Subject: [PATCH 10/15] Update docs/azure.mdx Co-authored-by: Chris Hakkaart Signed-off-by: Lenny --- docs/azure.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/azure.mdx b/docs/azure.mdx index 244e5d92f0..3e2aeec7fa 100644 --- a/docs/azure.mdx +++ b/docs/azure.mdx @@ -615,7 +615,7 @@ azure { ``` :::warning -Custom images require Microsoft Entra authentication (service principal or managed identity). The **Azure Batch account identity** must have read access to the gallery image, and the pool typically requires `virtualNetwork` to be set. +Custom images require Microsoft Entra authentication (service principal or managed identity). The **Azure Batch account identity** must have read access to the gallery image. The pool typically requires `virtualNetwork` to be set. ::: ### Advanced features From 8a3471daa1f79214cc32265b88036fd95c0f0b12 Mon Sep 17 00:00:00 2001 From: Lenny Van de Winkel Date: Mon, 27 Jul 2026 10:54:53 +0200 Subject: [PATCH 11/15] Tidy up documentation Signed-off-by: Lenny Van de Winkel --- docs/azure.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/azure.mdx b/docs/azure.mdx index 01c2d8a9e8..b02b2fde8d 100644 --- a/docs/azure.mdx +++ b/docs/azure.mdx @@ -622,7 +622,7 @@ Custom images require Microsoft Entra authentication (service principal or manag **Image verification** -By default, Nextflow only selects images that Azure Batch has formally verified. Set `allowUnverifiedImages` to `true` to also allow images that Azure Batch lists but has not verified. This setting is ignored when `virtualMachineImageId` is set. +By default, Nextflow selects only images that Azure Batch has verified. Set `allowUnverifiedImages` to `true` to also allow images that Azure Batch lists but has not verified. Ignored when `virtualMachineImageId` is set. ```groovy azure { From 0896c7c71e5ee661c880bd0ddf3913cdb5057862 Mon Sep 17 00:00:00 2001 From: Lenny Date: Mon, 27 Jul 2026 11:30:27 +0200 Subject: [PATCH 12/15] Update docs/azure.mdx Co-authored-by: Adam Talbot <12817534+adamrtalbot@users.noreply.github.com> Signed-off-by: Lenny --- docs/azure.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/azure.mdx b/docs/azure.mdx index 4abffc7eb1..7d37cef5e4 100644 --- a/docs/azure.mdx +++ b/docs/azure.mdx @@ -615,7 +615,7 @@ azure { ``` :::warning -Custom images require Microsoft Entra authentication (service principal or managed identity). The **Azure Batch account identity** must have read access to the gallery image. The pool typically requires `virtualNetwork` to be set. +Custom images require Microsoft Entra authentication (service principal or managed identity). The **Azure Batch account identity** must have read access to the gallery image. ::: ### Advanced features From f5e6f3c37600d20536ce23aaf9b6d46b06d02e45 Mon Sep 17 00:00:00 2001 From: Lenny Date: Mon, 27 Jul 2026 14:18:23 +0200 Subject: [PATCH 13/15] Update docs/azure.mdx Co-authored-by: Adam Talbot <12817534+adamrtalbot@users.noreply.github.com> Signed-off-by: Lenny --- docs/azure.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/azure.mdx b/docs/azure.mdx index 7d37cef5e4..a2f627fa53 100644 --- a/docs/azure.mdx +++ b/docs/azure.mdx @@ -605,8 +605,8 @@ To provision pool nodes from a custom VM image published in an [Azure Compute Ga azure { batch { pools { - { - virtualMachineImageId = '/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/' + { + virtualMachineImageId = '/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/' sku = 'batch.node.ubuntu 24.04' } } From 06119f975cd6db03a7ee17e8266d38f47ef9ddb3 Mon Sep 17 00:00:00 2001 From: Lenny Date: Mon, 27 Jul 2026 14:21:00 +0200 Subject: [PATCH 14/15] Update plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy Co-authored-by: Adam Talbot <12817534+adamrtalbot@users.noreply.github.com> Signed-off-by: Lenny --- .../src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy index 135d09c6b4..3b21a69e2b 100644 --- a/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy +++ b/plugins/nf-azure/src/main/nextflow/cloud/azure/config/AzPoolOpts.groovy @@ -212,7 +212,9 @@ class AzPoolOpts implements CacheFunnel, ConfigScope { hasher.putUnencodedChars(virtualNetwork ?: '') hasher.putBoolean(lowPriority) hasher.putUnencodedChars(virtualMachineImageId ?: '') - // 'allowUnverifiedImages' only affects marketplace image resolution; it's ignored when a gallery image is set + // 'allowUnverifiedImages' only affects marketplace image resolution; it's ignored when a gallery image is set. + // NOTE: only hashed when set, so that default configs keep the same hash as previous Nextflow versions + // (no auto-pool-id churn on upgrade) - do not change to an unconditional putBoolean if( !virtualMachineImageId && allowUnverifiedImages ) hasher.putBoolean(allowUnverifiedImages) hasher.putUnencodedChars(startTask.script ?: '') From 444039a4885f36420fb4ff00219a813f24136455 Mon Sep 17 00:00:00 2001 From: Lenny Van de Winkel Date: Mon, 27 Jul 2026 16:29:26 +0200 Subject: [PATCH 15/15] Add AddedInVersion tags for relevant configs --- docs/reference/config.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/reference/config.mdx b/docs/reference/config.mdx index 924c61a080..b1498b9833 100644 --- a/docs/reference/config.mdx +++ b/docs/reference/config.mdx @@ -511,6 +511,8 @@ The client ID for an Azure [managed identity](https://learn.microsoft.com/en-us/ ##### `azure.batch.pools..allowUnverifiedImages` + + Allow the use of unverified VM images when resolving the image from the Batch supported-images list (default: `false`). Ignored when `virtualMachineImageId` is set. ##### `azure.batch.pools..autoScale` @@ -583,6 +585,8 @@ The `startTask` that is executed as the node joins the Azure Batch node pool. ##### `azure.batch.pools..virtualMachineImageId` + + The resource ID of a custom VM image from an Azure Compute Gallery for the pool nodes (e.g., `/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images//versions/`). When set, `publisher` and `offer` are ignored, and `sku` must be the Batch node agent SKU id matching the image OS (for example, `batch.node.ubuntu 24.04`).