powershell 使用CSV文件设置维护时段。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了powershell 使用CSV文件设置维护时段。相关的知识,希望对你有一定的参考价值。

<#
.SYNOPSIS
    Sets maintenance windows using a CSV file.
.DESCRIPTION
    Sets maintenance windows using a CSV file and is triggered when the settings CSV file is saved. using file watcher.
.EXAMPLE
    Set-ClientMaintenanceWindows.ps1
.NOTES
    Created by Ioan Popovici
    Uses FileSystemWatcher to see when the CSV file is changed.
    Requirements
        Configuration Manager, Local storage for the configuration file.
    Important
        Configuration file must reside on local storage for the file watcher to work.
.LINK
    https://SCCM.Zone/Set-CSVMaintenanceWindows
.LINK
    https://SCCM.Zone/Set-CSVMaintenanceWindows-CHANGELOG
.LINK
    https://SCCM.Zone/Set-CSVMaintenanceWindows-GIT
.LINK
    https://SCCM.Zone/Issues
.COMPONENT
    CM
.FUNCTIONALITY
    Set Maintenance Window
#>

##*=============================================
##* VARIABLE DECLARATION
##*=============================================
#region VariableDeclaration

## Get script path and name
[String]$ScriptPath = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Definition)
[String]$ScriptName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Definition)

## CSV and log files initialization
#  Set the CSV mail settings and configuration file name
[String]$csvDataFileName = $ScriptName + 'Configuration'
[String]$csvSettingsFileName = $ScriptName + 'MailSettings'

#  Get CSV Settings and Data file name with extension
[String]$csvDataFileNameWithExtension = $csvDataFileName+'.csv'
[String]$csvSettingsFileNameWithExtension = $csvSettingsFileName+'.csv'

#  Assemble CSV Settings and Data file path
[String]$csvDataFilePath = (Join-Path -Path $ScriptPath -ChildPath $csvDataFileName)+'.csv'
[String]$csvSettingsFilePath = (Join-Path -Path $ScriptPath -ChildPath $csvSettingsFileName)+'.csv'

#  Assemble log file Path
[String]$LogFilePath = (Join-Path -Path $ScriptPath -ChildPath $ScriptName)+'.log'

## Initialize last write reference time with current time
[DateTime]$LastWriteTimeReference = (Get-Date)

## Global error result array list
[System.Collections.ArrayList]$Global:ErrorResult = @()

#endregion
##*=============================================
##* END VARIABLE DECLARATION
##*=============================================

##*=============================================
##* FUNCTION LISTINGS
##*=============================================
#region FunctionListings

#region Function Write-Log
Function Write-Log {
<#
.SYNOPSIS
    Writes data to file log, event log and console.
.DESCRIPTION
    Writes data to file log, event log and console.
.PARAMETER EventLogEntryMessage
    The event log entry message.
.PARAMETER EventLogName
    The event log to write to.
.PARAMETER FileLogName
    The file log name to write to.
.PARAMETER EventLogEntrySource
    The event log entry source.
.PARAMETER EventLogEntryID
    The event log entry ID.
.PARAMETER EventLogEntryType
    The event log entry type (Error | Warning | Information | SuccessAudit | FailureAudit).
.PARAMETER SkipEventLog
    Skip writing to event log.
.EXAMPLE
    Write-Log -EventLogEntryMessage 'Set-ClientMW was successful' -EventLogName 'Configuration Manager' -EventLogEntrySource 'Script' -EventLogEntryID '1' -EventLogEntryType 'Information'
.NOTES
    This is an internal script function and should typically not be called directly.
.LINK
    https://SCCM.Zone
.LINK
    https://SCCM.Zone/Git
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$False,Position=0)]
        [Alias('Message')]
        [String]$EventLogEntryMessage,
        [Parameter(Mandatory=$False,Position=1)]
        [Alias('EName')]
        [String]$EventLogName = 'Configuration Manager',
        [Parameter(Mandatory=$False,Position=2)]
        [Alias('Source')]
        [String]$EventLogEntrySource = $ScriptName,
        [Parameter(Mandatory=$False,Position=3)]
        [Alias('ID')]
        [int32]$EventLogEntryID = 1,
        [Parameter(Mandatory=$False,Position=4)]
        [Alias('Type')]
        [String]$EventLogEntryType = 'Information',
        [Parameter(Mandatory=$False,Position=5)]
        [Alias('SkipEL')]
        [switch]$SkipEventLog
    )

    ## Initialization
    #  Getting the date and time
    [String]$LogTime = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss').ToString()

    #  Archive log file if it exists and it's larger than 50 KB
    If ((Test-Path $LogFilePath) -and (Get-Item $LogFilePath).Length -gt 50KB) {
        Get-ChildItem -Path $LogFilePath | Rename-Item -NewName { $_.Name -Replace '.log','.lo_' } -Force
    }

    #  Create event log and event source if they do not exist
    If (-not ([System.Diagnostics.EventLog]::Exists($EventLogName)) -or (-not ([System.Diagnostics.EventLog]::SourceExists($EventLogEntrySource)))) {

        #  Create new event log and/or source
        New-EventLog -LogName $EventLogName -Source $EventLogEntrySource
    }

    ## Error logging
    If ($_.Exception) {

        #  Write to log
        Write-EventLog -LogName $EventLogName -Source $EventLogEntrySource -EventId $EventLogEntryID -EntryType 'Error' -Message "$EventLogEntryMessage `n$_"

        #  Write to console
        Write-Host `n$EventLogEntryMessage -BackgroundColor Red -ForegroundColor White
        Write-Host $_.Exception -BackgroundColor Red -ForegroundColor White

        #  Assemble log file line
        [String]$LogLine = "$LogTime : $_.Exception"

        #  Write to log file
        $LogLine | Out-File -FilePath $LogFilePath -Append -NoClobber -Force -Encoding 'UTF8' -ErrorAction 'Continue'

        #  Add to error result array
        $Global:ErrorResult.Add($LogLine)

        #  Breaking Cycle so we don't get stuck in a loop :)
        Break
    }
    Else {

        #  Skip event log if requested
        If ($SkipEventLog) {

            #  Write to console
            Write-Host $EventLogEntryMessage -BackgroundColor White -ForegroundColor Blue
        }
        Else {

            #  Write to event log
            Write-EventLog -LogName $EventLogName -Source $EventLogEntrySource -EventId $EventLogEntryID -EntryType $EventLogEntryType -Message $EventLogEntryMessage

            #  Write to console
            Write-Host $EventLogEntryMessage -BackgroundColor White -ForegroundColor Blue
        }
    }

    ##  Assemble log file line
    [String]$LogLine = "$LogTime : $EventLogEntryMessage"

    ## Write to log file
    $LogLine | Out-File -FilePath $LogFilePath -Append -NoClobber -Force -Encoding 'UTF8' -ErrorAction 'Continue'
}
#endregion

#region Function Get-MaintenanceWindows
Function Get-MaintenanceWindows {
<#
.SYNOPSIS
    Get existing maintenance windows.
.DESCRIPTION
    Get the existing maintenance windows for a collection.
.PARAMETER CollectionName
    Set the collection name for which to list the maintenance Windows.
.EXAMPLE
    Get-MaintenanceWindows -Collection 'Computer Collection'
.NOTES
    This is an internal script function and should typically not be called directly.
.LINK
    https://SCCM.Zone
.LINK
    https://SCCM.Zone/Git
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True,Position=0)]
        [Alias('Collection')]
        [String]$CollectionName
    )

    ## Get CollectionID
    Try {
        $CollectionID = (Get-CMDeviceCollection -Name $CollectionName -ErrorAction 'Stop').CollectionID
    }

    #  Write to log in case of failure
    Catch {
        Write-Log -Message "Getting $CollectionName ID - Failed!"
    }

    ## Get collection maintenance windows
    Try {
        Get-CMMaintenanceWindow -CollectionId $CollectionID -ErrorAction 'Stop'
    }

    #  Write to log in case of failure
    Catch {
        Write-Log -Message "Get maintenance windows for $CollectionName - Failed!"
    }
}
#endregion

#region Function Remove-MaintenanceWindows
Function Remove-MaintenanceWindows {
<#
.SYNOPSIS
    Remove ALL existing maintenance windows.
.DESCRIPTION
    Remove ALL existing maintenance windows from a collection.
.PARAMETER CollectionName
    The collection name for which to remove the maintenance windows.
.EXAMPLE
    Remove-MaintenanceWindows -Collection 'Computer Collection'
.NOTES
    This is an internal script function and should typically not be called directly.
.LINK
    https://SCCM.Zone
.LINK
    https://SCCM.Zone/Git
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True,Position=0)]
        [Alias('Collection')]
        [String]$CollectionName
    )

    ## Get collection ID
    Try {
        $CollectionID = (Get-CMDeviceCollection -Name $CollectionName -ErrorAction 'Stop').CollectionID
    }
    Catch {
        Write-Log -Message "Getting $CollectionName ID - Failed!"
    }

    ## Get collection maintenance windows and delete them
    Try {
        Get-CMMaintenanceWindow -CollectionId $CollectionID | ForEach-Object {
            Remove-CMMaintenanceWindow -CollectionID $CollectionID -Name $_.Name -Force -ErrorAction 'Stop'
            Write-Log -Message ($_.Name+' - Removed!') -SkipEventLog
        }
    }
    Catch {

        #  Write to log in case of failure
        Write-Log -Message "$_.Name  - Removal Failed!"
    }
}
#endregion

#region Function Set-MaintenanceWindows
Function Set-MaintenanceWindows {
<#
.SYNOPSIS
    Set maintenance windows.
.DESCRIPTION
    Set maintenance windows to a collection.
.PARAMETER CollectionName
    The collection name for which to set maintenance windows.
.PARAMETER Date
    The maintenance window date.
.PARAMETER StartTime
    The maintenance window start time.
.PARAMETER StopTime
    The maintenance window stop time.
.PARAMETER ApplyTo
    Maintenance window applicability (Any | SoftwareUpdates | TaskSequences).
.EXAMPLE
    Set-MaintenanceWindows -CollectionName 'Computer Collection' -Date '2017-09-21' -StartTime '01:00'  -StopTime '02:00' -ApplyTo SoftwareUpdates
.NOTES
    This is an internal script function and should typically not be called directly.
.LINK
    https://SCCM.Zone
.LINK
    https://SCCM.Zone/Git
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True,Position=0)]
        [Alias('Collection')]
        [String]$CollectionName,
        [Parameter(Mandatory=$True,Position=1)]
        [Alias('Da')]
        [String]$Date,
        [Parameter(Mandatory=$True,Position=2)]
        [Alias('SartT')]
        [String]$StartTime,
        [Parameter(Mandatory=$True,Position=3)]
        [Alias('StopT')]
        [String]$StopTime,
        [Parameter(Mandatory=$True,Position=4)]
        [Alias('Apply')]
        [String]$ApplyTo
    )

    ## Get collection ID
    Try {
        $CollectionID = (Get-CMDeviceCollection -Name $CollectionName -ErrorAction 'Stop').CollectionID
    }
    Catch {

        #  Write to log in case of failure
        Write-Log -Message "Getting $CollectionName ID - Failed!"
    }

    ## Setting maintenance window start and stop times
    Try {
        $MWStartTime = Get-Date -Format 'yyyy-MM-dd HH:mm' -Date ($Date+' '+$StartTime) -ErrorAction 'Stop'
        $MWStopTime = Get-Date -Format 'yyyy-MM-dd HH:mm' -Date ($Date+' '+$StopTime) -ErrorAction 'Stop'
    }
    Catch {

        #  Write to log in case of failure
        Write-Log -Message "Creating Start/Stop token for $CollectionName - Failed!"
    }

    ## Create the schedule token
    Try {
        $MWSchedule = New-CMSchedule -Start $MWStartTime -End $MWStopTime -NonRecurring -ErrorAction 'Stop'
    }
    Catch {

        #  Write to log in case of failure
        Write-Log -Message "Creating schedule token for $CollectionName - Failed!"
    }

    ## Set maintenance window naming convention
    If ($ApplyTo -eq 'Any') { $MWType = 'MWA' }
    ElseIf ($ApplyTo -match 'Software') { $MWType = 'MWU' }
    ElseIf ($ApplyTo -match 'Task') { $MWType = 'MWT' }

    # Set maintenance window name
    $MWName =  $MWType+'.NR.'+(Get-Date -Uformat %Y-%B-%d $MWStartTime -ErrorAction 'Continue')+'_'+$StartTime+'-'+$StopTime
    ## Set maintenance window on collection
    Try {
        $SetNewMW = New-CMMaintenanceWindow -CollectionID $CollectionID -Schedule $MWSchedule -Name $MWName -ApplyTo $ApplyTo -ErrorAction 'Stop'

        #  Write to log
        Write-Log -Message "$MWName - Set!" -SkipEventLog
    }
    Catch {

        #  Write to log in case of failure
        Write-Log -Message "Setting $MWName on $CollectionName - Failed!"
    }
}
#endregion

#region  Function Send-Mail
Function Send-Mail {
<#
.SYNOPSIS
    Send E-Mail to specified address.
.DESCRIPTION
    Send E-Mail body to specified address.
.PARAMETER From
    Source.
.PARAMETER To
    Destination.
.PARAMETER CC
    Carbon copy.
.PARAMETER Body
    E-Mail body.
.PARAMETER Attachments
    E-Mail Attachments.
.PARAMETER SMTPServer
    E-Mail SMTPServer.
.PARAMETER SMTPPort
    E-Mail SMTPPort.
.EXAMPLE
    Send-Mail -From 'test@test.com' -To "test@test.com" -Subject "test" -Body 'Test' -CC 'test@test.com' -Attachments 'C:\Temp\test.log'
.NOTES
    This is an internal script function and should typically not be called directly.
.LINK
    https://SCCM.Zone
.LINK
    https://SCCM.Zone/Git
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True,Position=0)]
        [String]$From,
        [Parameter(Mandatory=$True,Position=1)]
        [String]$To,
        [Parameter(Mandatory=$False,Position=2)]
        [String]$CC,
        [Parameter(Mandatory=$True,Position=3)]
        [String]$Subject,
        [Parameter(Mandatory=$True,Position=4)]
        [String]$Body,
        [Parameter(Mandatory=$False,Position=5)]
        [String]$Attachments = $LogFilePath,
        [Parameter(Mandatory=$True,Position=6)]
        [String]$SMTPServer,
        [Parameter(Mandatory=$True,Position=7)]
        [String]$SMTPPort
    )

    ## Send mail with error handling
    Try {

        #  With CC
        If ($CC -ne [String]::Empty -and $CC -ne 'NO') {
            Send-MailMessage -From $From -To $To -Subject $Subject -CC $CC -Body $Body -Attachments $Attachments -SmtpServer $SMTPServer -Port $SMTPPort -ErrorAction 'Stop'
        }

        #  Without CC
        Elseif ($CC -eq [String]::Empty -or $CC -eq 'NO') {
            Send-MailMessage -From $From -To $To -Subject $Subject -Body $Body -Attachments $Attachments -SmtpServer $SMTPServer -Port $SMTPPort -ErrorAction 'Stop'
        }
    }
    Catch {
        Write-Log -Message 'Send Mail - Failed!'
    }
}
#endregion

#region Function Start-DataProcessing
Function Start-DataProcessing {
<#
.SYNOPSIS
    Used for main data processing.
.DESCRIPTION
    Used for main data processing, for this script only.
.EXAMPLE
    Start-DataProcessing
.NOTES
    This is an internal script function and should typically not be called directly.
.LINK
    https://SCCM.Zone
.LINK
    https://SCCM.Zone/Git
#>
    ## Import SCCM PSH module and changing context
    Try {
        Import-Module $env:SMS_ADMIN_UI_PATH.Replace('\bin\i386','\bin\configurationmanager.psd1') -ErrorAction 'Stop'
    }
    Catch {
        Write-Log -Message 'Importing SCCM PSH module - Failed!'
    }

    #  Get the CMSITE SiteCode and change connection context
    $SiteCode = Get-PSDrive -PSProvider CMSITE

    #  Change the connection context
    Set-Location "$($SiteCode.Name):\"

    ## Import the Settings CSV file
    Try {
        $csvSettingsData = Import-Csv -Path $csvSettingsFilePath -Encoding 'UTF8' -ErrorAction 'Stop'
    }
    Catch {

        #  write to log
        Write-Log -Message 'Importing Settings CSV Data - Failed!'
    }

    ## Import the Collection CSV file
    Try {
        $csvCollectionData = Import-Csv -Path $csvDataFilePath -Encoding 'UTF8' -ErrorAction 'Stop'
    }
    Catch {

        #  write to log
        Write-Log -Message 'Importing Collection CSV Data - Failed!'
    }
    ## Process imported CSV file data
    Try {

        #  Process imported CSV file data
        $csvCollectionData | ForEach-Object {

            #  Check if we need to remove existing maintenance windows
            If ($_.RemoveExisting -eq 'YES' ) {

                #  Write to log
                Write-Log -Message ('Removing maintenance windows from:  '+$_.CollectionName) -SkipEventLog

                #  Remove maintenance window
                Remove-MaintenanceWindows -CollectionName $_.CollectionName
            }

            #  Set Maintenance Window
            Set-MaintenanceWindows -CollectionName $_.CollectionName -Date $_.Date -StartTime $_.StartTime -StopTime $_.StopTime -ApplyTo $_.ApplyTo
        }

        #  Initialize result array
        [array]$Result =@()

        #  Parsing CSV unique collection names
        $csvCollectionData.CollectionName | Select-Object -Unique | ForEach-Object {

            #  Getting maintenance windows for collection (split to new line)
            $MaintenanceWindows = Get-MaintenanceWindows -CollectionName $_ | ForEach-Object { $_.Name+"`n" }

            #  Assemble result with descriptors
            $Result+= "`nListing all maintenance windows for: "+$_+" "+"`n`n "+$MaintenanceWindows
        }

        #  Convert the result to String and write it to log
        [String]$ResultString = Out-String -InputObject $Result
        Write-Log -Message $ResultString

        ## Return to Script Path
        Set-Location $ScriptPath

        ## Remove SCCM PSH Module
        Remove-Module 'ConfigurationManager' -Force -ErrorAction 'Continue'
    }
    Catch {

        #  Not needed, empty
    }
    Finally {

        #  Send Mail Report if needed
        If ($csvSettingsData.SendMail -eq 'YES' -and -not $Global:ErrorResult) {

            #  Write to log
            Write-Log -Message "Sending Mail Report..."

            #  Sending mail
            Send-Mail -Subject 'Info: Setting Maintenance Window - Success!' -Body $ResultString -From $csvSettingsData.From -To $csvSettingsData.To -CC $csvSettingsData.CC -SMTPServer $csvSettingsData.SMTPServer -SMTPPort $csvSettingsData.SMTPPort

        }
        If ($csvSettingsData.SendMail -eq 'YES' -and $Global:ErrorResult) {

            #  Write to log
            Write-Log 'CSV Data Processing - Failed!'

            #  Write to log
            Write-Log -Message "Sending Error Mail Report..."

            #  Sending mail
            Send-Mail -Subject 'Warning: Setting Maintenance Window - Failed!' -Body "Errors: `n $Global:ErrorResult"  -From $csvSettingsData.From -To $csvSettingsData.To -CC $csvSettingsData.CC -SMTPServer $csvSettingsData.SMTPServer -SMTPPort $csvSettingsData.SMTPPort
        }
    }
}

#endregion

#region Function Test-FileChangeEvent
Function Test-FileChangeEvent {
<#
.SYNOPSIS
    Workaround for FileSystemWatcher firing multiple events during a write operation.
.DESCRIPTION
    FileSystemWatcher may fire multiple events on a write operation.
    It's a known problem but it's not a bug in FileSystemWatcher.
    This function is discarding events fired more than once a second.
.PARAMETER $WatchFilePath
    Specify file path to be watched.
.PARAMETER $LastWriteTimeReference
    Specify file last read time for reference.
.EXAMPLE
    Test-FileChangeEvent -WatchFilePath $WatchFilePath -LastWriteTimeReference $LastWriteTimeReference
.NOTES
    This is an internal script function and should typically not be called directly.
.LINK
    https://SCCM.Zone
.LINK
    https://SCCM.Zone/Git
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$True,Position=0)]
        [Alias('WatchPath')]
        [String]$WatchFilePath,
        [Parameter(Mandatory=$True,Position=1)]
        [Alias('WriteTimeReference')]
        [DateTime]$LastWriteTimeReference
    )

    ## Get file last write time
    Try {
        [DateTime]$FileLastWriteTime = (Get-ItemProperty -Path $WatchFilePath).LastWriteTime
    }
    Catch {

        #   write to log
        Write-Log -Message "Reading Last Write Time from $WatchFilePath - Failed!"
    }

    ## Test if the file change event is valid by comparing the file last write time and reference parameter specified time
    If (($FileLastWriteTime - $LastWriteTimeReference).Seconds -ge 1) {

        ## Write to log
        Write-Log -Message "`nFile change - Detected!" -SkipEventLog

        ## Start main data processing and wait for it to finish
        Start-DataProcessing | Out-Null
    }
    Else {

        ## Do nothing, the file change event was fired more than once a second
    }
}

#endregion

#endregion
##*=============================================
##* END FUNCTION LISTINGS
##*=============================================

##*=============================================
##* SCRIPT BODY
##*=============================================
#region ScriptBody

    ## Initialize file watcher and wait for file changes
    $FileWatcher = New-Object System.IO.FileSystemWatcher
    $FileWatcher.Path = $ScriptPath
    $FileWatcher.Filter = $csvDataFileNameWithExtension
    $FileWatcher.IncludeSubdirectories = $False
    $FileWatcher.NotifyFilter = [System.IO.NotifyFilters]::'LastWrite'
    $FileWatcher.EnableRaisingEvents = $True

    #  Register file watcher event
    Register-ObjectEvent -InputObject $FileWatcher -EventName 'Changed' -Action {

        # Test if we really need to start processing
        Test-FileChangeEvent -LastWriteTimeReference $LastWriteTimeReference -WatchFilePath $csvDataFilePath

        #  Reinitialize DateTime variable to be used on next file change event
        $LastWriteTimeReference = (Get-Date)
    }

#endregion

#endregion
##*=============================================
##* END SCRIPT BODY
##*=============================================

以上是关于powershell 使用CSV文件设置维护时段。的主要内容,如果未能解决你的问题,请参考以下文章

text 使用CSV文件设置维护时段。配置文件模板。

GKE 集群的维护时段

Google Cloud SQL 维护时段

PowerShell Export-Csv 导出与导入CSV文件

CSV 脚本的 PowerShell 获取计数器,编码问题

使用 PowerShell 将多个 CSV 文件合并为一个