【问题标题】:Get all AD computers created after a certain date获取在某个日期之后创建的所有 AD 计算机
【发布时间】:2023-03-19 19:24:01
【问题描述】:

我创建了下面的脚本来查找自特定日期以来创建的所有 Unix 计算机。它不会返回错误,但它也不起作用。脚本中只使用了一个日期,但两种格式的结果相同。

两种日期格式都没有失败,但它们不返回任何内容——查询只是显示为正在运行,但从不返回任何数据:

$SomeDate1 = 'Wednesday, November 7, 2018 2:41:59 PM'
$SomeDate2 = '2018-11-07 14:41:59.000'

Get-ADComputer -Filter '*' -Properties *  | Where {$($PSitem.CanonicalName) -like '*Unix*'  -and  $($PSitem.whenCreated) -gt $SomeDate1 } |` 
        Select Name,OperatingSystem,OperatingSystemVersion,ipv4Address,Created,whenCreated,Deleted,whenChanged,Modified,Description,@{Label="ServicePrincipalNames";Expression={$_.ServicePrincipalNames -join ";" }},DisplayName,Location,DistinguishedName,DNSHostName 

【问题讨论】:

  • 我正在为您写一个答案,但同时我建议您查看我的this answer 以了解如何使用-Filter 参数。 ADObjects 上有一个按日期过滤的示例
  • 看起来不错。谢谢
  • 注意CanonicalName 是一个计算属性,因此您还需要使用-Properties 参数显式提供它。你可以通过-Properties *, CanonicalName 来实现这一点,但通常你不想返回-Properties *,因为这会给你点击的 AD 控制器带来过多的负载。
  • 是的,我发现这很困难。我已经介绍了“-Properties *”让它工作。因此,使用“-Properties *,CanonicalName”可以减少服务器负载。我在没有日期的情况下这样做了,它在 21K 服务器上出错。我知道有36K要退回。也许我做这件事的效率不是很高。
  • 我的意思是你应该只指定你想要在返回后操作的属性,而不是所有可用于相关 ADObject 的属性。完全避免使用-Properties *,除了偶尔的调查案例,当您想查看可操作的内容或您确实需要记录ADObject所有静态属性时

标签: powershell powershell-5.1


【解决方案1】:

使用 -Filter 参数来执行此操作,但我们需要做一些工作才能以适合 whenCreated 的格式获取日期,因为它在 AD 架构中的定义与其他一些“日期”不同类型属性。下面的代码将起作用,解释如下:

注意:我在下面简要提到它,但如果你能提供帮助,你不想这样做-Properties *。返回对象的所有静态属性可能会导致您所击中的 DC 上的负载过大。仅指定要返回的属性。有关更详细的说明,请参阅此答案底部的linked answer


# Create a yyyMMddHHmmss.Z formatted date string in UTC
$SomeDate1= ( Get-Date 'Wednesday, November 7, 2018 2:41:59 PM' ).ToUniversalTime().ToString('yyyMMddHHmmss.Z')

# These are the properties we both want to return from AD and include in the final output
$propertiesToReturn =
  "Name",
  "OperatingSystem",
  "OperatingSystemVersion",
  "ipv4Address",
  "Created",
  "whenCreated",
  "Deleted",
  "whenChanged",
  "Modified",
  "Description",
  "DisplayName",
  "Location",
  "DistinguishedName",
  "DNSHostName"

# These are properties we need from AD but are not required directly in the final output
$additionalProperties =
  "CanonicalName",
  "ServicePrincipalNames"

# Define the computed property here for clarity
$spnComputedProperty = @{
  Label = "ServicePrincipalNames";
  Expression = {
    $_.ServicePrincipalNames -join ";"
  } 
}

# Obtain the target computer list and apply your select-object expression to it
Get-ADComputer -Filter "whenCreated -gt '${SomeDate1}'" -Properties ( $propertiesToReturn + $additionalProperties ) | Where-Object {
  $_.CanonicalName -match 'Unix'
} | Select-Object ( $propertiesToReturn + $spnComputedProperty )

现在,这里有很多变化,所以我将解释我做了什么以及为什么:


新变量和对现有变量的更改

  • 我省略了$SomeDate2,因为在您的代码示例中没有以其他方式引用它。
  • $SomeDate1 未定义为 AD 架构中的 Interval 类型,这与 LastLogonDate 等其他日期类型属性不同。它被定义为yyyMMddHHmmss.Z 格式的Generalized-Time 字符串,并且是UTC,因此我们需要以这种方式格式化的时间戳,而不是依赖@ 的默认ToString() 行为987654335@。如果我们不这样做,日期比较将不起作用。这很令人困惑,因为 RSAT AD Cmdlet 将此(和其他通用时间字符串)转换为本地化的 DateTime 字符串,以便在最终返回数据时更轻松地进行 PowerShell 处理。
    • 请注意,yyyMMddHHmmss.Z 不能直接转换回 DateTime 对象以在其他地方使用。
  • 为了清楚起见,我已经定义了从Get-ADComputer 返回的公共属性
    Select-Object 作为一个数组,并定义了另一个数组,其中包含我们只想从中返回的元素AD 进行进一步处理。它们分别是$propertiesToReturn$additionalProperties。这使我们不必在多个地方重新定义这些属性,并允许我们避免代价高昂的
    -Properties * 调用。
    • ServicePrincipalNames 包含在$additionalProperties 下,因为您想将属性值转换为字符串,所以我们不想将它的原始值包含在Select-Object 中。
    • CanonicalName 是一个计算属性,不能在-Filter
      -LDAPFilter 中过滤。我们必须在本地返回并处理此属性,即使您不希望它出现在最终输出中。
    • $propertiesToReturn 下定义的一些属性名称无论如何都会返回,但将它们包含在-Properties 数组中并没有什么坏处。
  • 另外,为了清楚起见,我将Select-Object 的计算属性定义为$spnComputedProperty 变量。这可以在单行上,但为了便于阅读,我在此处将其设置为多行。

用正确的-Filter 调用Get-ADComputer

现在我们的属性数组和日期字符串格式正确,我们终于可以调用
Get-ADComputer

  • 我们可以使用"whenCreated -gt '${SomeDate1}'" 过滤字符串(不要使用ScriptBlock-Filter)返回在$SomeDate1 之后创建的所有ADComputers .
  • 通常我不建议使用+ 连接字符串或数组,但这是一个方便的例外,不太可能导致内存问题。对于Get-ADComputer -Properties,我们将$propertiesToReturn$additionalProperties 作为单个数组提供。
    • 尝试使用语法-Properties $propertiesToReturn, $additionalProperties 将导致类型不匹配错误。
  • 我已将您的Where-Object 子句缩减为仅进一步过滤CanonicalName。如上所述,CanonicalName 是一个计算属性,不能使用
    -Filter-LDAPFilter 进行过滤,必须在此处完成。
    • 我还将-like 更改为-match 并从(技术上的正则表达式)表达式中删除了*,但如果您愿意,您可以将原始-like 子句与glob 一起使用。
  • 最后,我们像以前一样将结果通过管道传输到Select-Object,并执行与Get-ADComputer -Properties 相同的连接技巧。但是,这次我们添加了$spnComputedProperty

这应该为您提供在$SomeDate1 中指定的日期之后创建的所有ADComputersCanonicalName 中的Unix 具有您希望的属性,包括自定义的ServicePrincipalNames 字段。


注意避免将目标 DateTime 转换为 Generalized-Time 格式

从技术上讲,您可以使用以下任一过滤器来避免将您的 DateTime 转换为使用 Generalized-Time 格式:

Get-ADComputer -Filter 'whenCreated -lt $SomeDate1'

# Double-quoted variant is useful if you have other variables which
# should be directly rendered as part of the -Filter string
Get-ADComputer -Filter "whenCreated -lt `$SomeDate1"

我避免提及这一点的原因是因为这种行为没有得到很好的理解或记录。 cmdlet 有一些魔法来获取变量值,即使它不应该被呈现,因为它是一个文字字符串。由于人们对它的理解甚少,也不清楚是什么规则集定义了DateTime 的转换方式(例如,规则是否会根据属性和类型而改变,DateTimes 是否总是被转换为通用时间字符串?我们不知道)我不推荐这种方法。

whenCreated 的行为在 AD Schema 文档中有详细记录,如果我从一开始就查阅这些内容,就会很清楚需要如何制作 -Filter,而不是反复试验我经历了比较问题。文档链接如下。


其他资源

如果您在未正确过滤 AD 属性时遇到奇怪的行为,或者您只是想了解更多关于不同属性的信息,我建议您在 OpenSpecsAD Schema 文档中查找该属性。

另外,请参阅 this answer of mine,其中详细介绍了 RSAT AD cmdlet 上的 -Filter 参数。

【讨论】:

    【解决方案2】:

    这些广告过滤器往往是反复试验。它们是如何工作的并不完全清楚。这对我有用,使用 DateTime 变量、ScriptBlock 过滤器和 Name 而不是 CanonicalName(“错误:传递了使用构造属性的过滤器”)。

    get-adcomputer 的文档使用 -Filter 下的 Backus-Naur 表单中的脚本块显示。

    另一种选择是生成GeneralizedTime(除了需要分钟、秒和句点?)而不是包含时区的日期时间。

    $startdate = [datetime]'3/12/2018'
    
    Get-ADcomputer -Filter {name -like '*computer*' -and 
      whencreated -gt $startDate} -property whencreated,canonicalname
    

    使用字符串过滤器,$startDate 不能有多余的引号。整个表达式不能双引号。我在其他情况下也看到过这项工作。

    Get-ADcomputer -Filter "name -like '*computer*' -and 
      whencreated -gt '$startDate'" -property whencreated,canonicalname
    # error or no result
    

    日期不能是DateTime的字符串版本。

    Get-ADcomputer -Filter "name -like '*computer*' -and 
      whencreated -gt '3/12/2018 12:00:00 AM'" -property whencreated,canonicalname
    # no result
    

    一个有效的字符串过滤器:

    $startdate = [datetime]'3/12/2018'
    
    # get string version of scriptblock
    { name -like "*computer*" -and whencreated -gt $startdate }.tostring()  
     name -like "*computer*" -and whencreated -gt $startdate
    
    Get-ADcomputer -Filter 'name -like "*computer*" -and 
      whencreated -gt $startDate' -property whencreated,canonicalname
    

    【讨论】:

    • 是的,它会起作用,但这是一个非常糟糕的习惯。 AD cmdlet 实际上并不支持脚本块,当您尝试类似Import-Csv C:\userInfoWithEmails.csv | Foreach-Object { Get-ADUser -Filter { EmailAddress -eq $_.Email } } 时,不可避免地会导致混淆
    • @BendertheGreatest Foreach-Object { $email = $_.email; Get-ADUser -Filter { EmailAddress -eq $_.Email } } 问题是没有人了解这个过滤器的黑匣子是如何工作的。这是反复试验。
    • 但是,除了 AD cmdlet 和架构之外,在需要字符串的地方使用脚本块并教导不良的编码习惯是一个糟糕的选择。这是语法问题,而不是 AD 混淆。
    • @js2010 您的回答仍然包含不正确的信息。你可以使用双引号字符串,但你必须转义$,这样它就不会被PowerShell渲染到提供的过滤器字符串中。您的第二个代码块有一些不应该存在的额外位。如果您尝试在过滤器字符串本身中对其进行过滤,您也只会在构造属性上遇到错误,但您仍然可以将属性向下传递并通过Where-Object 或其他方式对其进行过滤,而不会出现问题。 OP 仍然可以根据 CanonicalName 管理返回的对象,只是不在 cmdlet 本身内。
    • @js2010 当网站的模组对你的回答做出改变时:这几乎总是符合网站的最佳利益,因为它们是由社区选出的。您为还原所做的编辑具有破坏性,因为您当前的答案有不需要的评论,应该清理。我鼓励您恢复到更好的修订版之一,并尊重此类评论应该被排除在帖子之外。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多