Reporting/ServerRake.Reporting.System.ps1

##################################
### COMMON FUNCTIONS: UPDATE ###
##################################

function Export-SystemReport {

    [CmdletBinding()]
    param (

        [Parameter(Mandatory = $true)]
        [string[]] $ComputerName,

        [Parameter(Mandatory = $true)]
        [string[]] $MaintenanceWindows,
        
        [Parameter()]
        [string] $ExportPath

    )

    $remoteScript = {

        param ($MaintenanceWindows)

        # ADMINISTRATORS
        $ADMINISTRATORS_Users   = Get-LocalGroupMember -Name "Administrators" | ? ObjectClass -EQ "User" 
        $ADMINISTRATORS_Groups  = Get-LocalGroupMember -Name "Administrators" | ? ObjectClass -EQ "Group"

        # SERVICES
        $SERVICES_AllServices   = Get-Service 

        # VOLUMES
        $VOLUMES_AllVolumes     = Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" 

        # NETWORKING
        $NETWORKING_Nic         = Get-WmiObject -Class Win32_NetworkAdapterConfiguration | Where-Object IPEnabled
        $NETWORKING_OpenPorts   = Get-NetTCPConnection -State Listen, Established | Sort-Object LocalPort -Unique

        # PATCHING
        $PATCHING_Hotfixes      = Get-HotFix | Select-Object -Property Description, HotFixID, InstalledOn | 
                                    Sort-Object -Property InstalledOn -Descending
        $PATCHING_TaniumGroup   = Get-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Tanium\Tanium Client\Sensor Data\Tags'
        $PATCHING_TaniumGroup   = ($PATCHING_TaniumGroup | Get-Member) | Where-Object { $_.Name -like "SRV*" -or $_.Name -like "WRK*" } | 
                                    Select-Object -Property Name
      # $PATCHING_ManualPatch = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Tanium\Tanium Client\Sensor Data\Tags"

        # SYSTEM
        $SYSTEM_ComputerInfo    = Get-ComputerInfo | 
                                    Select-Object -Property WindowsProductName, WindowsEditionId, CsManufacturer, `
                                        CsModel, CsDomain, CsDomainRole, TimeZone
        $SYSTEM_Ram             = Get-CimInstance win32_ComputerSystem | % { [math]::round($_.TotalPhysicalMemory / 1GB) }
        $SYSTEM_CpuCores        = (Get-CimInstance -ClassName Win32_Processor).NumberOfCores


        # LOGS
        $LOGS_SystemErrors      = Get-WinEvent -FilterHashtable @{LogName='System'; Level=2,3} -MaxEvents 100
        $LOGS_ApplicationErrors = Get-WinEvent -FilterHashtable @{LogName='Application'; Level=2,3} -MaxEvents 100

        # PROGRAMS
        $PROGRAMS_Installed     = @()
        $PROGRAMS_Installed     += Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
        $PROGRAMS_Installed     += Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
        $PROGRAMS_Installed     = $PROGRAMS_Installed | Where-Object { $null -ne $_.DisplayName } |     
                                    Select-Object -Property DisplayName, DisplayVersion, Publisher, InstallDate | 
                                    Sort-Object -Property Publisher

        # ENVIRONMENT & TIER
        switch -Regex ($env:COMPUTERNAME.SubString($env:COMPUTERNAME.length - 1) -match '^\d+$') {
            $true {   
                $environmentInt = 3;
                $tierInt = 6; 
            }

            $false {
                $environmentInt = 4;
                $tierInt = 6;
            }  
        }

        switch -Wildcard ($env:COMPUTERNAME.Substring($env:COMPUTERNAME.length - $environmentInt)) {
            "p*" { if ($env:COMPUTERNAME.Substring(2, 3) -eq 'KAN') { $environment = "DR" } else { $environment = "PRODUCTION" }; }
            "r*" { $environment = "DR" }
            "s*" { $environment = "NON-PRODUCTION" }
            default { $environment = "?" }
        }

        switch -Wildcard ($env:COMPUTERNAME.Substring($env:COMPUTERNAME.Length - $tierInt)) {
            "ap*" { $tier = "APPLICATION" }
            "sr*" { $tier = "REPORTING" }
            "we*" { $tier = "WEB" }
            "sq*" { $tier = "DATABASE" }
            "ql*" { $tier = "DATABASE" }
            default { $tier = "?" }
        }


        # FORMATTING: MAX CELL CHARACTERS ALLOWED BY GOOGLE SHEETS --- 50,000
        $GoogleCellCharThrottle = 49000

        # FORMATTING: PSOBJECTS TO STRINGS (FOR SHEET CELLS)
        if ($SERVICES_AllServices) {
            $ServiceStatus = $SERVICES_AllServices | % { "$($_.Status) --- $($_.Name) ::: $($_.DisplayName)`n" }
        } else {
            $ServiceStatus = ' '
        }

        if ($PATCHING_Hotfixes) {
            $PatchingStatus = $PATCHING_Hotfixes | % { "$($_.HotFixID) --- $($_.InstalledOn) --- $($_.Description)`n" }
        } else {
            $PatchingStatus = ' '
        }

        if ($PATCHING_TaniumGroup.Name -in $MaintenanceWindows.Keys) {
            $PatchingWindow = $MaintenanceWindows[$PATCHING_TaniumGroup.Name]
            $PatchingWindow = "$($PATCHING_TaniumGroup.Name) --- $($PatchingWindow.Day) --- $($PatchingWindow.Start) (Start) --- $($PatchingWindow.End) (End)"
        } else {
            $PatchingWindow = "$($PATCHING_TaniumGroup.Name) --- no schedule information available."
        }

        if ($LOGS_SystemErrors) {
            $SystemErrors = $LOGS_SystemErrors | % { "$($_.TimeCreated) --- $($_.LevelDisplayName) --- $($_.ProviderName) ::::[$($_.Message)]`n`n" }
            
            # VALIDATION: GOOGLE CHARACTER LIMIT
            $count = -1
            while ((-join $SystemErrors).Length -gt $GoogleCellCharThrottle) {
                $SystemErrors[$count] = $null
                $count--
            }

        } else {
            $SystemErrors = ' '
        }

        if ($LOGS_ApplicationErrors) {
            $ApplicationErrors = $LOGS_ApplicationErrors | % { "$($_.TimeCreated) --- $($_.LevelDisplayName) --- $($_.ProviderName) :::: [$($_.Message)]`n`n" }

            # VALIDATION: GOOGLE CHARACTER LIMIT
            $count = -1
            while ((-join $SystemErrors).Length -gt $GoogleCellCharThrottle) {
                $ApplicationErrors[$count] = $null
                $count--
            } 
            
        } else {
            $ApplicationErrors = ' '
        }

        if ($PROGRAMS_Installed) {
            $ProgramsInstalled = $PROGRAMS_Installed | % { "$($_.DisplayName)`n[Publisher] $($_.Publisher) --- [Install Date] $($_.InstallDate) --- v$($_.DisplayVersion)`n`n" }
        } else {
            $ProgramsInstalled = ' '
        }

        if ($VOLUMES_AllVolumes) {
            $VolumeInformation = $VOLUMES_AllVolumes | % { "$($_.DeviceID) --- $($_.VolumeName) --- $([Math]::Round($_.Size/1GB, 2)) (Total) --- $([Math]::Round($_.FreeSpace/1GB, 2)) (Free)`n" }
        } else {
            $VolumeInformation = ' '
        }

        if ($ADMINISTRATORS_Users) {
            $LocalAdmins = ($ADMINISTRATORS_Users.Name).split(' ') | % { "$($_)`n" }
        } else {
            $LocalAdmins = ' '
        }

        if ($ADMINISTRATORS_Groups) {
            $AdminGroups = ($ADMINISTRATORS_Groups.Name).split(' ') | % { "$($_)`n" }
        } else {
            $AdminGroups = ' '
        }
        
        if ($NETWORKING_OpenPorts) {

            $OpenPorts = ''
            $NETWORKING_OpenPorts | % {

                $process    = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
                $service    = Get-CimInstance -ClassName Win32_Service -Filter "ProcessId = $($_.OwningProcess)" -ErrorAction SilentlyContinue
                $OpenPorts  += "$($_.LocalPort) (Local) >>> $(if ($($_.RemotePort -ne 0)) {"$($_.RemotePort) (Remote) :"} else {"$($_.State) :"}) $(if ($($_.AppliedSetting)) {"$($_.AppliedSetting) :"}) $($process.ProcessName) $(if ($service) {"- $($service.DisplayName)"} else {"- $($process.Description)"})`n"
        
            }

        } else {
            $OpenPorts = ' '
        }

        # RESPONSE OBJECT
        $response = [PSCustomObject]@{

            'Host Name'                         = $env:COMPUTERNAME
            'Environment'                       = $environment
            'Tier'                              = $tier
            'Local Administrators'              = $LocalAdmins
            'Administrative Groups'             = $AdminGroups
            'OS'                                = $SYSTEM_ComputerInfo.WindowsProductName
            'OS Version'                        = $SYSTEM_ComputerInfo.WindowsEditionId
            'CPU Cores'                         = $SYSTEM_CpuCores
            'RAM (GB)'                          = $SYSTEM_Ram
            'Volumes (GB)'                      = $VolumeInformation
            'Patching'                          = $PatchingStatus
            'Patch Window (Local Time)'         = $PatchingWindow  
            'Time Zone'                         = $SYSTEM_ComputerInfo.TimeZone
            'Virtualization'                    = $SYSTEM_ComputerInfo.CsManufacturer
            'Hypervisor'                        = $SYSTEM_ComputerInfo.CsModel
            'Domain'                            = $SYSTEM_ComputerInfo.CsDomain
            'Domain Role'                       = $SYSTEM_ComputerInfo.CsDomainRole
            'IP Address'                        = $NETWORKING_Nic | % { "$($_.IPAddress)`n" }
            'Default Gateway'                   = $NETWORKING_Nic | % { "$($_.DefaultIPGateway)`n" }
            'Subnet'                            = $NETWORKING_Nic | % { "$($_.IPSubnet)`n" }
            'MAC Address'                       = $NETWORKING_Nic.MacAddress
            'Open Ports'                        = $OpenPorts
            'Service Status'                    = $SERVICEStatus
            'Programs Installed'                = $ProgramsInstalled
            'System Errors (100 Events)'        = $SystemErrors
            'Application Errors (100 Events)'   = $ApplicationErrors
            
        }

        Write-Host "Data Collected [$env:COMPUTERNAME]"

        return $response

    }

    $response = Send-RemoteScript -ComputerName $ComputerName -RemoteScript $remoteScript -ArgumentList $MaintenanceWindows

    if ($ExportPath) {
        
        try {

            $csvArray = @()
            $response | % {$csvArray += $_ | Select-Object -Property * -ExcludeProperty PSComputerName, PSShowComputerName, RunspaceId}

            $csvArray | Export-Csv -Path "$ExportPath\System_Report_$(Get-Date -Format "yyyyMMdd_HHmmss").csv" -NoTypeInformation 
            Write-Host "`nCSV file successfully exported to [$($ExportPath)].`n" -ForegroundColor Green
            
        } catch {

            throw $_
            
        }

     } else {

        return $response

     }


}

function Export-StorageReport {

    [CmdletBinding()]
    param (

        [Parameter(Mandatory = $True)]
        [String[]] $ComputerName,

        [Parameter(Mandatory = $True)]
        [String[]] $PrismCentral

    )

    if (!$secret) {

        throw "No secret sessions are currently opened. Start a session with 'Open-SecretSession' and try again."
    
    }

    if (!$nxAPICreds) {

        throw "No Nutanix credentials have been set. Use 'Set-NutanixCredentials -Username' to set credentails and try again."
    
    }


    $response = @()
    $serversDisk    =   Get-SystemDrives -ComputerName $ComputerName
    $serversRam     =   Get-SystemRam -ComputerName $ComputerName

    foreach ($pc in $PrismCentral) {
        
        $baseUrl        =   "https://$($pc):9440/api/nutanix/v3"

        $Headers = @{
            "Authorization" = "Basic $nxAPICreds"
            "Content-Type"  = "application/json"
            "Accept"        = "application/json"
        }

        # NUTANIX STORAGE CONTAINERS
        $containerMap = @{}
        $offsetby = 0
        $count = 0
        $totalContainersFound = $null

        do {

            $count += 500

            $containerBody = @{
                entity_type             = "storage_container"
                group_member_count      = $count
                group_member_offset     = $offsetby
                group_member_attributes = @(
                    @{ attribute = "container_name" }
                )
            } | ConvertTo-Json -Depth 5


            try {

                $containerResponse = Invoke-RestMethod -Uri "$baseUrl/groups" -Headers $Headers -Method Post -Body $containerBody -SkipCertificateCheck

            } catch {

                throw $_

            }

            foreach ($sc in $containerResponse.group_results[0].entity_results) {

                $uuid = $sc.entity_id
                $name = ($sc.data | Where-Object { $_.name -eq "container_name" }).values[0].values[0]

                if ($uuid -and $name) {
                    $containerMap[$uuid] = $name
                }

            }

            if (!$totalContainersFound) {$totalContainersFound = $containerResponse.metadata.total_matches}
            $offsetby += 500
            
        } while ($count -lt $totalContainersFound)

        # VMs ---> RESPONSE
        $offsetby = 0
        $count = 0
        $totalVMsFound = $null

        do {

            $count += 500

            $vmBody = @{ 
                kind = "vm" 
                offset = $offsetby
                length = $count 
            } | ConvertTo-Json

            try {
                
                $vmResponse = Invoke-RestMethod -Uri "$baseUrl/vms/list" -Headers $headers -Method Post -Body $vmBody -SkipCertificateCheck

            } catch {

                throw $_

            }

            foreach ($vm in $vmResponse.entities) {

                if (($vm.spec.name).ToUpper() -in $ComputerName) { #$serversDisk.Server

                    $containers = [System.Collections.Generic.HashSet[string]]::new()
                    $diskList = $vm.status.resources.disk_list

                    if ($diskList) {

                        foreach ($disk in $diskList) {

                            $scUuid = $disk.device_properties.disk_address.storage_container_uuid
                            $scRefName = $disk.storage_config.storage_container_reference.name

                            if ($scRefName) {
                                [void]$containers.Add($scRefName)
                            } elseif ($scUuid -and $containerMap.ContainsKey($scUuid)) {
                                [void]$containers.Add($containerMap[$scUuid])
                            } elseif ($scUuid) {
                                [void]$containers.Add($scUuid)
                            }

                        }

                    }        

                    $totalGB    =   ((($serversDisk | ? Server -eq $vm.spec.name).'Disk Size (GB)') | Measure-Object -Sum).Sum  
                    $freeGB     =   ((($serversDisk | ? Server -eq $vm.spec.name).'Disk Free (GB)') | Measure-Object -Sum).Sum 
                    $usedGB     =   $totalGB - $freeGB
                    $usedPct    =   [Math]::Round(($usedGB / $totalGB) * 100, 2)
                    $Ram        =   ($serversRam | ? Server -eq $vm.spec.name).'Memory (GB)'   

                    $response += [PSCustomObject]@{

                        Nutanix                         =   $PrismCentral
                        Server                          =   ($vm.spec.name).ToUpper()
                        'VM Power State'                =   $vm.spec.resources.power_state 
                        'VM Storage Capacity (GiB)'     =   $totalGB
                        'VM Storage Used (GiB)'         =   $usedGB
                        'VM Storage Free (GiB)'         =   $freeGB
                        'VM Storage Used %'             =   $usedPct
                        'VM Memory'                     =   $Ram
                        'VM Storage Datastore(s)'       =   if ($containers.Count -gt 0) { ($containers -join ", ") } else { "N/A" }
                    }

                    Write-Host "Data collected [$(($vm.spec.name).ToUpper())]"

                }

            }

            if (!$totalVMsFound) {$totalVMsFound = $vmResponse.metadata.total_matches}
            $offsetby += 500

        } while ($count -lt $totalVMsFound)

    }

    return $response

}

function Export-ADCOReport {
    
    [CmdletBinding()]
    param (

        [Parameter(Mandatory = $true)]
        [String] $Jumphost,

        [Parameter(Mandatory = $true)]
        [String[]] $ComputerName

    )

    $COs    = Get-ADCOs -Jumphost $Jumphost -ComputerName $ComputerName
    $AdminUsers = Get-AdminUsers -ComputerName $ComputerName
    $AdminGroups = Get-AdminGroups -ComputerName $ComputerName


    $response = @()
    foreach ($co in $COs) {

        $admins = $AdminUsers | ? Server -eq $co.Name | select -Property 'Admin Name' | % {$_.'Admin Name'}
        $groups = $AdminGroups | ? Server -eq $co.Name | select -Property 'Group Name' | % {$_.'Group Name'}
        $memberships = $co.MemberOf | % { ($_ -split ',')[0].Substring(3) }

        $response += [PSCustomObject]@{

            Server                  =   $co.Name
            "AD DNS Name"           =   $co.DNS
            "AD IP Address"         =   $co.IPv4Address
            "AD CO Created"         =   $co.whenCreated
            "AD CO Location"        =   $co.CanonicalName
            "AD Lockout State"      =   $co.LockedOut
            "AD Password Changed"   =   $co.PasswordLastSet
            "AD Primary Group"      =   ($co.PrimaryGroup -split ',')[0].Substring(3)
            "AD Memberships"        =   $memberships
            "Guest OS Admins"       =   $admins -join "`n"
            "Guest OS Groups"       =   $groups -join "`n"
            
        }
    
    }

    return $response

}