Skip to content

Commit 58df9c5

Browse files
authored
Command dsc parametersets (#490)
Implement CommandName and DSCResourceName parameter sets
1 parent cbccb23 commit 58df9c5

File tree

5 files changed

+260
-25
lines changed

5 files changed

+260
-25
lines changed

help/Find-PSResource.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ Searches for packages from a repository (local or remote), based on `-Name` and
1919

2020
### CommandNameParameterSet
2121
``` PowerShell
22-
[[-CommandName] <string[]>] [-ModuleName <string>] [-Version <string>] [-Prerelease] [-Tag <string[]>]
22+
[[-CommandName] <string[]>] [-ModuleName <string[]>] [-Version <string>] [-Prerelease] [-Tag <string[]>]
2323
[-Repository <string[]>] [-Credential <pscredential>] [-IncludeDependencies] [-WhatIf] [-Confirm] [<CommonParameters>]
2424
```
2525

2626
### DscResourceNameParameterSet
2727
``` PowerShell
28-
[[-DscResourceName] <string[]>] [-ModuleName <string>] [-Version <string>] [-Prerelease] [-Tag <string[]>]
28+
[[-DscResourceName] <string[]>] [-ModuleName <string[]>] [-Version <string>] [-Prerelease] [-Tag <string[]>]
2929
[-Repository <string[]>] [-Credential <pscredential>] [-IncludeDependencies] [-WhatIf] [-Confirm] [<CommonParameters>]
3030
```
3131

@@ -77,6 +77,47 @@ PS C:\> Find-PSResource -Name "Microsoft.PowerShell.SecretManagement" -Version "
7777

7878
This examples searches for the package with `-Name` "Microsoft.PowerShell.SecretManagement". It returns all versions which satisfy the specified `-Version` range by looking through the specified `-Repository` "PSGallery". At the time of writing this example those satisfying versions are: "0.9.1.0" and "1.0.0.0".
7979

80+
### Example 4
81+
```powershell
82+
PS C:\> Find-PSResource -CommandName "Get-TargetResource" -Repository PSGallery
83+
Name Version Prerelease ModuleName Repository
84+
---- ------- ---------- ---------- ----------
85+
Get-TargetResource 3.1.0.0 xPowerShellExecutionPolicy PSGallery
86+
Get-TargetResource 1.0.0.4 WindowsDefender PSGallery
87+
Get-TargetResource 1.2.0.0 SystemLocaleDsc PSGallery
88+
Get-TargetResource 1.0.0.0 xInternetExplorerHomePage PSGallery
89+
Get-TargetResource 4.0.1055.0 OctopusDSC PSGallery
90+
Get-TargetResource 1.2.0.0 cRegFile PSGallery
91+
Get-TargetResource 1.1.0.0 cWindowsErrorReporting PSGallery
92+
Get-TargetResource 1.0.0.0 cVNIC PSGallery
93+
Get-TargetResource 1.1.17.0 supVsts PSGallery
94+
95+
```
96+
97+
This examples searches for all module resources with `-CommandName` "Get-TargetResource" from the `-Repository` PSGallery. It returns all the module resources which include a command named "Get-TargetResource" and also lists the following information for each module resource: version, name (displayed under ModuleName) and repository. To access the rest of the properties of the parent module resource, you can access the `$_.ParentResource` of the PSIncludedResourceInfo object returned from the CommandName parameter set.
98+
99+
### Example 5
100+
```powershell
101+
PS C:\> Find-PSResource -CommandName "Get-TargetResource" -ModuleName "SystemLocaleDsc" -Repository PSGallery
102+
Name Version Prerelease ModuleName Repository
103+
---- ------- ---------- ---------- ----------
104+
Get-TargetResource 1.2.0.0 SystemLocaleDsc PSGallery
105+
```
106+
107+
This examples searches for a module resource with a command named "Get-TargetResource" (via the `-CommandName` parameter), specifically from the module resource "SystemLocaleDsc" (via the `-ModuleName` parameter) from the `-Repository` PSGallery. The "SystemLocaleDsc" resource does indeed include a command named Get-TargetResource so this resource will be returned. The returned object lists the name of the command (displayed under Name) and the following information for the parent module resource: version, name (displayed under ModuleName) and repository. To access the rest of the properties of the parent module resource, you can access the `$_.ParentResource` of the PSIncludedResourceInfo object returned from the CommandName parameter set.
108+
109+
### Example 6
110+
```powershell
111+
PS C:\> Find-PSResource -DscResourceName "SystemLocale" -Repository PSGallery
112+
Name Version Prerelease ModuleName Repository
113+
---- ------- ---------- ---------- ----------
114+
Get-TargetResource 8.5.0.0 ComputerManagementDsc PSGallery
115+
Get-TargetResource 1.2.0.0 SystemLocaleDsc PSGallery
116+
117+
```
118+
119+
This examples searches for all module resources with `-DscResourceName` "SystemLocale" from the `-Repository` PSGallery. It returns all the module resources which include a DSC resource named "SystemLocale" and also lists the following information for each module resource: version, name (displayed under ModuleName) and repository. To access the rest of the properties of the parent module resource, you can access the `$_.ParentResource` of the PSIncludedResourceInfo object returned from the DSCResourceName parameter set.
120+
80121
## PARAMETERS
81122

82123
### -Credential

src/PSGet.Format.ps1xml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,39 @@
2525
</TableRowEntries>
2626
</TableControl>
2727
</View>
28+
<View>
29+
<Name>PSIncludedResourceInfoTable</Name>
30+
<ViewSelectedBy>
31+
<TypeName>Microsoft.PowerShell.PowerShellGet.UtilClasses.PSIncludedResourceInfo</TypeName>
32+
</ViewSelectedBy>
33+
<TableControl>
34+
<TableHeaders>
35+
<TableColumnHeader><Label>Name</Label></TableColumnHeader>
36+
<TableColumnHeader>
37+
<Label>Version</Label>
38+
</TableColumnHeader>
39+
<TableColumnHeader>
40+
<Label>Prerelease</Label>
41+
</TableColumnHeader>
42+
<TableColumnHeader>
43+
<Label>ModuleName</Label>
44+
</TableColumnHeader>
45+
<TableColumnHeader>
46+
<Label>Repository</Label>
47+
</TableColumnHeader>
48+
</TableHeaders>
49+
<TableRowEntries>
50+
<TableRowEntry>
51+
<TableColumnItems>
52+
<TableColumnItem><PropertyName>Name</PropertyName></TableColumnItem>
53+
<TableColumnItem><ScriptBlock>$_.ParentResource.Version</ScriptBlock></TableColumnItem>
54+
<TableColumnItem><ScriptBlock>$_.ParentResource.PrereleaseLabel</ScriptBlock></TableColumnItem>
55+
<TableColumnItem><ScriptBlock>$_.ParentResource.Name</ScriptBlock></TableColumnItem>
56+
<TableColumnItem><ScriptBlock>$_.ParentResource.Repository</ScriptBlock></TableColumnItem>
57+
</TableColumnItems>
58+
</TableRowEntry>
59+
</TableRowEntries>
60+
</TableControl>
61+
</View>
2862
</ViewDefinitions>
2963
</Configuration>

src/code/FindPSResource.cs

Lines changed: 96 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
2121
"PSResource",
2222
DefaultParameterSetName = ResourceNameParameterSet,
2323
SupportsShouldProcess = true)]
24-
[OutputType(typeof(PSResourceInfo))]
24+
[OutputType(typeof(PSResourceInfo), typeof(PSCommandResourceInfo))]
2525
public sealed class FindPSResource : PSCmdlet
2626
{
2727
#region Members
@@ -76,7 +76,7 @@ public sealed class FindPSResource : PSCmdlet
7676
[Parameter(ParameterSetName = CommandNameParameterSet)]
7777
[Parameter(ParameterSetName = DscResourceNameParameterSet)]
7878
[ValidateNotNullOrEmpty]
79-
public string ModuleName { get; set; }
79+
public string[] ModuleName { get; set; }
8080

8181
/// <summary>
8282
/// Specifies a list of command names that searched module packages will provide. Wildcards are supported.
@@ -134,11 +134,11 @@ public sealed class FindPSResource : PSCmdlet
134134
protected override void BeginProcessing()
135135
{
136136
_source = new CancellationTokenSource();
137-
_cancellationToken = _source.Token;
138-
139-
// Create a respository story (the PSResourceRepository.xml file) if it does not already exist
140-
// This is to create a better experience for those who have just installed v3 and want to get up and running quickly
141-
RepositorySettings.CheckRepositoryStore();
137+
_cancellationToken = _source.Token;
138+
139+
// Create a respository story (the PSResourceRepository.xml file) if it does not already exist
140+
// This is to create a better experience for those who have just installed v3 and want to get up and running quickly
141+
RepositorySettings.CheckRepositoryStore();
142142
}
143143

144144
protected override void StopProcessing()
@@ -155,19 +155,11 @@ protected override void ProcessRecord()
155155
break;
156156

157157
case CommandNameParameterSet:
158-
ThrowTerminatingError(new ErrorRecord(
159-
new PSNotImplementedException("CommandNameParameterSet is not yet implemented. Please rerun cmdlet with other parameter set."),
160-
"CommandParameterSetNotImplementedYet",
161-
ErrorCategory.NotImplemented,
162-
this));
158+
ProcessCommandOrDscParameterSet(isSearchingForCommands: true);
163159
break;
164160

165161
case DscResourceNameParameterSet:
166-
ThrowTerminatingError(new ErrorRecord(
167-
new PSNotImplementedException("DscResourceNameParameterSet is not yet implemented. Please rerun cmdlet with other parameter set."),
168-
"DscResourceParameterSetNotImplementedYet",
169-
ErrorCategory.NotImplemented,
170-
this));
162+
ProcessCommandOrDscParameterSet(isSearchingForCommands: false);
171163
break;
172164

173165
default:
@@ -256,6 +248,93 @@ private void ProcessResourceNameParameterSet()
256248
}
257249
}
258250

251+
private void ProcessCommandOrDscParameterSet(bool isSearchingForCommands)
252+
{
253+
var commandOrDSCNamesToSearch = Utils.ProcessNameWildcards(
254+
pkgNames: isSearchingForCommands ? CommandName : DscResourceName,
255+
errorMsgs: out string[] errorMsgs,
256+
isContainWildcard: out bool nameContainsWildcard);
257+
258+
if (nameContainsWildcard)
259+
{
260+
WriteError(new ErrorRecord(
261+
new PSInvalidOperationException("Wilcards are not supported for -CommandName or -DSCResourceName for Find-PSResource. So all CommandName or DSCResourceName entries will be discarded."),
262+
"CommandDSCResourceNameWithWildcardsNotSupported",
263+
ErrorCategory.InvalidArgument,
264+
this));
265+
return;
266+
}
267+
268+
foreach (string error in errorMsgs)
269+
{
270+
WriteError(new ErrorRecord(
271+
new PSInvalidOperationException(error),
272+
"ErrorFilteringCommandDscResourceNamesForUnsupportedWildcards",
273+
ErrorCategory.InvalidArgument,
274+
this));
275+
}
276+
277+
// this catches the case where Name wasn't passed in as null or empty,
278+
// but after filtering out unsupported wildcard names there are no elements left in commandOrDSCNamesToSearch
279+
if (commandOrDSCNamesToSearch.Length == 0)
280+
{
281+
return;
282+
}
283+
284+
var moduleNamesToSearch = Utils.ProcessNameWildcards(
285+
pkgNames: ModuleName,
286+
errorMsgs: out string[] moduleErrorMsgs,
287+
isContainWildcard: out bool _);
288+
289+
foreach (string error in moduleErrorMsgs)
290+
{
291+
WriteError(new ErrorRecord(
292+
new PSInvalidOperationException(error),
293+
"ErrorFilteringModuleNamesForUnsupportedWildcards",
294+
ErrorCategory.InvalidArgument,
295+
this));
296+
}
297+
298+
if (moduleNamesToSearch.Length == 0)
299+
{
300+
moduleNamesToSearch = new string[] {"*"};
301+
}
302+
303+
FindHelper findHelper = new FindHelper(_cancellationToken, this);
304+
List<PSResourceInfo> foundPackages = new List<PSResourceInfo>();
305+
306+
foreach (PSResourceInfo package in findHelper.FindByResourceName(
307+
name: moduleNamesToSearch,
308+
// provide type so Scripts endpoint for PSGallery won't be searched
309+
type: isSearchingForCommands? ResourceType.Command : ResourceType.DscResource,
310+
version: Version,
311+
prerelease: Prerelease,
312+
tag: Tag,
313+
repository: Repository,
314+
credential: Credential,
315+
includeDependencies: IncludeDependencies))
316+
{
317+
foundPackages.Add(package);
318+
}
319+
320+
// if a single package contains multiple commands we are interested in, return a unique entry for each:
321+
// Command1 , PackageA
322+
// Command2 , PackageA
323+
foreach (string nameToSearch in commandOrDSCNamesToSearch)
324+
{
325+
foreach (var package in foundPackages)
326+
{
327+
// this check ensures DSC names provided as a Command name won't get returned mistakenly
328+
// -CommandName "command1", "dsc1" <- (will not return or add DSC name)
329+
if ((isSearchingForCommands && package.Includes.Command.Contains(nameToSearch)) ||
330+
(!isSearchingForCommands && package.Includes.DscResource.Contains(nameToSearch)))
331+
{
332+
WriteObject(new PSCommandResourceInfo(nameToSearch, package));
333+
}
334+
}
335+
}
336+
}
337+
259338
#endregion
260339
}
261340
}

src/code/PSResourceInfo.cs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,37 @@ public Dependency(string dependencyName, VersionRange dependencyVersionRange)
199199

200200
#endregion
201201

202+
#region PSCommandResourceInfo
203+
public sealed class PSCommandResourceInfo
204+
{
205+
// this object will represent a Command or DSCResource
206+
// included by the PSResourceInfo property
207+
208+
#region Properties
209+
public string Name { get; }
210+
211+
public PSResourceInfo ParentResource { get; }
212+
#endregion
213+
214+
#region Constructor
215+
216+
/// <summary>
217+
/// Constructor
218+
///
219+
/// </summary>
220+
/// <param name="name">Name of the command or DSC resource</param>
221+
/// <param name="parentResource">the parent module resource the command or dsc resource belongs to</param>
222+
public PSCommandResourceInfo(string name, PSResourceInfo parentResource)
223+
{
224+
Name = name;
225+
ParentResource = parentResource;
226+
}
227+
228+
#endregion
229+
}
230+
231+
#endregion
232+
202233
#region PSResourceInfo
203234

204235
public sealed class PSResourceInfo
@@ -485,7 +516,14 @@ public static bool TryConvert(
485516
}
486517

487518
try
488-
{
519+
{
520+
var typeInfo = ParseMetadataType(metadataToParse, repositoryName, type, out ArrayList commandNames, out ArrayList dscResourceNames);
521+
var resourceHashtable = new Hashtable();
522+
resourceHashtable.Add(nameof(PSResourceInfo.Includes.Command), new PSObject(commandNames));
523+
resourceHashtable.Add(nameof(PSResourceInfo.Includes.DscResource), new PSObject(dscResourceNames));
524+
var includes = new ResourceIncludes(resourceHashtable);
525+
526+
489527
psGetInfo = new PSResourceInfo(
490528
additionalMetadata: null,
491529
author: ParseMetadataAuthor(metadataToParse),
@@ -494,7 +532,7 @@ public static bool TryConvert(
494532
dependencies: ParseMetadataDependencies(metadataToParse),
495533
description: ParseMetadataDescription(metadataToParse),
496534
iconUri: ParseMetadataIconUri(metadataToParse),
497-
includes: null,
535+
includes: includes,
498536
installedDate: null,
499537
installedLocation: null,
500538
isPrelease: ParseMetadataIsPrerelease(metadataToParse),
@@ -509,7 +547,8 @@ public static bool TryConvert(
509547
repository: repositoryName,
510548
repositorySourceLocation: null,
511549
tags: ParseMetadataTags(metadataToParse),
512-
type: ParseMetadataType(metadataToParse, repositoryName, type),
550+
// type: ParseMetadataType(metadataToParse, repositoryName, type),
551+
type: typeInfo,
513552
updatedDate: null,
514553
version: ParseMetadataVersion(metadataToParse));
515554

@@ -803,14 +842,20 @@ private static string[] ParseMetadataTags(IPackageSearchMetadata pkg)
803842
return pkg.Tags.Split(Delimeter, StringSplitOptions.RemoveEmptyEntries);
804843
}
805844

806-
private static ResourceType ParseMetadataType(IPackageSearchMetadata pkg, string repoName, ResourceType? pkgType)
845+
private static ResourceType ParseMetadataType(IPackageSearchMetadata pkg,
846+
string repoName,
847+
ResourceType? pkgType,
848+
out ArrayList commandNames,
849+
out ArrayList dscResourceNames)
807850
{
808851
// possible type combinations:
809852
// M, C
810853
// M, D
811854
// M
812855
// S
813856

857+
commandNames = new ArrayList();
858+
dscResourceNames = new ArrayList();
814859
string[] tags = ParseMetadataTags(pkg);
815860
ResourceType currentPkgType = ResourceType.Module;
816861

@@ -838,15 +883,18 @@ private static ResourceType ParseMetadataType(IPackageSearchMetadata pkg, string
838883
currentPkgType &= ~ResourceType.Module;
839884
currentPkgType |= ResourceType.Script;
840885
}
841-
if (tag.StartsWith("PSCommand_"))
886+
if (tag.StartsWith("PSCommand_", StringComparison.InvariantCultureIgnoreCase))
842887
{
843888
currentPkgType |= ResourceType.Command;
889+
commandNames.Add(tag.Split('_')[1]);
844890
}
845-
if (String.Equals(tag, "PSIncludes_DscResource", StringComparison.InvariantCultureIgnoreCase))
891+
if (tag.StartsWith("PSDscResource_", StringComparison.InvariantCultureIgnoreCase))
846892
{
847893
currentPkgType |= ResourceType.DscResource;
894+
dscResourceNames.Add(tag.Split('_')[1]);
848895
}
849896
}
897+
850898
return currentPkgType;
851899
}
852900

0 commit comments

Comments
 (0)