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

[玩转系统] 使用 PowerShell 和事件日志的 CIM-ple 方式

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

使用 PowerShell 和事件日志的 CIM-ple 方式


我一直在寻找新的做事方式。我经常尝试找到一种方法来创建易于使用的东西,而不需要大量的 PowerShell 脚本。我也喜欢使用最终结果作为教学辅助工具,因此即使您不需要最终产品,我也希望您能学到一两个可以在自己的脚本项目中使用的技巧。我今天想到的任务是获取事件日志信息的更好方法。不是事件本身,而是事件日志文件。里面有多少条目?它有多大?正在使用多少配置的日志?这就是我的想法。

我们一直有 Get-Eventlog cmdlet,它将使用 -List 参数提供一些此类信息。

[玩转系统] 使用 PowerShell 和事件日志的 CIM-ple 方式

每个对象都有更多内容,您可以使用带有 Select-Object 的 PowerShell 表达式来获得所需的结果。您还可以编写一个函数来简化该过程。但如果您必须走这条路,我建议找到一种使用 CIM 或 PowerShell 远程处理的方法。 PowerShell 中的大部分远程访问都是通过旧协议完成的,我相信我们应该努力在 WsMan 上做更多的事情。由于事件日志文件是通过 WMI 通过 Win32_NTEventLogFile 公开的,因此我们可以使用 Get-CimInstance 来检索它们。

[玩转系统] 使用 PowerShell 和事件日志的 CIM-ple 方式

是的,输出不同。但是通过查看属性,我可以创建一个函数,以便更轻松地查询自定义对象并将其写入具有更多相关信息的管道。

[玩转系统] 使用 PowerShell 和事件日志的 CIM-ple 方式

在我的测试中,这也比使用 Get-Eventlog 要快得多。想知道我是如何做到这一点以及它是如何运作的吗?从 GitHub 获取脚本文件的副本。

Get-EventLogFile.ps1:

#requires -version 3.0


Function Get-EventLogFile {

 Get-EventLogFile -computername chi-p50

Computername    : CHI-P50
Log             : Application
NumberOfRecords : 1300
Path            : C:\WINDOWS\System32\Winevt\Logs\Application.evtx
SizeMB          : 1.07
MaxSizeMB       : 20
PctUsed         : 5.33
LastModified    : 6/15/2016 9:37:19 AM
ModifiedAge     : 05:16:23.2285115

Computername    : CHI-P50
Log             : DNS Server
NumberOfRecords : 535
Path            : C:\WINDOWS\System32\Winevt\Logs\DNS Server.evtx
SizeMB          : 1.07
MaxSizeMB       : 100
PctUsed         : 1.07
LastModified    : 6/15/2016 9:37:20 AM
ModifiedAge     : 05:16:21.5171060
...
.EXAMPLE
PS C:\> Get-CimSession | Get-EventLogFile | Sort PctUsed -descending | Out-Gridview -title "Event Logs"

.EXAMPLE
PS C:\> Get-EventLogFile -Name application -Computername chi-web02


Computername    : CHI-WEB02
LogName         : Application
NumberOfRecords : 7174
Path            : C:\Windows\System32\Winevt\Logs\Application.evtx
SizeMB          : 5.07
MaxSizeMB       : 20
PctUsed         : 25.33
LastModified    : 6/7/2016 10:36:07 AM
ModifiedAge     : 8.04:28:42.4684355

.EXAMPLE
PS C:\> Get-EventlogFile -computername chi-dc04,chi-dc01,chi-p50 -name "DNS Server" | Format-Table -group @{Name="Computer";Expression={"$($_.Computername) - $($_.Path)"}} -property *Size*,PctUsed,NumberOfRecords

   Computer: CHI-DC04 - C:\Windows\System32\Winevt\Logs\DNS Server.evtx

SizeMB MaxSizeMB PctUsed NumberOfRecords
------ --------- ------- ---------------
  1.07        16    6.67            1503


   Computer: CHI-P50 - C:\WINDOWS\System32\Winevt\Logs\DNS Server.evtx

SizeMB MaxSizeMB PctUsed NumberOfRecords
------ --------- ------- ---------------
  1.07       100    1.07             535


   Computer: CHI-DC01 - C:\Windows\System32\Winevt\Logs\DNS Server.evtx

SizeMB MaxSizeMB PctUsed NumberOfRecords
------ --------- ------- ---------------
  1.07        16    6.67            2283

.EXAMPLE
PS C:\> Get-EventLogFile chi-core01 -ListOnly

Computername LogName                NumberOfRecords
------------ -------                ---------------
CHI-CORE01   Application                      33834
CHI-CORE01   HardwareEvents                       0
CHI-CORE01   Internet Explorer                    0
CHI-CORE01   Key Management Service               0
CHI-CORE01   Operations Manager               32190
CHI-CORE01   Security                         97428
CHI-CORE01   System                          106947
CHI-CORE01   Windows PowerShell               11273

.EXAMPLE
PS C:\> Get-CimSession | Get-EventLogFile -ListOnly -Name Security

Computername LogName  NumberOfRecords
------------ -------  ---------------
CHI-P50      Security           30672
CHI-WEB02    Security           28260

.EXAMPLE
PS C:\> get-eventlogfile -comp chi-dc01,chi-scom01,chi-sql01 -skip -ListOnly | Sort Computername,LogName | format-table -GroupBy Computername -property Logname,NumberOfRecords

   Computername: CHI-DC01

LogName                       NumberOfRecords
-------                       ---------------
Active Directory Web Services            4192
Application                             30358
DFS Replication                          2033
Directory Service                        2454
DNS Server                               2283
File Replication Service                 1460
Operations Manager                      23323
Security                                56140
System                                  42337
Windows PowerShell                      18378


   Computername: CHI-SCOM01

LogName            NumberOfRecords
-------            ---------------
Application                   7400
Operations Manager           17948
Security                     28522
System                       64870
Windows PowerShell           13678


   Computername: CHI-SQL01

LogName            NumberOfRecords
-------            ---------------
Application                  26905
Operations Manager           30193
Security                     34248
System                       65403
Windows PowerShell           14105

Get a simple list of event logs, skipping those with 0 entries.
.NOTES
Version      : 1.0
Last Updated : June 17, 2016

Learn more about PowerShell:
http://jdhitsolutions.com/blog/essential-powershell-resources/

  ****************************************************************
   DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED 
   THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   
   YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, 
   DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             
  ****************************************************************

.LINK
Get-CimInstance
.LINK
Get-EventLog

.INPUTS
[string]
[cimsession]

.OUTPUTS
[pscustomobject]
#>

[CmdletBinding(DefaultParameterSetName="Computername")]

Param
(
[Parameter(
  ParameterSetName = "Computername",
  ValueFromPipelineByPropertyName,
  ValueFromPipeline,
  Position=0
  )]

[ValidateNotNullorEmpty()]
[Alias("cn")]
[string[]]$Computername = $env:Computername,

[Parameter(
  ParameterSetName = "Session",
  ValueFromPipeline
)]
[Alias("cs")]
[ValidateNotNullorEmpty()]
[Microsoft.Management.Infrastructure.CimSession[]]$CimSession,

[Parameter(ParameterSetName = "Computername")]
[Parameter(ParameterSetName = "Session")]
[ValidateNotNullorEmpty()]
[string]$Name,

[Parameter(ParameterSetName="Computername")]
[Parameter(ParameterSetName="Session")]
[switch]$ListOnly,

[Parameter(ParameterSetName="Computername")]
[Parameter(ParameterSetName="Session")]
[switch]$SkipEmptyLog,

[alias("timeout")]
[uint32]$OperationTimeoutSec

)

Begin {
    Write-Verbose "[BEGIN  ] Starting: $($MyInvocation.Mycommand)"
   
    #display PSBoundparameters formatted nicely for Verbose output  
   [string]$pb = ($PSBoundParameters | format-table -AutoSize | Out-String).TrimEnd()
   Write-Verbose "[BEGIN  ] PSBoundparameters: `n$($pb.split("`n").Foreach({"$("`t"*4)$_"}) | Out-String) `n" 

   $PSBoundParameters.Add("Classname","Win32_NTEventLogFile")
   $PSBoundParameters.Add("ErrorAction","Stop")

   #define a set of Properties to return
   $Properties = @{Name="Computername";Expression={$_.CSName}},
   @{Name="LogName";Expression={$_.LogFileName}},
   "NumberOfRecords",
   @{Name="Path";Expression={$_.Name}},
   @{Name="SizeMB";Expression = {[math]::Round($_.FileSize/1MB,2)}},
   @{Name="MaxSizeMB";Expression = {$_.MaxFileSize/1MB -as [int]}},
   @{Name="PctUsed";Expression= {[math]::Round(($_.FileSize/$_.maxFileSize)*100,2)}},
   "LastModified",
   @{Name="ModifiedAge";Expression={(Get-Date) - $_.LastModified}}

   #create a filter if $Name is specified
   if ($Name) {
     #remove from PSBoundparameters
     $PSBoundParameters.Remove("Name") | Out-Null
     
     $filter = "logfilename = '$Name'"
     Write-Verbose "[BEGIN  ] Adding filter: $filter"
     $PSBoundParameters.Add("Filter",$filter)
         
   }

   if ($SkipEmptyLog -And $Name) {
        #update existing filter
        #remove from PSBoundparameters
        $PSBoundParameters.Remove("SkipEmptyLog") | Out-Null
        $filter+= " AND NumberofRecords0"
        Write-Verbose "[BEGIN  ] Updating filter: $filter"
        $PSBoundParameters.Filter = $filter
     
   }
   elseif ($SkipEmptyLog) {
       #remove from PSBoundparameters
       $PSBoundParameters.Remove("SkipEmptyLog") | Out-Null

       #create filter to only filter out logs with no records
       $filter+= "NumberofRecords0"
       Write-Verbose "[BEGIN  ] Adding filter: $filter"
       $PSBoundParameters.Add("Filter",$filter)
   }

   if ($ListOnly) {
        #update PSBoundparameters
        #limit Get-CimInstance to only retrieving the required
        #properties which should speed up the query.
        $PSBoundParameters.Add("Property", @("Logfilename","NumberofRecords","CSName"))
        $PSBoundParameters.Remove("ListOnly") | Out-Null

        #define a list properties
        $ListProperties = @{Name="Computername";Expression={$_.CSName}},
        @{Name="LogName";Expression={$_.LogFileName}},
        "NumberOfRecords"
   }
} #begin

Process {
    Write-Verbose "[PROCESS] Using parameter set: $($PSCmdlet.parameterSetName)"
    #PSBoundParameters might change depending on what is piped in
    [string]$pb = ($PSBoundParameters | Format-Table -AutoSize | Out-String).TrimEnd()
    Write-Verbose "[PROCESS] PSBoundparameters: `n$($pb.split("`n").Foreach({"$("`t"*4)$_"}) | Out-String) `n" 
  
    Try {
        if ($ListOnly) {
            Get-CimInstance @PSBoundParameters | Select $ListProperties
        }
        else {
            Get-CimInstance @PSBoundParameters | Select $Properties
        }
    }
    Catch {
        Write-Error $_
    }                     
} #process

End {
    Write-Verbose "[END    ] Ending: $($MyInvocation.Mycommand)"
} #end

} #close function

该功能包括完整的帮助和示例,我已尝试在整个过程中记录内部评论。但让我谈谈一些亮点。

首先,因为该函数本质上是 Get-CimInstance 的包装器,所以我希望能够同时使用计算机名称和 CimSessions。您会在“参数”部分注意到我使用了 2 个参数集。您将看到两组都配置为接受来自管道的值。这是有效的,因为当 PowerShell 处理传入时,它会检测对象类型并选择适当的参数集。

我的函数还包括 Get-CimInstance 使用的一些其他参数,例如 -Filter。执行命令时指定的任何内容都会成为内置变量 $PSBoundParameters 的一部分,该变量是一个哈希表。我用 Write-Verbose 显示它,这在故障排除时很方便。 $PSBoundParameters 的伟大之处在于我可以 splat。我的函数的主要部分是 Get-CimInstance。

Get-CimInstance @PSBoundParameters | Select $ListProperties

但是,这意味着对于其他参数(例如 -Name),我需要调整 PSBoundParameters,因为 -Name 不是 Get-CimInstance 的一部分。

if ($Name) {
     #remove from PSBoundparameters
     $PSBoundParameters.Remove("Name") | Out-Null
     
     $filter = "logfilename = '$Name'"
     Write-Verbose "[BEGIN  ] Adding filter: $filter"
     $PSBoundParameters.Add("Filter",$filter)
         
   }

我可以删除绑定的参数,但这不会删除 $Name 的值。它只是将其从哈希表中删除。我仍然可以使用该参数。在本例中,我定义了一个 WMI 过滤器,并将过滤器添加到 $PSBoundParameters,因为它是 Get-CimInstance 的一部分。

我认为使脚本更易于阅读的另一件事是在 Begin 块中定义一组属性。这使得 Process 块变得更加简单。在我的函数中,我知道我想要获取某些属性并使用某些名称。我还想用一些计算值创建新属性。例如,我想显示已使用值的百分比:实际文件大小使用了多少配置的最大大小。

#define a set of Properties to return
   $Properties = @{Name="Computername";Expression={$_.CSName}},
   @{Name="LogName";Expression={$_.LogFileName}},
   "NumberOfRecords",
   @{Name="Path";Expression={$_.Name}},
   @{Name="SizeMB";Expression = {[math]::Round($_.FileSize/1MB,2)}},
   @{Name="MaxSizeMB";Expression = {$_.MaxFileSize/1MB -as [int]}},
   @{Name="PctUsed";Expression= {[math]::Round(($_.FileSize/$_.maxFileSize)*100,2)}},
   "LastModified",
   @{Name="ModifiedAge";Expression={(Get-Date) - $_.LastModified}}

我还结合了这些技术。我想要一个选项来显示事件日志的快速列表,仅显示日志名称和条目数。

if ($ListOnly) {
        #update PSBoundparameters
        #limit Get-CimInstance to only retrieving the required
        #properties which should speed up the query.
        $PSBoundParameters.Add("Property", @("Logfilename","NumberofRecords","CSName"))
        $PSBoundParameters.Remove("ListOnly") | Out-Null

        #define a list properties
        $ListProperties = @{Name="Computername";Expression={$_.CSName}},
        @{Name="LogName";Expression={$_.LogFileName}},
        "NumberOfRecords"
   }

Get-CimInstance 支持仅检索选定的属性,这可以提高性能,因此我将其添加到 $PSBoundParameters,摆脱函数的参数并在使用 -ListOnly 时定义属性集合。

[玩转系统] 使用 PowerShell 和事件日志的 CIM-ple 方式

过去每当我使用 Get-Eventlog 时,我通常都必须添加一个步骤来过滤掉没有条目的日志。所以我将这种能力纳入了我的职能中。尽管我想要将其与其他参数结合起来的选项。

if ($SkipEmptyLog -And $Name) {
        #update existing filter
        #remove from PSBoundparameters
        $PSBoundParameters.Remove("SkipEmptyLog") | Out-Null
        $filter+= " AND NumberofRecords<>0"
        Write-Verbose "[BEGIN  ] Updating filter: $filter"
        $PSBoundParameters.Filter = $filter
     
   }
   elseif ($SkipEmptyLog) {
       #remove from PSBoundparameters
       $PSBoundParameters.Remove("SkipEmptyLog") | Out-Null

       #create filter to only filter out logs with no records
       $filter+= "NumberofRecords<>0"
       Write-Verbose "[BEGIN  ] Adding filter: $filter"
       $PSBoundParameters.Add("Filter",$filter)
   }

请记住,我一直在考虑大规模管理并同时查询多个服务器。我可能想要查询 100 台服务器上的特定事件日志并跳过任何具有 0 条目的事件日志。

以下是该函数实际运行的一些示例。

get-cimsession | get-eventlogfile -Name System | Out-Gridview -Title "System"

我可以通过管道将现有 CIM 会话传递给该命令。

[玩转系统] 使用 PowerShell 和事件日志的 CIM-ple 方式

或者找到已满的日志

get-cimsession | get-eventlogfile | Where {$_.PctUsed -ge 100 } | sort PctUsed,Computername -Descending | select Computername,LogName,*Size*,PctUsed | format-table

[玩转系统] 使用 PowerShell 和事件日志的 CIM-ple 方式

显然我需要解决域控制器上的安全日志问题!

我可以想到很多方法来使用这个功能。我当然很想听听你的消息。这能解决任何问题吗?你捡到什么有用的东西了吗?你会如何使用它?如果您遇到任何错误或有升级请求,请在 GitHub 页面的评论中发布。

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

取消回复欢迎 发表评论:

关灯