Powershell #{} Pipeline Error - powershell

I have two custom powershell functions meant to be used like:
Get-Users | Select LoginName | Get-UserJobStatus -StartDate 2013-02-19 -EndDate 2013-02-21
When this is ran, I receive an error from the web service:
#{LoginName=UserName} not found
Which makes sense, because the username that's passed to the web service function should be just UserName and not #{LoginName=username}. How do I format that correctly?
function Global:Get-BackupJobStatus
{
[cmdletBinding()]
Param(
[Parameter(
Mandatory=$True,
HelpMessage='Specific account name.',
ParameterSetName='LoginName',
ValueFromPipeline=$True
)]
[System.String[]]$LoginName = $null,
[Parameter(
Mandatory=$false,
HelpMessage='Start date for report.'
)]
[System.DateTime]$startDate,
[Parameter(
Mandatory=$false,
HelpMessage='Ending date for report.'
)]
[System.DateTime]$EndDate
)
Begin {
$tempXml = New-Object System.Xml.XmlDocument
Write-Output $StartDate.toString("yyyy-MM-dd")
}
Process{
$tempXml.Load("https://webservicecall/info.do?LoginName="+$LoginName+"&Date="+$startDate.toString("yyyy-MM-dd"))
}
End {
Write-Output $tempXml.Users.User
}
}

The problem is that your function is expecting a string-object as the loginname input and you are sending a pscustomobject(created by select) with a property called loginname. To fix this, use select-object's -ExpandProperty parameter to send the value of loginnameinstead of objects with that property. Like this:
Get-Users | Select -ExpandProperty LoginName |
Get-UserJobStatus -StartDate 2013-02-19 -EndDate 2013-02-21

Alternately, you could add ValueFromPipelineByPropertyName=$true to allow you to bind the LoginName property of the object in the pipeline to the $LoginName parameter

If you're going to use "ValuefromPipeline", then it needs to be just the value:
Get-Users | Select -ExpandProperty LoginName | Get-UserJobStatus -StartDate 2013-02-19 -EndDate 2013-02-21

Related

How to set the description of a local group to blank

Consider the following code:
# create test group
New-LocalGroup -Name 'Group1' -Description 'xxx'
# update test group description to blank
Set-LocalGroup -Name 'Group1' -Description '' # fails
Set-LocalGroup -Name 'Group1' -Description $null # fails
On the contrary it is possible to create a group without description:
New-LocalGroup -Name 'Group2'
How is it possible to update the group description of a local group to blank without removing the group first? This happens on PowerShell 5.1.
Although there are some AD attributes that requires the Putex method, that doesn't count for the Description attribute. Meaning my assumption in the initial comment to the question is wrong, and it is possible to clear the Description attribute with just the Put method`:
$Name = 'Group1'
New-LocalGroup -Name $Name -Description 'xxx'
$Group = [ADSI]"WinNT://./$Name,group"
$Group.Put('Description', '')
$Group.SetInfo()
Get-LocalGroup -Name $Name
Name Description
---- -----------
Group1
The issue lays purely in the cmdlet parameter definitions. Without going into the C# programming, you might just pull this from the proxy command:
$Command = Get-Command Set-LocalGroup
$MetaData = [System.Management.Automation.CommandMetadata]$Command
$ProxyCommand = [System.Management.Automation.ProxyCommand]::Create($MetaData)
$ProxyCommand
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium', HelpUri='https://go.microsoft.com/fwlink/?LinkId=717979')]
param(
[Parameter(Mandatory=$true)]
[ValidateNotNull()] # <-- Here is the issue
[ValidateLength(0, 48)]
[string]
${Description},
...
In other words, to quick and dirty workaround this with a proxy command:
function SetLocalGroup {
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium', HelpUri='https://go.microsoft.com/fwlink/?LinkId=717979')]
param(
[Parameter(Mandatory=$true)]
[AllowEmptyString()] # <-- Modified
[ValidateLength(0, 48)]
[string]
${Description},
[Parameter(ParameterSetName='InputObject', Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[ValidateNotNull()]
[Microsoft.PowerShell.Commands.LocalGroup]
${InputObject},
[Parameter(ParameterSetName='Default', Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[ValidateNotNull()]
${Name},
[Parameter(ParameterSetName='SecurityIdentifier', Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[ValidateNotNull()]
[System.Security.Principal.SecurityIdentifier]
${SID})
end {
if ($Description) { Set-LocalGroup #PSBoundParameters }
elseif ($Name) {
$Group = [ADSI]"WinNT://./$Name,group"
$Group.Put('Description', '')
$Group.SetInfo()
}
}
}
SetLocalGroup -Name 'Group1' -Description ''
Related bug report: #16049 AllowEmptyString()] for -Description in Set-LocalGroup/SetLocalUser
As Set-LocalGroup fails on that, the only other way I can think of is using ADSI:
$group = [ADSI]"WinNT://$env:COMPUTERNAME/Group1,group"
$group.Description.Value = [string]::Empty
$group.CommitChanges()
It's a workaround of course and I agree with iRon you should do a bug report on this.

cmdlet for getting information about which computer locks out AD account

I need to find out which computer is calling for locking out my account. I can do it in GUI by opening the event viewer and finding a log event in security log, but that's time consuming and since in our environment this does happen very frequently, I need a faster solution. I wrote this command:
Get-EventLog security -ComputerName DC -InstanceId 4740 | ? {$_.Message -like "MyUserName"} | FL
I tried also -match instead of -like but neither gave any results. Anyone knows which operator to use to get what I need?
Like #vonPryz I would normally use the altools, but it responds with the DC the account was locked out on and then I came across the code below at https://thesysadminchannel.com/get-account-lock-out-source-powershell/ looks like just the ticket, one of the prerequisites is to allow remote event view access on the DC's to be queried.
Code from the site copied below, should it go offline, all credit to Paul at the SysAdmin Channel:
#requires -Module ActiveDirectory
#Import-Module ActiveDirectory -EA Stop
Function Get-AccountLockoutStatus {
<#
.Synopsis
This will iterate through all your domain controllers by default and checks for event 4740 in event viewer. To use this, you must dot source the file and call the function.
For updated help and examples refer to -Online version.
.DESCRIPTION
This will go through all domain controllers by default and check to see if there are event ID for lockouts and display the information in table with Username, Time, Computername and CallerComputer.
For updated help and examples refer to -Online version.
.NOTES
Name: Get-AccountLockoutStatus
Author: The Sysadmin Channel
Version: 1.01
DateCreated: 2017-Apr-09
DateUpdated: 2017-Apr-09
.LINK
https://thesysadminchannel.com/get-account-lock-out-source-powershell -
.PARAMETER ComputerName
By default all domain controllers are checked. If a computername is specified, it will check only that.
.PARAMETER Username
If a username is specified, it will only output events for that username.
.PARAMETER DaysFromToday
This will set the number of days to check in the event logs. Default is 3 days.
.EXAMPLE
Get-AccountLockoutStatus
Description:
Will generate a list of lockout events on all domain controllers.
.EXAMPLE
Get-AccountLockoutStatus -ComputerName DC01, DC02
Description:
Will generate a list of lockout events on DC01 and DC02.
.EXAMPLE
Get-AccountLockoutStatus -Username Username
Description:
Will generate a list of lockout events on all domain controllers and filter that specific user.
.EXAMPLE
Get-AccountLockoutStatus -DaysFromToday 2
Description:
Will generate a list of lockout events on all domain controllers going back only 2 days.
#>
[CmdletBinding()]
param(
[Parameter(
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string[]] $ComputerName = (Get-ADDomainController -Filter * | select -ExpandProperty Name),
[Parameter()]
[string] $Username,
[Parameter()]
[int] $DaysFromToday = 3
)
BEGIN {
$Object = #()
}
PROCESS {
Foreach ($Computer in $ComputerName) {
try {
$EventID = Get-WinEvent -ComputerName $Computer -FilterHashtable #{Logname = 'Security'; ID = 4740; StartTime = (Get-Date).AddDays(-$DaysFromToday)} -EA 0
Foreach ($Event in $EventID) {
$Properties = #{Computername = $Computer
Time = $Event.TimeCreated
Username = $Event.Properties.value[0]
CallerComputer = $Event.Properties.value[1]
}
$Object += New-Object -TypeName PSObject -Property $Properties | Select ComputerName, Username, Time, CallerComputer
}
} catch {
$ErrorMessage = $Computer + " Error: " + $_.Exception.Message
} finally {
if ($Username) {
Write-Output $Object | Where-Object {$_.Username -eq $Username}
} else {
Write-Output $Object
}
$Object = $null
}
}
}
END {}
}

Thycotic Secret Server Pass Creds to Powershell

I am trying to get credentials straight out of Thycotic Secret Server through powershell instead of copying and pasting each and every username/password. Has anyone came across this before?
I wrote a function just for this type of thing.
Mandatory fields are
-Webservice which needs to be point to sswebservice.asmx usually located as https://{Base Address}/webservices/sswebservice.asmx
-Credential which is your Thycotic login.
-searchTerm which is the string you are searching for.
function Get-Secret{
Param (
[Parameter(Mandatory=$False)]
[string] $WebService,
[Parameter(Mandatory=$True)]
[pscredential] $Credential,
[string] $Organization = $Null,
[Parameter(Mandatory=$True)]
[string] $SearchTerm = $Null,
[Parameter(ParameterSetName='Only',Mandatory=$false)]
[switch] $CountOnly,
[Parameter(ParameterSetName='Only',Mandatory=$false)]
[switch] $SummeryOnly,
[switch] $Raw
)
$Service = New-WebServiceProxy -uri $WebService -UseDefaultCredential
$LoginResult = $Service.Authenticate($($Credential.GetNetworkCredential().Username), $($Credential.GetNetworkCredential().Password), $Organization, $($Credential.GetNetworkCredential().Domain))
if($LoginResult.errors){
throw $LoginResult.errors
return
}
$Secret_IDs = $Service.SearchSecrets($LoginResult.token, $searchTerm, $true, $true)
if($Secret_IDs.errors){
throw $Secret_IDs.errors
return
}
if($CountOnly){
return $Secret_IDs.SecretSummaries.count
}
if($SummeryOnly){
return $Secret_IDs.SecretSummaries
}
$Response = #()
foreach($Secret_ID in $Secret_IDs.SecretSummaries){
$Secret = $Service.GetSecret($LoginResult.token, $Secret_ID.SecretID, $false, $null).secret
$Response += $Secret
}
if($Raw){
return $Response
}else{
return $Response | Foreach-object{
Write-Output "$($_.Name)"
Foreach($item in $_.Items){
Write-Output "$($item.FieldDisplayName) : $($item.Value)"
}
Write-Output "`r`n"
}
}
}
Basic Usage
Get-Secret -WebService "https://Stuff/sswebservice.asmx" -Credential $ThycoticCredentials -SearchTerm "HELLO"
Other usages is the parameter -raw. This will return an object based on the return from Thycotic.
You can narrow down to the field items
Get-Secret -WebService "https://Stuff/sswebservice.asmx" -Credential $ThycoticCredentials -SearchTerm "HELLO" -raw | select -ExpandProperty Items
And even narrow down to values (this one getting field Username)
Get-Secret -WebService "https://Stuff/sswebservice.asmx" -Credential $ThycoticCredentials -SearchTerm "HELLO" -raw | select -ExpandProperty Items | ?{$_.fieldname -like 'username'} | select -ExpandProperty value

How to easily create PSObject from function arguments to return result to pipeline?

Currently I'm doing like that:
function Invoke-Service
{
Param(
[parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[int] $Id,
[parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string] $Name,
[parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[int] $Age
)
DoSomeWork |
New-Object PSObject -Property #{ Id = $Id; Name = $Name; Age = $Age }
}
This function can obtain it's parameters directly or from Import-Csv output, or select output.
But very often I want to continue processing down the pipeline with all the power of PSObject:
Import-Csv -Path "data.csv" |
Invoke-Service |
... #
And my question is: do I need to call New-Object enumerating all parameters or is there a keyword or other technique which I missed?
Use the $PSBoundParameters variable:
New-Object psobject -Property $PSBoundParameters
$PSBoundParameters is an automatic variable that contains a hashtable with an entry per named parameter, using the full parameter name as the key and the parameter argument as the value, so in your example it's already 100% aligned with the input you're trying to pass

Powershell gwmi for other users

I am trying to collect user profile information for users on a machine and I was wondering if I could get it with gwmi. Here is how I get printers for the current user:Get-WmiObject win32_printer. How can I get the same info for the user "Test" on the same machine?
As it happens, I can't sleep, so I came up with these 2 functions:
function Get-UserSid {
[CmdletBinding()]
param(
[Parameter(
ParameterSetName='NTAccount',
Mandatory=$true,
ValueFromPipeline=$true,
Position=0
)]
[System.Security.Principal.NTAccount]
$Identity ,
[Parameter(
ParameterSetName='DomainAndUser',
Mandatory=$true
)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('^[^\\]+$')]
[String]
$Domain ,
[Parameter(
ParameterSetName='DomainAndUser',
Mandatory=$true
)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('^[^\\]+$')]
[String]
$User
)
Begin {
if ($PSCmdlet.ParameterSetName -eq 'DomainAndUser') {
$Identity = New-Object System.Security.Principal.NTAccount -ArgumentList $Domain,$User
}
}
Process {
$Identity.Translate([System.Security.Principal.SecurityIdentifier])
}
}
function Get-PrinterNameByUser {
[CmdletBinding(DefaultParameterSetName='Ambiguous')]
param(
[Parameter(
ParameterSetName='ByAccount',
Mandatory=$true
)]
[System.Security.Principal.NTAccount]
$Account ,
[Parameter(
ParameterSetName='BySID',
Mandatory=$true
)]
[System.Security.Principal.SecurityIdentifier]
$SID ,
[Parameter(
ParameterSetName='Ambiguous',
Mandatory=$true,
Position=0,
ValueFromPipeline=$true
)]
[ValidateNotNullOrEmpty()]
[String]
$Identity
)
Begin {
Write-Verbose "Parameter Set Name: $($PSCmdlet.ParameterSetName)"
if ($PSCmdlet.ParameterSetName -eq 'ByAccount') {
$SID = $Account | Get-UserSid
}
}
Process {
if ($PSCmdlet.ParameterSetName -eq 'Ambiguous') {
try {
$SID = [System.Security.Principal.SecurityIdentifier]$Identity
} catch [System.InvalidCastException] {
$Account = [System.Security.Principal.NTAccount]$Identity
$SID = $Account | Get-UserSid
}
}
Get-ChildItem -Path "Registry::\HKEY_Users\$($SID.Value)\Printers" | Select-Object -ExpandProperty Property -Unique
}
}
Usage
Get-PrinterNameByUser Test
Get-PrinterNameByUser 'domain\test'
Get-PrinterNameByUser 'S-1-S-21-65454546-516413534-4444'
All of those could be piped as well:
'Test' | Get-PrinterNameByUser
'domain\test' | Get-PrinterNameByUser
'S-1-S-21-65454546-516413534-4444' | Get-PrinterNameByUser
'S-1-S-21-65454546-516413534-4444','user1','machine\user2','domain\user3' | Get-PrinterNameByUser
Explanation
In the registry at HKU\S-ID-HERE\Printers there are some keys with properties. The property names are the printers. I wasn't able to test this on enough machines, so I wasn't certain which key(s) I should check, and whether they would be different depending on whether it was a local or network printer, etc., so I'm just getting the properties from all the keys and returning the unique ones.
The helper function Get-UserSid just provides a convenient way to get a SID from a user name.
Most of Get-PrinterNameByUser is just code to figure out what you've given it and translate it at needed. The meat of it that returns what you want is just the one line:
Get-ChildItem -Path "Registry::\HKEY_Users\$($SID.Value)\Printers" | Select-Object -ExpandProperty Property -Unique