Function PowerShell-导出Csv:无法将参数绑定到参数';InputObject';因为它是空的
我正在使用我在网上找到的一个PowerShell函数,它非常有效。在将其合并到针对给定域中的Windows服务器使用的脚本中时,我将输出导出为.csv。它确实可以工作,但是在脚本运行的整个过程中,我发现了关于“InputObject”的错误,我只是想清理一下,但我不知道问题出在哪里。感谢您的帮助。下面是我使用的函数,然后是我使用该函数的脚本,然后是我得到的错误 功能Function PowerShell-导出Csv:无法将参数绑定到参数';InputObject';因为它是空的,function,powershell,csv,Function,Powershell,Csv,我正在使用我在网上找到的一个PowerShell函数,它非常有效。在将其合并到针对给定域中的Windows服务器使用的脚本中时,我将输出导出为.csv。它确实可以工作,但是在脚本运行的整个过程中,我发现了关于“InputObject”的错误,我只是想清理一下,但我不知道问题出在哪里。感谢您的帮助。下面是我使用的函数,然后是我使用该函数的脚本,然后是我得到的错误 功能 Function Get-LocalGroupMembership { <# .SYNOPSIS
Function Get-LocalGroupMembership {
<#
.SYNOPSIS
Recursively list all members of a specified Local group.
.DESCRIPTION
Recursively list all members of a specified Local group. This can be run against a local or
remote system or systems. Recursion is unlimited unless specified by the -Depth parameter.
Alias: glgm
.PARAMETER Computername
Local or remote computer/s to perform the query against.
Default value is the local system.
.PARAMETER Group
Name of the group to query on a system for all members.
Default value is 'Administrators'
.PARAMETER Depth
Limit the recursive depth of a query.
Default value is 2147483647.
.PARAMETER Throttle
Number of concurrently running jobs to run at a time
Default value is 10
#>
[cmdletbinding()]
Param (
[parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('CN','__Server','Computer','IPAddress')]
[string[]]$Computername = $env:COMPUTERNAME,
[parameter()]
[string]$Group = "Administrators",
[parameter()]
[int]$Depth = ([int]::MaxValue),
[parameter()]
[Alias("MaxJobs")]
[int]$Throttle = 10
)
Begin {
$PSBoundParameters.GetEnumerator() | ForEach {
Write-Verbose $_
}
#region Extra Configurations
Write-Verbose ("Depth: {0}" -f $Depth)
#endregion Extra Configurations
#Define hash table for Get-RunspaceData function
$runspacehash = @{}
#Function to perform runspace job cleanup
Function Get-RunspaceData {
[cmdletbinding()]
param(
[switch]$Wait
)
Do {
$more = $false
Foreach($runspace in $runspaces) {
If ($runspace.Runspace.isCompleted) {
$runspace.powershell.EndInvoke($runspace.Runspace)
$runspace.powershell.dispose()
$runspace.Runspace = $null
$runspace.powershell = $null
} ElseIf ($runspace.Runspace -ne $null) {
$more = $true
}
}
If ($more -AND $PSBoundParameters['Wait']) {
Start-Sleep -Milliseconds 100
}
#Clean out unused runspace jobs
$temphash = $runspaces.clone()
$temphash | Where {
$_.runspace -eq $Null
} | ForEach {
Write-Verbose ("Removing {0}" -f $_.computer)
$Runspaces.remove($_)
}
} while ($more -AND $PSBoundParameters['Wait'])
}
#region ScriptBlock
$scriptBlock = {
Param ($Computer,$Group,$Depth,$NetBIOSDomain,$ObjNT,$Translate)
$Script:Depth = $Depth
$Script:ObjNT = $ObjNT
$Script:Translate = $Translate
$Script:NetBIOSDomain = $NetBIOSDomain
Function Get-LocalGroupMember {
[cmdletbinding()]
Param (
[parameter()]
[System.DirectoryServices.DirectoryEntry]$LocalGroup
)
# Invoke the Members method and convert to an array of member objects.
$Members= @($LocalGroup.psbase.Invoke("Members"))
$Counter++
ForEach ($Member In $Members) {
Try {
$Name = $Member.GetType().InvokeMember("Name", 'GetProperty', $Null, $Member, $Null)
$Path = $Member.GetType().InvokeMember("ADsPath", 'GetProperty', $Null, $Member, $Null)
# Check if this member is a group.
$isGroup = ($Member.GetType().InvokeMember("Class", 'GetProperty', $Null, $Member, $Null) -eq "group")
If (($Path -like "*/$Computer/*")) {
$Type = 'Local'
} Else {$Type = 'Domain'}
New-Object PSObject -Property @{
Computername = $Computer
Name = $Name
Type = $Type
ParentGroup = $LocalGroup.Name[0]
isGroup = $isGroup
Depth = $Counter
}
If ($isGroup) {
# Check if this group is local or domain.
#$host.ui.WriteVerboseLine("(RS)Checking if Counter: {0} is less than Depth: {1}" -f $Counter, $Depth)
If ($Counter -lt $Depth) {
If ($Type -eq 'Local') {
If ($Groups[$Name] -notcontains 'Local') {
$host.ui.WriteVerboseLine(("{0}: Getting local group members" -f $Name))
$Groups[$Name] += ,'Local'
# Enumerate members of local group.
Get-LocalGroupMember $Member
}
} Else {
If ($Groups[$Name] -notcontains 'Domain') {
$host.ui.WriteVerboseLine(("{0}: Getting domain group members" -f $Name))
$Groups[$Name] += ,'Domain'
# Enumerate members of domain group.
Get-DomainGroupMember $Member $Name $True
}
}
}
}
} Catch {
$host.ui.WriteWarningLine(("GLGM{0}" -f $_.Exception.Message))
}
}
}
Function Get-DomainGroupMember {
[cmdletbinding()]
Param (
[parameter()]
$DomainGroup,
[parameter()]
[string]$NTName,
[parameter()]
[string]$blnNT
)
Try {
If ($blnNT -eq $True) {
# Convert NetBIOS domain name of group to Distinguished Name.
$objNT.InvokeMember("Set", "InvokeMethod", $Null, $Translate, (3, ("{0}{1}" -f $NetBIOSDomain.Trim(),$NTName)))
$DN = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $Translate, 1)
$ADGroup = [ADSI]"LDAP://$DN"
} Else {
$DN = $DomainGroup.distinguishedName
$ADGroup = $DomainGroup
}
$Counter++
ForEach ($MemberDN In $ADGroup.Member) {
$MemberGroup = [ADSI]("LDAP://{0}" -f ($MemberDN -replace '/','\/'))
New-Object PSObject -Property @{
Computername = $Computer
Name = $MemberGroup.SamAccountName[0]
Type = 'Domain'
ParentGroup = $NTName
isGroup = ($MemberGroup.Class -eq "group")
Depth = $Counter
}
# Check if this member is a group.
If ($MemberGroup.Class -eq "group") {
If ($Counter -lt $Depth) {
If ($Groups[$MemberGroup.name[0]] -notcontains 'Domain') {
Write-Verbose ("{0}: Getting domain group members" -f $MemberGroup.name[0])
$Groups[$MemberGroup.name[0]] += ,'Domain'
# Enumerate members of domain group.
Get-DomainGroupMember $MemberGroup $MemberGroup.Name[0] $False
}
}
}
}
} Catch {
$host.ui.WriteWarningLine(("GDGM{0}" -f $_.Exception.Message))
}
}
#region Get Local Group Members
$Script:Groups = @{}
$Script:Counter=0
# Bind to the group object with the WinNT provider.
$ADSIGroup = [ADSI]"WinNT://$Computer/$Group,group"
Write-Verbose ("Checking {0} membership for {1}" -f $Group,$Computer)
$Groups[$Group] += ,'Local'
Get-LocalGroupMember -LocalGroup $ADSIGroup
#endregion Get Local Group Members
}
#endregion ScriptBlock
Write-Verbose ("Checking to see if connected to a domain")
Try {
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Root = $Domain.GetDirectoryEntry()
$Base = ($Root.distinguishedName)
# Use the NameTranslate object.
$Script:Translate = New-Object -comObject "NameTranslate"
$Script:objNT = $Translate.GetType()
# Initialize NameTranslate by locating the Global Catalog.
$objNT.InvokeMember("Init", "InvokeMethod", $Null, $Translate, (3, $Null))
# Retrieve NetBIOS name of the current domain.
$objNT.InvokeMember("Set", "InvokeMethod", $Null, $Translate, (1, "$Base"))
[string]$Script:NetBIOSDomain =$objNT.InvokeMember("Get", "InvokeMethod", $Null, $Translate, 3)
} Catch {Write-Warning ("{0}" -f $_.Exception.Message)}
#region Runspace Creation
Write-Verbose ("Creating runspace pool and session states")
$sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
$runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host)
$runspacepool.Open()
Write-Verbose ("Creating empty collection to hold runspace jobs")
$Script:runspaces = New-Object System.Collections.ArrayList
#endregion Runspace Creation
}
Process {
ForEach ($Computer in $Computername) {
#Create the powershell instance and supply the scriptblock with the other parameters
$powershell = [powershell]::Create().AddScript($scriptBlock).AddArgument($computer).AddArgument($Group).AddArgument($Depth).AddArgument($NetBIOSDomain).AddArgument($ObjNT).AddArgument($Translate)
#Add the runspace into the powershell instance
$powershell.RunspacePool = $runspacepool
#Create a temporary collection for each runspace
$temp = "" | Select-Object PowerShell,Runspace,Computer
$Temp.Computer = $Computer
$temp.PowerShell = $powershell
#Save the handle output when calling BeginInvoke() that will be used later to end the runspace
$temp.Runspace = $powershell.BeginInvoke()
Write-Verbose ("Adding {0} collection" -f $temp.Computer)
$runspaces.Add($temp) | Out-Null
Write-Verbose ("Checking status of runspace jobs")
Get-RunspaceData @runspacehash
}
}
End {
Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@(($runspaces | Where {$_.Runspace -ne $Null}).Count)))
$runspacehash.Wait = $true
Get-RunspaceData @runspacehash
#region Cleanup Runspace
Write-Verbose ("Closing the runspace pool")
$runspacepool.close()
$runspacepool.Dispose()
#endregion Cleanup Runspace
}
}
Set-Alias -Name glgm -Value Get-LocalGroupMembership
我得到的错误
$Servers = Get-ADComputer `
-searchbase ‘OU=RDS Servers,OU=Production,OU=Servers,DC=xxx,DC=xxxx,DC=xxx’ `
-properties OperatingSystem -Filter * | `
Where-Object {$_.operatingsystem -Like "*Windows*"} | select name
foreach ($Server in $Servers) {
Get-LocalGroupMembership -Computername $Server.Name | `
Export-Csv D:\Scripts\ServerLocalAdmin\Reports\LocalAdmins.csv `
-Append -NoTypeInformation
}
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
At D:\Scripts\ServerLocalAdmin\ServerLocalAdmin.ps1:5 char:87
+ ... rver.Name | Export-Csv D:\Scripts\ServerLocalAdmin\Reports\LocalAdmin ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.Exp
ortCsvCommand
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
At D:\Scripts\ServerLocalAdmin\ServerLocalAdmin.ps1:5 char:87
+ ... rver.Name | Export-Csv D:\Scripts\ServerLocalAdmin\Reports\LocalAdmin ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.Exp
ortCsvCommand
由于您正在遍历包含服务器的对象,而我们无法访问该数据,因此您可能应该尝试在foreach循环中添加一个输出,以查看对象中的哪些项是问题所在。
听起来好像Get-LocalGroupMembership正在为您的某些服务器返回空值。您只需更改以下内容:
foreach ($Server in $Servers)
{
Get-LocalGroupMembership -Computername $Server.Name |
Export-Csv D:\Scripts\ServerLocalAdmin\Reports\LocalAdmins.csv -Append -NoTypeInformation
}
为此:
foreach ($Server in $Servers)
{
$localMembership = Get-LocalGroupMembership -Computername $Server.Name
if($localMembership)
{
$localMembership | Export-Csv D:\Scripts\ServerLocalAdmin\Reports\LocalAdmins.csv -Append -NoTypeInformation
}
}
或者这个:
foreach ($Server in $Servers)
{
$localMembership = Get-LocalGroupMembership -Computername $Server.Name
if(-not $localMembership){continue}
$localMembership | Export-Csv D:\Scripts\ServerLocalAdmin\Reports\LocalAdmins.csv -Append -NoTypeInformation
}
正如@Zucchini提供的答案中所述,您得到的错误是,因为函数为您的一些服务器返回了$null
值。要重现错误,您可以尝试:
PS /~> $null | Export-Csv test.csv
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
At line:1 char:9
+ $null | Export-Csv test.csv
+ ~~~~~~~~~~~~~~~~~~~
...
值得一提的是,在每次循环迭代时将结果附加到CSV是非常低效的,脚本的磁盘I/O越少,其运行速度就越快:)
因此,这是如何使脚本运行得更快的一个示例:
$result = [system.collections.generic.list[pscustomobject]]::new()
foreach ($Server in $Servers)
{
$localMembership = Get-LocalGroupMembership -Computername $Server.Name
if($localMembership)
{
$result.Add($localMembership)
}
}
$result | Export-Csv D:\Scripts\ServerLocalAdmin\Reports\LocalAdmins.csv -NoTypeInformation
编辑:
下面是一个工作示例,函数在任何地方都返回空值
$result = [system.collections.generic.list[pscustomobject]]::new()
foreach ($Server in $Servers)
{
$localMembership = Get-LocalGroupMembership -Computername $Server.Name
foreach($i in $localMembership|?{$_})
{
$result.Add([pscustomobject]@{
Name=$i.Name
Depth=$i.Depth
ParentGroup=$i.ParentGroup
Type=$i.Type
ComputerName=$i.ComputerName
isGroup=$i.isGroup
})
}
}
$result | Export-Csv D:\Scripts\ServerLocalAdmin\Reports\LocalAdmins.csv -NoTypeInformation
我明天会尝试一下,并让你知道,我感谢你的帮助。所以我仔细阅读了你的每一条建议。首先,我得到了与我的原始脚本相同的问题。对于第二个建议,我也得到了同样的行为。对于您提到的第三个建议,我没有发现任何错误,但是输出不正确。它看起来像下面的…“Count”、“Length”、“LongLength”、“Rank”、“SyncRoot”、“IsReadOnly”、“IsFixedSize”、“IsSynchronized”、“178”、“178”、“178”、“178”、“1”、“System.Object[]”、“False”、“True”、“False”、“False”、“185”、“185”、“1”、“System.Object[]”、“False”、“True”、“False”我将尝试并测试从一个csv文件(我知道正在连接)中运行的服务器列表。我正在测试的OU总共有13台服务器。最终,我将运行另一个包含近1500台服务器的OU。我知道有些问题可能是WinRM。这是一场试图确保允许通过多个子网和防火墙进行连接的战斗。我将与我们的网络团队在这方面进行更多的合作。在导入我知道是从.csv文件连接的服务器时,我也会遇到同样的错误。因此,对于无法连接的服务器来说,这似乎不是一个问题。这一定是我假设的函数本身。最后,我知道它是有效的,我只是希望通过消除错误来加快速度。我曾经在我所有的服务器上运行过一次,大约需要12个小时。总的来说,这个功能是惊人的打破了所有用户添加到本地管理组。