From 536794f77657af44e52eec8b9d3260a6bc6e76d7 Mon Sep 17 00:00:00 2001 From: Pete Maan Date: Tue, 19 Sep 2023 19:43:11 +0100 Subject: [PATCH] 0.1 - Update 2 (#4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update deploy-github.ps1 Lowered minimum version to publish GitHub release. * ✨🚩UPDATE New-IDSession Adds saml authentication support, when providing SAML assertion from an external IDP * Update CHANGELOG.md --- CHANGELOG.md | 13 ++- .../Private/Complete-SamlAuthentication.ps1 | 60 +++++++++++ IdentityCommand/Private/Get-IDResponse.ps1 | 26 +++-- .../Private/Start-SamlAuthentication.ps1 | 78 ++++++++++++++ IdentityCommand/Public/New-IDSession.ps1 | 94 +++++++++++----- .../en-US/IdentityCommand-help.xml | 74 ++++++++++++- Tests/Complete-SamlAuthentication.Tests.ps1 | 92 ++++++++++++++++ Tests/Get-IDResponse.Tests.ps1 | 6 ++ Tests/Start-SamlAuthentication.Tests.ps1 | 100 ++++++++++++++++++ build/deploy-github.ps1 | 4 +- docs/collections/_commands/New-IDSession.md | 37 ++++++- 11 files changed, 539 insertions(+), 45 deletions(-) create mode 100644 IdentityCommand/Private/Complete-SamlAuthentication.ps1 create mode 100644 IdentityCommand/Private/Start-SamlAuthentication.ps1 create mode 100644 Tests/Complete-SamlAuthentication.Tests.ps1 create mode 100644 Tests/Start-SamlAuthentication.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d6bd21..07eff8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Change Log All notable changes to this project will be documented in this file. -## [unreleased] - 2023-08-30 +## [unreleased] - 2023-09-19 ### Added - N/A @@ -12,6 +12,17 @@ All notable changes to this project will be documented in this file. ### Fixed - N/A +## [0.1 - Update 2] - 2023-09-19 + +### Added +- N/A + +### Changed +- `New-IDSession` - Adds federated authentication support, with ability to provide a SamlResponse from an external IDP + +### Fixed +- N/A + ## [0.1 - Update 1] - 2023-08-30 Additional Functions diff --git a/IdentityCommand/Private/Complete-SamlAuthentication.ps1 b/IdentityCommand/Private/Complete-SamlAuthentication.ps1 new file mode 100644 index 0000000..0263a6c --- /dev/null +++ b/IdentityCommand/Private/Complete-SamlAuthentication.ps1 @@ -0,0 +1,60 @@ +Function Complete-SamlAuthentication { + <# + .SYNOPSIS + Completes a saml authentication request + + .DESCRIPTION + Complete the SAML authentication session against CyberArk Identity. + + This request utilizes the cookies returned after Start-SamlAuthentication. + The CyberArk ISPSS tenant should respond and set additional cookies that are used for subsequent authentication. + + .PARAMETER LogonRequest + The LogonRequest created via New-IDSession + + .EXAMPLE + $LogonRequest | Complete-SamlAuthentication + + Complete the SAML authentication process, started by Start-SamlAuthentication. + + .NOTES + Pete Maan 2023 + #> + + [CmdletBinding(SupportsShouldProcess)] + param( + [parameter( + Mandatory = $true, + ValueFromPipeline = $true + )] + [ValidateNotNullOrEmpty()] + [hashtable]$LogonRequest + ) + + process { + + #Setup request. This command will return html, so supress output/html error detection + $Script:ExpectHtml = $true + $LogonRequest['Method'] = 'GET' + $LogonRequest['Uri'] = "$Script:tenant_url/login" + + if ($PSCmdlet.ShouldProcess($Script:tenant_url, 'Send Assertion')) { + + try { + + #Perform Start Authentication + $IDSession = Invoke-IDRestMethod @LogonRequest + + #Output IDSession + $IDSession + + } catch { throw $PSItem } + + } + + $Script:ExpectHtml = $false + #TODO: Check if sucesful auth or error + + } + +} \ No newline at end of file diff --git a/IdentityCommand/Private/Get-IDResponse.ps1 b/IdentityCommand/Private/Get-IDResponse.ps1 index fca45ed..2f5d20f 100644 --- a/IdentityCommand/Private/Get-IDResponse.ps1 +++ b/IdentityCommand/Private/Get-IDResponse.ps1 @@ -49,20 +49,30 @@ function Get-IDResponse { If ($IDResponse -match '') { - #Fail if HTML received from request to API + If ($Script:ExpectHtml) { + #HTML output expected, null the html result + $IDResponse = $null - $PSCmdlet.ThrowTerminatingError( + } + + Else { + + #Fail if HTML received from request to API + + $PSCmdlet.ThrowTerminatingError( - [System.Management.Automation.ErrorRecord]::new( + [System.Management.Automation.ErrorRecord]::new( - 'Unexpected HTML Response Received. Check the URL provided for your Identity Portal.', - $StatusCode, - [System.Management.Automation.ErrorCategory]::NotSpecified, - $APIResponse + 'Unexpected HTML Response Received. Check the URL provided for your Identity Portal.', + $StatusCode, + [System.Management.Automation.ErrorCategory]::NotSpecified, + $APIResponse + + ) ) - ) + } } diff --git a/IdentityCommand/Private/Start-SamlAuthentication.ps1 b/IdentityCommand/Private/Start-SamlAuthentication.ps1 new file mode 100644 index 0000000..860b14b --- /dev/null +++ b/IdentityCommand/Private/Start-SamlAuthentication.ps1 @@ -0,0 +1,78 @@ +Function Start-SamlAuthentication { + <# + .SYNOPSIS + Starts SAML user authentication + + .DESCRIPTION + Start SAML user authentication against CyberArk Identity. + + When the user wants to authenticate to CyberArk Identity providing a SAML Response. + Successful response should contain the following cookies: .ASPXAUTH, antixss, CCSID, podloc, sessdata, userdata + Returned cookies will be saved in the WebSession object used by the module for future operations. + + .PARAMETER LogonRequest + The LogonRequest created via New-IDSession + + .PARAMETER SAMLResponse + Credential object containing username and password required to authenticate to CyberArk Identity. + + .EXAMPLE + $LogonRequest | Start-SamlAuthentication SAMLResponse $SAMLResponse + + Start the SAML authentication process using the specified SAMLResponse. + + .NOTES + Pete Maan 2023 + #> + + [CmdletBinding(SupportsShouldProcess)] + param( + [parameter( + Mandatory = $true, + ValueFromPipeline = $true + )] + [ValidateNotNullOrEmpty()] + [hashtable]$LogonRequest, + + #SAML Assertion + [Parameter( + Mandatory = $true, + ValueFromPipelinebyPropertyName = $true + )] + [ValidateNotNullOrEmpty()] + [string]$SAMLResponse + ) + + process { + + #Setup request. This command will return html, so supress output/html error detection + $Script:ExpectHtml = $true + $LogonRequest['ContentType'] = 'application/x-www-form-urlencoded' + $LogonRequest['Uri'] = "$Script:tenant_url/my" + + $LogonRequest['Body'] = @{ + + SAMLResponse = $SAMLResponse + + } + + if ($PSCmdlet.ShouldProcess($Script:tenant_url, 'Send SAML Assertion')) { + + try { + + #Perform Start Authentication + $IDSession = Invoke-IDRestMethod @LogonRequest + + #Output IDSession + $IDSession + + } catch { throw $PSItem } + + } + + $Script:ExpectHtml = $false + #TODO: Check for expected cookies + + } + +} \ No newline at end of file diff --git a/IdentityCommand/Public/New-IDSession.ps1 b/IdentityCommand/Public/New-IDSession.ps1 index abc9b4e..15ed0eb 100644 --- a/IdentityCommand/Public/New-IDSession.ps1 +++ b/IdentityCommand/Public/New-IDSession.ps1 @@ -15,10 +15,20 @@ Function New-IDSession { #User Creds [Parameter( Mandatory = $true, - ValueFromPipelinebyPropertyName = $true + ValueFromPipelinebyPropertyName = $true, + ParameterSetName = 'Credential' )] [ValidateNotNullOrEmpty()] - [PSCredential]$Credential + [PSCredential]$Credential, + + #SAML Assertion + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelinebyPropertyName = $true, + ParameterSetName = 'SAML' + )] + [String]$SAMLResponse ) @@ -45,58 +55,84 @@ Function New-IDSession { $LogonRequest['Headers'] = @{'accept' = '*/*' } - #*Start Authentication - $IDSession = $LogonRequest | Start-Authentication -Credential $Credential + switch ($PSCmdlet.ParameterSetName) { + + 'Credential' { + #*Start Authentication + $IDSession = $LogonRequest | Start-Authentication -Credential $Credential + break + } + 'SAML' { + #*Send SAML Assertion + $IDSession = $LogonRequest | Start-SamlAuthentication -SAMLResponse $SAMLResponse + break + } + } - #Set request properties for Advance. + #Set request properties for Next authentication stage. $LogonRequest.Remove('SessionVariable') $LogonRequest['Headers'].Add('X-IDAP-NATIVE-CLIENT', $true) #Set Module Scope variables Set-Variable -Name TenantId -Value $IDSession.TenantId -Scope Script Set-Variable -Name SessionId -Value $IDSession.SessionId -Scope Script + #? does SessionId need to be available in script scope? - #The MFA Bit - keep a reference to $IDSession for the MFA Package - $ThisSession = $IDSession - for ($Challenge = 0; $Challenge -lt $(($ThisSession.Challenges).Count); $Challenge++) { + switch ($PSCmdlet.ParameterSetName) { - #Iterate through presented challenges - if ($($IDSession.Summary) -eq 'NewPackage') { + 'Credential' { - #Initialise loop and $ThisSession if NewPackage Challenges are presented - $Challenge = 0 + #The MFA Bit - keep a reference to $IDSession for the MFA Package $ThisSession = $IDSession - if ($null -ne $ThisSession.EventDescription) { Write-Warning -Message $ThisSession.EventDescription } + for ($Challenge = 0; $Challenge -lt $(($ThisSession.Challenges).Count); $Challenge++) { - } + #Iterate through presented challenges + if ($($IDSession.Summary) -eq 'NewPackage') { - #Get Current Challenge Mechanisms - $Mechanisms = $ThisSession.Challenges[$Challenge] | Select-Object -ExpandProperty Mechanisms + #Initialise loop and $ThisSession if NewPackage Challenges are presented + $Challenge = 0 + $ThisSession = $IDSession + if ($null -ne $ThisSession.EventDescription) { Write-Warning -Message $ThisSession.EventDescription } - #select challenge mechanism - $Mechanism = Select-ChallengeMechanism -Mechanisms $Mechanisms + } - try { + #Get Current Challenge Mechanisms + $Mechanisms = $ThisSession.Challenges[$Challenge] | Select-Object -ExpandProperty Mechanisms - #answer challenge mechanism - $Answer = Get-MechanismAnswer -Mechanism $Mechanism -Credential $Credential + #select challenge mechanism + $Mechanism = Select-ChallengeMechanism -Mechanisms $Mechanisms - #*Advance Authentication - $IDSession = $LogonRequest | Start-AdvanceAuthentication -Mechanism $Mechanism -Answer $Answer + try { - } catch { + #answer challenge mechanism + $Answer = Get-MechanismAnswer -Mechanism $Mechanism -Credential $Credential - throw $PSItem + #*Advance Authentication + $IDSession = $LogonRequest | Start-AdvanceAuthentication -Mechanism $Mechanism -Answer $Answer - } + } catch { + + throw $PSItem + + } + + if ($($IDSession.Summary) -eq 'NewPackage') { - if ($($IDSession.Summary) -eq 'NewPackage') { + #New Package Recieved, decrement counter so we go round the loop again to evaluate. + $Challenge-- - #New Package Recieved, decrement counter so we go round the loop again to evaluate. - $Challenge-- + } + } + + break } + 'SAML' { + #*Get Saml ID Token + $IDSession = $LogonRequest | Complete-SamlAuthentication + break + } } switch ($IDSession.Summary) { diff --git a/IdentityCommand/en-US/IdentityCommand-help.xml b/IdentityCommand/en-US/IdentityCommand-help.xml index 194017a..128d979 100644 --- a/IdentityCommand/en-US/IdentityCommand-help.xml +++ b/IdentityCommand/en-US/IdentityCommand-help.xml @@ -1234,7 +1234,8 @@ Use this command to authenticate to CyberArk Identity. Allows a user to provide authentication details, and satisfy any required MFA challenges. - Currently supports all authentication mechanisms except U2F & DUO. + Currently supports all Identity MFA authentication mechanisms except U2F & DUO. + Supports federated authentication when providing a SamlAssertion from a configured external IDP. @@ -1286,6 +1287,55 @@ False + + New-IDSession + + tenant_url + + The URL of the CyberArk Identity tenant to authenticate to. + + String + + String + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + SAMLResponse + + A SAML assertion obtained from an external IDP for the account with which to authenticate. + + String + + String + + + None + + @@ -1336,6 +1386,18 @@ None + + SAMLResponse + + A SAML assertion obtained from an external IDP for the account with which to authenticate. + + String + + String + + + None + @@ -1374,11 +1436,19 @@ -------------------------- Example 1 -------------------------- PS C:\> $Cred = Get-Credential -PS C:\> New-IDSession -tenant_url https://some.tenant.cyberark.cloud -Credential $Cred +PS C:\> New-IDSession -tenant_url https://sometenant.id.cyberark.cloud -Credential $Cred Initiates authentication to specified tenant, and allows selection and answer of any required MFA challenges. + + -------------------------- Example 2 -------------------------- + PS C:\> $SAMLResponse = New-SAMLInteractive -LoginIDP "https://1234.idp.com/app/cyberarkidentity/i5d7/sso/saml" +PS C:\> New-IDSession -tenant_url https://sometenant.id.cyberark.cloud -SAMLResponse $SAMLResponse + + Authenticates to CyberArk Identity using SAML assertion from external IDP (obtained using PS-SAML-Interactive) + + diff --git a/Tests/Complete-SamlAuthentication.Tests.ps1 b/Tests/Complete-SamlAuthentication.Tests.ps1 new file mode 100644 index 0000000..d259dab --- /dev/null +++ b/Tests/Complete-SamlAuthentication.Tests.ps1 @@ -0,0 +1,92 @@ +Describe $($PSCommandPath -Replace '.Tests.ps1') { + + BeforeAll { + #Get Current Directory + $Here = Split-Path -Parent $PSCommandPath + + #Assume ModuleName from Repository Root folder + $ModuleName = Split-Path (Split-Path $Here -Parent) -Leaf + + #Resolve Path to Module Directory + $ModulePath = Resolve-Path "$Here\..\$ModuleName" + + #Define Path to Module Manifest + $ManifestPath = Join-Path "$ModulePath" "$ModuleName.psd1" + + if ( -not (Get-Module -Name $ModuleName -All)) { + + Import-Module -Name "$ManifestPath" -ArgumentList $true -Force -ErrorAction Stop + + } + + } + + InModuleScope $(Split-Path (Split-Path (Split-Path -Parent $PSCommandPath) -Parent) -Leaf ) { + + BeforeEach { + $Script:Version = '1.0' + $Script:tenant_url = 'https://somedomain.id.cyberark.cloud' + $Script:WebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession + $LogonRequest = @{ } + $LogonRequest['Method'] = 'POST' + $LogonRequest['SessionVariable'] = 'IDSession' + $LogonRequest['Headers'] = @{'accept' = '*/*' } + $Creds = New-Object System.Management.Automation.PSCredential ('SomeUser', $(ConvertTo-SecureString 'SomePassword' -AsPlainText -Force)) + + Mock Invoke-IDRestMethod -MockWith { + + } + + } + + Context 'Input' { + + It 'sends request' { + $LogonRequest | Complete-SamlAuthentication + Assert-MockCalled Invoke-IDRestMethod -Times 1 -Exactly -Scope It + + } + + It 'sends request to expected endpoint' { + $LogonRequest | Complete-SamlAuthentication + Assert-MockCalled Invoke-IDRestMethod -ParameterFilter { + + $URI -eq 'https://somedomain.id.cyberark.cloud/login' + + } -Times 1 -Exactly -Scope It + + } + + It 'uses expected method' { + $LogonRequest | Complete-SamlAuthentication + Assert-MockCalled Invoke-IDRestMethod -ParameterFilter { $Method -match 'GET' } -Times 1 -Exactly -Scope It + + } + + It 'throws on error' { + Mock Invoke-IDRestMethod -MockWith { + throw 'Some Error' + } + { $LogonRequest | Complete-SamlAuthentication } | Should -Throw -ExpectedMessage 'Some Error' + + } + + } + + Context 'Output' { + + It 'provides no output' { + + Mock Invoke-IDRestMethod -MockWith { + + } + + $LogonRequest | Complete-SamlAuthentication | Should -BeNullOrEmpty + + } + + } + + } + +} \ No newline at end of file diff --git a/Tests/Get-IDResponse.Tests.ps1 b/Tests/Get-IDResponse.Tests.ps1 index e4ab3ae..49336a1 100644 --- a/Tests/Get-IDResponse.Tests.ps1 +++ b/Tests/Get-IDResponse.Tests.ps1 @@ -75,6 +75,12 @@ Describe $($PSCommandPath -Replace '.Tests.ps1') { { Get-IDResponse -APIResponse $HTMLResponse } | Should -Throw } + It 'has no output if HTML expetted' { + $Script:ExpectHtml = $true + Get-IDResponse -APIResponse $HTMLResponse | Should -BeNullOrEmpty + $Script:ExpectHtml = $false + } + } } diff --git a/Tests/Start-SamlAuthentication.Tests.ps1 b/Tests/Start-SamlAuthentication.Tests.ps1 new file mode 100644 index 0000000..c102a79 --- /dev/null +++ b/Tests/Start-SamlAuthentication.Tests.ps1 @@ -0,0 +1,100 @@ +Describe $($PSCommandPath -Replace '.Tests.ps1') { + + BeforeAll { + #Get Current Directory + $Here = Split-Path -Parent $PSCommandPath + + #Assume ModuleName from Repository Root folder + $ModuleName = Split-Path (Split-Path $Here -Parent) -Leaf + + #Resolve Path to Module Directory + $ModulePath = Resolve-Path "$Here\..\$ModuleName" + + #Define Path to Module Manifest + $ManifestPath = Join-Path "$ModulePath" "$ModuleName.psd1" + + if ( -not (Get-Module -Name $ModuleName -All)) { + + Import-Module -Name "$ManifestPath" -ArgumentList $true -Force -ErrorAction Stop + + } + + } + + InModuleScope $(Split-Path (Split-Path (Split-Path -Parent $PSCommandPath) -Parent) -Leaf ) { + + BeforeEach { + $Script:Version = '1.0' + $Script:tenant_url = 'https://somedomain.id.cyberark.cloud' + $Script:WebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession + $LogonRequest = @{ } + $LogonRequest['Method'] = 'POST' + $LogonRequest['SessionVariable'] = 'IDSession' + $LogonRequest['Headers'] = @{'accept' = '*/*' } + $SamlResponse = 'SomeSamlResponse' + + Mock Invoke-IDRestMethod -MockWith { + + } + + } + + Context 'Input' { + + It 'sends request' { + $LogonRequest | Start-SamlAuthentication -SamlResponse $SamlResponse + Assert-MockCalled Invoke-IDRestMethod -Times 1 -Exactly -Scope It + + } + + It 'sends request to expected endpoint' { + $LogonRequest | Start-SamlAuthentication -SamlResponse $SamlResponse + Assert-MockCalled Invoke-IDRestMethod -ParameterFilter { + + $URI -eq 'https://somedomain.id.cyberark.cloud/my' + + } -Times 1 -Exactly -Scope It + + } + + It 'uses expected method' { + $LogonRequest | Start-SamlAuthentication -SamlResponse $SamlResponse + Assert-MockCalled Invoke-IDRestMethod -ParameterFilter { $Method -match 'POST' } -Times 1 -Exactly -Scope It + + } + + It 'sends body with expected SamlResponse' { + $LogonRequest | Start-SamlAuthentication -SamlResponse $SamlResponse + Assert-MockCalled Invoke-IDRestMethod -ParameterFilter { + $Body.SamlResponse -eq 'SomeSamlResponse' + } -Times 1 -Exactly -Scope It + + } + + It 'throws on error' { + Mock Invoke-IDRestMethod -MockWith { + throw 'Some Error' + } + { $LogonRequest | Start-SamlAuthentication -SamlResponse $SamlResponse } | Should -Throw -ExpectedMessage 'Some Error' + + } + + } + + Context 'Output' { + + It 'provides no output' { + + Mock Invoke-IDRestMethod -MockWith { + + } + + $LogonRequest | Start-SamlAuthentication -SamlResponse $SamlResponse | Should -BeNullOrEmpty + + } + + } + + } + +} \ No newline at end of file diff --git a/build/deploy-github.ps1 b/build/deploy-github.ps1 index 0b7ec04..7731ad6 100644 --- a/build/deploy-github.ps1 +++ b/build/deploy-github.ps1 @@ -48,7 +48,7 @@ if (-not ($ENV:APPVEYOR_PULL_REQUEST_NUMBER)) { Write-Host 'Deploy Process: GitHub Release' -ForegroundColor Yellow - If ($env:APPVEYOR_BUILD_VERSION -ge '1.0.0') { + If ($env:APPVEYOR_BUILD_VERSION -ge '0.1.0') { <# Create New Release #> @@ -102,7 +102,7 @@ if (-not ($ENV:APPVEYOR_PULL_REQUEST_NUMBER)) { <# Not Master Branch #> Write-Host "$ENV:APPVEYOR_REPO_BRANCH Branch; No Release" -ForegroundColor Cyan - exit; + exit } diff --git a/docs/collections/_commands/New-IDSession.md b/docs/collections/_commands/New-IDSession.md index af0c7c9..8793501 100644 --- a/docs/collections/_commands/New-IDSession.md +++ b/docs/collections/_commands/New-IDSession.md @@ -12,27 +12,43 @@ Authenticates a user to a CyberArk Identity tenant. ## SYNTAX +### Credential ``` New-IDSession [-tenant_url] [-Credential] [-WhatIf] [-Confirm] [] ``` +### SAML +``` +New-IDSession [-tenant_url] -SAMLResponse [-WhatIf] [-Confirm] [] +``` + ## DESCRIPTION Use this command to authenticate to CyberArk Identity. Allows a user to provide authentication details, and satisfy any required MFA challenges. -Currently supports all authentication mechanisms except U2F & DUO. +Currently supports all Identity MFA authentication mechanisms except U2F & DUO. + +Supports federated authentication when providing a SamlAssertion from a configured external IDP. ## EXAMPLES ### Example 1 ``` PS C:\> $Cred = Get-Credential -PS C:\> New-IDSession -tenant_url https://some.tenant.cyberark.cloud -Credential $Cred +PS C:\> New-IDSession -tenant_url https://sometenant.id.cyberark.cloud -Credential $Cred ``` Initiates authentication to specified tenant, and allows selection and answer of any required MFA challenges. +### Example 2 +``` +PS C:\> $SAMLResponse = New-SAMLInteractive -LoginIDP "https://1234.idp.com/app/cyberarkidentity/i5d7/sso/saml" +PS C:\> New-IDSession -tenant_url https://sometenant.id.cyberark.cloud -SAMLResponse $SAMLResponse +``` + +Authenticates to CyberArk Identity using SAML assertion from external IDP (obtained using PS-SAML-Interactive) + ## PARAMETERS ### -Confirm @@ -55,7 +71,7 @@ Credential object containing username and password required to authenticate to C ```yaml Type: PSCredential -Parameter Sets: (All) +Parameter Sets: Credential Aliases: Required: True @@ -96,6 +112,21 @@ Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` +### -SAMLResponse +A SAML assertion obtained from an external IDP for the account with which to authenticate. + +```yaml +Type: String +Parameter Sets: SAML +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).