Skip to content

Command dsc parametersets #490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Sep 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions help/Find-PSResource.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ Searches for packages from a repository (local or remote), based on `-Name` and

### CommandNameParameterSet
``` PowerShell
[[-CommandName] <string[]>] [-ModuleName <string>] [-Version <string>] [-Prerelease] [-Tag <string[]>]
[[-CommandName] <string[]>] [-ModuleName <string[]>] [-Version <string>] [-Prerelease] [-Tag <string[]>]
[-Repository <string[]>] [-Credential <pscredential>] [-IncludeDependencies] [-WhatIf] [-Confirm] [<CommonParameters>]
```

### DscResourceNameParameterSet
``` PowerShell
[[-DscResourceName] <string[]>] [-ModuleName <string>] [-Version <string>] [-Prerelease] [-Tag <string[]>]
[[-DscResourceName] <string[]>] [-ModuleName <string[]>] [-Version <string>] [-Prerelease] [-Tag <string[]>]
[-Repository <string[]>] [-Credential <pscredential>] [-IncludeDependencies] [-WhatIf] [-Confirm] [<CommonParameters>]
```

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

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".

### Example 4
```powershell
PS C:\> Find-PSResource -CommandName "Get-TargetResource" -Repository PSGallery
Name Version Prerelease ModuleName Repository
---- ------- ---------- ---------- ----------
Get-TargetResource 3.1.0.0 xPowerShellExecutionPolicy PSGallery
Get-TargetResource 1.0.0.4 WindowsDefender PSGallery
Get-TargetResource 1.2.0.0 SystemLocaleDsc PSGallery
Get-TargetResource 1.0.0.0 xInternetExplorerHomePage PSGallery
Get-TargetResource 4.0.1055.0 OctopusDSC PSGallery
Get-TargetResource 1.2.0.0 cRegFile PSGallery
Get-TargetResource 1.1.0.0 cWindowsErrorReporting PSGallery
Get-TargetResource 1.0.0.0 cVNIC PSGallery
Get-TargetResource 1.1.17.0 supVsts PSGallery

```

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.

### Example 5
```powershell
PS C:\> Find-PSResource -CommandName "Get-TargetResource" -ModuleName "SystemLocaleDsc" -Repository PSGallery
Name Version Prerelease ModuleName Repository
---- ------- ---------- ---------- ----------
Get-TargetResource 1.2.0.0 SystemLocaleDsc PSGallery
```

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.

### Example 6
```powershell
PS C:\> Find-PSResource -DscResourceName "SystemLocale" -Repository PSGallery
Name Version Prerelease ModuleName Repository
---- ------- ---------- ---------- ----------
Get-TargetResource 8.5.0.0 ComputerManagementDsc PSGallery
Get-TargetResource 1.2.0.0 SystemLocaleDsc PSGallery

```

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.

## PARAMETERS

### -Credential
Expand Down
34 changes: 34 additions & 0 deletions src/PSGet.Format.ps1xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,39 @@
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>PSIncludedResourceInfoTable</Name>
<ViewSelectedBy>
<TypeName>Microsoft.PowerShell.PowerShellGet.UtilClasses.PSIncludedResourceInfo</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>Name</Label></TableColumnHeader>
<TableColumnHeader>
<Label>Version</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>Prerelease</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>ModuleName</Label>
</TableColumnHeader>
<TableColumnHeader>
<Label>Repository</Label>
</TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>Name</PropertyName></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.Version</ScriptBlock></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.PrereleaseLabel</ScriptBlock></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.Name</ScriptBlock></TableColumnItem>
<TableColumnItem><ScriptBlock>$_.ParentResource.Repository</ScriptBlock></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>
113 changes: 96 additions & 17 deletions src/code/FindPSResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
"PSResource",
DefaultParameterSetName = ResourceNameParameterSet,
SupportsShouldProcess = true)]
[OutputType(typeof(PSResourceInfo))]
[OutputType(typeof(PSResourceInfo), typeof(PSCommandResourceInfo))]
public sealed class FindPSResource : PSCmdlet
{
#region Members
Expand Down Expand Up @@ -76,7 +76,7 @@ public sealed class FindPSResource : PSCmdlet
[Parameter(ParameterSetName = CommandNameParameterSet)]
[Parameter(ParameterSetName = DscResourceNameParameterSet)]
[ValidateNotNullOrEmpty]
public string ModuleName { get; set; }
public string[] ModuleName { get; set; }

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

// Create a respository story (the PSResourceRepository.xml file) if it does not already exist
// This is to create a better experience for those who have just installed v3 and want to get up and running quickly
RepositorySettings.CheckRepositoryStore();
}

protected override void StopProcessing()
Expand All @@ -155,19 +155,11 @@ protected override void ProcessRecord()
break;

case CommandNameParameterSet:
ThrowTerminatingError(new ErrorRecord(
new PSNotImplementedException("CommandNameParameterSet is not yet implemented. Please rerun cmdlet with other parameter set."),
"CommandParameterSetNotImplementedYet",
ErrorCategory.NotImplemented,
this));
ProcessCommandOrDscParameterSet(isSearchingForCommands: true);
break;

case DscResourceNameParameterSet:
ThrowTerminatingError(new ErrorRecord(
new PSNotImplementedException("DscResourceNameParameterSet is not yet implemented. Please rerun cmdlet with other parameter set."),
"DscResourceParameterSetNotImplementedYet",
ErrorCategory.NotImplemented,
this));
ProcessCommandOrDscParameterSet(isSearchingForCommands: false);
break;

default:
Expand Down Expand Up @@ -256,6 +248,93 @@ private void ProcessResourceNameParameterSet()
}
}

private void ProcessCommandOrDscParameterSet(bool isSearchingForCommands)
{
var commandOrDSCNamesToSearch = Utils.ProcessNameWildcards(
pkgNames: isSearchingForCommands ? CommandName : DscResourceName,
errorMsgs: out string[] errorMsgs,
isContainWildcard: out bool nameContainsWildcard);

if (nameContainsWildcard)
{
WriteError(new ErrorRecord(
new PSInvalidOperationException("Wilcards are not supported for -CommandName or -DSCResourceName for Find-PSResource. So all CommandName or DSCResourceName entries will be discarded."),
"CommandDSCResourceNameWithWildcardsNotSupported",
ErrorCategory.InvalidArgument,
this));
return;
}

foreach (string error in errorMsgs)
{
WriteError(new ErrorRecord(
new PSInvalidOperationException(error),
"ErrorFilteringCommandDscResourceNamesForUnsupportedWildcards",
ErrorCategory.InvalidArgument,
this));
}

// this catches the case where Name wasn't passed in as null or empty,
// but after filtering out unsupported wildcard names there are no elements left in commandOrDSCNamesToSearch
if (commandOrDSCNamesToSearch.Length == 0)
{
return;
}

var moduleNamesToSearch = Utils.ProcessNameWildcards(
pkgNames: ModuleName,
errorMsgs: out string[] moduleErrorMsgs,
isContainWildcard: out bool _);

foreach (string error in moduleErrorMsgs)
{
WriteError(new ErrorRecord(
new PSInvalidOperationException(error),
"ErrorFilteringModuleNamesForUnsupportedWildcards",
ErrorCategory.InvalidArgument,
this));
}

if (moduleNamesToSearch.Length == 0)
{
moduleNamesToSearch = new string[] {"*"};
}

FindHelper findHelper = new FindHelper(_cancellationToken, this);
List<PSResourceInfo> foundPackages = new List<PSResourceInfo>();

foreach (PSResourceInfo package in findHelper.FindByResourceName(
name: moduleNamesToSearch,
// provide type so Scripts endpoint for PSGallery won't be searched
type: isSearchingForCommands? ResourceType.Command : ResourceType.DscResource,
version: Version,
prerelease: Prerelease,
tag: Tag,
repository: Repository,
credential: Credential,
includeDependencies: IncludeDependencies))
{
foundPackages.Add(package);
}

// if a single package contains multiple commands we are interested in, return a unique entry for each:
// Command1 , PackageA
// Command2 , PackageA
foreach (string nameToSearch in commandOrDSCNamesToSearch)
{
foreach (var package in foundPackages)
{
// this check ensures DSC names provided as a Command name won't get returned mistakenly
// -CommandName "command1", "dsc1" <- (will not return or add DSC name)
if ((isSearchingForCommands && package.Includes.Command.Contains(nameToSearch)) ||
(!isSearchingForCommands && package.Includes.DscResource.Contains(nameToSearch)))
{
WriteObject(new PSCommandResourceInfo(nameToSearch, package));
}
}
}
}

#endregion
}
}
60 changes: 54 additions & 6 deletions src/code/PSResourceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,37 @@ public Dependency(string dependencyName, VersionRange dependencyVersionRange)

#endregion

#region PSCommandResourceInfo
public sealed class PSCommandResourceInfo
{
// this object will represent a Command or DSCResource
// included by the PSResourceInfo property

#region Properties
public string Name { get; }

public PSResourceInfo ParentResource { get; }
#endregion

#region Constructor

/// <summary>
/// Constructor
///
/// </summary>
/// <param name="name">Name of the command or DSC resource</param>
/// <param name="parentResource">the parent module resource the command or dsc resource belongs to</param>
public PSCommandResourceInfo(string name, PSResourceInfo parentResource)
{
Name = name;
ParentResource = parentResource;
}

#endregion
}

#endregion

#region PSResourceInfo

public sealed class PSResourceInfo
Expand Down Expand Up @@ -485,7 +516,14 @@ public static bool TryConvert(
}

try
{
{
var typeInfo = ParseMetadataType(metadataToParse, repositoryName, type, out ArrayList commandNames, out ArrayList dscResourceNames);
var resourceHashtable = new Hashtable();
resourceHashtable.Add(nameof(PSResourceInfo.Includes.Command), new PSObject(commandNames));
resourceHashtable.Add(nameof(PSResourceInfo.Includes.DscResource), new PSObject(dscResourceNames));
var includes = new ResourceIncludes(resourceHashtable);


psGetInfo = new PSResourceInfo(
additionalMetadata: null,
author: ParseMetadataAuthor(metadataToParse),
Expand All @@ -494,7 +532,7 @@ public static bool TryConvert(
dependencies: ParseMetadataDependencies(metadataToParse),
description: ParseMetadataDescription(metadataToParse),
iconUri: ParseMetadataIconUri(metadataToParse),
includes: null,
includes: includes,
installedDate: null,
installedLocation: null,
isPrelease: ParseMetadataIsPrerelease(metadataToParse),
Expand All @@ -509,7 +547,8 @@ public static bool TryConvert(
repository: repositoryName,
repositorySourceLocation: null,
tags: ParseMetadataTags(metadataToParse),
type: ParseMetadataType(metadataToParse, repositoryName, type),
// type: ParseMetadataType(metadataToParse, repositoryName, type),
type: typeInfo,
updatedDate: null,
version: ParseMetadataVersion(metadataToParse));

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

private static ResourceType ParseMetadataType(IPackageSearchMetadata pkg, string repoName, ResourceType? pkgType)
private static ResourceType ParseMetadataType(IPackageSearchMetadata pkg,
string repoName,
ResourceType? pkgType,
out ArrayList commandNames,
out ArrayList dscResourceNames)
{
// possible type combinations:
// M, C
// M, D
// M
// S

commandNames = new ArrayList();
dscResourceNames = new ArrayList();
string[] tags = ParseMetadataTags(pkg);
ResourceType currentPkgType = ResourceType.Module;

Expand Down Expand Up @@ -838,15 +883,18 @@ private static ResourceType ParseMetadataType(IPackageSearchMetadata pkg, string
currentPkgType &= ~ResourceType.Module;
currentPkgType |= ResourceType.Script;
}
if (tag.StartsWith("PSCommand_"))
if (tag.StartsWith("PSCommand_", StringComparison.InvariantCultureIgnoreCase))
{
currentPkgType |= ResourceType.Command;
commandNames.Add(tag.Split('_')[1]);
}
if (String.Equals(tag, "PSIncludes_DscResource", StringComparison.InvariantCultureIgnoreCase))
if (tag.StartsWith("PSDscResource_", StringComparison.InvariantCultureIgnoreCase))
{
currentPkgType |= ResourceType.DscResource;
dscResourceNames.Add(tag.Split('_')[1]);
}
}

return currentPkgType;
}

Expand Down
Loading