Skip to content

Commit 1dd3e45

Browse files
committed
Merge branch 'main' of https://github.com/Gijsreyn/operation-methods into add-skip-function
2 parents deee5fd + 2075fb9 commit 1dd3e45

25 files changed

+465
-148
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
---
2+
description: Reference for the 'lastIndexOf' DSC configuration document function
3+
ms.date: 08/29/2025
4+
ms.topic: reference
5+
title: lastIndexOf
6+
---
7+
8+
## Synopsis
9+
10+
Returns an integer for the index of the last occurrence of an item in an array.
11+
If the item isn't present, returns -1.
12+
13+
## Syntax
14+
15+
```Syntax
16+
lastIndexOf(arrayToSearch, itemToFind)
17+
```
18+
19+
## Description
20+
21+
The `lastIndexOf()` function searches an array from the end to the beginning
22+
and returns the zero-based index of the last matching element. String
23+
comparisons are case-sensitive. If no match is found, `-1` is returned.
24+
25+
Supported `itemToFind` types:
26+
27+
- string (case-sensitive)
28+
- number (integer)
29+
- array (deep equality)
30+
- object (deep equality)
31+
32+
## Examples
33+
34+
### Example 1 - Find the last rollout slot for a server role (strings)
35+
36+
Use `lastIndexOf()` to locate where a particular role (like a web node)
37+
appears last in a planned rollout sequence. This is handy when you need to
38+
schedule a final step (for example, draining traffic) after the last matching
39+
node has been processed. This example uses [`createArray()`][02] to build the
40+
list of nodes.
41+
42+
```yaml
43+
# lastindexof.example.1.dsc.config.yaml
44+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
45+
resources:
46+
- name: Rollout Plan
47+
type: Microsoft.DSC.Debug/Echo
48+
properties:
49+
output:
50+
lastWebIndex: "[lastIndexOf(createArray('web01','db01','web02','cache01','web03'), 'web03')]"
51+
lastWebFamilyIndex: "[lastIndexOf(createArray('web01','db01','web02','cache01','web02'), 'web02')]"
52+
```
53+
54+
```bash
55+
dsc config get --file lastindexof.example.1.dsc.config.yaml
56+
```
57+
58+
```yaml
59+
results:
60+
- name: Rollout Plan
61+
type: Microsoft.DSC.Debug/Echo
62+
result:
63+
actualState:
64+
output:
65+
lastWebIndex: 4
66+
lastWebFamilyIndex: 4
67+
messages: []
68+
hadErrors: false
69+
```
70+
71+
Note that string comparison is case-sensitive. Searching for `WEB02` would
72+
return `-1` in this example.
73+
74+
### Example 2 - Locate the last matching configuration object (objects)
75+
76+
Deep equality lets you search arrays of objects. Here we find the last
77+
occurrence of a feature flag object with a specific name. This example uses
78+
[`createObject()`][03] to build objects and [`createArray()`][10] to build the
79+
collection.
80+
81+
```yaml
82+
# lastindexof.example.2.dsc.config.yaml
83+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
84+
resources:
85+
- name: Feature Flags
86+
type: Microsoft.DSC.Debug/Echo
87+
properties:
88+
output:
89+
lastBetaIndex: "[lastIndexOf(createArray(createObject('name','Beta'), createObject('name','Gamma'), createObject('name','Beta')), createObject('name','Beta'))]"
90+
```
91+
92+
```bash
93+
dsc config get --file lastindexof.example.2.dsc.config.yaml
94+
```
95+
96+
```yaml
97+
results:
98+
- name: Feature Flags
99+
type: Microsoft.DSC.Debug/Echo
100+
result:
101+
actualState:
102+
output:
103+
lastBetaIndex: 2
104+
messages: []
105+
hadErrors: false
106+
```
107+
108+
Property order in objects doesn't matter. The following also returns `1` due to
109+
deep equality: `lastIndexOf(array(createObject('a',1,'b',2), createObject('b',2,'a',1)), createObject('a',1,'b',2))`.
110+
111+
## Parameters
112+
113+
### arrayToSearch
114+
115+
The array to search. Required.
116+
117+
```yaml
118+
Type: array
119+
Required: true
120+
Position: 1
121+
```
122+
123+
### itemToFind
124+
125+
The item to search for. Required.
126+
127+
```yaml
128+
Type: string | number | array | object
129+
Required: true
130+
Position: 2
131+
```
132+
133+
## Output
134+
135+
Returns a number representing the last index or -1 if not found.
136+
137+
```yaml
138+
Type: number
139+
```
140+
141+
## Related functions
142+
143+
- [`indexOf()`][00] - First occurrence index in an array
144+
- [`contains()`][01] - Checks for presence in arrays/objects/strings
145+
146+
<!-- Link reference definitions -->
147+
[00]: ./indexOf.md
148+
[01]: ./contains.md
149+
[02]: ./createArray.md
150+
[03]: ./createObject.md

dsc/src/subcommand.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, parame
319319
}
320320
};
321321

322+
configurator.context.dsc_version = Some(env!("CARGO_PKG_VERSION").to_string());
323+
322324
if let ConfigSubCommand::Set { what_if , .. } = subcommand {
323325
if *what_if {
324326
configurator.context.execution_type = ExecutionKind::WhatIf;

dsc/tests/dsc_discovery.tests.ps1

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ Describe 'tests for resource discovery' {
9595
$env:DSC_RESOURCE_PATH = $testdrive
9696
Set-Content -Path "$testdrive/test.dsc.resource.json" -Value $manifest
9797
$out = dsc resource list 2>&1
98-
write-verbose -verbose ($out | Out-String)
9998
$out | Should -Match 'WARN.*?Validation.*?invalid version' -Because ($out | Out-String)
10099
}
101100
finally {
@@ -166,21 +165,18 @@ Describe 'tests for resource discovery' {
166165
$env:PSModulePath = $oldPSModulePath
167166
}
168167

169-
It 'Verify non-zero exit code when resource not found' {
168+
It 'Verify non-zero exit code when resource not found: <cmdline>' -TestCases @(
169+
@{ cmdline = "dsc resource get -r abc/def" }
170+
@{ cmdline = "dsc resource get --all -r abc/def" }
171+
@{ cmdline = "dsc resource set -r abc/def -i 'abc'" }
172+
@{ cmdline = "dsc resource test -r abc/def -i 'abc'" }
173+
@{ cmdline = "dsc resource delete -r abc/def -i 'abc'" }
174+
@{ cmdline = "dsc resource export -r abc/def" }
175+
@{ cmdline = "dsc resource schema -r abc/def" }
176+
) {
177+
param($cmdline)
170178

171-
$out = dsc resource get -r abc/def
172-
$LASTEXITCODE | Should -Be 7
173-
$out = dsc resource get --all -r abc/def
174-
$LASTEXITCODE | Should -Be 7
175-
$out = 'abc' | dsc resource set -r abc/def -f -
176-
$LASTEXITCODE | Should -Be 7
177-
$out = 'abc' | dsc resource test -r abc/def -f -
178-
$LASTEXITCODE | Should -Be 7
179-
$out = 'abc' | dsc resource delete -r abc/def -f -
180-
$LASTEXITCODE | Should -Be 7
181-
$out = dsc resource export -r abc/def
182-
$LASTEXITCODE | Should -Be 7
183-
$out = dsc resource schema -r abc/def
179+
Invoke-Expression $cmdline 2>$null
184180
$LASTEXITCODE | Should -Be 7
185181
}
186182

dsc/tests/dsc_extension_discover.tests.ps1

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ Describe 'Discover extension tests' {
108108
$out.Count | Should -Be 1 -Because ($out | Out-String)
109109
$out.type | Should -Be 'Test/DiscoverRelative'
110110
$out = dsc resource list 2> $TestDrive/error.log
111-
write-verbose -verbose (Get-Content -Path "$TestDrive/error.log" -Raw)
112111
$LASTEXITCODE | Should -Be 0
113112
$out | Should -BeNullOrEmpty
114113
$errorMessage = Get-Content -Path "$TestDrive/error.log" -Raw

dsc/tests/dsc_functions.tests.ps1

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,4 +434,33 @@ Describe 'tests for function expressions' {
434434
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log -Raw)
435435
($out.results[0].result.actualState.output | Out-String) | Should -BeExactly ($expected | Out-String)
436436
}
437+
438+
It 'lastIndexOf function works for: <expression>' -TestCases @(
439+
@{ expression = "[lastIndexOf(createArray('a', 'b', 'a', 'c'), 'a')]"; expected = 2 }
440+
@{ expression = "[lastIndexOf(createArray(10, 20, 30, 20), 20)]"; expected = 3 }
441+
@{ expression = "[lastIndexOf(createArray('Apple', 'Banana'), 'apple')]"; expected = -1 }
442+
@{ expression = "[lastIndexOf(createArray(createArray('a','b'), createArray('c','d'), createArray('a','b')), createArray('a','b'))]"; expected = 2 }
443+
@{ expression = "[lastIndexOf(createArray(createObject('name','John'), createObject('name','Jane'), createObject('name','John')), createObject('name','John'))]"; expected = 2 }
444+
@{ expression = "[lastIndexOf(createArray(), 'test')]"; expected = -1 }
445+
# Objects are compared by deep equality: same keys and values are equal, regardless of property order.
446+
# Both createObject('a',1,'b',2) and createObject('b',2,'a',1) are considered equal.
447+
# Therefore, lastIndexOf returns 1 (the last position where an equal object occurs).
448+
@{ expression = "[lastIndexOf(createArray(createObject('a',1,'b',2), createObject('b',2,'a',1)), createObject('a',1,'b',2))]"; expected = 1 }
449+
@{ expression = "[lastIndexOf(createArray('1','2','3'), 1)]"; expected = -1 }
450+
@{ expression = "[lastIndexOf(createArray(1,2,3), '1')]"; expected = -1 }
451+
) {
452+
param($expression, $expected)
453+
454+
$config_yaml = @"
455+
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
456+
resources:
457+
- name: Echo
458+
type: Microsoft.DSC.Debug/Echo
459+
properties:
460+
output: "$expression"
461+
"@
462+
$out = dsc -l trace config get -i $config_yaml 2>$TestDrive/error.log | ConvertFrom-Json
463+
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log -Raw)
464+
($out.results[0].result.actualState.output | Out-String) | Should -BeExactly ($expected | Out-String)
465+
}
437466
}

dsc/tests/dsc_include.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Describe 'Include tests' {
3535
"@
3636
$configPath = Join-Path $TestDrive 'config.dsc.yaml'
3737
$config | Set-Content -Path $configPath
38-
dsc config get -f $configPath
38+
dsc config get -f $configPath 2>$null
3939
$LASTEXITCODE | Should -Be 2
4040
}
4141

dsc/tests/dsc_securitycontext.tests.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Describe 'Tests for configuration security context metadata' {
1313
}
1414

1515
It 'Require admin' {
16-
$out = dsc config get -f $PSScriptRoot/../examples/require_admin.yaml
16+
$out = dsc config get -f $PSScriptRoot/../examples/require_admin.yaml 2>$null
1717
if ($isAdmin) {
1818
$LASTEXITCODE | Should -Be 0
1919
$out | Should -Not -BeNullOrEmpty
@@ -24,7 +24,7 @@ Describe 'Tests for configuration security context metadata' {
2424
}
2525

2626
It 'Require non-admin' {
27-
$out = dsc config get -f $PSScriptRoot/../examples/require_nonadmin.yaml
27+
$out = dsc config get -f $PSScriptRoot/../examples/require_nonadmin.yaml 2>$null
2828
if ($isAdmin) {
2929
$LASTEXITCODE | Should -Be 2
3030
}

dsc/tests/dsc_set.tests.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Describe 'resource set tests' {
6161
"_exist": false
6262
}
6363
'@
64-
$null = registry config set --input $json
64+
$null = registry config set --input $json 2>$null
6565
}
6666
}
6767

@@ -73,7 +73,7 @@ Describe 'resource set tests' {
7373
"_exist": false
7474
}
7575
'@
76-
$null = registry config set --input $json
76+
$null = registry config set --input $json 2>$null
7777
}
7878
}
7979

dsc/tests/dsc_test.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Describe 'resource test tests' {
99
"valueName": "ProductName"
1010
}
1111
'@
12-
$current = registry config get --input $json
12+
$current = registry config get --input $json 2>$null
1313
$out = $current | dsc resource test -r Microsoft.Windows/Registry -f -
1414
$LASTEXITCODE | Should -Be 0
1515
$out = $out | ConvertFrom-Json

dsc/tests/dsc_variables.tests.ps1

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ resources:
2222
output: "[variables('myVariable')]"
2323
'@
2424
$out = dsc config get -i $configYaml | ConvertFrom-Json
25-
Write-Verbose -Verbose $out
2625
$LASTEXITCODE | Should -Be 0
2726
$out.results[0].result.actualState.output | Should -Be 'bar'
2827
}
@@ -39,7 +38,6 @@ resources:
3938
output: "[variables('myVariable')]"
4039
'@
4140
$out = dsc config get -i $configYaml 2>&1 | Out-String
42-
Write-Verbose -Verbose $out
4341
$LASTEXITCODE | Should -Be 2
4442
$out | Should -BeLike "*Variable 'myVariable' does not exist or has not been initialized yet*"
4543
}

0 commit comments

Comments
 (0)