Untitled

mail@pastecode.io avatar
unknown
powershell
2 years ago
11 kB
3
Indexable
Never

#Take input from XML file
[xml]$configInformation = (Get-Content -Path C:\Users\a0334614\Desktop\Maintenance\MaintenanceConfig.xml)
[string]$url = $configInformation.Settings.apiHostname
[string]$username = $configInformation.Settings.Username
[array]$tags = $configInformation.Settings.Tags.Tag
[string]$passwordFromFile = $configInformation.Settings.PasswordEncryptedString
$SecureStringPassword = $passwordFromFile | ConvertTo-SecureString
$Password = (New-Object PSCredential "user",$SecureStringPassword).GetNetworkCredential().Password


<#
.Synopsis
    This function will send credentials to api and in exchange for an auth token

.Outputs
    Response.token: A token that can be used to authenticate future requests
#>
function Get-AuthToken
{
    Write-Verbose "Requesting auth token from api..."
    #$password = Get-Content $passwordFileLocation
    $payload = @{
        client_id  = 'clientID'
        grant_type = 'password'
        username   = "$username"
        password   = "$password"
    } | ConvertTo-Json
    $response = Invoke-RestMethod -Uri ($Url + "/api/api/v1/auth") -Method POST -Body $payload -ContentType application/json
    return $response.token
}

<#
.Synopsis
    This function will get machines from api associated with a given tag

.Outputs
    Machines: an array of machine objects
#>
function Get-Machine
{
    param (
        $Token,
        $Tag,
        $Url
    )
    $machines = Invoke-RestMethod -Method GET -Uri ($Url + "/api/api/v1/servers/?tag=$Tag") -ContentType application/json -Headers $Header
    return $machines
}

<#
.Synopsis
    This function will PATCH a given machine in api by adjusting its maintenance status

.Parameter
    InOrOut a string representing the maintenance status to set the machine to
#>
function Update-MachineStatus
{
    param (
        $machine,
        $header
    )

    $body = @{
        attributes = @{
            name            = $machine.attributes.name
            serverType      = $machine.attributes.serverType
            maintenanceMode = $machine.attributes.maintenanceMode
            tags = $machine.attributes.tags
            associatedApplications = $machine.attributes.associatedApplications
        }
    } | ConvertTo-Json -depth 4

    $id = $machine.id
    Invoke-RestMethod -Method PATCH -Uri ($url + "/api/api/v1/servers/$id") -ContentType application/json -Body $Body -Headers $header > $null
}

<#
.Synopsis
    This function will call the other functions for input. Then it will take the appropriate machines out and poll them until they are drained.
    Once they transition out of service, any maintenance actions can be performed without end user impact. After that completes, machines are
    brought back into service and the next tag is taken down.

    Status updates are given along the way for troubleshooting but user interaction is not required.
#>
function Main
{
    foreach ($tag in $tags)
    {
        $AuthToken = Get-AuthToken
        [array]$deadcomputerNames = @()
        [array]$aliveMachines = @()
        [array]$aliveMachineNames = @()
        [array]$jobs = @()
        $body = ""

        $header = @{
            'Content-Type'  = 'application/json'
            'Authorization' = "Bearer $AuthToken"
        }

        $machines = Get-Machine -token $AuthToken -tag $tag -Url $URL
        #$machines
        foreach ($machine in $machines.data)
        {
            $machineID = $machine.ID
            $MachineName = $machine.attributes.Name
            if (Test-Connection $machineName -Count 2 -Quiet)
            {
                $aliveMachines += $machine
                $machine.attributes.maintenanceMode = "TransitioningOutofService"
                #Update-MachineStatus -machine $machine -header $header > $null
                $aliveMachineNames += $MachineName
            }
            else
            {
                Write-Host "fail"
                $deadcomputerNames += $machine.attributes.name
            }
        }

        $deadcomputerNames
        $aliveMachines
        foreach ($machine in $aliveMachines)
        {
            $machineID = $machine.ID
            $j = Start-Job -Name $('OutofServicePoller' + $machine.attributes.name) -ScriptBlock {
                param(
                    $header,
                    $machineID,
                    $url
                )
                $machineStatus = "default"
                while ($machinestatus -ne "Out of service")
                {
                    Write-Host $machinestatus
                    Start-Sleep -Seconds 10
                    $machineStatus = (Invoke-RestMethod -Method GET -Uri ($url + "/api/api/v1/servers/$machineid") -ContentType application/json -Headers $header).data.attributes.maintenanceMode
                    Write-Host $machinestatus
                }
            } -ArgumentList $header, $machineID, $url
            $jobs += $j.id
        }
        Write-Host "Waiting for $tag servers to go out of service..."
        Wait-Job -Id $jobs > $null
        Write-Host "All $tag servers are out of service"
        <#
            Do some maintenance stuff
        #>
        <#$aliveMachineNames.ForEach({"$_" | Out-File "C:\EpicSource\UltimateMaintenance\Servers.txt" -Append})
        $scriptPath = "C:\EpicSource\UltimateMaintenance\Deploy-Package.ps1"
        $params = "-ServerListFile 'c:\epicSource\UltimateMaintenance\servers.txt' -InstallerPath 'C:\EpicSource\UltimateMaintenance\ECSMMSApplicationRequestRouting3.msi'"
        Invoke-Expression -Command "$scriptPath $params"#>
        #& "C:\EpicSource\UltimateMaintenance\Deploy-Package.ps1" -Servers $aliveMachines -InstallerPath "C:\EpicSource\UltimateMaintenance\ECSMMSApplicationRequestRouting3.msi"
            Write-Host $aliveMachines.attributes.name
        
            #Example 1: Reboot machines
            Write-Host "Rebooting machines with tag $tag"
            Get-Job -Name "*restart-computer*" | Remove-Job
            
            
            #Invoke-CimMethod -ComputerName $aliveMachines.attributes.name -ClassName 'Win32_OperatingSystem' -MethodName 'Reboot'
            $aliveMachines.attributes.name | Restart-Computer -AsJob -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Out-Null
            (get-job | Where-Object {$_.Command -match "^Restart-Computer.*"}) | Wait-Job | Out-Null
            Write-Host "Completed Rebooting machines with tag $tag"
        
        <#>
            Example 2: Rotate through installing a prerequisite that requires a reboot

            $scriptPath = "C:\EpicSource\UltimateMaintenance\Deploy-Package.ps1"
            $params = "-ServerListFile $aliveMachinesPath -InstallerPath 'C:\EpicSource\UltimateMaintenance\ECSMMSApplicationRequestRouting3.msi'"
            Invoke-Expression -Command "$scriptPath $params"
        #>

        
        #Example 3: Install Windows Updates
        #Source for Module https://www.powershellgallery.com/packages/PSWindowsUpdate/2.2.0.2
        $AlexsPassword = Get-Content 'C:\Users\a0334614\Desktop\Powershell-maintenance\password.txt'
        $AlexsUsername = "insel\a033461"
        $User = $AlexsUsername
        $PWord = ConvertTo-SecureString -String $AlexsPassword -AsPlainText -Force
        $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
        
        foreach($machine in $aliveMachines)
        {
            $s = New-PSSession -ComputerName $machine -Credential $Credential
            $i = Invoke-Command -Session $s -ScriptBlock{
                if(!(Test-Path -Path "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSWindowsUpdates"))
                {
                    New-Item -ItemType Directory -Path "C:\Windows\System32\WindowsPowerShell\v1.0\Modules" -Name "PSWindowsUpdate" -ErrorAction SilentlyContinue
                }
                [bool]$doesPsd1Exist = Test-Path -Path "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSWindowsUpdate\PSWindowsUpdate.psd1"
                return $doesPsd1Exist
            }
            if(!$i)
            {
                Start-Sleep -Seconds 2
                Copy-Item -ToSession $s -Path "C:\Users\bharriso\Downloads\pswindowsupdate.2.2.0.2.zip" -Destination "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSWindowsUpdate"
                Invoke-Command -Session $s -ScriptBlock{
                    Unblock-File -Path "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSWindowsUpdate\pswindowsupdate.2.2.0.2.zip"

                    Expand-Archive -Path "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSWindowsUpdate\pswindowsupdate.2.2.0.2.zip" -DestinationPath "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSWindowsUpdate"
                    Remove-Item -Path "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSWindowsUpdate\pswindowsupdate.2.2.0.2.zip"
                    Import-Module -Name PSWindowsUpdate
                }
            }
        }

        foreach($machine in $aliveMachines)
        {
            $machineName = $machine.attributes.name
            #Invoke-WUJob -ComputerName $machineName -Script { ipmo PSWindowsUpdate; Install-WindowsUpdate -AcceptAll -AutoReboot | Out-File "C:\Windows\PSWindowsUpdate.log"} -RunNow -Confirm:$false -Verbose -ErrorAction Ignore
            Download-WindowsUpdate -ForceDownload -ForceInstall
        }
        

        foreach ($machine in $aliveMachines)
        {
            $machineID = $machine.ID
            $machine.attributes.maintenanceMode = "InService"
            update-machinestatus -machine $machine -header $header > $null
        }

        #send email to admin with info on what just ran and what machines are "dead'"
        if($aliveMachineNames.Count -ge 1)
        {
            $Body += ("The following machine(s) have had maintenance actions taken on them successfully: " +  "<br />" + $aliveMachineNames + "<br />" +  "<br />")
        }
        if($deadcomputerNames.Count -ge 1)
        {
            $Body += ("The following machine(s) were not able to be connected to: " +  "<br />" +  $deadcomputerNames + "<br />" + "<br />")
        }
        $body += "Have an Epic day"
        
        # If we're sending email alerts import settings from EmailAlertsConfig.xml
        $emailSettings = @{ }
        if ($EmailAlerts)
        {
            [xml] $emailXML = (Get-Content -Path .\EmailAlertConfig.xml)
            $emailSettings = @{
                To         = $emailXML.Settings.MailTo
                From       = $emailXML.Settings.MailFrom
                Subject    = "MaintenanceActions Taken"
                SmtpServer = $emailXML.Settings.SmtpServer
                Port       = $emailXML.Settings.SmtpPort
            }
        }

        function _sendEmailAlert
        {
            param (
                [Parameter(Mandatory = $true)]
                [string] $Body,

                # Hashtable of parameters for the Send-MailMessage function
                # Passed using @settings
                [Parameter(Mandatory = $true)]
                [hashtable] $Settings
            )
            if ($EmailAlerts.IsPresent)
            {
                Send-MailMessage @Settings -Body $Body -BodyAsHtml
            }
        }

        #send email to admin with info on what just moved
        _sendEmailAlert -Settings $emailSettings -Body $Body
    }
}

Main