PowerShell / PSResourceGet

PSResourceGet is the package manager for PowerShell
https://www.powershellgallery.com/packages/Microsoft.PowerShell.PSResourceGet
MIT License
494 stars 93 forks source link

Get-CredentialInfo cmdlet request #1200

Open Stephanevg opened 1 year ago

Stephanevg commented 1 year ago

Prerequisites

Steps to reproduce

Hi,

I was following the this article in order to register an Azure artifact reposistory. It specifically mentions: You can also use with the -Credential parameter.

I used the following snippet to try the register the PsResource:


import-module Microsoft.PowerShell.PSResourceGet -force
$Credential = Get-Credential -UserName "My@Email.com" -Message "Enter your ADO credentials" #generated the PAT as described in the article

$FriendlyName = "MyFriendlyName"
$ProjectName = "MyProjectName"

Register-PSResourceRepository -Name $FriendlyName -Uri "https://pkgs.dev.azure.com/$($ProjectName)/_packaging/$($ProjectName)/nuget/v3/index.json" -Credential $Credential

But I get the following error:

Cannot bind parameter 'CredentialInfo'. Cannot convert the "System.Management.Automation.PSCredential" value of type "System.Management.Automation.PSCredential" to type
     | "Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo".

I guess this is a

Expected behavior

It should register the repository

Actual behavior

Cannot bind parameter 'CredentialInfo'. Cannot convert the "System.Management.Automation.PSCredential" value of type "System.Management.Automation.PSCredential" to type
     | "Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo".

Error details

Exception             : 
    Type              : System.Management.Automation.ParameterBindingException
    Message           : Cannot bind parameter 'CredentialInfo'. Cannot convert the "System.Management.Automation.PSCredential" value of type "System.Management.Automation.PSCredential" to type "Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo".
    ParameterName     : CredentialInfo
    ParameterType     : Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo
    TypeSpecified     : psobject
    ErrorId           : CannotConvertArgumentNoMessage
    Line              : 1
    Offset            : 166
    CommandInvocation : 
        MyCommand        : Register-PSResourceRepository
        BoundParameters  : 
            Comparer : System.OrdinalIgnoreCaseComparer
            Count    : 2
            Keys     : 
                Length : 4

                Length : 3
            Values   : 
                Length : 13

                Length : 93
            SyncRoot : 
                Comparer : System.OrdinalIgnoreCaseComparer
                Count    : 2
                Keys     : 
                    Length : 4

                    Length : 3
                Values   : 
                    Length : 13

                    Length : 93
                SyncRoot : 
                    Comparer : System.OrdinalIgnoreCaseComparer
                    Count    : 2
                    Keys     : 
                        Length : 4

                        Length : 3
                    Values   : 
                        Length : 13

                        Length : 93
                    SyncRoot : 
                        Comparer : System.OrdinalIgnoreCaseComparer
                        Count    : 2
                        Keys     : 
                            Length : 4

                            Length : 3
                        Values   : 
                            Length : 13

                            Length : 93
                        SyncRoot : 
                            Comparer : System.OrdinalIgnoreCaseComparer
                            Count    : 2
                            Keys     : 
                                Length : 4

                                Length : 3
                            Values   : 
                                Length : 13

                                Length : 93
                            SyncRoot : 
                                Comparer : System.OrdinalIgnoreCaseComparer
                                Count    : 2
                                Keys     : 
                                    Length : 4

                                    Length : 3
                                Values   : 
                                    Length : 13

                                    Length : 93
                                SyncRoot : 
                                    Comparer : System.OrdinalIgnoreCaseComparer
                                    Count    : 2
                                    Keys     : …
                                    Values   : …
                                    SyncRoot : …
        ScriptLineNumber : 1
        OffsetInLine     : 1
        HistoryId        : 26
        Line             : Register-PSResourceRepository -Name "FeedDistrict2" -Uri "https://pkgs.dev.azure.com/DigitalDistrict01/_packaging/DigitalDistrict01/nuget/v3/index.json" -Credential $Credential
        PositionMessage  : At line:1 char:1
                           + Register-PSResourceRepository -Name "FeedDistrict2" -Uri "https://pkg …
                           + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        InvocationName   : Register-PSResourceRepository
        PipelineLength   : 1
        PipelinePosition : 1
    ErrorRecord       : 
        Exception             : 
            Type    : System.Management.Automation.ParentContainsErrorRecordException
            Message : Cannot bind parameter 'CredentialInfo'. Cannot convert the "System.Management.Automation.PSCredential" value of type "System.Management.Automation.PSCredential" to type "Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo".
            HResult : -2146233087
        CategoryInfo          : InvalidArgument: (:) [Register-PSResourceRepository], ParentContainsErrorRecordException
        FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository
        InvocationInfo        : 
            MyCommand        : Register-PSResourceRepository
            ScriptLineNumber : 1
            OffsetInLine     : 166
            HistoryId        : 26
            Line             : Register-PSResourceRepository -Name "FeedDistrict2" -Uri "https://pkgs.dev.azure.com/DigitalDistrict01/_packaging/DigitalDistrict01/nuget/v3/index.json" -Credential $Credential
            PositionMessage  : At line:1 char:166
                               + … kaging/DigitalDistrict01/nuget/v3/index.json" -Credential $Credential
                               +                                                             ~~~~~~~~~~~
            CommandOrigin    : Internal
        ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
    TargetSite        : 
        Name          : CoerceTypeAsNeeded
        DeclaringType : System.Management.Automation.ParameterBinderBase, System.Management.Automation, Version=7.3.4.500, Culture=neutral, PublicKeyToken=31bf3856ad364e35
        MemberType    : Method
        Module        : System.Management.Automation.dll
    Data              : System.Collections.ListDictionaryInternal
    InnerException    : 
        Type        : System.Management.Automation.PSInvalidCastException
        ErrorRecord : 
            Exception             : 
                Type    : System.Management.Automation.ParentContainsErrorRecordException
                Message : Cannot convert the "System.Management.Automation.PSCredential" value of type "System.Management.Automation.PSCredential" to type "Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo".
                HResult : -2146233087
            CategoryInfo          : InvalidArgument: (:) [], ParentContainsErrorRecordException
            FullyQualifiedErrorId : ConvertToFinalInvalidCastException
        TargetSite  : 
            Name          : ThrowInvalidCastException
            DeclaringType : System.Management.Automation.LanguagePrimitives
            MemberType    : Method
            Module        : System.Management.Automation.dll
        Message     : Cannot convert the "System.Management.Automation.PSCredential" value of type "System.Management.Automation.PSCredential" to type "Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo".
        Source      : System.Management.Automation
        HResult     : -2147467262
        StackTrace  : 
   at System.Management.Automation.LanguagePrimitives.ThrowInvalidCastException(Object valueToConvert, Type resultType)
   at System.Management.Automation.LanguagePrimitives.ConvertNoConversion(Object valueToConvert, Type resultType, Boolean recurse, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable backupTable)
   at System.Management.Automation.LanguagePrimitives.ConversionData`1.Invoke(Object valueToConvert, Type resultType, Boolean recurse, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable backupTable)
   at System.Management.Automation.LanguagePrimitives.ConvertTo(Object valueToConvert, Type resultType, Boolean recursion, IFormatProvider formatProvider, TypeTable backupTypeTable)
   at System.Management.Automation.ParameterBinderBase.CoerceTypeAsNeeded(CommandParameterInternal argument, String parameterName, Type toType, ParameterCollectionTypeInformation collectionTypeInfo, Object currentValue)
    Source            : System.Management.Automation
    HResult           : -2146233087
    StackTrace        : 
   at System.Management.Automation.ParameterBinderBase.CoerceTypeAsNeeded(CommandParameterInternal argument, String parameterName, Type toType, ParameterCollectionTypeInformation collectionTypeInfo, Object currentValue)
   at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindNamedParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter)
   at System.Management.Automation.ParameterBinderController.BindNamedParameters(UInt32 parameterSets, Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
   at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
   at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
--- End of stack trace from previous location ---
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
   at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
   at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
CategoryInfo          : InvalidArgument: (:) [Register-PSResourceRepository], ParameterBindingException
FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository
InvocationInfo        : 
    MyCommand        : Register-PSResourceRepository
    ScriptLineNumber : 1
    OffsetInLine     : 166
    HistoryId        : 26
    Line             : Register-PSResourceRepository -Name "FeedDistrict2" -Uri "https://pkgs.dev.azure.com/DigitalDistrict01/_packaging/DigitalDistrict01/nuget/v3/index.json" -Credential $Credential
    PositionMessage  : At line:1 char:166
                       + … kaging/DigitalDistrict01/nuget/v3/index.json" -Credential $Credential
                       +                                                             ~~~~~~~~~~~
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1

Environment data

ModuleType Version    PreRelease Name                                ExportedCommands
---------- -------    ---------- ----                                ----------------
Binary     0.5.22     beta22     Microsoft.PowerShell.PSResourceGet  {Find-PSResource, Get-InstalledPSResource, Get-PSResourceRepository, Get-PSScriptFileInfo…}

Name                           Value
----                           -----
PSVersion                      7.3.4
PSEdition                      Core
GitCommitId                    7.3.4
OS                             Microsoft Windows 10.0.22000
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

youngturk2 commented 1 year ago

The problem is literally in the error message you provided: the "Credential" parameter for this cmdlet doesn't accept a pscredential object like you would expect it to, but instead is looking for a "PSCredentialinfo" object that is native to the psresourceget module (Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo). The actual problem is that Microsoft, in its infinite wisdom, has not created documentation that tells us how to create instances of this new object, since the documentation that they link to on the main github page literally links to help content for the very first version of the module, which is completely unacceptable. I would tell microsoft to get its act together, but it is clear where their priorities lie, and it is not in making this an easy to use, quality module.

youngturk2 commented 1 year ago

I for one, had to use Ilspy to look at the resources defined in the psresourceget module in order to find the answer on my own (thanks for absolutely nothing, Microsoft), and this is what I found:

public PSCredentialInfo(string vaultName, string secretName, PSCredential credential = null)
{
    VaultName = vaultName;
    SecretName = secretName;
    Credential = credential;
}

So, this would indicate to me that, in order to create the pscredentialinfo object, you need to install the Microsoft.PowerShell.SecretManagement and Microsoft.PowerShell.SecretStore powershell modules, which allows you to create local secret vaults in which you can store and pull secrets. But, this is just an educated guess, as, again, microsoft hasn't said a thing about this.

alerickson commented 1 year ago

Hi @youngturk2, there's no need for aggressive comments, you can read the code of conduct we abide by and ask users to abide by as well here: code of conduct. We are currently working on documentation, you can look at the tests in this repository for examples of how to use the PSCredentialInfo object in the meanwhile: PSCredentialInfoTests The parameter also takes a hashtable, so it's not necessary to pass in the object itself.

Stephanevg commented 1 year ago

Hi @alerickson

It was very clear to me that a PsCredentialInfo instance was requested. What surprised me, is the fact that up until now, is that the -Credential has been used on so many different cmdlets, and it standardly asks for an object of type pscredential.

In this case, to me that actually felt like a bug, as I was actually expecting the the parameter -credential to expect a pscredential NOT a PscredentialInfo

Just for completness, I add the extract to the article released on the powershell blog not so long ago:

Azure DevOps Feeds To get the uri for your feed go to dev.azure.com > Artifacts > select desired feed > click “Connect to Feed” > choose “NuGet.exe”. then runRegister-PSResourceRepository -Name "AdoFeedName" -Uri If it is a public feed creds arent needed. If it is a private feed go to dev.azure.com > User Settings in top right corner > Personal Access Tokens > Create PAT token. In your PowerShell terminal, create credential with your username as your email account used for ADO, password will be PAT token from prior step. To use the credential persistence feature of PSResourceGet reference the docs. You can also use with the -Credential parameter.

to me, how it is written there, it sounds like you can either use the persistance feature OR use the (good old) -credential parameter.

In other terms, IF you don't wan't to use the credential persistence feautre, you don't have to. But in this case, it doesn't work I would say.

This would make sense, as it would follow the standard that has been in place since the beginning of powershell. It would become quite a mess and very confusing for the users if suddently we would have official cmdlets that would accept a PsCredential object, and others that would accept PsCredentialInfo.

I feel like the intention of -Credential was not to be able to pass a PsCredentialInfo, but that it was for (the good old) PsCredential type. (which would feel completley logic to me).

MVKozlov commented 1 year ago

may be the good choise will be renaming -Credential to -CredentialInfo to show that they are different types

Stephanevg commented 1 year ago

Yes, I agree that we should rename the -Credential parameter as it brings to much confusion. tagging @SydneyhSmith for visibility.

alerickson commented 1 year ago

I completely agree, -CredentialInfo makes more sense here

alerickson commented 1 year ago

I just took a closer look to address this issue, but Register-PSResourceRepository actually does not have a -Credential parameter, only a -CredentialInfo parameter. Somehow the parameter binding mapped it to CredentialInfo which caused a bit of confusion. So really the issue is that the blog post should have said -CredentialInfo. Sorry for the confusion about that, I should've caught that earlier. If there's any other issues with Credentials, comment here and I can reopen this issue, or feel free to open up a new issue.

Stephanevg commented 1 year ago

Hi, you are right @alerickson , my bad, I didn't see it as well.

I still think there is some confusing around the -CredentialInfo parameter. There seem to be no existing cmdlet that allows one to get the object of type CredentialInfo.

Look at this example:

In order to register a PsRessource registry, I need to pass a PsCredentialInfo, but neither of the cmdlets actually return me an object of that type.

Get-Secret returns a System.Management.Automation.PSCredential Get-SecretInfo returns a Microsoft.PowerShell.SecretManagement.SecretInformation

Maybe I missed it, but the only way I found to get a PsCredentialInfo is via the example below.

$PsCredentialInfo = [Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo]::new("SecretStore","mod_prd")
$url = "https://pkgs.dev.azure.com/MyLongURL"

Register-PSResourceRepository -Name "syn_mod_prd" -Uri $Url -Force -CredentialInfo $PsCredentialInfo -Trusted

I am surprised, as I was really thinking that Get-SecretInfo would give me the right type. (Or perhaps I am doing something wrong?)

I believe this is a bit confusing, as we have 3 different type of credentials now, and the one we actually need for the Register-PsSressourceRepository can only be created using this method above, which might not be very accessible to everyone.

Wouldn't it make sense to have a Get-CredentialInfo cmdlet?

Stephanevg commented 1 year ago

@alerickson could you please re-open the ticket? looks like i cant my self

SydneyhSmith commented 1 year ago

One thing to consider is that -CredentialInfo also takes a hash table like -CredentialInfo @{ VaultName = 'VaultName'; SecretName = 'SecretName' }... but agree that a cmdlet would be a good feature request!

OranguTech commented 6 days ago

Sorry if this appears as just a "+1" comment, but I'd like to re-iterate how confusing the current setup and documentation is. (Found this GH issue searching for similar answers, even though I have the version of the module that at least has the correct parameter name. Somehow I ended up trying to use [Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo] which of course doesn't exist.)

Thinking about this a bit, I think there's two main contributors:

Parameter/Object Type(Class?) name:

CredentialInfo/PSCredentialInfo are misleading names - there's nothing there that would lead me to think it was related to the SecretManagement modules. This is especially problematic because PSCredential is such a commonly used Type.

Why is creating and passing in a new type (that I've never encountered before) even required? (It's fine/preferred to keep supporting it, of course). I'd request/suggest a new parameter set for the PSResourceRepository commands that just takes the same two strings we currently use to create the credentialinfo object.

My personal preference/wish would be to rename PSCredentialInfo to something like PSSecretManagementInfo, or whatever Class is already used in SecretManagementInfo. (I realize that's a bigger lift.)

Documentation:

Having both PowershellGet and PSResourceGet under the same documentation area, combined with how the version selection works is confusing. It's weird, at best, to be at https://learn.microsoft.com/en-us/powershell/gallery/powershellget/how-to/credential-persistence?view=powershellget-2.x , reading about PSResourceGet. I've been working w/ Powershell for around 10 years now, and I was still getting confused - I can't imagine how it must be for someone coming in fresh.

I believe the documentation should better call out:

OranguTech commented 5 days ago

Update to the above, just stumbled upon another PSCredentialInfo class https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.commands.automation.model.credentialinfo - different namespaces, but still.