当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 更好的 PowerShell 属性

作者:精品下载站 日期:2024-12-14 08:07:16 浏览:14 分类:玩电脑

更好的 PowerShell 属性


我最近和我的朋友 Gladys Kravitz 谈论了她使用 Active Directory 和 DirectorySearcher 对象编写的一些 PowerShell 脚本。由于多种原因,远程服务器管理工具 (RSAT) 中的 Active Directory 模块不是一个选项。但那很好。 Gladys 是一位经验丰富的 AD 管理员和 PowerShell 脚本编写者。尽管如此,她对命令的输出仍有疑问,这导致了一些有趣的工作,您可能会发现这些工作很有价值。尽管我将讨论特定的对象类型,但您可以将几个原则应用于 PowerShell 脚本编写。

搜索结果属性

让我们从一些 PowerShell 代码开始,使用 DirectorySearcher 对象查找用户帐户。

$searcher = New-Object System.DirectoryServices.DirectorySearcher
$searcher.filter ="(&(objectcategory=person)(objectclass=user))"
$searcher.FindOne().Properties

DirectorySearcher 具有 FindOne() 和 FindAll() 方法。生成的对象具有 Properties 属性。

[玩转系统] 更好的 PowerShell 属性

您可以通过多种方式使用它。

New-Object -typename PSObject -property $searcher.FindOne().Properties | Select Name,Description,WhenChanged

[玩转系统] 更好的 PowerShell 属性

将值的格式放在一边,请注意属性名称。功能齐全,但外观不专业。这始终是一个选择。

New-Object -typename PSObject -property $searcher.FindOne().Properties | 
Select-Object @{Name="Name";Expression = {$_.name}},
@{Name="Description";Expression = {$_.Description}},
@{Name="WhenChanged";Expression = {$_.WhenChanged}}

或者,您可以使用类型名称定义自定义对象并使用自定义格式文件。但这是一项繁重的工作,而且不灵活。

这是创建对象的另一种方法,但具有正确大小写的属性名称。

$r = $searcher.FindOne()
$r.Properties.GetEnumerator() | 
Where-Object { $_.key -match "Name|Description|WhenChanged|memberof|logoncount" } | 
ForEach-Object -Begin {
    #initialize an empty hashtable
    $h = @{}
} -Process {
    #convert to title case
    $n = [cultureinfo]::CurrentCulture.TextInfo.ToTitleCase($_.name.tolower())
    if ($_.value.count -gt 1) {
        #get the array of values
        $v = $_.value
    }
    else {
        #get the single value
        $v = $_.value[0]
    }
    #add the property name and value to the hashtable
    $h.Add($n, $v)
} -End { 
    #create a custom object from the hashtable
    [pscustomobject]$h 
}

在这种特殊情况下,我要格式化的对象是 DirectoryServices.ResultCollection,它本质上是一个哈希表,因此需要不同的方法。 GetEnumerator() 方法将集合中的每个元素写入具有 Key 和 Value 属性的对象。我的示例是过滤特定“属性”的键。

然后将过滤后的对象通过管道传送到 ForEach-Object。在管道中处理任何对象之前,我初始化一个空的哈希表。接下来,对于每个过滤结果,我将名称(“key”的别名)转换为标题大小写。为此,我总是在调用该方法之前将字符串全部小写。此重新格式化的名称和相应的值将添加到哈希表中。处理完所有内容后,我将哈希表转换为自定义对象。

[玩转系统] 更好的 PowerShell 属性

这是有希望的,但需要更加灵活。 ForEach-Object 结构与采用管道输入的高级 PowerShell 函数的格式相同。换句话说,我有一个原型。

创建配置数据

我的想法是获取 DirectorySearcher 结果并创建一个自定义对象,用格式正确的名称和值替换属性名称。但在查看输出时,我想要的不仅仅是标题大小写。 “Logoncount”的格式可能看起来更好,如“LogonCount”。我更喜欢 SamAccountName。我想用格式正确的名称替换传入的属性名称。我需要按照我想要的方式格式化所有可能的属性名称的列表。因为我知道我最终会希望将此列表用作哈希表,所以我将创建一个配置数据文件,稍后可以将其与 Import-PowerShellDataFile 一起使用。

下面是根据对象类型生成不同配置数据文件的 PowerShell 脚本。

#requires -version 5.1

<# Create-ADPropertyFile.ps1
 proof-of-concept code to create an Active Directory property configuration data file
 .\Create-ADPropertyFile.ps1 -Filter "(&(objectclass=organizationalunit)(Name=Employees))" -FilePath .\adou.psd1
 .\Create-ADPropertyFile.ps1 Group -FilePath .\adgroup.psd1
#>

[cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = "byType")]
Param(
    [Parameter(Position = 0, ParameterSetName = "byType")]
    [ValidateSet("User", "Group", "Computer", "OU")]
    [string]$ObjectType = "User",

    [Parameter(HelpMessage = "Specify an LDAP search filter to a template object.", ParameterSetName = "byFilter")]
    [ValidateNotNullOrEmpty()]
    [string]$Filter,

    [Parameter(Mandatory, HelpMessage = "Specify the filename and path to the psd1 file.")]
    [ValidateScript({ Test-Path (Split-Path $_) })]
    [string]$FilePath
)

if ($pscmdlet.ParameterSetName -eq "byType") {

    Switch ($ObjectType) {
        "User" { $filter = "(&(objectcategory=person)(objectclass=user))" }
        "Group" { $filter = "(Objectclass=group)" }
        "Computer" { $filter = "(objectclass=computer)" }
        "OU" { $filter = "(objectclass=organizationalunit)" }
    }
}

$searcher = New-Object System.DirectoryServices.DirectorySearcher
$searcher.filter = $filter
#don't need values from the search
$searcher.PropertyNamesOnly = $true
$searcher.FindOne().Properties.GetEnumerator() | Sort-Object -Property Key |
ForEach-Object -Begin {
    #initialize a list
    $list = [System.Collections.Generic.List[string]]::new()
    $list.add("@{")
} -Process {
    $value = [cultureinfo]::CurrentCulture.TextInfo.ToTitleCase($($_.key.ToLower()))
    #add each property to the list
    $List.Add("$($_.key) = '$value'")
} -End {
    #close the psd1
    $list.Add("}")
}
#save the list to a file
$list | Out-File -FilePath $FilePath

#edit psd1 file with your desired formatting

默认过滤器将找到第一个匹配结果。但是,DirectorySearcher 将仅返回已定义的属性名称。您可能希望创建一个模板对象,其中包含您打算使用的已定义的所有属性,然后使用自定义 LDAP 过滤器构建属性文件到那个物体。

我将使用该脚本为用户对象创建 psd1 文件。

c:\scripts\Create-ADPropertyFile.ps1 -ObjectType User -FilePath c:\scripts\aduser.psd1

我可以编辑文件并调整属性名称。

@{
    accountexpires         = 'AccountExpires'
    admincount             = 'AdminCount'
    adspath                = 'AdsPath'
    badpasswordtime        = 'BadPasswordTime'
    badpwdcount            = 'BadPwdCount'
    cn                     = 'CN'
    codepage               = 'CodePage'
    countrycode            = 'CountryCode'
    department             = "Department"
    description            = 'Description'
    displayname            = "DisplayName"
    distinguishedname      = 'DistinguishedName'
    dscorepropagationdata  = 'DscorePropagationData'
    givenname              = "GivenName"
    instancetype           = 'InstanceType'
    iscriticalsystemobject = 'IsCriticalSystemObject'
    lastlogoff             = 'LastLogoff'
    lastlogon              = 'Lastlogon'
    lastlogontimestamp     = 'LastLogonTimestamp'
    logoncount             = 'LogonCount'
    logonhours             = 'LogonHours'
    memberof               = 'MemberOf'
    name                   = 'Name'
    objectcategory         = 'ObjectCategory'
    objectclass            = 'ObjectClass'
    objectguid             = 'ObjectGuid'
    objectsid              = 'ObjectSid'
    primarygroupid         = 'PrimaryGroupId'
    pwdlastset             = 'PwdlastSet'
    samaccountname         = 'SamAccountName'
    samaccounttype         = 'SamAccountType'
    sn                     = "Surname"
    title                  = "Title"
    useraccountcontrol     = 'UserAccountcontrol'
    usnchanged             = 'UsnChanged'
    usncreated             = 'UsnCreated'
    whenchanged            = 'WhenChanged'
    whencreated            = 'WhenCreated'
}

}

如果您知道其他 LDAP 属性名称,则可以手动添加它们。最后一步是使用该文件。

优化广告搜索结果

我们倾向于避免在 PowerShell 中使用单一命令。相反,我们想利用管道。我有使用 DirectorySearcher 来查找对象的代码。

[玩转系统] 更好的 PowerShell 属性

我想利用这些结果并优化属性名称。

Function Optimize-ADSearchResult {
    [cmdletbinding()]
    Param(
        [Parameter(
            Position = 0,
            Mandatory,
            ValueFromPipeline,
            HelpMessage = "This should be the input from an ADSearcher FindOne() or FindAll() method."
        )]
        [System.DirectoryServices.SearchResult]$InputObject,

        [Parameter(
            Mandatory,
            HelpMessage = "Enter the path to the psd1 file with your property names. See Create-ADPropertyFile."
        )]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ })]
        [ValidatePattern('\.psd1$')]
        [string]$ConfigurationData,

        [Parameter(HelpMessage = "Specify a custom type name, like CorpADUser. You might add this if using a custom format file or type extensions.")]
        [ValidateNotNullOrEmpty()]
        [string]$TypeName
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        #import the configuration data
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Importing configuration data from $(Convert-Path $ConfigurationData)"
        $PropertyData = Import-PowerShellDataFile -Path $ConfigurationData
    } #begin
    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing input"

        $InputObject.properties.GetEnumerator() |
        ForEach-Object -Begin {
            $new = [ordered]@{ }
            if ($TypeName) {
                Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Using typename $TypeName"
                $new.Add("PSTypename", $TypeName)
            }
        } -Process {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing property $($_.key)"
            #Get formatted property name from configuration data
            if ($PropertyData.Contains($_.key)) {
                $name = $PropertyData["$($_.key)"]
            }
            else {
                $name = $_.key
            }
            if ($_.value.count -gt 1) {
                $value = $_.value
            }
            else {
                $value = $_.value[0]
            }
            $new.Add($name, $value)
        } -End {
            Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating output"
            New-Object -TypeName psobject -Property $new
        }
    } #process
    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end
}

该函数查看每个搜索结果并尝试匹配配置数据中的属性名称。如果找到匹配项,就会使用它。否则,PowerShell 将使用原始属性名称。

我可以将所有属性名称哈希表存储在函数中,但这会增加复杂性和不必要的长度。如果我需要添加属性,我必须编辑该函数。将数据与实现它的代码分开要好得多。我的函数是使用配置数据的一个很好的例子。

我可以获取搜索结果,使用此功能对其进行优化,然后选择我需要的内容。

 $searcher = New-Object System.DirectoryServices.DirectorySearcher
 $searcher.filter = "(&(objectcategory=person)(objectclass=user)(department=sales))"
 $searcher.FindAll() | 
 Optimize-ADSearchResult -ConfigurationData C:\scripts\aduser.psd1 |
 Select-Object -property DistinguishedName,Givenname,Surname,Description,Title

[玩转系统] 更好的 PowerShell 属性

概括

这篇文章中发生了很多事情——许多移动部分,可能还有一些新命令。我希望您能检查一下代码,并至少在脑子里走一遍。请随时留下评论和问题。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯