From ebdc1e7df5e5edbeb59418d46d6e53b209766fa1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Mar 2019 15:53:58 +0100 Subject: [PATCH] additional functions --- Export-ScheduledTask.ps1 | 69 ++++ Export-ScriptsToModule.ps1 | 269 ++++++++++++++ Get-ADGroupMemberAddDate.ps1 | 70 ++++ Get-ADGroupMemberChangesHistory.ps1 | 83 +++++ Get-AccountFromSID.ps1 | Bin 0 -> 2752 bytes Get-AdministrativeEvents.ps1 | 213 +++++++++++ Get-ComputerInfo.ps1 | 501 +++++++++++++++++++++++++ Get-CurrentLoad.ps1 | 368 +++++++++++++----- Get-FailedScheduledTask.ps1 | 192 ++++++++++ Get-FirewallLog.ps1 | 432 ++++++++++++++++++++++ Get-FirewallRules.ps1 | 138 +++++++ Get-FolderSize.ps1 | 162 ++++++++ Get-InstalledSoftware.ps1 | 106 ++++++ Get-PendingReboot.ps1 | 66 ++++ Get-ReliabilityHistory.ps1 | 162 ++++++++ Get-SIDFromAccount.ps1 | 61 +++ Get-Shutdown.ps1 | 548 +++++++++++++++++++++++++++ Get-Uptime.ps1 | 40 ++ Get-WinEventArchivedIncluded.ps1 | 413 +++++++++++++++++++++ Invoke-Command2.ps1 | 185 ++++++++-- Invoke-NetworkCapture.ps1 | 245 ++++++++++++ Reset-KrbtgtAccount.ps1 | Bin 0 -> 63936 bytes Search-ADObjectACL.ps1 | 226 +++++++++++ Search-GPOSetting.ps1 | 96 +++++ Shutdown-Computer.ps1 | 286 ++++++++++++++ Start-TCPPortListener.ps1 | 118 ++++++ Test-Connection2.ps1 | 154 ++++++++ Test-Path2.ps1 | 60 +++ Test-Port.ps1 | Bin 0 -> 16440 bytes Watch-EventLog.ps1 | 243 ++++++++++++ Write-Log.ps1 | 555 ++++++++++++++++++++++++++++ 31 files changed, 5932 insertions(+), 129 deletions(-) create mode 100644 Export-ScheduledTask.ps1 create mode 100644 Export-ScriptsToModule.ps1 create mode 100644 Get-ADGroupMemberAddDate.ps1 create mode 100644 Get-ADGroupMemberChangesHistory.ps1 create mode 100644 Get-AccountFromSID.ps1 create mode 100644 Get-AdministrativeEvents.ps1 create mode 100644 Get-ComputerInfo.ps1 create mode 100644 Get-FailedScheduledTask.ps1 create mode 100644 Get-FirewallLog.ps1 create mode 100644 Get-FirewallRules.ps1 create mode 100644 Get-FolderSize.ps1 create mode 100644 Get-InstalledSoftware.ps1 create mode 100644 Get-PendingReboot.ps1 create mode 100644 Get-ReliabilityHistory.ps1 create mode 100644 Get-SIDFromAccount.ps1 create mode 100644 Get-Shutdown.ps1 create mode 100644 Get-Uptime.ps1 create mode 100644 Get-WinEventArchivedIncluded.ps1 create mode 100644 Invoke-NetworkCapture.ps1 create mode 100644 Reset-KrbtgtAccount.ps1 create mode 100644 Search-ADObjectACL.ps1 create mode 100644 Search-GPOSetting.ps1 create mode 100644 Shutdown-Computer.ps1 create mode 100644 Start-TCPPortListener.ps1 create mode 100644 Test-Connection2.ps1 create mode 100644 Test-Path2.ps1 create mode 100644 Test-Port.ps1 create mode 100644 Watch-EventLog.ps1 create mode 100644 Write-Log.ps1 diff --git a/Export-ScheduledTask.ps1 b/Export-ScheduledTask.ps1 new file mode 100644 index 0000000..4ed30e8 --- /dev/null +++ b/Export-ScheduledTask.ps1 @@ -0,0 +1,69 @@ +function Export-ScheduledTasks { + <# + .SYNOPSIS + Exportuje scheduled tasky ve formě XML souborů. + + .DESCRIPTION + Ve výchozím nastavení exportuje tasky z rootu do adresáře C:\temp\backup. + + .PARAMETER Computername + Stroje ze kterých se budou zálohovat scheduled tasky. + + .PARAMETER TaskPath + Cesta ze které se budou exportovat tasky. Výchozí hodnota je "\" tedy root. Zapisovat ve tvaru "\Správa" "\Microsoft\Windows" atp. + + .PARAMETER BackupPath + Kam se budou XML ukládat. + + .EXAMPLE + Export-ScheduledTasks + Vyexportuje tasky z rootu do adresáře C:\temp\backup + + .EXAMPLE + Export-ScheduledTasks -comp sirene01 -taskPath "\Správa" + Vyexportuje tasky z "\Správa" do adresáře C:\temp\backup na stroji sirene01 + + .NOTES + Author: Ondřej Šebela - ztrhgf@seznam.cz + + .LINK + about_functions_advanced + + .LINK + about_comment_based_help + #> + [CmdletBinding()] + param + ( + [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + $Computername = $env:COMPUTERNAME + , + [Parameter(Position = 1)] + [ValidateNotNull()] + [ValidatePattern('(?# Cesta musí začít znakem \)^\\')] # kontrola ze zacina lomitkem + $TaskPath = "\" + , + [Parameter(Position = 2)] + [ValidateNotNull()] + [ValidateScript( {Test-Path $_})] # kontrola jestli cesta existuje + # [ValidateScript({$_ -match "^\\\\\w+\\\w+"})] # kontrola jestli jde o UNC cestu + $BackupPath = "C:\temp\backup" + + ) + + PROCESS { + ForEach ($Computer in $Computername) { + if (!(Test-Path $BackupPath )) { New-Item -type directory "$BackupPath" } + $sch = New-Object -ComObject("Schedule.Service") + $sch.Connect("$Computer") + $tasks = $sch.GetFolder("$TaskPath").GetTasks(0) + $tasks | % { + $xml = $_.Xml + $task_name = $_.Name + $outfile = "$BackupPath\{0}.xml" -f $task_name + $xml | Out-File $outfile + } + } + } +} diff --git a/Export-ScriptsToModule.ps1 b/Export-ScriptsToModule.ps1 new file mode 100644 index 0000000..8bbf9dc --- /dev/null +++ b/Export-ScriptsToModule.ps1 @@ -0,0 +1,269 @@ +function Export-ScriptsToModule { + <# + .SYNOPSIS + Funkce pro vytvoreni PS modulu z PS funkci ulozenych v ps1 souborech v zadanem adresari. + + !!! Aby se v generovanych modulech korektne exportovaly funkce je potreba, + mit funkce ulozene v ps1 souboru se shodnym nazvem (Invoke-Command2 funkci v Invoke-Command2.ps1 souboru) + + !!! POZOR v PS konzoli musi byt vybran font, ktery nekazi UTF8 znaky, jinak zpusobuje problemy!!! + + .PARAMETER configHash + Hash obsahujici dvojice, kde klicem je cesta k adresari se skripty a hodnotou cesta k adresari, do ktereho se vygeneruje modul. + napr.: @{"$PowershellProfileStore\scripts" = "$PowershellProfileStore\Modules\Scripts"} + + .PARAMETER enc + Jake kodovani se ma pouzit pro vytvareni modulu a cteni skriptu + + Vychozi je UTF8. + + .PARAMETER includeUncommitedUntracked + Vyexportuje i necomitnute a untracked funkce z repozitare + + .PARAMETER dontCheckSyntax + Prepinac rikajici, ze se u vytvoreneho modulu nema kontrolovat syntax. + Kontrola muze byt velmi pomala, pripadne mohla byt uz provedena v ramci kontroly samotnych skriptu + + .EXAMPLE + Export-ScriptsToModule @{"C:\DATA\POWERSHELL\repo\CVT_repo\scripts\scripts" = "c:\DATA\POWERSHELL\repo\CVT_repo\modules\Scripts"} + #> + + [CmdletBinding()] + param ( + [ValidateNotNullOrEmpty()] + $configHash + , + $enc = 'utf8' + , + [switch] $includeUncommitedUntracked + , + [switch] $dontCheckSyntax + ) + + if (!(Get-Command Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue) -and !$dontCheckSyntax) { + Write-Warning "Syntaxe se nezkontroluje, protoze neni dostupna funkce Invoke-ScriptAnalyzer (soucast modulu PSScriptAnalyzer)" + } + function _generatePSModule { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $scriptFolder + , + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + $moduleFolder + , + [switch] $includeUncommitedUntracked + ) + + if (!(Test-Path $scriptFolder)) { + throw "Cesta $scriptFolder neexistuje" + } + + $modulePath = Join-Path $moduleFolder ((Split-Path $moduleFolder -Leaf) + ".psm1") + $function2Export = @() + $alias2Export = @() + $lastCommitFileContent = @{} + # necomitnute zmenene skripty a untracked do modulu nepridam, protoze nejsou hotove + $location = Get-Location + Set-Location $scriptFolder + $unfinishedFile = @() + try { + # necomitnute zmenene soubory + $unfinishedFile += @(git ls-files -m --full-name) + # untracked + $unfinishedFile += @(git ls-files --others --exclude-standard --full-name) + } catch { + throw "Zrejme neni nainstalovan GIT, nepodarilo se ziskat seznam zmenenych souboru v repozitari $scriptFolder" + } + Set-Location $location + + # + # existuji modifikovane necomitnute/untracked soubory + # abych je jen tak nepreskocil pri generovani modulu, zkusim dohledat verzi z posledniho commitu a tu pouzit + if ($unfinishedFile) { + [System.Collections.ArrayList] $unfinishedFile = @($unfinishedFile) + + # Start-Process2 umi vypsat vystup (vcetne chyb) primo do konzole, takze se da pres Select-String poznat, jestli byla chyba + function Start-Process2 { + [CmdletBinding()] + param ( + [string] $filePath = 'notepad.exe', + [string] $argumentList = '/c dir', + [string] $workingDirectory = (Get-Location) + ) + + $p = New-Object System.Diagnostics.Process + $p.StartInfo.UseShellExecute = $false + $p.StartInfo.RedirectStandardOutput = $true + $p.StartInfo.RedirectStandardError = $true + $p.StartInfo.WorkingDirectory = $workingDirectory + $p.StartInfo.FileName = $filePath + $p.StartInfo.Arguments = $argumentList + [void]$p.Start() + # $p.WaitForExit() # s timto pokud git show HEAD:$file neco vratilo, se proces nikdy neukoncil.. + $p.StandardOutput.ReadToEnd() + $p.StandardError.ReadToEnd() + } + + Set-Location $scriptFolder + $unfinishedFile2 = $unfinishedFile.Clone() + $unfinishedFile2 | % { + $file = $_ + $lastCommitContent = Start-Process2 git "show HEAD:$file" + if (!$lastCommitContent -or $lastCommitContent -match "exists on disk, but not in 'HEAD'") { + Write-Warning "Preskakuji zmeneny ale necomitnuty/untracked soubor: $file" + } else { + $fName = [System.IO.Path]::GetFileNameWithoutExtension($file) + # upozornim, ze pouziji verzi z posledniho commitu, protoze aktualni je nejak upravena + Write-Warning "$fName ma necommitnute zmeny. Pro vygenerovani modulu pouziji jeho verzi z posledniho commitu" + # ulozim obsah souboru tak jak vypadal pri poslednim commitu + $lastCommitFileContent.$fName = $lastCommitContent + # z $unfinishedFile odeberu, protoze obsah souboru pridam, i kdyz z posledniho commitu + $unfinishedFile.Remove($file) + } + } + Set-Location $location + + # unix / nahradim za \ + $unfinishedFile = $unfinishedFile -replace "/", "\" + $unfinishedFileName = $unfinishedFile | % { [System.IO.Path]::GetFileName($_) } + + if ($includeUncommitedUntracked -and $unfinishedFileName) { + Write-Warning "Vyexportuji i tyto zmenene, ale necomitnute/untracked funkce: $($unfinishedFileName -join ', ')" + $unfinishedFile = @() + } + } + + # + # v seznamu ps1 k exportu do modulu ponecham pouze ty, ktere jsou v konzistentnim stavu + # odfiltruji sablony funkci (zacinaji _) a skripty/funkce, ktere delaji v modulu problemy (CredMan) + $script2Export = (Get-ChildItem (Join-Path $scriptFolder "*.ps1") -File).FullName | where { + $fName = [System.IO.Path]::GetFileNameWithoutExtension($_) + if (($unfinishedFile -and $unfinishedFile -match ($_ -replace "\\", "\\" -replace "\.", "\.")) -or $fName -match "^_.*" -or $fName -match "^CredMan") { + return $false + } else { + return $true + } + } + + if (!$script2Export -and $lastCommitFileContent.Keys.Count -eq 0) { + return "V $scriptFolder neni zadna vyhovujici funkce k exportu do $moduleFolder. Ukoncuji" + } + + # smazu existujici modul + if (Test-Path $modulePath -ErrorAction SilentlyContinue) { + Remove-Item $moduleFolder -Recurse -Confirm:$false -ErrorAction SilentlyContinue + } + + # vytvorim slozku modulu + [Void][System.IO.Directory]::CreateDirectory($moduleFolder) + + Write-Verbose "Do $modulePath`n" + + # do hashe $lastCommitFileContent pridam dvojice, kde klic je jmeno funkce a hodnotou jeji textova definice + $script2Export | % { + $fName = [System.IO.Path]::GetFileNameWithoutExtension($_) + if (!$lastCommitFileContent.containsKey($fName)) { + # obsah souboru z disku pridam pouze pokud jiz neni pridan, abych si neprepsal fce vytazene z posledniho commitu + $content = Get-Content $_ -Encoding $enc + $lastCommitFileContent.$fName = $content + } + } + + # + # z hodnot v hashi (jmeno funkce a jeji textovy obsah) vygeneruji psm modul + # poznacim jmeno funkce a pripadne aliasy pro Export-ModuleMember + $lastCommitFileContent.GetEnumerator() | % { + $fName = $_.Key + $content = $_.Value + + Write-Verbose "- exportuji funkci: $fName" + + $function2Export += $fName + # ulozim si pripadne nastaveni aliasu (Set-Alias), pro Export-ModuleMember + $setAliasRow = $content | where {$_ -match "^\s*Set-Alias"} + $setAliasRow | % { + $parts = $_ -split "\s+" + + if ($_ -match "-na") { + # alias nastaven jmennym parametrem + # ziskam hodnotu parametru + $i = 0 + $parPosition + $parts | % { + if ($_ -match "-na") { + $parPosition = $i + } + ++$i + } + + $alias2Export += $parts[$parPosition + 1] + Write-Verbose "- exportuji alias: $($parts[$parPosition + 1])" + } else { + # alias nastaven pozicnim parametrem + $alias2Export += $parts[1] + Write-Verbose "- exportuji alias: $($parts[1])" + } + } + + # odstraneni requires pozadavku na verzi + $content = $content -replace "^#requires -version \d+[\d\.\d]*" # konci cislem nebo cislo.cislo + + $content | Out-File $modulePath -Append $enc + #"#endregion" | Out-File $modulePath -Append $enc + "" | Out-File $modulePath -Append $enc + } + + # nastavim, co se ma z modulu exportovat + # rychlejsi (pri naslednem importu modulu) je, pokud se exportuji jen explicitne vyjmenovane funkce/aliasy nez pouziti * + # 300ms vs 15ms :) + + if (!$function2Export) { + throw "Neexistuji zadne funkce k exportu! Spatne zadana cesta??" + } else { + if ($function2Export -match "#") { + Remove-Item $modulePath -recurse -force -confirm:$false + throw "Exportovane funkce obsahuji v nazvu nepovoleny znak #. Modul jsem smazal." + } + + "Export-ModuleMember -function $($function2Export -join ', ')" | Out-File $modulePath -Append $enc + } + + if ($alias2Export) { + if ($alias2Export -match "#") { + Remove-Item $modulePath -recurse -force -confirm:$false + throw "Exportovane aliasy obsahuji v nazvu nepovoleny znak #. Modul jsem smazal." + } + "Export-ModuleMember -alias $($alias2Export -join ', ')" | Out-File $modulePath -Append $enc + } + } # konec funkce _generatePSModule + + # ze skriptu vygeneruji modul + $configHash.GetEnumerator() | % { + $scriptFolder = $_.key + $moduleFolder = $_.value + + $param = @{ + scriptFolder = $scriptFolder + moduleFolder = $moduleFolder + verbose = $true + } + if ($includeUncommitedUntracked) { + $param["includeUncommitedUntracked"] = $true + } + + Write-Output "Generuji modul $moduleFolder ze skriptu v $scriptFolder" + _generatePSModule @param + + if (!$dontCheckSyntax -and (Get-Command Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue)) { + # zkontroluji syntax vytvoreneho modulu + $syntaxError = Invoke-ScriptAnalyzer $moduleFolder -Severity Error + if ($syntaxError) { + Write-Warning "V modulu $moduleFolder byly nalezeny tyto problemy:" + $syntaxError + } + } + } +} \ No newline at end of file diff --git a/Get-ADGroupMemberAddDate.ps1 b/Get-ADGroupMemberAddDate.ps1 new file mode 100644 index 0000000..dc8991e --- /dev/null +++ b/Get-ADGroupMemberAddDate.ps1 @@ -0,0 +1,70 @@ +function Get-ADGroupMemberAddDate { + <# + .SYNOPSIS + Vypise kdy byl dany uzivatel/skupina pridan do skupin, jichz je aktualne clenem. + Informace ziskava z replikacnich metadat. + + .DESCRIPTION + Vypise kdy byl dany uzivatel/skupina pridan do skupin, jichz je aktualne clenem. + Informace ziskava z replikacnich metadat. + + .PARAMETER userName + Jmeno AD uzivatele, jehoz vysledky mne zajimaji. + + .PARAMETER groupName + Jmeno AD skupiny, jejiz vysledky mne zajimaji. + + .PARAMETER server + Z jakeho serveru se maji ziskat replikacni metadata. + + Vychozi je PDC emulator v AD. + + .EXAMPLE + Get-ADGroupMemberAddDate -username sebela + + Vypise kdy byl ad ucet sebela pridan do AD skupin, jichz je aktualne clenem. + + .NOTES + cerpano z https://blogs.technet.microsoft.com/ashleymcglone/2012/10/17/ad-group-history-mystery-powershell-v3-repadmin/ + #> + + [CmdletBinding(DefaultParameterSetName = 'Default')] + param ( + [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "Default")] + [ValidateNotNullOrEmpty()] + $userName + , + [Parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "Group")] + [ValidateNotNullOrEmpty()] + $groupName + , + [ValidateNotNullOrEmpty()] + [string] $server = (Get-ADDomainController -Discover | Select-Object -ExpandProperty HostName) + ) + + if ($userName) { + try { + $obj = Get-ADUser $userName -ErrorAction Stop + } catch { + throw "Zadany uzivatel nebyl v AD nalezen" + } + + $objectMemberOf = Get-ADUser $obj.DistinguishedName -Properties memberOf + } else { + try { + $obj = Get-ADGroup $groupName -ErrorAction Stop + } catch { + throw "Zadana skupina nebyla v AD nalezena" + } + + $objectMemberOf = Get-ADGroup $obj.DistinguishedName -Properties memberOf + } + + $objectMemberOf | Select-Object -ExpandProperty memberOf | + ForEach-Object { + Get-ADReplicationAttributeMetadata $_ -Server $server -ShowAllLinkedValues | + Where-Object {$_.AttributeName -eq 'member' -and + $_.AttributeValue -eq $obj.DistinguishedName} | + Select-Object @{n = 'Added'; e = {$_.FirstOriginatingCreateTime}}, Object + } | Sort-Object Added -Descending +} \ No newline at end of file diff --git a/Get-ADGroupMemberChangesHistory.ps1 b/Get-ADGroupMemberChangesHistory.ps1 new file mode 100644 index 0000000..c644e46 --- /dev/null +++ b/Get-ADGroupMemberChangesHistory.ps1 @@ -0,0 +1,83 @@ +Function Get-ADGroupMemberChangesHistory { + <# + .SYNOPSIS + Vypise historii zmen ve clenstvi dane AD skupiny. + Informace ziskava z replikacnich metadat. + + .DESCRIPTION + Vypise historii zmen ve clenstvi dane AD skupiny. + Informace ziskava z replikacnich metadat. + Pro kazdeho clena zobrazuje pouze jednu posledni kaci (pridani/odebrani) + + .PARAMETER groupName + Jmeno AD skupiny. + + .PARAMETER hour + Jak stare zmeny clenstvi me zajimaji. + + Vychozi je 24 hodin. + + .PARAMETER server + Z jakeho serveru se maji ziskat replikacni metadata. + + Vychozi je PDC emulator v AD. + + .PARAMETER rawOutput + Prepinac rikajici, ze se maji vypsat vsechny dostupne atributy. + Muze byt dobre pri diagnostice? + + .EXAMPLE + Get-ADGroupMemberChangesHistory -groupName ucebnyRemoteDesktop + + Vypise zmeny ve skupine ucebnyRemoteDesktop za poslednich 24 hodin. + + .EXAMPLE + Get-ADGroupMemberChangesHistory -groupName ucebnyRemoteDesktop -hour (365*24) + + Vypise zmeny ve skupine ucebnyRemoteDesktop za posledni rok. + + .NOTES + cerpano z https://blogs.technet.microsoft.com/ashleymcglone/2014/12/17/forensics-monitor-active-directory-privileged-groups-with-powershell/ + #> + + [CmdletBinding()] + Param ( + [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [string] $groupName + , + [int] $hour = 24 + , + [ValidateNotNullOrEmpty()] + [string] $server = (Get-ADDomainController -Discover | Select-Object -ExpandProperty HostName) + , + [switch] $rawOutput + ) + + begin { + Write-Warning "Vypise zmeny ve skupine $groupname za poslednich $hour hodin.`nPro kazdeho clena skupiny zobrazuje pouze jednu posledni akci!" + + try { + $group = Get-ADGroup $groupName -Property name, distinguishedname -ErrorAction Stop + } catch { + throw "Nepodarilo se dohledat informace ke skupine $groupName. Existuje?" + } + } + + process { + $Members = Get-ADReplicationAttributeMetadata -Object $Group.DistinguishedName -ShowAllLinkedValues -Server $server | + Where-Object {$_.IsLinkValue -and $_.AttributeName -eq 'member'} + + if (!$rawOutput) { + $members = $members | Select-Object @{name = 'Member'; expression = {$_.AttributeValue}}, @{name = 'Changed'; expression = {$_.LastOriginatingChangeTime}}, @{name = 'Action'; expression = { + if ($_.LastOriginatingDeleteTime -eq '1/1/1601 1:00:00 AM') {'added'} else {'removed'}} + } + } + + if (!$rawOutput) { + $Members | Where-Object {$_.Changed -gt (Get-Date).AddHours(-1 * $Hour)} | Sort-Object Changed -Descending + } else { + $Members | Where-Object {$_.LastOriginatingChangeTime -gt (Get-Date).AddHours(-1 * $Hour)} | Sort-Object LastOriginatingChangeTime -Descending + } + } +} \ No newline at end of file diff --git a/Get-AccountFromSID.ps1 b/Get-AccountFromSID.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..f7de1155c1ec0d157a080a149882b5e924a422e2 GIT binary patch literal 2752 zcmc(h%T5$g5Qd8-COm3s~jKG8eBOxqsXpp&QE|?jE@9L6;FW^G` z{yN=E4?)BmX42i&b*iiW`m0Wz`S$hDI##oywe8sINEv%>5pUD>1Z@$W*4y^Xn#ffv z*s85y^}$B$mJQn@8;hkZYdf}X802Lrkvc&u zJ1`@@J`No_cgnH^mu0-EpCat#^CG9|T(ji=#_{Cs6>@^4W z{N~Z#u?KdKwRxN6DeVIC0@^wMat;ewNq?W`f@^27&Yk!0pWvH@FYVZd^Hs!ah6pWc zDn9=u`eFQM;vB~8aV!OPciE9u5NJ2IZOJ?M!0$&kVUxBOTQ{g>ldFnWU&^C?yYy1B z61t&@#TvRAUNtG|eAl%_8%J)^J?f=AT4kP0xkXr>ve(#G+#;E7d2AYdQE^1k1us9h zUm|ke_o2KriPvSfD~pT1UU-xS$Rm)ousL#@QFfiCO+*hTJ2O2`uDdHoQ(TS@?C`0W z;a_3$(3xt1>M|~K1F@FluIrG!I7spj^`G?UlE*kb#<6`%rpe4%Bxg;>_jKxpuS9gQ zW@7;+!--Kfoix&S@UEJ6(TH_TL!A%moysLvtIn%Vvtv8t61oiCGYXD?K&*w%O{3c; zaJb~5jZy{dC+u*socO{~1s)qx=%1k+pv(LirzgP!ln4I3j z&e;i;WpF1Gqw4u+N=*OIi+D)pOAHq{5*jj{rd?2d)k~o;UAHBO`lP+);v-Nrt9{ + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "zadej jmeno stroje/ů")] + [Alias("c", "CN", "__Server", "IPAddress", "Server", "Computer", "SamAccountName")] + [ValidateNotNullOrEmpty()] + [String[]] $ComputerName = $env:computername + , + [Parameter(Mandatory = $false, Position = 2)] + [int] $Newest + , + [ValidateScript( { + If (($_.getType().name -eq "string" -and [DateTime]::Parse($_)) -or ($_.getType().name -eq "dateTime")) { + $true + } else { + Throw "Zadejte ve formatu dle vaseho culture. Pro cs-CZ napr.: 15.2.2019 15:00. Pro en-US pak prohodit den a mesic." + } + })] + [Alias("from")] + $After + , + [ValidateScript( { + If (($_.getType().name -eq "string" -and [DateTime]::Parse($_)) -or ($_.getType().name -eq "dateTime")) { + $true + } else { + Throw "Zadejte ve formatu dle vaseho culture. Pro cs-CZ napr.: 15.2.2019 15:00. Pro en-US pak prohodit den a mesic." + } + })] + [Alias("to")] + $Before + , + [ValidateNotNullOrEmpty()] + [string[]] $LogName + , + [switch] $JustLogNames + , + [ValidateSet(1, 2, 3)] + [ValidateNotNullOrEmpty()] + [array] $severity = @(1, 2, 3) + ) + + BEGIN { + #test + $ComputerName = $ComputerName | % {$_.tolower()} # PS 2.0 neumi tolower na [string[]] + if ($ComputerName -contains ($env:COMPUTERNAME).ToLower() -and !([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + Write-Error "Vyzaduje admin prava. Ukoncuji." + Break + } + + # ve vychozim stavu vypise udalosti za posledni den + if (!$after -and !$newest -and !$before) { + $after = (Get-Date).addDays(-1) + Write-Warning "Vyhledaji se udalosti za posledni den" + } + + if ($after -and $after.getType().name -eq "string") {$after = [DateTime]::Parse($after)} + if ($before -and $before.getType().name -eq "string") {$before = [DateTime]::Parse($before)} + + if ($after -and $before -and $after -gt $before) { + throw "From nesmi byt vetsi nez To" + } + if ($after -and $before -and $after -eq $before) { + throw "After je stejne jako before. Ukoncuji." + } + + $functionString = Get-FunctionString -Function Convert-DateToXmlDate, Format-XMLIndent + } + + PROCESS { + Invoke-Command2 -computerName $ComputerName { + param ($newest, $after, $before, $logName, $justLogNames, $functionString, $severity) + + if ($PSVersionTable.PSVersion.Major -lt 3) { + # pouzite funkce pouzivaji nepodporovane operatory atd + Write-Warning "Ukoncuji. Na $env:COMPUTERNAME je nepodporovana verze PS (je potreba alespon verze 3.0)" + return + } + + # dot sourcingem zpristupnim pomocne funkce z jejich textove definice + $scriptblock = [System.Management.Automation.ScriptBlock]::Create($functionString) + . $scriptblock + + ### vytvoreni XML dotazu + # zjistim vsechny dostupne logy + $allLogs = Get-WinEvent -ListLog * -ea silentlycontinue | select isenabled, logname + if (!$allLogs) { throw "Na $env:COMPUTERNAME se nepodarilo ziskat seznam logu" } + + # do include davejte Microsoft-* logy, ktere, chcete do vysledku zahrnout (koncici /Admin se pridavaji automaticky) + # obsah include je potreba (pri vydani noveho OS) aktualizovat + # pozn.: bohuzel se nedaji pridat vsechny dostupne logy, protoze je horni limit na jejich pocet v XML query + $include = 'Microsoft-AppV-Client/Virtual Applications', 'Microsoft-Windows-DataIntegrityScan/CrashRecovery', 'Microsoft-Windows-WindowsBackup/ActionCenter', "Microsoft-Windows-Hyper-V-VMMS-Networking", "Microsoft-Windows-Hyper-V-VMMS-Storage", 'Microsoft-Windows-StorageSpaces-Driver/Operational', 'Microsoft-Windows-Ntfs/Operational', 'Microsoft-Windows-Ntfs/WHC', 'Microsoft-Windows-Disk/Operational', 'Microsoft-Windows-Storage-Disk/Admin', 'Microsoft-Windows-Storage-Disk/Analytic', 'Microsoft-Windows-Storage-Disk/Debug', 'Microsoft-Windows-Storage-Disk/Operational' + $adminViewLogs = $allLogs | where { $_.isenabled -eq $true } | % { + if ($include -contains $_.logname) {$_} + elseif (($_.logname -match "^Microsoft-" -and $_.logname -notmatch '/Admin$') -or $_.logname -match 'ForwardedEvents') {} + else {$_} + } | select -exp logname + + Write-Verbose "Seznam logu k prohledani:`n$($adminViewLogs -join "`n")" + + # pridam zadane logy z LogName do seznamu logu, pokud existuji + if ($logName) { + foreach ($log in $logName) { + if ($allLogs.logname -contains $log) { + $adminViewLogs += $log + } else { + Write-Warning "Zadany log $log z parametru LogName na $env:COMPUTERNAME neexistuje, ignoruji" + } + } + } + + # zajimaji mne pouze nazvy logu, ze kterych budu vypisovat chyby + if ($justLogNames) { + return New-Object PSObject -Property ([Ordered]@{Computer = $env:COMPUTERNAME; Logs = $adminViewLogs }) + } + + # vygeneruji XML filtr pro jednotlive logy + # vracim pouze Warning, Error a Critical udalosti + $severity | % { + if ($severityFilter) {$severityFilter += " or "} + $severityFilter += "Level=$_" + } + $adminViewLogs | % { $filterLogs += "" } + + # zakladni XML dotaz + [xml] $xml = " + + + $filterLogs + + + " + # pridani filtrovani dle data do XML dotazu + if ($after -and $before) { + $startDate = Convert-DateToXmlDate $after + $endDate = Convert-DateToXmlDate $before + $dateFilter = " and TimeCreated[@SystemTime>=`'$startDate`' and @SystemTime<=`'$endDate`']]]" + } elseif ($before) { + $endDate = Convert-DateToXmlDate $before + $dateFilter = " and TimeCreated[@SystemTime<=`'$endDate`']]]" + } elseif ($after) { + $startDate = Convert-DateToXmlDate $after + $dateFilter = " and TimeCreated[@SystemTime>=`'$startDate`']]]" + } + if ($dateFilter) { + # upravim kazdy select v XML v nodu Query + for ($i = 0; $i -lt $xml.QueryList.Query.Select.'#text'.length; $i++) { + $xml.QueryList.Query.childnodes.item($i).'#text' = $xml.QueryList.Query.childnodes.item($i).'#text'.replace(']]', $dateFilter) + } + } + + Write-Verbose "Vysledny XML dotaz:`n$(Format-XMLIndent $xml)" + + ### nachystani parametru pro Get-WinEvent + $params = @{ + erroraction = 'silentlycontinue' # nekdy se objevovaly nonterminating chyby s chybejicimi popisky u eventu atd + FilterXml = $xml + } + # omezeni na pocet vracenych zaznamu + if ($newest) { + $params.MaxEvents = $newest + } + + ### vypsani pozadovanych udalosti ze systemoveho logu + Get-WinEvent @params | Select-Object @{n = 'Computer'; e = {$_.Machinename}}, Message, TimeCreated, Id, LevelDisplayName, LogName, ProviderName + } -argumentList $newest, $after, $before, $LogName, $JustLogNames, $functionString, $severity | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId + } +} \ No newline at end of file diff --git a/Get-ComputerInfo.ps1 b/Get-ComputerInfo.ps1 new file mode 100644 index 0000000..57ca4e2 --- /dev/null +++ b/Get-ComputerInfo.ps1 @@ -0,0 +1,501 @@ +<# +TODO: +dodelat propertyset pro snadne filtrovani informaci +#> +Function Get-ComputerInfo { + <# + .SYNOPSIS + Fce pro získání základních informací o stroji. + + .DESCRIPTION + Fce využívá WMI pro získání informací o HW (NIC, CPU, MB, BIOS, RAM, HDD,...), ale i OS (kdo je přihlášen, kdy byl OS nainstalován, verze, sdílené složky, uživatelé, administrátoři, tiskárny,...). Informace je možné získat i z remote strojů. Výpis do OGV nezobrazí všechny informace! + + .PARAMETER COMPUTERNAME + Parametr udávající seznam strojů, pro získání informací. + + .PARAMETER DETAILED + Switch určující množství vypsaných informací. + + .EXAMPLE + Get-ComputerInfo + + Vypíše informace o tomto stroji. + + .EXAMPLE + Get-ComputerInfo -ComputerName sirene13 + + Vypíše informace o stroji sirene13 + + .EXAMPLE + Get-ComputerInfo -ComputerName $b311 -detailed + + Vypíše detailní informace o strojích v B311. + + .EXAMPLE + Get-ComputerInfo -ComputerName $b311 -detailed | select computername,"OS version","CPU Name","Users" + + Vypíše hostname,verzi OS, jméno CPU a seznam lokálních uživatelů na strojích v B311. + + .EXAMPLE + Get-ComputerInfo -ComputerName $b311 -detailed | where {$_.users -like "*_naiada10*"} | select computername,"Users" + + Vypíše hostname a seznam lokálních uživatelů na strojích v B311, které mezi uživateli mají účet se jménem _naiada10. + + .NOTES + Author: Ondřej Šebela - ztrhgf@seznam.cz + #> + + [CmdletBinding()] + param ( + [Parameter(Position = 0, ValueFromPipeline = $true)] + [Alias("CN", "Computer")] + [ValidateNotNullOrEmpty()] + [String[]] $computerName = "$env:COMPUTERNAME" + , + [switch] $detailed + # , + # [ValidateSet("start","unexpected_shutdown","shutdown_or_restart","bsod","wake_up","sleep")] + # $Filter = @("start","unexpected_shutdown","shutdown_or_restart","bsod","wake_up","sleep") + ) + + BEGIN { + } + + PROCESS { + Invoke-Command2 $ComputerName -ArgumentList $detailed, $win10Version { + param ($detailed, $win10Version) + + $computer = $env:COMPUTERNAME + # Vytvoření objektu, do kterého později vložím property definované v hashtable $ht + $object = New-Object PSObject + # Vytvoření seřazeného hash table pro ukládání property a jejich hodnot + $ht = [ordered]@{} + + if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + ++$hasAdminRights + } + + + $ErrorActionPreference = 'SilentlyContinue' + + ### ziskani WMI dat + $WMI_CPU = Get-WmiObject -Class Win32_Processor + $WMI_BIOS = Get-WmiObject -Class Win32_BIOS + $WMI_BASEBOARD = Get-WmiObject -Class Win32_BaseBoard + $WMI_CS = Get-WmiObject -Class Win32_ComputerSystem + $WMI_OS = Get-WmiObject -Class Win32_OperatingSystem + $WMI_PMA = Get-WmiObject -Class win32_PhysicalMemoryArray + $WMI_PM = Get-WmiObject -Class Win32_PhysicalMemory + $WMI_HDD = Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType = '3'" + $WMI_HDD2 = Get-WmiObject -Class Win32_DiskDrive + $WMI_PARTITION = Get-WmiObject -Class Win32_DiskPartition + if ($detailed) { + # vcetne virtualnich adapteru + $WMI_NIC = Get-WmiObject -Class Win32_NetworkAdapter | where {$_.PhysicalAdapter -eq $true} + } else { + $WMI_NIC = Get-WmiObject -Class Win32_NetworkAdapter | where {$_.PhysicalAdapter -eq $true -and $_.PNPDeviceID -notlike "ROOT\*"} + } + $WMI_NAC = Get-WmiObject -Class Win32_NetworkAdapterConfiguration | where {$_.IPEnabled -eq $true -or $_.Caption -like "*Hyper-V*" -or $_.MACAddress} + $WMI_NICDRIVER = Get-WmiObject -Class win32_pnpsigneddriver -Filter "deviceclass='net'" + $WMI_GPU = Get-WmiObject -Class Win32_VideoController + $WMI_PageFile = Get-WmiObject Win32_PageFileusage | Select-Object Name, AllocatedBaseSize, PeakUsage + if ($detailed) { + $WMI_MONITOR = Get-WmiObject WmiMonitorID -Namespace root\wmi + $WMI_DD = Get-WmiObject Win32_DiskDrive + $WMI_DD2 = Get-WmiObject -namespace root\wmi –class MSStorageDriver_FailurePredictStatus | Select InstanceName, PredictFailure, Reason + # Write-Progress -ParentId 1 -Activity "Collecting Data: Win32_UserAccount" -Status "Percent Complete: $([int](($n/$d)*100))%" -PercentComplete (($n/$d)*100);$n++ + $WMI_LU = Get-WmiObject -Class Win32_UserAccount -Namespace "root\cimv2" -Filter "LocalAccount='$True'" + # Write-Progress -ParentId 1 -Activity "Collecting Data: Win32_Printer" -Status "Percent Complete: $([int](($n/$d)*100))%" -PercentComplete (($n/$d)*100);$n++ + $WMI_PRT = Get-WmiObject -Class Win32_Printer + # Write-Progress -ParentId 1 -Activity "Collecting Data: Win32_PrintJob" -Status "Percent Complete: $([int](($n/$d)*100))%" -PercentComplete (($n/$d)*100);$n++ + #$WMI_PJ = Get-WmiObject "Win32_PrintJob" + # Write-Progress -ParentId 1 -Activity "Collecting Data: Win32_Share" -Status "Percent Complete: $([int](($n/$d)*100))%" -PercentComplete (($n/$d)*100);$n++ + $WMI_SF = Get-WmiObject -Class Win32_Share + #$WMI_DRIVERS = Get-WmiObject Win32_PnPSignedDriver | where {$_.driverversion -ne $null} | select DeviceName, DriverVersion | sort devicename + if ($hasAdminRights) { $WMI_BITLOCKER = Get-WmiObject -namespace root\CIMv2\Security\MicrosoftVolumeEncryption -class Win32_EncryptableVolume } + $TPM = Get-WMIObject –class Win32_Tpm –Namespace root\cimv2\Security\MicrosoftTpm + } + + #Write-Progress -ParentId 1 -Activity "Collecting Data: MSFT_DISK" -Status "Percent Complete: $([int](($n/$d)*100))%" -PercentComplete (($n/$d)*100);$n++ + #$WMI_MSFT = Get-WmiObject -Class MSFT_DISK -Namespace ROOT\Microsoft\Windows\Storage -computername $Computer | select FriendlyName,IsBoot + + #region zjisteni zdali je potreba restart + $WinBuild = $WMI_OS.BuildNumber + $CBSRebootPend, $RebootPending = $false, $false + if ([int]$WinBuild -ge 6001) { + $CBSRebootPend = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing' | where {$_.pschildname -like 'RebootPending'} + $OSArchitecture = $WMI_OS.OSArchitecture + } else { + $OSArchitecture = "**Unavailable**" + } + + # Querying Session Manager for both 2K3 & 2K8 for the PendingFileRenameOperations REG_MULTI_SZ to set PendingReboot value. + $RegValuePFRO = Get-ItemProperty 'HKLM:\system\CurrentControlSet\Control\Session Manager\' | select -exp pendingFileRenameOperations + + # Querying WindowsUpdate\Auto Update for both 2K3 & 2K8 for "RebootRequired" + $WUAURebootReq = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update' | where {$_.pschildname -like 'RebootRequired'} + + If ($CBSRebootPend -or $RegValuePFRO -or $WUAURebootReq) { + $RebootPending = $true + } + + + ### naplneni objektu ziskanymi informacemi + $ht.add("ComputerName", $computer.ToUpper()) + $ht.add("Domain", $WMI_CS.Domain) + if ($detailed) { + if ($BSOD = Get-WinEvent -FilterHashtable @{logname = "system"; providername = "Microsoft-Windows-WER-SystemErrorReporting"; id = "1001"} | select-object -property timecreated) { + $ht.add("BSOD Count", $BSOD.count) + $ht.add("BSOD Times", $BSOD.timecreated) + } + } + + # dostupne jazyky (per user bych musel vytahnout z jeho registru) + $language = $WMI_OS.MUILanguages -join ", " + + $ht.add("OS Name", $WMI_OS.Caption + " ($language) " + $OSArchitecture) + if ($detailed) { $ht.add('OS System Drive', $WMI_OS.SystemDrive) } + if ($detailed) { $ht.add('OS System Device', $WMI_OS.SystemDevice) } + + # 1709, 1603 atp (tvar: rokmesic) + $4digit_os_version = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ReleaseId).ReleaseId + + [version] $detailedOsVersion = [System.Environment]::OSVersion.Version + + # $win10Version pochazi z modulu Computers a obsahuje jmeno a 4 ciselne oznaceni windows 10 verzi + $human_os_version = "unknown" + if ($win10Version) { + try { + $human_os_version = $win10Version[$WMI_OS.Version] + } catch { + Write-Warning "`$win10Version neobsahuje pozadovanou verzi Windows 10 ($($WMI_OS.Version)). Doplnte je v modulu Computers" + } + } else { + Write-Warning "Neni naimportovan modul Computers obsahujici potrebnou promennou `$win10Version" + } + + $ht.add("OS Version", ('{0} ({1} ({2}))' -f $detailedOsVersion.ToString(), $human_os_version, $4digit_os_version)) + if ($detailed) { $ht.add('OS Service Pack', [string]$WMI_OS.ServicePackMajorVersion + '.' + $WMI_OS.ServicePackMinorVersion) } + if ($detailed) { $ht.add('OS Language', $WMI_OS.OSLanguage) } + $ht.add('OS Boot Time', $WMI_OS.ConvertToDateTime($WMI_OS.LastBootUpTime)) + $ht.add('OS Install Date', $WMI_OS.ConvertToDateTime($WMI_OS.InstallDate)) + if ($detailed) { $ht.add('PageFile location', $WMI_PageFile.name) } + $ht.add('PageFile size (MB)', $WMI_PageFile.AllocatedBaseSize) + if ($detailed) { $ht.add('PageFile peak usage (MB)', $WMI_PageFile.PeakUsage)} + $ht.add("Computer Hardware Manufacturer", $WMI_CS.Manufacturer) + $ht.add("Computer Hardware Model", $WMI_CS.Model) + $ht.add("BaseBoardManufacturer", $WMI_BASEBOARD.Manufacturer) + $ht.add("BaseBoardName", $WMI_BASEBOARD.Product) + $ht.add("BaseBoardSN", $WMI_BASEBOARD.SerialNumber) + $ht.add("BaseBoardStatus", $WMI_BASEBOARD.Status) + $ht.add("RebootPending", $RebootPending) + if ($detailed) { $ht.add("RebootPendingKey", $RegValuePFRO) } + $ht.add("CBSRebootPending", $CBSRebootPend) + $ht.add("WinUpdRebootPending", $WUAURebootReq) + + # HDD + if ($WMI_HDD) { + $WMI_HDD | Select 'DeviceID', 'Size', 'FreeSpace' | Foreach { + $ht.add("HDD Volume $($_.DeviceID)", ('' + ($_.FreeSpace / 1GB).ToString('N') + ' GB free of ' + ($_.Size / 1GB).ToString('N') + ' GB')) # with + ($_.Size/1GB - $_.FreeSpace/1GB).ToString('N') +' GB Used Space' + } + } + # HDD 2 + if ($HDDModel = $WMI_HDD2 | where {$_.InterfaceType -notlike "*USB*"} | select -exp model | sort) { + # pouzivam v monitor_HW_changes + $ht.add("HDDs", $HDDModel) + } + + # BITLOCKER + if ($Detailed) { + if ($WMI_BITLOCKER) { + $WMI_BITLOCKER | select DriveLetter, IsVolumeInitializedforProtection | ? {$_.DriveLetter} | sort DriveLetter| % { + $ht.add("Bitlocker on $($_.DriveLetter)", $_.IsVolumeInitializedforProtection) + } + } + if (!$hasAdminRights) { + Write-Warning "Bez admin prav neni mozne zjistit stav Bitlockeru" + } + } + + # ziskani IP adres pro dany stroj dle jeho DNS jmena + if ($ips = [System.Net.Dns]::GetHostAddresses($computer) | foreach { $_.IPAddressToString} ) { + $ht.add('IP Address(es) from DNS', ($ips -join ', ')) + } else { + $ht.add('IP Address from DNS', 'Could not resolve') + } + + # NIC + if ($WMI_NIC) { + $i = 1 + $WMI_NIC | Foreach { + $index = $_.Index + $name = $_.name + $NetAdap = $WMI_NAC | Where-Object {$index -eq $_.Index} + $NetAdapDriver = $WMI_NICDRIVER | Where-Object {$_.devicename -eq $name} + If ([int]$WMI_OS.BuildNumber -ge 6001) { + $PhysAdap = $_.PhysicalAdapter + $Speed = "{0:0} Mbit" -f $($_.Speed / 1000000) + } Else { + $PhysAdap = "**Unavailable**" + $Speed = "**Unavailable**" + } + + $ht.add("NIC$i Name", $_.Name) + $ht.add("NIC$i FriendlyName", $_.NetConnectionID) + if ($detailed) { + $ht.add("NIC$i Manufacturer", $_.Manufacturer) + $ht.add("NIC$i DriverProviderName", $NetAdapDriver.DriverProviderName) + $ht.add("NIC$i DriverVersion", $NetAdapDriver.DriverVersion) + $ht.add("NIC$i InfName", $NetAdapDriver.InfName) + $ht.add("NIC$i InstallDate", $NetAdapDriver.InstallDate) + $ht.add("NIC$i DHCPEnabled", $NetAdap.DHCPEnabled) + $ht.add("NIC$i DHCPServer", $NetAdap.DHCPServer) + } + $ht.add("NIC$i MACAddress", $NetAdap.MACAddress) + $ht.add("NIC$i IPAddress", $NetAdap.IPAddress) + if ($detailed) { + $ht.add("NIC$i IPSubnetMask", $NetAdap.IPSubnet) + $ht.add("NIC$i DefaultGateway", $NetAdap.DefaultIPGateway) + $ht.add("NIC$i DNSServerOrder", $NetAdap.DNSServerSearchOrder) + $ht.add("NIC$i DNSSuffixSearch", $NetAdap.DNSDomainSuffixSearchOrder) + $ht.add("NIC$i PhysicalAdapter", $PhysAdap) + $ht.add("NIC$i Speed", $Speed) + } + $i = $i + 1 + } + } + + # CPU + if ($WMI_CPU) { + $ht.add('CPU Physical Processors', @($WMI_CPU).count) + $i = 1 + + $WMI_CPU | Foreach { + $ht.add("CPU$i Name", ($_.Name -replace '\s+', ' ')) + $ht.add("CPU$i Cores", $($_.NumberOfCores)) + + if ($detailed) { + $ht.add("CPU$i Logical Processors", $($_.NumberOfLogicalProcessors)) + $ht.add("CPU$i Clock Speed", "$($_.MaxClockSpeed) MHz") + $ht.add("CPU$i Description", $($_.Description)) + $ht.add("CPU$i Socket", $($_.SocketDesignation)) + $ht.add("CPU$i Status", $($_.Status)) + $ht.add("CPU$i Manufacturer", $($_.Manufacturer)) + } + ++$i + } + } + + # RAM + if ($WMI_OS) { + $WMI_OS | Foreach { + $TotalRAM = “{0:N2}” -f ($_.TotalVisibleMemorySize / 1MB) + $FreeRAM = “{0:N2}” -f ($_.FreePhysicalMemory / 1MB) + $UsedRAM = “{0:N2}” -f ($_.TotalVisibleMemorySize / 1MB - $_.FreePhysicalMemory / 1MB) + $RAMPercentFree = “{0:N2}” -f (($FreeRAM / $TotalRAM) * 100) + $TotalVirtualMemorySize = “{0:N2}” -f ($_.TotalVirtualMemorySize / 1MB) + $FreeVirtualMemory = “{0:N2}” -f ($_.FreeVirtualMemory / 1MB) + $FreeSpaceInPagingFiles = “{0:N2}” -f ($_.FreeSpaceInPagingFiles / 1MB) + } + $ht.add('RAM Total GB', $TotalRAM) + $ht.add('RAM Free GB', $FreeRAM) + $ht.add('RAM Used GB', $UsedRAM) + $ht.add('RAM Percentage Free', $RAMPercentFree) + if ($detailed) { + $ht.add('RAM TotalVirtualMemorySize', $TotalVirtualMemorySize) + $ht.add('RAM FreeVirtualMemory', $FreeVirtualMemory) + $ht.add('RAM FreeSpaceInPagingFiles', $FreeSpaceInPagingFiles) + $WMI_PMA | ForEach { $RAMSlots += $_.MemoryDevices } + $ht.add("RAM Slots", $RAMSlots) + $ht.add("RAM Slots Occupied", (@($WMI_PM).count)) + + if ($WMI_PM) { + $i = 1 + $WMI_PM | Foreach { + $ht.add("RAM$i BankLabel", $_.BankLabel) + $ht.add("RAM$i DeviceLocator", $_.DeviceLocator) + $ht.add("RAM$i Capacity MB", ($_.Capacity / 1MB)) + $ht.add("RAM$i Manufacturer", $_.Manufacturer) + $ht.add("RAM$i PartNumber", $_.PartNumber) + $ht.add("RAM$i SerialNumber", $_.SerialNumber) + $ht.add("RAM$i Speed", $_.Speed) + ++$i + } + } + } + } + + # GPU + if ($WMI_GPU) { + $ht.add('GPU Name', $WMI_GPU.name) + $ht.add('GPU Driver Version', $WMI_GPU.driverversion) + $ht.add('GPU Driver Date', $WMI_GPU.ConvertToDateTime($WMI_GPU.DriverDate)) + $ht.add('Resolution', $WMI_GPU.VideoModeDescription) + } + + if ($Detailed -and $WMI_MONITOR) { + function Decode { + If ($args[0] -is [System.Array]) { + [System.Text.Encoding]::ASCII.GetString($args[0]) + } + } + + $i = 1 + $WMI_MONITOR | Foreach { + # informace nejsou uplne presne, brat s rezervou + $ht.add("MONITOR$i Name", (Decode $_.UserFriendlyName -notmatch 0)) + $ht.add("MONITOR$i SN", (Decode $_.SerialNumberID -notmatch 0)) + } + } + + # BIOS/UEFI type + if ($Detailed) { + if ($hasAdminRights -and (Get-Command Confirm-SecureBootUEFI -ErrorAction SilentlyContinue)) { + try { + $secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop + $type = 'UEFI' + } catch { + # Get-SecureBootUEFI konci chybou, pokud se spousti na OS v BIOS rezimu + $type = 'BIOS' + } + } else { + $type = 'BIOS' + # urcuji neprimo podle toho jestli existuje GPT systemove oddil (v BIOS rezimu by z toho neslo nabootovat) + if ($WMI_PARTITION -and ($WMI_PARTITION | where {$_.Type -eq "GPT: System" -and $_.Bootable -eq $True -and $_.BootPartition -eq $True})) { + $type = 'UEFI' + } + } + $ht.add('BIOS Type', $type) + } + + # BIOS + if ($WMI_BIOS) { + $ht.add('BIOS Manufacturer', $WMI_BIOS.Manufacturer) + $ht.add('BIOS Name', $WMI_BIOS.Name) + $ht.add('BIOS Version', $WMI_BIOS.SMBIOSBIOSVersion) + } + + if ($Detailed) { + # SecureBoot + if ($secureBoot -eq $true) { + # pozor, $secureBoot se plni pouze u Detailed vypisu + $ht.add('SecureBoot', 'enabled') + } elseif ($secureBoot -eq $false) { + $ht.add('SecureBoot', 'disabled') + } else { + $ht.add('SecureBoot', '**unknown**') + } + + # TPM cip + $ht.add('TPM', $TPM.SpecVersion) + } + + # dalsi detailni informace + if ($detailed) { + # HDD + $i = 1 + $WMI_DD | foreach { + # $model = $_.model + $ht.add("HDD$i Model", $_.model) + $ht.add("HDD$i SN", $_.SerialNumber) + $ht.add("HDD$i InterfaceType", $_.InterfaceType) + $ht.add("HDD$i Size", (“{0:N1}” -f ($_.size / 1gb))) + $ht.add("HDD$i Partitions", $_.Partitions) + # $ht.add("HDD$i IsBoot",($WMI_MSFT | where {$_.FriendlyName -eq "$model"} | select IsBoot)) + $i = $i + 1 + } + + $WMI_DD2 | foreach { + if ($_.PredictFailure -eq $true) { + $ht.add("HDD InstanceName", $_.InstanceName) + $ht.add("HDD PredictFailure", $_.PredictFailure) + $ht.add("HDD Reason", $_.Reason) + } + } + + # Local Administrators + $AdministratorsMembers = net localgroup administrators | where {$_ -AND $_ -notmatch "command completed successfully"} | select -skip 4 + $ht.add("Local Administrators", $AdministratorsMembers) + + # Local Users + if ($WMI_LU) { + $ht.add("Users", ($WMI_LU | select -exp name | Out-String)) + } + + # Printers + if ($WMI_PRT) { + $i = 1 + $WMI_PRT | foreach { + $ht.add("Printer$i Name", $_.Name) + $ht.add("Printer$i Default", $_.Default) + $ht.add("Printer$i DriverName", $_.DriverName) + $ht.add("Printer$i PortName", $_.PortName) + $ht.add("Printer$i Shared", $_.Shared) + if ($_.Shared) {$ht.add("Printer$i ShareName", $_.ShareName)} + $i = $i + 1 + } + } + + #region vypsani zaseknutych print jobu + # pro Win8 + # if($WinBuild -gt 7601) + # { + # $PrinterWithError = Get-Printer -ComputerName $Computer | where PrinterStatus -eq Error + # if($PrinterWithError) + # { + # $PrinterWithError | Get-PrintJob + # } + # } + ## pro jine OS + # else + # { + # viz https://sites.google.com/site/godunder/powershell/ultimate-printer-print-queue-print-job-error-stuck-status-monitor-repair-report + # $i = 1 + # $PrinterWithError = $WMI_PJ | where {($_.jobstatus -ne $null) -and ($_.jobstatus -ne "") -and ($_.jobstatus -ne "Printing") -and ($_.jobstatus -ne "Spooling") -and ($_.jobstatus -ne "Spooling | Printing")} | + # foreach { + # $ht.add("PrinterWithError$i",$_) + # $i = $i + 1 + # } + #endregion + + # Shares + if ($WMI_SF) { + $Paths = @{} + $WMI_SF | Foreach { $Paths.$($_.Name -join ', ') = $_.Path } + + $i = 0 + $Paths.GetEnumerator() | Foreach { + $i++; $ht.add("Share$i", '' + $_.Name + ' (' + $_.Value + ')') + } + } + + # #region ovladace a jejich verze + # if ($WMI_DRIVERS) { + # $ht.add("DRIVERS:",'') + # $WMI_DRIVERS | foreach { + # if ($_.DeviceName -and $_.DriverVersion) { + # $ht.add($_.DeviceName,$_.DriverVersion) + # } + # } + # } + # #endregion + } + + # opetovna aktivace vypisovani chyb + $ErrorActionPreference = 'Continue' + + # PRIDANI ZISKANYCH PROPERTY DO OBJEKTU $OBJECT + $object | Add-Member -NotePropertyMembers $ht + + # VYTVORENI PROPERTYSET PRO SNADNEJSI FILTROVANI VYSLEDKU + $object | Add-Member PropertySet "LOU" @("ComputerName", "Logged On User") + $object | Add-Member PropertySet "RAM" @("ComputerName", "RAM*") + $object | Add-Member PropertySet "CPU" @("ComputerName", "CPU*") + + $object + } + } + + END { + } +} \ No newline at end of file diff --git a/Get-CurrentLoad.ps1 b/Get-CurrentLoad.ps1 index 7238e62..269737b 100644 --- a/Get-CurrentLoad.ps1 +++ b/Get-CurrentLoad.ps1 @@ -1,57 +1,118 @@ function Get-CurrentLoad { <# .SYNOPSIS - Fce slouží k vypsání aktualniho zatizeni stroje. Konkretne CPU, RAM, GPU, disky a sit. - Vypis se automaticky aktualizuje. - + Fce slouží k vypsání aktualniho zatizeni stroje. Konkretne CPU, RAM, GPU, HDD a NIC (sit). + Vypis se automaticky aktualizuje. + .DESCRIPTION - Fce slouží k vypsání aktualniho zatizeni stroje. Konkretne CPU, RAM, GPU, disky a sit. - Vypis se automaticky aktualizuje. - + Fce slouží k vypsání aktualniho zatizeni stroje. Konkretne CPU, RAM, GPU, HDD a NIC (sit). + Vypis se automaticky aktualizuje. + + ! Na Server OS je potreba povolit diskove countery prikazem "diskperf –Y" ! + .PARAMETER computerName Jmeno stroje, na kterem se ma mereni provest. - + .PARAMETER includeGPU Prepinac rikajici, ze se ma merit i zatizeni GPU. - Toto mereni ja trochu narocnejsi na CPU, proto na vyzadani. + Toto mereni ja trochu narocnejsi na CPU, proto jen na vyzadani. + + .PARAMETER topProcess + Slouzi k vypsani 5 nejvic zatezujicich procesu pro vybranou oblast mereni. + Mozne hodnoty jsou: CPU, GPU, HDD, RAM a NIC. .PARAMETER detailed - Slouzi k vypsani 5 nejvic zatezujicich procesu pro vybranou oblast. - Mozne hodnoty jsou CPU, GPU, HDD, RAM a NIC. + U kterych oblasti mereni se maji vypsat dalsi/podrobnejsi countery. + Mozne hodnoty jsou: HDD. + U disku vypise Read/Write zatizeni jednotlivych disku. + U Tiered Storage queue atd. .PARAMETER updateSpeed Po kolika veterinach se maji vysledky obnovovat. Vychozi je 1. + .PARAMETER measure + Co vse se ma merit. Je mozne neco ubrat kvuli prehlednosti/rychlosti/mensi narocnosti na dany stroj. + Standardne se meri: CPU, RAM, HDD, NIC + + .PARAMETER captureOutput + Prepinac rikajici, ze se ma vystup zazanemant do csv souboru. + Cesta k csv se nastavuje v parametru path. + + .PARAMETER capturePath + Cesta k csv souboru, do ktereho se maji namerene vysledky ulozit. + Vychozi je C:\Windows\Temp\hostname_datummereni.csv. + + Pro zobrazeni vysledku lze pouzit prikaz: + Import-Csv $capturePath -Delimiter ";" + + Pokud merim na lokalnim stroji, tak se po preruseni mereni (CTRL + C) vypise cesta s merenimi. + Pokud na remote, tak se vypise pred samotnym merenim (neumim jednoduse odchytit CTRL + C) + .EXAMPLE Get-CurrentLoad - Bude vypisovat aktualni zatizeni na tomto stroji. + Vypise aktualni zatizeni na tomto stroji. .EXAMPLE Get-CurrentLoad -computername titan01 - Bude vypisovat aktualni zatizeni na stroji titan01. + Vypise aktualni zatizeni na stroji titan01. + + .EXAMPLE + Get-CurrentLoad -topProcess CPU + + Vypise 5 procesu, ktere nejvic zatezuji CPU na tomto stroji. + + .EXAMPLE + Get-CurrentLoad -measure CPU, HDD + + Vypise aktualni zatizeni CPU a HDD na tomto stroji. .EXAMPLE - Get-CurrentLoad -detailed CPU + Get-CurrentLoad -measure CPU, HDD -detailed HDD + + Vypise aktualni zatizeni CPU a HDD na tomto stroji. Navic zobrazi i Read a Write zatizeni jednotlivych disku. + + .EXAMPLE + Get-CurrentLoad -captureOutput + + Vypise aktualni zatizeni a navic vysledky ulozi do C:\Windows\TEMP\_.csv na stroji, kde probiha mereni. + + Pro jejich zobrazeni lze pouzit prikaz: Import-Csv C:\Windows\TEMP\_.csv -Delimiter ";" - Bude vypisovat 5 procesu, ktere nejvic zatezuji CPU na tomto stroji. - - .NOTES + .NOTES Author: Ondřej Šebela - ztrhgf@seznam.cz #> [cmdletbinding()] + [Alias("top")] param ( [string] $computerName = $env:COMPUTERNAME , [switch] $includeGPU , - [ValidateSet('CPU', 'RAM', 'HDD', 'NIC', 'GPU')] + [ValidateSet('CPU', 'RAM', 'HDD', 'NIC', 'GPU')] + [string] $topProcess + , + [ValidateSet('HDD')] [string] $detailed , + [ValidateSet('CPU', 'RAM', 'HDD', 'NIC', 'GPU')] + [string[]] $measure = ('CPU', 'RAM', 'HDD', 'NIC') + , [int] $updateSpeed = 1 + , + [switch] $captureOutput + , + [ValidateScript( { + If ($_ -match '\.csv$' -and (Test-Path $_ -IsValid) -and (Split-Path $_ -Qualifier)) { + $true + } else { + Throw "Zadejte cestu ve tvaru C:\temp\vysledky.csv." + } + })] + [string] $capturePath = '' ) begin { @@ -61,9 +122,27 @@ function Get-CurrentLoad { throw "Pro beh je potreba funkce Invoke-Command2" } - $scriptBlock = { - param ($includeGPU, $detailed, $updateSpeed) - + if ($includeGPU) { + # counter celkem zatezuje CPU, proto jen na vyzadani + $measure += 'GPU' + } + } + + process { + Invoke-Command2 -computerName $computerName -argumentList $measure, $topProcess, $updateSpeed, $detailed, $captureOutput, $capturePath, $env:computername { + param ($measure, $topProcess, $updateSpeed, $detailed, $captureOutput, $capturePath, $computerName) + + if ($captureOutput -and !$capturePath) { + $capturePath = "$env:windir\TEMP\$env:COMPUTERNAME`_$(Get-Date -f ddMMyyyyHHmms).csv" + } + + if ($captureOutput -and $env:COMPUTERNAME -ne $computerName) { + # nespoustim na localhostu == nebude fungovat odchyceni CTRL + C == vypisi info hned + $capturePathUNC = "\\$env:COMPUTERNAME\" + ($capturePath -replace ":", "$") + Write-Warning "Zachyceny vystup najdete v $capturePathUNC.`nPro zobrazeni pouzijte: Import-Csv `"$capturePathUNC`" -Delimiter `";`"" + Start-Sleep 3 + } + # jmeno counteru musi byt dle jazyku OS # chtel jsem pro dynamicke zjisteni lokalizovaneho jmena counteru pouzit funkcihttp://www.powershellmagazine.com/2013/07/19/querying-performance-counters-from-powershell/ ale bylo to nespolehlive, napr u 'Bytes Sent/sec' to vratilo jine ID na ceskem a jine na anglickem OS $osLanguage = (Get-WmiObject -Class Win32_OperatingSystem -Property MUILanguages).MUILanguages @@ -79,6 +158,8 @@ function Get-CurrentLoad { $processor = 'Processor' $physicalDisk = 'PhysicalDisk' $percentDiskTime = '% Disk Time' + $percentDiskReadTime = '% Disk Read Time' + $percentDiskWriteTime = '% Disk Write Time' $memory = 'Memory' $availableMBytes = 'Available MBytes' $networkInterface = 'Network Interface' @@ -96,161 +177,266 @@ function Get-CurrentLoad { $processor = 'Procesor' $physicalDisk = 'Fyzický disk' $percentDiskTime = '% času disku' + #TODO pridat detailed diskove countery + $percentDiskReadTime = 'TODO' + $percentDiskWriteTime = 'TODO' $memory = 'Paměť' - $availableMBytes = 'počet MB k dispozici' + $availableMBytes = 'počet MB k dispozici' $networkInterface = 'Rozhraní sítě' $bytesSentSec = 'Bajty odeslané/s' $bytesReceivedSec = 'Bajty přijaté/s' } else { throw "pro tento jazyk ($osLanguage) nejsou nastaveny lokalizovana jmena counteru" } - + # nastavim countery, ktere budu merit - if ($detailed) { - switch ($detailed) { + if ($topProcess) { + switch ($topProcess) { 'CPU' { $counterList = @("\$process(*)\$percentProcessorTime") } # '\Process(*)\% Processor Time' 'RAM' { $counterList = @("\$process(*)\$workingSet") } # '\Process(*)\Working Set' 'HDD' { $counterList = @("\$process(*)\$IODataOperationsSec") } # '\Process(*)\IO Data Operations/sec' 'NIC' { $counterList = @("\$process(*)\$IODataOperationsSec")} # '\Process(*)\IO Data Operations/sec' 'GPU' { $counterList = @("\$GPUEngine(*)\$utilizationPercentage") } # '\GPU Engine(*)\Utilization Percentage' - Default { throw "nedefinovano" } + Default { throw "nedefinovano" } } - + # na Hyper-V serveru chci u procesu vmwp vypsat, k jakemu VM patri # proto zjistim jmeno, ktere se pouziva v counterech a odpovidajici PID, abych umel pozdeji sparovat se zatezujicim procesem - $isHyperVServer = (Get-WmiObject -Namespace "root\virtualization\v2" -Query 'select elementname, caption from Msvm_ComputerSystem where caption = "Virtual Machine"' -ErrorAction SilentlyContinue | select ElementName).count # pokud na nem bezi nejake virtualy, povazuji jej za Hyper-V server + $isHyperVServer = (Get-WmiObject -Namespace "root\virtualization\v2" -Query 'select elementname, caption from Msvm_ComputerSystem where caption = "Virtual Machine"' -ErrorAction SilentlyContinue | select ElementName).count # pokud na nem bezi nejake virtualy, povazuji jej za Hyper-V server if ($isHyperVServer) { $vmwpPID = (Get-Counter "\$process(*vmwp*)\$IDprocess" -ea SilentlyContinue).CounterSamples $PID2VMName = Get-WmiObject Win32_Process -Filter "Name like '%vmwp%'" -property processid, commandline | Select-Object ProcessId, @{Label = "VMName"; Expression = {(Get-VM -Id $_.Commandline.split(" ")[1] | Select-Object VMName).VMName}} } } else { - [System.Collections.ArrayList] $counterList = @("\$processor(*)\$percentProcessorTime", "\$physicalDisk(*)\$percentDiskTime", "\$memory\$availableMBytes") # "\Processor(*)\% Processor Time", "\PhysicalDisk(*)\% Disk Time", "\Memory\Available MBytes" - - Get-WmiObject -Class Win32_NetworkAdapter -Property physicalAdapter, netEnabled, speed, name | where {$_.PhysicalAdapter -eq $true -and $_.NetEnabled -eq $true} | select name, speed | % { - $null = $counterList.Add("\$networkInterface($($_.name))\$bytesSentSec") # "\Network Interface(*)\Bytes Sent/sec" - $null = $counterList.Add("\$networkInterface($($_.name))\$bytesReceivedSec") # "\Network Interface(*)\Bytes Received/sec" + # vytvorim seznam counteru, ktere budu sledovat + [System.Collections.ArrayList] $counterList = @() + # countery pridam postupne kvuli specifickemu poradi + if ('CPU' -in $measure -or 'CPU' -in $detailed) { + $null = $counterList.Add("\$processor(*)\$percentProcessorTime") # "\Processor(*)\% Processor Time" + } + + if ('HDD' -in $measure -or 'HDD' -in $detailed) { + $null = $counterList.Add("\$physicalDisk(*)\$percentDiskTime") # "\PhysicalDisk(*)\% Disk Time" + } + + # pridam extra countery pro Read a Write u jednotlivych disku + if ('HDD' -in $detailed) { + $null = $counterList.Add("\$physicalDisk(*)\$percentDiskReadTime") + $null = $counterList.Add("\$physicalDisk(*)\$percentDiskWriteTime") + #TODO pridat i TIERED STORAGE countery POKUD EXISTUJI + #TODO pridat i queue } - - if ($includeGPU) { - # counter celkem zatezuje CPU, proto jen na vyzadani + + if ('RAM' -in $measure -or 'RAM' -in $detailed) { + $null = $counterList.Add("\$memory\$availableMBytes") # , "\Memory\Available MBytes" + $physicalRAMMB = ((Get-WmiObject -Class Win32_OperatingSystem -Property TotalVisibleMemorySize).TotalVisibleMemorySize / 1kb) + } + + # countery pro sitova rozhrani pridavam per NIC + if ('NIC' -in $measure -or 'NIC' -in $detailed) { + Get-WmiObject -Class Win32_NetworkAdapter -Property physicalAdapter, netEnabled, speed, name | where {$_.PhysicalAdapter -eq $true -and $_.NetEnabled -eq $true} | select @{n = 'name'; e = {$_.name -replace '\(', '[' -replace '\)', ']'}}, speed | % { # kulate zavorky nahrazuji za hranate, protoze v takovem tvaru je nazev v perf counteru + $null = $counterList.Add("\$networkInterface($($_.name))\$bytesSentSec") # "\Network Interface(*)\Bytes Sent/sec" + $null = $counterList.Add("\$networkInterface($($_.name))\$bytesReceivedSec") # "\Network Interface(*)\Bytes Received/sec" + } + } + + if ('GPU' -in $measure) { $null = $counterList.Add("\$gpuEngine(*)\$utilizationPercentage") # '\GPU Engine(*)\Utilization Percentage' } - - $physicalRAMMB = ((Get-WmiObject -Class Win32_OperatingSystem -Property TotalVisibleMemorySize).TotalVisibleMemorySize / 1kb) } - - # pokud predavam jen 1 counter, musim prevest na string + + # pokud predavam jen 1 counter, musim prevest na string kvuli Get-Counter if ($counterList.Count -eq 1) { - $counterList = $counterList[0] + [string] $counterList = $counterList[0] } - + + # abych mohl po ukonceni mereni stickem CTRL+C vypsat jeste cestu k souboru s merenimi, musim upravit chovani teto zkratky + if ($captureOutput -and $env:COMPUTERNAME -eq $computerName) { + [console]::TreatControlCAsInput = $true + } + while (1) { + # pokud se vystup mereni uklada do souboru, tak pri ukonceni skriptu zkratkou CTRL + C vypisi cestu k tomuto souboru + if ($captureOutput -and $env:COMPUTERNAME -eq $computerName -and [console]::KeyAvailable) { + # spoustim na localhostu == bude fungovat odchyceni CTRL + C + $key = [system.console]::readkey($true) + if (($key.modifiers -band [consolemodifiers]"control") -and ($key.key -eq "C")) { + [console]::TreatControlCAsInput = $false + Write-Warning "Zachyceny vystup najdete v $capturePath.`nPro zobrazeni pouzijte: Import-Csv `"$capturePath`" -Delimiter `";`"" + break + } + } + # ziskam vysledky pozadovanych perf. counteru $actualResults = Get-Counter $counterList -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | Group-Object path | % { - $_ | Select-Object -Property Name, @{ n = 'Value'; e = { ($_.Group.CookedValue | Measure-Object -Average).Average }} + $_ | Select-Object -Property Name, @{ n = 'Value'; e = { ($_.Group.CookedValue | Measure-Object -Average).Average }} } - + if (!$actualResults) { throw "Nejsou zadne vysledky mereni, existuji countery: $($CounterList -join ', ') na stroji: $env:COMPUTERNAME?" } - + Clear-Host - - if ($detailed) { + + # do result ulozim vysledky mereni kvuli exportu do csv + try { + $result = [ordered] @{date = Get-Date} + } catch { + # starsi PS verze neumi [ordered] + $result = @{date = Get-Date} + } + + if ($topProcess) { # upozornim, ze zobrazuji celkove IO zatizeni danymi procesy, ne pouze HDD ci NIC - if ($detailed -in 'HDD', 'NIC') { - "zobrazeny !vsechny! typy IO operaci procesu (HDD + NIC + ...)" + if ($topProcess -in 'HDD', 'NIC') { + "zobrazeny !vsechny! typy IO operaci procesu (HDD + NIC + ...)" } - + # vypisi jen nejvic zatezujici procesy - $actualResults | where {$_.name -notlike "*idle*" -and $_.name -notlike "*_total*" -and $_.value -ne 0} | - Sort-Object value -Descending | - Select-Object -First 5 | + $subResult = '' + $actualResults | where {$_.name -notlike "*idle*" -and $_.name -notlike "*_total*" -and $_.value -ne 0} | + Sort-Object value -Descending | + Select-Object -First 5 | ForEach-Object { $name = ([regex]"\(([^)]+)\)").Matches($_.name).Value $value = [math]::Round($_.value, 2) - if ($detailed -eq 'RAM') { + if ($topProcess -eq 'RAM') { $value = ([math]::Round($_.value / 1MB, 2)).tostring() + ' MB' - } elseif ($detailed -eq 'GPU') { + } elseif ($topProcess -eq 'GPU') { # GPU counter zobrazuje PID procesu,, prevedu na jmeno $processId = ([regex]"\(pid_([^_)]+)").Matches($_.name).captures.groups[1].value $processName = Get-WmiObject win32_process -Property name, ProcessId | where {$_.processId -eq $processId} | select -exp name - + $name = $processName - } - + } + # vypisi i jaky virtual reprezentuje proces vmwp if ($name -like "*vmwp*" -and $isHyperVServer) { $ppid = $vmwpPid | where {$_.path -like "*$name*"} | select -exp CookedValue $vmName = $pid2VMName | where {$_.processid -eq $ppid} | select -exp vmname $name = "$name (VM: $vmName)" } - - "{0}: {1}" -f $name, $value + + $name = $name -replace '\(|\)' + + "{0}: {1}" -f $name, $value + + if ($captureOutput) { + if ($subResult) { $subResult += ", " } + $subResult += $name, "$value%" -join ' ' + } } + + if ($captureOutput) { $result['topProcess'] = $subResult } } else { # vypisi celkove zatizeni CPU, HDD, RAM, ... + + # zde si ulozim zatizeni GPU $GPUTotal = 0 - + + # pokud nejde o pole objektu, prevedu, abych mohl pouzit getenumerator() + if ($actualResults.GetType().basetype.name -ne 'Array') { + $actualResults = @(, $actualResults) + } + $actualResults.GetEnumerator() | % { $item = $_ switch -Wildcard ($_.name) { "*\$percentProcessorTime" { $core = ([regex]"\(([^)]+)\)").Matches($_).Value - "CPU $core %: " + [math]::Round($item.Value, 2) + $name = "CPU $core %: " + $value = [math]::Round($item.Value, 2) + + $name + $value + + if ($captureOutput) { $result[$name] = $value } } - + "*\$availableMBytes" { - "RAM used %: " + [math]::Round((($physicalRAMMB - $item.Value) / ($physicalRAMMB / 100)), 2) + $name = "RAM used %: " + $value = [math]::Round((($physicalRAMMB - $item.Value) / ($physicalRAMMB / 100)), 2) + + $name + $value + + if ($captureOutput) { $result[$name] = $value } } - + "*\$percentDiskTime" { if ($item.name -like "*_total*") {return} - $name = ([regex]"\(([^)]+)\)").Matches($_).Value - "DISK time $name %: " + [math]::Round($item.Value, 2) + $dName = ([regex]"\(([^)]+)\)").Matches($_).Value + $name = "DISK Total time $dName %: " + $value = [math]::Round($item.Value, 2) + + $name + $value + + if ($captureOutput) { $result[$name] = $value } } - + + "*\$percentDiskReadTime" { + if ($item.name -like "*_total*") {return} + $dName = ([regex]"\(([^)]+)\)").Matches($_).Value + $name = "DISK Read time $dName %: " + $value = [math]::Round($item.Value, 2) + + $name + $value + + if ($captureOutput) { $result[$name] = $value } + } + + "*\$percentDiskWriteTime" { + if ($item.name -like "*_total*") {return} + $dName = ([regex]"\(([^)]+)\)").Matches($_).Value + $name = "DISK Write time $dName %: " + $value = [math]::Round($item.Value, 2) + + $name + $value + + if ($captureOutput) { $result[$name] = $value } + } + "*\$networkInterface*" { - $name = ([regex]"\(([^)]+)\)").Matches($_).Value - + $nName = ([regex]"\(([^)]+)\)").Matches($_).Value + if ($item.name -like "*$sent*") { $action = 'sent' } else { $action = 'received' } - - "NIC $name $action MB: " + [math]::Round($item.Value / 1MB, 2) + + $name = "NIC $nName $action MB: " + $value = [math]::Round($item.Value / 1MB, 2) + + $name + $value + + if ($captureOutput) { $result[$name] = $value } } - + "*$GPUEngine*" { # GPU nema zadny souhrny _total counter, sectu vsechny hodnoty a vypisi po ukonceni foreach loopu $GPUTotal += $item.Value } - + Default { #$item.name + ": " + [math]::Round($item.Value / 1MB, 2) throw "nedefinovany counter" } } } # konec foreach projiti ziskanych vysledku - + if ($GPUTotal) { - "GPU %: " + [math]::Round($GPUTotal, 2) + $name = "GPU %: " + $value = [math]::Round($GPUTotal, 2) + + $name + $value + + if ($captureOutput) { $result[$name] = $value } } + } # konec else pro vypsani celkove zateze + + # vyexportovani vysledku do CSV + if ($captureOutput) { + New-Object -TypeName PSObject -Property $result | Export-Csv $capturePath -Append -NoTypeInformation -Delimiter ';' -Force -Encoding UTF8 } - + Start-Sleep $updateSpeed } # konec while - } # konec scriptblock - } - - process { - $params = @{ - "scriptBlock" = $scriptBlock - "argumentList" = $includeGPU, $detailed, $updateSpeed - } - - if ($computerName -ne $env:COMPUTERNAME) { - $params.computerName = $computerName - } - - Invoke-Command @params - } + } # konec Invoke-Command2 + } # konec process } \ No newline at end of file diff --git a/Get-FailedScheduledTask.ps1 b/Get-FailedScheduledTask.ps1 new file mode 100644 index 0000000..2e4054b --- /dev/null +++ b/Get-FailedScheduledTask.ps1 @@ -0,0 +1,192 @@ +function Get-FailedScheduledTask { + <# + .SYNOPSIS + Vypise scheduled tasky, ktere skoncily neuspechem. + + .DESCRIPTION + Vypise scheduled tasky, ktere skoncily neuspechem. + Kontroluji se vsechny ci jen uzivateli vytvorene tasky na zadanych strojich, + ktere byly naposledy spusteny pred X dny. + Automaticky se ignoruji disablovane a stare tasky, neni-li receno jinak. + + Vyzaduje admin prava pokud ma byt spusteno vuci localhostu! + + .PARAMETER computerName + Seznam stroju, na kterych se maji sched. tasky zkontrolovat + + .PARAMETER justUserTasks + Prepinac rikajici, ze se maji kontrolovat pouze uzivateli vytvorene tasky + + .PARAMETER justActive + Prepinac rikajici, ze se maji vypsat pouze enablovane tasky, ktere skoncily chybou max pred lastRunBeforeDays dny + nebo maji nastaveno opakovani a maji se znovu spustit behem 24 hodin + + .PARAMETER lastRunBeforeDays + Pocet dnu dozadu, kdy mohl byt sched. task naposled spusten + Limituji tak, jak stare tasky se maji kontrolovat + + .PARAMETER sendEmail + Zdali se ma poslat email s nalezenymi chybami + + .PARAMETER to + Na jakou adresu se ma email poslat. + Vychozi je aaa@bbb.cz + + .EXAMPLE + Import-Module Scripts,Computers -ErrorAction Stop + Get-FailedScheduledTask -computerName $servers -JustUserTasks -LastRunBeforeDays 1 -sendEmail + + Na strojich z $servers zkontroluje user sched. tasky spustene za poslednich 24 hodin a pokud nalezne + nejake skoncene chybou, posle jejich seznam na admin@fi.muni.cz + + .NOTES + Author: Sebela Ondrej + #> + + [cmdletbinding()] + param ( + $computerName = @($env:COMPUTERNAME) + , + [switch] $justUserTasks + , + [int] $lastRunBeforeDays = 1 + , + [switch] $justActive + , + [switch] $sendEmail + , + [string] $to = 'aaa@bbb.cz' + ) + + begin { + if (!(Get-Command Write-Log -ea SilentlyContinue)) { + throw "Vyzaduje funkci Write-Log." + } + + $Error.Clear() + + $ComputerName = {$ComputerName.tolower()}.invoke() + + Write-Log "Kontroluji failnute scheduled tasky na: $($ComputerName -join ', ')" + + # kontrola, ze bezi s admin pravy + if ($env:COMPUTERNAME -in $computerName -and !([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + throw "Nebezi s admin pravy, coz je vyzadovano, pokud spoustite vuci localhostu" + } + + } + + process { + # schtasks pouzivam takto zvlastne, aby nebyla zkomolena diakritika (deje se u nativnich prikazu spoustenych pres psremoting) + $failedTasks = invoke-command2 -computername $computerName -ArgumentList $lastRunBeforeDays, $justUserTasks, $justActive { + param($lastRunBeforeDays, $justUserTasks, $justActive) + + # pomocne funkce + function ConvertTo-DateTime { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true, Position = 0)] + [ValidateNotNullOrEmpty()] + [String] $date + , + [Parameter(Mandatory = $false, Position = 1)] + [ValidateNotNullOrEmpty()] + [String[]] $format = ('d.M.yyyy', 'd.M.yyyy H:m', 'd.M.yyyy H:m:s') + ) + + $result = New-Object DateTime + + $convertible = [DateTime]::TryParseExact( + $Date, + $Format, + [System.Globalization.CultureInfo]::InvariantCulture, + [System.Globalization.DateTimeStyles]::None, + [ref]$result) + + if ($convertible) { + $result + } else { + } + } + + # mohl bych pouzit Get-ScheduledTask a Get-ScheduledTaskInfo, ale na starsich OS neexistuji + # pres Start-Job spoustim proto, ze nativni prikazy v remote session pri "klasickem" spusteni nevraci korektne diakritiku + $job = Start-Job ([ScriptBlock]::Create('schtasks.exe /query /s localhost /V /FO CSV')) + $null = Wait-Job $job + $tasks = Receive-Job $job | ConvertFrom-Csv + Remove-Job $job + + # odfiltruji duplicitni zaznamy (kazdy task je tam tolikrat, kolik ma triggeru) + [System.Collections.ArrayList] $uniqueTask = @() + $tasks | % { + if ($_.taskname -notin $uniqueTask.taskname) { + $null = $uniqueTask.add($_) + } + } + $tasks = $uniqueTask + + if ($justUserTasks) { + $domainName = $env:userdomain # netbios jmeno domeny (ntfi) + $computer = $env:COMPUTERNAME + if (!$domainName -or $domainName -eq $computer) { $domainName = 'ntfi' } + $tasks = $tasks | where {($_.author -like "$domainName\*" -or $_.author -like "$computer\*")} + } + + # tasky, ktere pri poslednim spusteni skoncily chybou + # nektere nenulove result kody ignoruji, protoze nejde o skutecne chyby + # 267009 = task is currently running + # 267014 = task task was terminated by user + # 267011 = task has not yet run + # -2147020576 = operator or administrator has refused the request + # -2147216609 = an instance of this task is already running + $tasks = $tasks | where {($_.'last Result' -ne 0 -and $_.'last Result' -notin (267009, 267014, 267011, -2147020576, -2147216609) -and $_.'last run time' -ne 'N/A')} + + #TODO tento zpusob filtrovani nezachyti problemy u tasku, ktere se vytvareji pomoci GPO v replace modu, protoze pri kazdem gpupdate dojde k replace tasku, tzn ztrate informaci + # dalo by se vyresit tahanim informaci z event logu, kde se loguje historie per taskname + + if ($justActive) { + # vratim jen enablovane tasky, ktere byly spusteny max pred $LastRunBeforeDays dny + # nebo se opakuji a maji byt spusteny behem 24 hodin znovu + $tasks = $tasks | where { + $_.'Scheduled Task State' -eq 'Enabled' ` + -and ( + ($(try {ConvertTo-DateTime $_.'last run time' -ea stop} catch {Get-Date 1.1.1999}) -gt [datetime]::now.AddDays( - $LastRunBeforeDays))` + -or + ($_.'Repeat: Every' -ne "N/A" -and ($(try {ConvertTo-DateTime ($_.'Next Run Time') -ea stop} catch {Get-Date 1.1.9999}) -lt [datetime]::now.AddDays(1))) + ) + } + } + + # vypisi vysledek + $tasks | select taskname, 'last result', 'last run time', 'next run time', @{n = 'Computer'; e = {$env:COMPUTERNAME}} + } -ErrorAction SilentlyContinue + } + + end { + if ($Error) { + Write-Log -ErrorRecord $Error + } + + if ($failedTasks) { + Write-Log -Message $($failedTasks | Format-List taskname, 'last result', 'last run time', computer | Out-String) + + $body = "Ahoj,`nnize je seznam failnutych scheduled tasku za minuly den:`n`n" + $body += $failedTasks | Format-List taskname, 'last result', 'last run time', computer | Out-String + $body += "`n`n`nKontrola probiha na: $($computerName -join ', ')" + + if ($Error) { + $body += "`n`n`n Obevily se chyby:`n$($Error | out-string)" + } + + if ($sendEmail) { + Send-Email -Subject "Failnute scheduled tasky spustene za $LastRunBeforeDays poslednich dnu" -Body $body -To $To + } + } else { + if ($justActive) { + $t = " (spustene od $([datetime]::now.AddDays( - $LastRunBeforeDays)))" + } + + Write-Log "Zadne neuspesne spustene sched. tasky$t nenalezeny" + } + } +} diff --git a/Get-FirewallLog.ps1 b/Get-FirewallLog.ps1 new file mode 100644 index 0000000..9ef5fbd --- /dev/null +++ b/Get-FirewallLog.ps1 @@ -0,0 +1,432 @@ +#TODO log muze byt i najinem miste, dokonce kazdy fw profil jej muze mit jinde, zjistit prikazem: netsh advfirewall show allprofiles | Select-String Filename | % { $_ -replace "%systemroot%",$env:systemroot } ale to bych teda musel zjistovat na danem stroji (invoke-command) +function Get-FirewallLog { + <# + .SYNOPSIS + Funkce do konzole, pomoci Out-GridView ci cmtrace vypise aktualni obsah FW logu na zadanem stroji. + Podle zadaneho from/to se pripadne zaznamy zobrazi z archivu logu na zalohovacim serveru. + + .DESCRIPTION + Funkce do konzole, pomoci Out-GridView ci cmtrace vypise aktualni obsah FW logu na zadanem stroji. + Podle zadaneho from/to se pripadne zaznamy zobrazi z archivu logu na zalohovacim serveru. + + .PARAMETER computerName + Jmeno stroje, z nehoz se vypise FW log. + + .PARAMETER live + Prepinac rikajici, ze se bude do konzole vypisovat realtime obsah logu. + Pro lepsi citelnost se obsah formatuje pomoci Format-Table. + + .PARAMETER ogv + Prepinac rikajici, ze se ma vystup vypsat pomoci Out-GridView. + Vyhoda je, ze Out-GridView umoznuje filtrovani, ale zase nebude realtime. + + .PARAMETER cmtrace + Prepinac rikajici, ze se ma log otevrit v cmtrace toolu. + Vyhoda je, ze cmtrace ukazuje realtime data, ale zase neumi pokrocile filtrovani. + + .PARAMETER from + Od jakeho casu se ma vypisovat obsah logu. + Zadavejte datum ve tvaru dle vaseho culture. Tzn pro ceske napr 15.3.2019 15:00. Pro anglicky pak prohodit mesic a den. + + .PARAMETER to + Do jakeho casu se ma vypisovat obsah logu. + Zadavejte datum ve tvaru dle vaseho culture. Tzn pro ceske napr 15.3.2019 15:00. Pro anglicky pak prohodit mesic a den. + + .PARAMETER dstPort + Cislo ciloveho portu. + + .PARAMETER srcPort + Cislo zdrojoveho portu. + + .PARAMETER dstIP + Cilova IP. + + .PARAMETER srcIP + Zdrojova IP. + + .PARAMETER action + Typ FW akce. + allow ci drop + + .PARAMETER protocol + Jmeno pouziteho protokolu. + tcp, udp, ... + + .PARAMETER path + Smer komunikace. + receive, send + + .PARAMETER logPath + Lokalni cesta k firewall logu. + Vychozi je "C:\System32\LogFiles\Firewall\pfirewall.log". + Zmente pouze pokud se logy ukladaji jinde. + + .EXAMPLE + Get-FirewallLog -live + + Zacne do konzole vypisovat realtime obsah FW logu ($env:windir\System32\LogFiles\Firewall\pfirewall.log). + + .EXAMPLE + Get-FirewallLog -live -dstPort 3389 -protocol TCP -action allow + + Zacne do konzole vypisovat realtime obsah FW logu ($env:windir\System32\LogFiles\Firewall\pfirewall.log). + A to pouze zaznamy kde cilovy port je 3389, protokol TCP a komunikace byla povolena. + + .EXAMPLE + Get-FirewallLog -live -action drop + + Zacne do konzole vypisovat realtime obsah FW logu ($env:windir\System32\LogFiles\Firewall\pfirewall.log). + A to pouze dropnutou komunikaci. + + .EXAMPLE + Get-FirewallLog -computerName titan01 + + Vypise do konzole obsah FW logu ($env:windir\System32\LogFiles\Firewall\pfirewall.log) ze stroje titan01. + + .EXAMPLE + Get-FirewallLog -computerName titan01 -ogv + + Vypise pomoci Out-GridView obsah FW logu ($env:windir\System32\LogFiles\Firewall\pfirewall.log) ze stroje titan01. + + .EXAMPLE + Get-FirewallLog -computerName titan01 -ogv -from ((Get-Date).addminutes(-10)) -srcIP 147.251.48.120 + + Vypise pomoci Out-GridView obsah FW logu ($env:windir\System32\LogFiles\Firewall\pfirewall.log) ze stroje titan01. + A to pouze zaznamy za poslednich 10 minut pochazejici z adresy 147.251.48.120. + + .EXAMPLE + Get-FirewallLog -ogv -from "12/7/2018 6:59:42" + + Vypise pomoci Out-GridView obsah FW logu ($env:windir\System32\LogFiles\Firewall\pfirewall.log). + A to pouze zaznamy od 7 prosince 6:59:42. + + .EXAMPLE + Get-FirewallLog -computerName titan01 -cmtrace + + Vypise pomoci cmtrace.exe obsah FW logu ($env:windir\System32\LogFiles\Firewall\pfirewall.log) ze stroje titan01. + #> + + [CmdletBinding(DefaultParameterSetName = "default")] + param ( + [Parameter(Position = 0, ParameterSetName = "default")] + [Parameter(Position = 0, ParameterSetName = "live")] + [Parameter(Position = 0, ParameterSetName = "ogv")] + [Parameter(Position = 0, ParameterSetName = "cmtrace")] + [string] $computerName = $env:COMPUTERNAME + , + [Parameter(ParameterSetName = "live")] + [switch] $live + , + [Parameter(ParameterSetName = "ogv")] + [switch] $ogv + , + [Parameter(ParameterSetName = "cmtrace")] + [switch] $cmtrace + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [Parameter(ParameterSetName = "cmtrace")] + [ValidateScript( { + If (($_.getType().name -eq "string" -and [DateTime]::Parse($_)) -or ($_.getType().name -eq "dateTime")) { + $true + } else { + Throw "Zadejte ve formatu dle vaseho culture. Pro cs-CZ napr.: 15.2.2019 15:00. Pro en-US pak prohodit den a mesic." + } + })] + $from + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [Parameter(ParameterSetName = "cmtrace")] + [ValidateScript( { + If (($_.getType().name -eq "string" -and [DateTime]::Parse($_)) -or ($_.getType().name -eq "dateTime")) { + $true + } else { + Throw "Zadejte ve formatu dle vaseho culture. Pro cs-CZ napr.: 15.2.2019 15:00. Pro en-US pak prohodit den a mesic." + } + })] + $to + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [ValidateNotNullOrEmpty()] + [int[]] $dstPort + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [ValidateNotNullOrEmpty()] + [int[]] $srcPort + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [ValidateNotNullOrEmpty()] + [ipaddress[]] $dstIP + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [ValidateNotNullOrEmpty()] + [ipaddress[]] $srcIP + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [ValidateSet("allow", "drop")] + [string] $action + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [ValidateScript( {$_ -match '^[a-z]+$'} )] + [string[]] $protocol + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [ValidateSet("receive", "send")] + [string] $path + , + [Parameter(ParameterSetName = "default")] + [Parameter(ParameterSetName = "live")] + [Parameter(ParameterSetName = "ogv")] + [Parameter(ParameterSetName = "cmtrace")] + [ValidateNotNullOrEmpty()] + $logPath = "C:\Windows\System32\LogFiles\Firewall\pfirewall.log" + ) + + begin { + if ($from -and $from.getType().name -eq "string") {$from = [DateTime]::Parse($from)} + if ($to -and $to.getType().name -eq "string") {$to = [DateTime]::Parse($to)} + if ($from -and $to -and $from -gt $to) { + throw "From nesmi byt vetsi nez To" + } + + if ($computerName -notmatch "$env:COMPUTERNAME|localhost|\.") { + $logPath = "\\$computerName\" + $logPath -replace ":", "$" + } + + if (Test-Path $logPath -ErrorAction SilentlyContinue) { + $logToShow = @($logPath) + } + + if ($from -or $to) { + # pokud uzivatele zajima konkretni datum, teprve zacnu resit moznost, ze budu muset prozkoumat i .old log ci archiv logu + + # + # vytvorim seznam dostupnych logu pro zadany stroj + # do seznamu pridavam od nejstarsich, abych jej nemusel pozdeji slozite radit + $availableLogs = @() + # pridam logy z CVT archivu + $logBackupFolder = "\\nejakyserver\e$\Backups\FirewallLogs\$computerName" + if (Test-Path $logBackupFolder -ErrorAction SilentlyContinue) { + $availableLogs += Get-ChildItem $logBackupFolder -Filter *.log -ErrorAction SilentlyContinue | Sort-Object -Property LastWriteTime | Select-Object -ExpandProperty FullName + } + # Windows si automaticky uklada predchozi verzi logu do souboru s koncovkou .old + # .old soubory automaticky zalohuji na backup server, proto uz v puvodnim umisteni nemusi byt + $logOldpath = Join-Path $(Split-Path $logPath -Parent) "pfirewall.log.old" + if (Test-Path $logOldpath -ErrorAction SilentlyContinue) { + $availableLogs += $logOldpath + } + # pridam aktualni FW log soubor + if (Test-Path $logPath -ErrorAction SilentlyContinue) { + $availableLogs += $logPath + } + + # + # udelam si hash s lastwritetime a zejmena creationTime, ktery se neda ziskat z atributu souboru, protoze obsahuje nesmyslne udaje + # creationTime teda plnim tak, ze pouziji lastWriteTime predchoziho logu + 1 vterina + $logProperty = @{} + $availableLogs | % { + $lastWriteTime = (Get-Item $_).LastWriteTime + $position = $availableLogs.indexOf($_) + if ($position -eq 0) { + $creationTime = (Get-Date ((Get-Item $_).LastWriteTime)).addDays(-1) + } else { + $creationTime = (Get-Date ((Get-Item ($availableLogs[$position - 1])).LastWriteTime).AddSeconds(1)) + } + + $logProperty.$_ = [PSCustomObject] @{path = $_; CreationTime = $creationTime; LastWriteTime = $lastWriteTime} + } + + # + # do logToShow ulozim logy, ktere mohou realne obsahovat hledane udaje (dle from/to) + $logToShow = $availableLogs + if ($from) { + $logToShow = $logToShow | Where-Object { + $logPath = $_ + $logProperty.$logPath.LastWriteTime -ge $from + } + } + if ($to) { + $logToShow = $logToShow | Where-Object { + $logPath = $_ + $logProperty.$logPath.CreationTime -le $to + } + } + } + + Write-Verbose "Zobrazim obsah:`n$($logToShow -join ', ')" + + if (!$logToShow) { + throw "Zadne logy k zobrazeni" + } + + $command = "Get-Content $($logToShow -join ',') -ReadCount 10000" + + if ($live) { + $command += ' -Wait' + } + + $command += ' | ConvertFrom-Csv -Delimiter " " -Header "date", "time", "action", "protocol", "src-ip", "dst-ip", "src-port", "dst-port", "size", "tcpflags", "tcpsyn", "tcpack", "tcpwin", "icmptype", "icmpcode", "info", "path" | Select-Object @{n = "dateTime"; e = {($_.date -replace "[^\d-]") + " " + $_.time}}, * -ExcludeProperty date, time' + + $filter = '' + + if ($dstPort) { + if ($filter) { + $filter += " -and" + } + + $i = 0 + $dstPort | % { + if ($i) { + $filter += " -or" + } + ++$i + + $filter += " `$_.'dst-port' -eq $_" + } + } + + if ($srcPort) { + if ($filter) { + $filter += " -and" + } + + $i = 0 + $srcPort | % { + if ($i) { + $filter += " -or" + } + ++$i + + $filter += " `$_.'src-port' -eq $_" + } + } + + if ($dstIP) { + if ($filter) { + $filter += " -and" + } + + $i = 0 + $dstIP | % { + if ($i) { + $filter += " -or" + } + ++$i + + $filter += " `$_.'dst-ip' -eq `"$_`"" + } + } + + if ($srcIP) { + if ($filter) { + $filter += " -and" + } + + $i = 0 + $srcIP | % { + if ($i) { + $filter += " -or" + } + ++$i + + $filter += " `$_.'src-ip' -eq `"$_`"" + } + } + + if ($action) { + if ($filter) { + $filter += " -and" + } + + $filter += " `$_.action -eq `"$action`"" + } + + if ($protocol) { + if ($filter) { + $filter += " -and" + } + + $i = 0 + $protocol | % { + if ($i) { + $filter += " -or" + } + ++$i + + $filter += " `$_.protocol -eq `"$protocol`"" + } + } + + if ($path) { + if ($filter) { + $filter += " -and" + } + + $filter += " `$_.path -eq `"$path`"" + } + + if ($from) { + if ($filter) { + $filter += " -and" + } + + $filter += " `(Get-Date `$_.datetime) -ge `"$from`"" + } + + if ($to) { + if ($to -gt (Get-Date)) { + Write-Warning "Zadali jste cas v budoucnosti. Parametr To ignoruji" + } else { + if ($filter) { + $filter += " -and" + } + + $filter += " `(Get-Date `$_.datetime) -le `"$to`"" + } + } + + if ($filter) { + $command += " | Where-Object {$filter}" + } + + if ($live) { + # pro lepsi prehlednost u realtime sledovani naformatuji pomoci Format-Table + $command += ' | Format-Table' + } elseif ($ogv) { + $command += " | Out-GridView -Title `"FW log - $computerName`"" + } + + # pouziti cmtrace je vylucne s ostatnimi parametry + if ($cmtrace) { + $command = '' + $logToShow | % { + $command += "try { cmtrace.exe `"$_`" } catch { throw 'Nastroj cmtrace neni dostupny' };" + } + } + } + + process { + Write-Verbose "Spoustim prikaz:`n$command" + Invoke-Expression $command + } + + end { + } +} \ No newline at end of file diff --git a/Get-FirewallRules.ps1 b/Get-FirewallRules.ps1 new file mode 100644 index 0000000..fcd316f --- /dev/null +++ b/Get-FirewallRules.ps1 @@ -0,0 +1,138 @@ +#TODO dodelat propertysety na name a direction, ktere nemohou byt spolu! +#TODO displayName a action take nemohou byt spolu! +#TODO domenove GPO nejsou z nejakeho duvodu videt pokud taham info z activeStore, ale v rsop store videt jsou +# prijde mi ale ze jen ty, ktere maji i svou lokalni variantu, tzn pokud lokalne takove pravidlo se stejnym jmenem neexistuje, tak se ukaze viz "Remote Administration (NP-In)" na aeneas1 +<# +$rules=Get-NetFirewallRule -PolicyStore activestore -Direction Inbound -Action Allow,Block,NotConfigured -Enabled True|?{$_.Name -like '*desktop*'} +$rules | % { + $_|Get-NetFirewallAddressFilter +} + +vs + +$rules2=Get-NetFirewallRule -PolicyStore rsop -Direction Inbound -Action Allow,Block,NotConfigured -Enabled True|?{$_.Name -like '*desktop*'} +$rules2 | % { + $_|Get-NetFirewallAddressFilter +} + +A MOZNA JESTE LEPSI PRIKLAD + +$rules=Get-NetFirewallRule -PolicyStore activestore -all|?{$_.Name -like '*desktop*' -and $_.PolicyStoreSourceType -eq "grouppolicy"} +$rules | % { $_|Get-NetFirewallAddressFilter -PolicyStore rsop } + +vs + +$rules | % { $_|Get-NetFirewallAddressFilter -PolicyStore activeStore } + +#> +function Get-FirewallRules { + [CmdletBinding()] + param ( + $computername = $env:COMPUTERNAME + #, + #[string] $name = "*" + , + [ValidateSet('inbound', 'outbound')] + [string []] $direction + , + [ValidateSet('true', 'false')] + [string] $enabled = 'true' + , + [ValidateSet('Allow', 'Block', 'NotConfigured')] + [string []] $action = ('Allow', 'Block', 'NotConfigured') + , + [switch] $justGPORules + , + [switch] $inactiveIncluded + ) + + begin { + # odkud se maji FW pravidla nacist + # ActiveStore by mel obsahovat merge domenovych a lokalnich pravidel, tzn vsechna ktera se realne aplikuji + $policyStore = "ActiveStore" + if ($justGPORules) { + # RSOP store obsahuje pouze FW pravidla vytvorena pomoci domenovych GPO + $policyStore = "RSOP" + } + } + + process { + Invoke-Command2 -computerName $computerName { + + param ($name, $direction, $enabled, $action, $policyStore, $inactiveIncluded) + + # nactu odpovidajici FW pravidla + if ($direction) { + $FirewallRules = Get-NetFirewallRule -direction $direction -PolicyStore $policyStore -action $action -enabled $enabled + } else { + $FirewallRules = Get-NetFirewallRule -PolicyStore $policyStore -action $action -enabled $enabled + # $FirewallRules = Get-NetFirewallRule -DisplayName $name -PolicyStore $policyStore -action $action -enabled $enabled + } + if (!$inactiveIncluded) { + # odfiltruji neaktivni pravidla (napr ptoto, ze jde o lokalni a jejich aplikace je zakazana) + $FirewallRules = $FirewallRules | ? {$_.primarystatus -eq "OK"} + } + $FirewallRuleSet = @() + $ErrorActionPreference = 'silentlyContinue' # nektere cmdlety koncily chybou, protoze FW pravidlo nenalezly? + + ForEach ($Rule In $FirewallRules) { + # iteruji skrze nalezena FW pravidla a pro kazde zjistim vsechny dostupna nastaveni + + # aby se zobrazily spravna nastaveni, divam se do odpovidajiciho storu (v RSOP jsou ulozene domenove definovane FW pravidla) + # v cmdletech je totiz zrejme bug, kdy opkud existuje pravidlo se shodnym nzavem lokalne i def. skrze GPO, tak se zobrazi pro domenove pravidlo nastaveni toho lokalniho + $store = "ActiveStore" + if ($Rule.PolicyStoreSourceType -eq "groupPolicy") { + $store = "RSOP" + } + + Write-Verbose "Zpracovavam `"$($Rule.DisplayName)`" ($($Rule.Name)) ze storu $store" + + $AdressFilter = $Rule | Get-NetFirewallAddressFilter -PolicyStore $store + $PortFilter = $Rule | Get-NetFirewallPortFilter -PolicyStore $store + $ApplicationFilter = $Rule | Get-NetFirewallApplicationFilter -PolicyStore $store + $ServiceFilter = $Rule | Get-NetFirewallServiceFilter -PolicyStore $store + $InterfaceFilter = $Rule | Get-NetFirewallInterfaceFilter -PolicyStore $store + $InterfaceTypeFilter = $Rule | Get-NetFirewallInterfaceTypeFilter -PolicyStore $store + $SecurityFilter = $Rule | Get-NetFirewallSecurityFilter -PolicyStore $store + + $HashProps = [PSCustomObject]@{ + Name = $Rule.Name + DisplayName = $Rule.DisplayName + Description = $Rule.Description + Group = $Rule.Group + Enabled = $Rule.Enabled + Profile = $Rule.Profile + Platform = $Rule.Platform -join ', ' + Direction = $Rule.Direction + Action = $Rule.Action + EdgeTraversalPolicy = $Rule.EdgeTraversalPolicy + LooseSourceMapping = $Rule.LooseSourceMapping + LocalOnlyMapping = $Rule.LocalOnlyMapping + Owner = $Rule.Owner + LocalAddress = $AdressFilter.LocalAddress -join ', ' + RemoteAddress = $AdressFilter.RemoteAddress -join ', ' + Protocol = $PortFilter.Protocol + LocalPort = $PortFilter.LocalPort -join ', ' + RemotePort = $PortFilter.RemotePort -join ', ' + IcmpType = $PortFilter.IcmpType -join ', ' + DynamicTarget = $PortFilter.DynamicTarget + Program = $ApplicationFilter.Program -Replace "$($ENV:SystemRoot.Replace("\","\\"))\\", "%SystemRoot%\" -Replace "$(${ENV:ProgramFiles(x86)}.Replace("\","\\").Replace("(","\(").Replace(")","\)"))\\", "%ProgramFiles(x86)%\" -Replace "$($ENV:ProgramFiles.Replace("\","\\"))\\", "%ProgramFiles%\" + Package = $ApplicationFilter.Package + Service = $ServiceFilter.Service + InterfaceAlias = $InterfaceFilter.InterfaceAlias -join ', ' + InterfaceType = $InterfaceTypeFilter.InterfaceType + LocalUser = $SecurityFilter.LocalUser + RemoteUser = $SecurityFilter.RemoteUser + RemoteMachine = $SecurityFilter.RemoteMachine + Authentication = $SecurityFilter.Authentication + Encryption = $SecurityFilter.Encryption + OverrideBlockRules = $SecurityFilter.OverrideBlockRules + } + + $FirewallRuleSet += $HashProps + } + + $FirewallRuleSet + } -argumentList $name, $direction, $enabled, $action, $policyStore, $inactiveIncluded + } +} \ No newline at end of file diff --git a/Get-FolderSize.ps1 b/Get-FolderSize.ps1 new file mode 100644 index 0000000..d52de27 --- /dev/null +++ b/Get-FolderSize.ps1 @@ -0,0 +1,162 @@ +# TODO: fce neumí přistupovat do systémových adresářů jako System Volume Information, ošetřit. +function Get-FolderSize { + <# + .SYNOPSIS + Fce vypisuje velikost adresáře a počet v něm obsažených souborů. + + .DESCRIPTION + Fce vypisuje velikost adresáře a počet v něm obsažených souborů. + Využívá robocopy. + + .PARAMETER ComputerName + Povinný parametr, udává seznam strojů. Akceptuje i input pipeline. + + .PARAMETER FolderPath + Povinný parametr udávající cestu. Např.: C:\temp + + .PARAMETER Unit + Parametr udávající v jakých jednotkách chceme výstup. Výchozí je MB, ale možné jsou i KB a GB. + Dle toho se i pojmenuje sloupec obsahujici velikost (napr.: Size (MB)) + + .PARAMETER Exclude + Filtr udavajici, jake soubory se maji ignorovat. + Je mozne pouzivat wildcard * a v pripade vice koncovek oddelit mezerou. + + Napr.: '*.ps1 *.txt' + + .PARAMETER Include + Filtr udavajici, pouze jake soubory se maji pocitat. + Je mozne pouzivat wildcard * a v pripade vice koncovek oddelit mezerou. + + Napr.: '*.ps1 *.txt' + + .EXAMPLE + $b311 | Get-FolderSize -d C:\temp + + Ukáže velikost C:\temp na strojích v B311. + + .EXAMPLE + Get-FolderSize sirene01, bympkin C:\temp + + Ukáže velikost C:\temp na strojich sirene01 a bumpkin. + + .EXAMPLE + Get-FolderSize sirene01, bympkin C:\temp -exclude '*.exe *.msi' + + Ukáže velikost C:\temp na strojich sirene01 a bumpkin. Ale nezapocitaji se exe a msi soubory. + + .EXAMPLE + Get-FolderSize sirene01, bympkin C:\temp -include '*.txt' + + Ukáže velikost vsech txt souboru v C:\temp na strojich sirene01 a bumpkin. + + .NOTES + Author: Ondřej Šebela - ztrhgf@seznam.cz + #> + + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "zadej jmeno stroje/ů")] + [ValidateNotNullOrEmpty()] + [Alias("c", "CN", "__Server", "IPAddress", "Server", "Computer", "Name", "SamAccountName")] + [string[]] $computerName = $env:computername + , + [Parameter(Mandatory = $true, Position = 1)] + [Alias("f", "d", "path", "dir", "directory")] + [ValidateScript( { + If ($_ -match '^[a-z][$:]\\\w+') { + $true + } else { + Throw "$_ zadana cesta neni ve spravnem tvaru, tzn: C:\neco" + } + })] + [string] $folderPath + , + [Parameter(Mandatory = $false, Position = 2, HelpMessage = "V jakých jednotkách chceš vidět výslednou velikost. Výchozí je MB, ale může být i GB či KB")] + [ValidateSet('GB', 'MB', 'KB')] + [String] $unit = 'MB' + , + [ValidateScript( { + If ($_ -notmatch ',') { + $true + } else { + Throw "`nZadany filtr: $_ obsahuje carku. Jednotlive prvky filtru oddelujte mezerou!`nNapriklad: '*.ps1 *.txt'" + } + })] + [string] $include + , + [ValidateScript( { + If ($_ -notmatch ',') { + $true + } else { + Throw "`nZadany filtr: $_ obsahuje carku. Jednotlive prvky filtru oddelujte mezerou!`nNapriklad: '*.ps1 *.txt'" + } + })] + [string] $exclude + ) + + BEGIN { + # odstranim zaverecne lomitko, protoze zpusobovalo problem u robocopy + # stejne tak $ nahradim za : (abych mohl rovnou vkladat vykopirovane UNC cesty s $ namisto dvojtecky) + $folderPath = $folderPath -replace "\\$" -replace '$\\', ':\' + } + + PROCESS { + Invoke-Command2 -computerName $computerName { + param ($folderPath, $Unit, $Exclude, $Include) + + $Computer = $env:computername + + if (test-connection -computername $Computer -Count 1 -quiet) { + # prevod lokalni cesty na sitovou + $folderUNCPath = "\\" + $Computer + "\" + $folderPath -replace ":", "$" + + if (Test-Path $folderUNCPath) { + if ($Exclude) { + # pridam robocopy parametr /xF k zadanemu filtru + $Exclude = "/xF $Exclude" + } + + # pomoci robocopy spocitam velikost zadane cesty + # cestu zadavam explicitne aby se nepouzila nejaka starsi verze napr. z 'Windows Resource Kits' + $robocopyPath = join-path $env:windir 'System32\Robocopy.exe' + + $result = invoke-expression "$robocopyPath `"$folderUNCPath`" NULL $Include /L /XJ /R:0 /W:1 /NP /E /BYTES /NFL /NDL /NJH /MT:64 /NC $Exclude" + if (! $?) { ++$chyba } + + # naplnim ziskanymi udaji objekt + $object = [PSCustomObject]@{ + ComputerName = $Computer + FilesCount = ($result[-5] -replace "Files :\s+\d+\s+(\d+) .+", '$1').trim() # beru az druhy pocet, protoze az ten ukazuje soubory prosle pripadnym filtrem + "Size ($Unit)" = [math]::Round(($result[-4] -replace "Bytes :\s+\d+\s+(\d+) .+", '$1').trim() / "1$Unit", 2) # beru az druhou velikost, protoze az ta odpovida vyfiltrovanym souborum + Path = $folderPath + } + + if ($chyba) { + $object.FilesCount = NULL + $object."Size ($Unit)" = "Error" + } + + if ($result -like "*ERROR 5 *") { + $object.FilesCount = NULL + $object."Size ($Unit)" = "Access denied" + } + + # vypisu na vystup + $object + + } else { + Write-Output "$Computer nema pozadovany adresar" + } + } else { + Write-Output "$Computer nepinga" + } + } -ArgumentList $folderPath, $Unit, $Exclude, $Include + } + END { + } +} + +# NASTAVENI ALIASU +Set-Alias gfs Get-FolderSize \ No newline at end of file diff --git a/Get-InstalledSoftware.ps1 b/Get-InstalledSoftware.ps1 new file mode 100644 index 0000000..992615b --- /dev/null +++ b/Get-InstalledSoftware.ps1 @@ -0,0 +1,106 @@ +function Get-InstalledSoftware { + <# + .SYNOPSIS + Fce pro zjištění nainstalovaného software. + + .DESCRIPTION + Fce získává jak 32 tak 64 bit aplikace. + Pokud se zadá i parametr $ProgramName, tak dojde k vyhledání software s daným stringem v názvu + Standardně nezobrazuje aktualizace ani bezpečností záplaty (tedy *Update for Microsoft* a *Security Update for Microsoft*) + + .PARAMETER ComputerName + Parametr určující kde se má fce spustit. + + .PARAMETER ProgramName + Nepovinný parametr, sloužící pro vyfiltrování konkrétního jména aplikace. + + .PARAMETER DontIgnoreUpdates + Switch pro zobrazení aktualizací. + + .PARAMETER Property + Jaké vlastnosti klíče se mají vypsat. + + .PARAMETER Ogv + Switch. Vystup se posle do out-gridview. + + .EXAMPLE + $hala | get-installedsoftware -ProgramName winamp + + .NOTES + Převzato z https://gallery.technet.microsoft.com/scriptcenter/Get-RemoteProgram-Get-list-de9fd2b4 a upraveno. + #> + + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] + [string[]] $computerName = $env:COMPUTERNAME + , + [Parameter(Position = 1)] + [string] $programName + , + [switch] $dontIgnoreUpdates + , + [string[]] $property = ('DisplayVersion', 'UninstallString') + , + [switch] $ogv + ) + + BEGIN { + } + + PROCESS { + $result = Invoke-Command2 -ComputerName $computerName { + param ($Property, $DontIgnoreUpdates, $ProgramName) + + $RegistryLocation = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\', 'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\' + $HashProperty = @{} + $SelectProperty = @('ProgramName', 'ComputerName') + if ($Property) { + $SelectProperty += $Property + } + + $RegBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $env:COMPUTERNAME) + foreach ($CurrentReg in $RegistryLocation) { + if ($RegBase) { + $RegBase.OpenSubKey($CurrentReg).GetSubKeyNames() | + ForEach-Object { + if ($Property) { + foreach ($CurrentProperty in $Property) { + $HashProperty.$CurrentProperty = ($RegBase.OpenSubKey("$CurrentReg$_")).GetValue($CurrentProperty) + } + } + $HashProperty.ComputerName = $Computer + $HashProperty.ProgramName = ($DisplayName = ($RegBase.OpenSubKey("$CurrentReg$_")).GetValue('DisplayName')) + if ($DisplayName) { + if ($DontIgnoreUpdates) { + if ($ProgramName) { + New-Object -TypeName PSCustomObject -Property $HashProperty | + Select-Object -Property $SelectProperty | where { $_.ProgramName -like "*$ProgramName*" } + } else { + New-Object -TypeName PSCustomObject -Property $HashProperty | + Select-Object -Property $SelectProperty + } + } else { + if ($ProgramName) { + New-Object -TypeName PSCustomObject -Property $HashProperty | + Select-Object -Property $SelectProperty | where { $_.ProgramName -notlike "*Update for Microsoft*" -and $_.ProgramName -notlike "Security Update*" -and $_.ProgramName -like "*$ProgramName*" } + } else { + New-Object -TypeName PSCustomObject -Property $HashProperty | + Select-Object -Property $SelectProperty | where { $_.ProgramName -notlike "*Update for Microsoft*" -and $_.ProgramName -notlike "Security Update*" } + } + } + } + } + } + } + } -argumentList $property, $dontIgnoreUpdates, $programName + } + + END { + if ($ogv) { + $result | Out-GridView -PassThru -Title "Nainstalovany SW" + } else { + $result + } + } +} \ No newline at end of file diff --git a/Get-PendingReboot.ps1 b/Get-PendingReboot.ps1 new file mode 100644 index 0000000..f7a7f6c --- /dev/null +++ b/Get-PendingReboot.ps1 @@ -0,0 +1,66 @@ +function Get-PendingReboot { + <# + .SYNOPSIS + The PowerShell script which can be used to check if the server is pending reboot. + .DESCRIPTION + The PowerShell script which can be used to check if the server is pending reboot. + .PARAMETER ComputerName + Gets the server reboot status on the specified computer. + .EXAMPLE + C:\PS> C:\Script\FindServerIsPendingReboot.ps1 -ComputerName "WIN-VU0S8","WIN-FJ6FH","WIN-FJDSH","WIN-FG3FH" + + ComputerName RebootIsPending + ------------ --------------- + WIN-VU0S8 False + WIN-FJ6FH True + WIN-FJDSH True + WIN-FG3FH True + + This command will get the reboot status on the specified remote computers. + #> + + param + ( + [Parameter(Mandatory = $false, ValueFromPipeline = $true)] + [ValidateNotNullOrEmpty()] + [String[]]$ComputerName = $env:COMPUTERNAME + ) + + process { + $result = Invoke-Command2 -ComputerName $ComputerName -ScriptBlock { + #$PendingFile = $false + $AutoUpdate = $false + $CBS = $false + $SCCMPending = $false + $ErrorActionPreference = 'silentlycontinue' + + $AutoUpdate = Test-Path -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" + + # Determine SCCM 2012 reboot require + $SCCMReboot = Invoke-CimMethod -Namespace 'Root\ccm\clientSDK' -ClassName 'CCM_ClientUtilities' -Name 'DetermineIfRebootPending' + + If ($SCCMReboot) { + If ($SCCMReboot.RebootPending -or $SCCMReboot.IsHardRebootPending) { + $SCCMPending = $true + } + } + + # Determine PendingFileRenameOperations exists of not + # Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\PendFileKeyPath' -name PendingFileRenameOperations} + + # The servicing stack is available on all Windows Vista and Windows Server 2008 installations. + $CBS = Test-Path -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" + + If ($AutoUpdate -or $CBS -or $SCCMPending) { + $RebootIsPending = $true + } else { + $RebootIsPending = $false + } + + return New-Object -TypeName PSObject -Property @{ComputerName = $env:COMPUTERNAME; RebootIsPending = $RebootIsPending} + } + + $result | Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceID + } +} + diff --git a/Get-ReliabilityHistory.ps1 b/Get-ReliabilityHistory.ps1 new file mode 100644 index 0000000..b715cc0 --- /dev/null +++ b/Get-ReliabilityHistory.ps1 @@ -0,0 +1,162 @@ +function Get-ReliabilityHistory { + <# + .SYNOPSIS + Vypise stroje ze seznamu, ktere maji prumerny index spolehlivosti nizsi nez zadany. + Prumer se pocita za poslednich X dnu (kde X je hodnota daysOld) + Zaroven i vysledek ulozi jako csv soubor. + + .DESCRIPTION + Vypise stroje ze seznamu, ktere maji prumerny index spolehlivosti nizsi nez zadany. + Prumer se pocita za poslednich X dnu (kde X je hodnota daysOld). + Ziskava udaje z nastroje reliability history dostupneho ze start menu. + Vypise i prumerne hodnoty za kazdy den. + Zaroven i vysledek ulozi jako csv soubor. + + .PARAMETER computerName + Seznam stroju, ktere se maji zkontrolovat. + + .PARAMETER stabilityIndexUnder + Stroje s nizsim prumernym indexem stability budou vypsany. + Vychozi je hodnota 5 (z 10). + + .PARAMETER daysOld + Pocet dnu, za ktere se maji posbirat data o spolehlivosti. + Vychozi je 7. + + .PARAMETER exportCSV + Prepinac, zdali se ma vyexportovat podrobna historie reliability systemu. + + .PARAMETER CSVPath + Cesta k CSV souboru, do ktereho se budou exportovat podrobne vysledky. + Vychozi je $env:TEMP\ErrorRecords.csv. + + .PARAMETER sendEmail + Prepinac, zdali se ma poslat info i emailem. + Posle se vcetne CSV s podrobnymi zaznamy o reliability systemu v priloze. + + .PARAMETER to + Komu se ma email poslat. + Vychozi je aaa@bbb.cz. + + .PARAMETER returnObject + Vystup se nenaformatuje pomoci Format-Table. Tzn potreba, pokud se ma s vystupem dale pracovat. + Vystup standardne formatuji, aby byl prehledny. + + .EXAMPLE + Get-ReliabilityHistory -computerName nox, demeter -stabilityIndexUnder 5 -sendEmail -to sebela@fi.muni.cz -daysOld 7 + + Ziska informace o spolehlivosti stroju nox a demeter. Pokud je jejich index stability nizsi nez 5, + tak dojde k vypsani informaci o stabilite a poslani emailu s podrobnou csv prilohou. + Index se pocita ze zaznamu za poslednich 7 dni. + + .EXAMPLE + Get-ReliabilityHistory -exportCSV + + Ziska informace o spolehlivosti localhostu. Pokud e index za poslednich 7 dnu nizsi nez 5, + tak dojde k vypsani informaci o stabilite a vygenerovani CSV s jednotlivymi udalostmi. + + .NOTES + https://4sysops.com/archives/monitoring-windows-system-stability-with-powershell/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+4sysops+%284sysops%29 + #> + + param ( + [ValidateNotNullOrEmpty()] + [string[]] $computerName = $env:COMPUTERNAME + , + [ValidateNotNullOrEmpty()] + [int] $stabilityIndexUnder = 5 + , + [switch] $sendEmail + , + [ValidateNotNullOrEmpty()] + [int] $daysOld = 7 + , + [switch] $exportCSV + , + [string] $CSVPath = (Join-Path $env:TEMP 'ErrorRecords.csv') + , + [ValidateNotNullOrEmpty()] + [string] $to = 'aaa@bbb.cz' + , + [switch] $returnObject + ) + + begin { + $startTime = (Get-Date (Get-Date).AddDays( - $daysOld) -Format "dd.MM.yyyy") + $result = New-Object System.Collections.ArrayList + $csv = New-Object System.Collections.ArrayList + if ($sendEmail) { $exportCSV = $true } + } + + process { + foreach ($computer in $computerName) { + $params = @{className = 'win32_reliabilitystabilitymetrics'; ComputerName = $computer; filter = "TimeGenerated > `'$startTime`'"; ErrorAction = 'SilentlyContinue'} + if ($computer -eq $env:COMPUTERNAME) { $params.Remove("ComputerName") } # vuci localhostu nedelam remote + $reliabilityStabilityMetrics = Get-CimInstance @params | Select-Object @{n = 'Computer'; e = {$computer}}, SystemStabilityIndex, TimeGenerated + + if (!$reliabilityStabilityMetrics) { Write-Host "Na $computer jsem neziskal zadne zaznamy."; continue } + + $averageStabilityIndex = [math]::Round(($reliabilityStabilityMetrics | Measure-Object -Property SystemStabilityIndex -Average).Average, 1) + $lastStabilityIndex = [math]::Round(($reliabilityStabilityMetrics | select -first 1 | select -ExpandProperty systemStabilityIndex), 1) + if ($averageStabilityIndex -le $stabilityIndexUnder) { + $params = @{ComputerName = $computer; ClassName = 'win32_reliabilityRecords'; filter = "TimeGenerated > '$startTime'"} + if ($computer -eq $env:COMPUTERNAME) { $params.Remove("ComputerName") } # vuci localhostu nedelam remote + $reliabilityRecords = Get-CimInstance @params | Select-Object @{n = 'Computer'; e = {$computer}}, EventIdentifier, LogFile, Message, ProductName, RecordNumber, SourceName, TimeGenerated + + # ulozim zaznamy, ktere pozdeji vyexportuji do csv + if ($exportCSV) { + $reliabilityRecords | % { $null = $csv.add($_) } + } + + $txt += "`n`n$computer ma prumerny index $averageStabilityIndex (aktualne $lastStabilityIndex). Vypis po dnech:`n" + + # zobrazim i prumer za jednotlive dny at se z toho da pripadne neco vysledovat + $obj = [PSCustomObject] @{computer = $computer; average = $averageStabilityIndex} + $metricsGroupedByDay = $reliabilityStabilityMetrics | group {$_.timegenerated.date} | sort {$_.name -as [datetime]} + foreach ($oneDayMatrics in $metricsGroupedByDay) { + $records = $oneDayMatrics.group.systemstabilityindex -split " " + $sum = 0 + foreach ($record in $records) { + $sum += $record + } + # pridam informaci do objektu + $obj | Add-Member -MemberType NoteProperty -Name $(Get-Date ($oneDayMatrics.name) -Format dd.MM) -value $([math]::Round(($sum / $records.count), 1)) + + $txt += "$(Get-Date ($oneDayMatrics.name) -Format dd.MM): $([math]::Round(($sum/$records.count), 1)) | " + } + $result += $obj + + # odmazani zbytecneho rozdelovace na konci + $txt = $txt -replace " \| $" + } else { + "$computer ma index stability ($averageStabilityIndex) vyssi nez nastaveny limit." + } + } # end foreach computerName + } + + end { + if ($result) { + # vypisu vysledky + if ($returnObject) { + # vrati objekt + $result | sort average + } else { + # vrati text + $result | sort average | Format-Table + } + + if ($csv) { + "CSV se zaznamy je ulozeno v $CSVPath" + $csv | Export-CSV $CSVPath -Encoding UTF8 -NoTypeInformation -Force + } + } + + # poslu vysledky emailem + if ($txt -and $sendEmail) { + $body = "Hola,`nnize je seznam stroju, ktere maji prumerny reliability index nizsi nez $stabilityIndexUnder.`nMereni probihalo od $startTime dosud.$txt" + $body += "`n`nKonkretni chyby naleznete v priloze." + + send-email -to $to -subject 'Stroje s nizkym reliability indexem' -body $body -attachment $CSVPath + } + } +} \ No newline at end of file diff --git a/Get-SIDFromAccount.ps1 b/Get-SIDFromAccount.ps1 new file mode 100644 index 0000000..c327954 --- /dev/null +++ b/Get-SIDFromAccount.ps1 @@ -0,0 +1,61 @@ +function Get-SIDFromAccount { + <# +.SYNOPSIS +Fce pro zjisteni SID zadaneho uzivatele ci skupiny. +.DESCRIPTION +Ve vychozim nastaveni preklada lokalni ucty. Pro domenove je potreba zadat i nepovinny parametr domain. +Ale pozor, pokud dany ucet nebude nalezen lokalne, tak se zkusi nalezt v domene. +.PARAMETER AccountName +Jmeno uzivatele ci skupiny. +.PARAMETER Domain +Switch, ktery se pouziva pokud chci prekladat domenove ucty. +.PARAMETER ComputerName +Jmeno stroje, na kterem ma dojit k prekladu loginu. +.EXAMPLE +Get-SIDFromAccount administrator +.EXAMPLE +Get-SIDFromAccount administrator -domain +.EXAMPLE +Get-SIDFromAccount -accountname _sokrates05 -computername sokrates05 +#> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "zadej jmeno uzivatele ci skupiny")] + $AccountName + , + [switch]$Domain + , + [string]$computerName + ) + + if ($computerName) { + Invoke-Command -ComputerName $ComputerName -ScriptBlock { + if ($using:Domain) { + # ziskani jmena domeny do ktere je stroj zapojeny + $DomainName = (gwmi WIN32_ComputerSystem).Domain + } else { + $DomainName = "" + } + + try { + ((New-Object System.Security.Principal.NTAccount("$DomainName", "$using:AccountName")).Translate([System.Security.Principal.SecurityIdentifier])).Value + } catch { + throw "Ucet $using:AccountName se nepodarilo prelozit. Bud neexistuje nebo jste spatne zvolili jeho typ (lokalni|domenovy)." + } + } + } else { + if ($Domain) { + # ziskani jmena domeny do ktere je stroj zapojeny + $DomainName = (gwmi WIN32_ComputerSystem).Domain + } else { + $DomainName = "" + } + + try { + ((New-Object System.Security.Principal.NTAccount("$DomainName", "$AccountName")).Translate([System.Security.Principal.SecurityIdentifier])).Value + } catch { + throw "Ucet $AccountName se nepodarilo prelozit. Bud neexistuje nebo jste spatne zvolili jeho typ (lokalni|domenovy).`nPokud jde o computer ucet, nezapomente dat za jmeno $" + } + } +} diff --git a/Get-Shutdown.ps1 b/Get-Shutdown.ps1 new file mode 100644 index 0000000..a376150 --- /dev/null +++ b/Get-Shutdown.ps1 @@ -0,0 +1,548 @@ +#requires -modules psasync + +<# +TODO: +- pridat moznost filtrovat dle zadaneho data +- vyresit ze nekdy je vic restartu za sebou po nekolika sekundach aniz by mezi nimi byl nejaky start, zrejme jen duplicita v logu +- zda se ze neukazuje hibernace! + - http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/WMI+Detect~Standby~or~Hibernation~Event.txt + - http://www.dreamincode.net/forums/topic/175535-detect-system-wake-up-from-sleephibernatestand-by/ + - http://blog.nirsoft.net/2013/07/04/new-utility-for-windows-vista782008-that-displays-the-logonlogoff-times/ + +!!!!!POZOR pokud u get-date pouziji -format tak vraci string jinak datetime!!!! + +#> +function Get-Shutdown { + <# + .SYNOPSIS + Fce vrati casy zapnuti|vypnuti|uspani|probuzeni|restartu|bsod|neocekavanych vypnuti zadaneho stroje. + + .DESCRIPTION + U unexpected shutdowns eventu vraci v message jmeno tehdy prihlaseneho uzivatele. Pokud se nezobrazi, zvyste hodnotu days_to_search. + Pokud je prvni vraceny event typu start, message bude obsahovat jeho uptime ve formatu dd:hh:mm:ss. + BSOD se ziskavaji z minidump adresare (ziskavani z event logu nebylo spolehlive). + Pro zobrazeni podrobnych informaci o BSOD je potreba mit v jedne z nastavenych cest bluescreenview.exe (ozkousena verze je 1.55 x64, starsi nefungovaly s UNC!) + Pro zobrazeni podrobnych BSOD informaci je treba spustit s admin pravy. + Message cas obsahuje uzivatele, ktery akci provedl, pokud je tato informace dostupna v eventu. + + .PARAMETER ComputerName + Jmeno stroje/u. + + .PARAMETER Newest + Pocet udalosti k vypsani. Vychozi hodnota je 4. + + .PARAMETER Filter + Pole stringu, ktere urcuji jake udalosti se maji vypsat. Ve vychozim nastaveni obsahuje vsechny moznosti. + Mozne varianty jsou "start", "unexpected_shutdown", "shutdown_or_restart", "bsod", "wake_up", "sleep" + + .PARAMETER days_to_search + Číslo udávající kolik dnů před posledním unexp. shutdownem se má hledat přihlášení uživatele. + Výchozí je 7 dnů. + + .PARAMETER maxMinutes + Číslo udávající o kolik minut po BSOD musí být unexp. event, aby došlo k jeho smazání. + Předpokládáme, že oba záznamy reprezentují jeden pád systému. + Výchozí je 10 minut. + + .PARAMETER bluescreenviewexe_path + Obsahuje preddefinovane pole s moznymi cestami k nirsoft utilite bluescreenview. Nebo muzete zadat vlastni. + Funguje 1.55 verze x64. + + .PARAMETER silent + Prepinac rikajici, ze pokud nebude dostupny BluScreenView tool, tak se nebude poptavat jeho stazeni. + Vhodne pri pouziti ve skriptech. + + .EXAMPLE + Get-Shutdown kronos,titan05 5 + + .EXAMPLE + Get-Shutdown kronos,titan05 -newest 5 -filter "shutdown_or_restart","bsod" + + .EXAMPLE + get-shutdown $hala -filter bsod | select computer,message | fl * + + pro zobrazeni podrobneho infa o poslednich ctyrech BSOD na strojich v hale + #> + [CmdletBinding()] + param( + [Parameter(Position = 0, Mandatory = $false)] + [ValidateNotNullOrEmpty()] + $computerName = $env:computername + , + + [Parameter(Position = 1)] + [ValidateNotNull()] + [int] $newest = 4 + , + [ValidateSet("start", "unexpected_shutdown", "shutdown_or_restart", "bsod", "wake_up", "sleep")] + [ValidateNotNullOrEmpty()] + $filter = @("start", "unexpected_shutdown", "shutdown_or_restart", "bsod", "wake_up", "sleep") + , + [int] $days_to_search = 7 + , + [int] $maxMinutes = 10 + , + [string[]] $bluescreenviewexe_path = @("$env:tmp\bluescreenview-x64\bluescreenview.exe", '\\ad.fi.muni.cz\dfs\bin\NirSoft\bluescreenview.exe') + , + [switch] $silent + ) + + BEGIN { + $AsyncPipelines = @() + $pool = Get-RunspacePool 20 + $Events = New-Object System.Collections.ArrayList + + # prevedu na arraylist abych mohl pouzivat remove + $filter = {$filter}.invoke() + + # kontrola ze je dostupny bluescreenview + if ($filter -contains "bsod") { + $bsodViewerExists = 0 + foreach ($path in $bluescreenviewexe_path) { + if (Test-Path $path -ErrorAction SilentlyContinue) { + $bsodViewerExists = 1 + $bluescreenviewexe_path = $path + break + } + } + + if (!$bsodViewerExists -and !$silent) { + write-warning "BlueScreenView.exe neni dostupny na zadne ze zadanych cest $bluescreenviewexe_path." + $answer = Read-Host "Chcete jej stahnout z internetu (ne kazda verze funguje z CMD!) ? a|n" + if ($answer -eq 'a') { + try { + $webAddress = 'http://www.nirsoft.net/utils/bluescreenview-x64.zip' + $DownloadDestination = "$env:tmp\bluescreenview-x64.zip" + [Void][System.IO.Directory]::CreateDirectory((Split-Path $DownloadDestination)) + $ExtractedTools = $DownloadDestination -replace '.zip' + Invoke-WebRequest $webAddress -OutFile $DownloadDestination + if (Test-Path $ExtractedTools) { + Remove-Item $ExtractedTools -Confirm:$false -Recurse + } + [Void][System.IO.Directory]::CreateDirectory($ExtractedTools) + + $null = Unzip-File $DownloadDestination $ExtractedTools + # s vychozim CFG souborem nefunguje ziskavani infa ze vzdalenych minidump souboru = upravim + $CFGFile = join-path $ExtractedTools 'BlueScreenView.cfg' + $CFGFileContent = @' +[General] +ShowGridLines=0 +SaveFilterIndex=0 +ShowInfoTip=1 +ShowTimeInGMT=0 +VerSplitLoc=16383 +LowerPaneMode=1 +MarkDriversInStack=1 +AddExportHeaderLine=0 +ComputersFile= +LoadFrom=3 +DumpChkCommand=""%programfiles%\Debugging Tools for Windows\DumpChk.exe" "%1"" +MarkOddEvenRows=0 +SingleDumpFile= +WinPos=2C 00 00 00 00 00 00 00 01 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF D0 00 00 00 D0 00 00 00 50 03 00 00 B0 02 00 00 +Columns=B4 00 00 00 78 00 01 00 96 00 02 00 6E 00 03 00 6E 00 04 00 6E 00 05 00 6E 00 06 00 6E 00 07 00 96 00 08 00 78 00 09 00 8C 00 0A 00 82 00 0B 00 78 00 0C 00 78 00 0D 00 50 00 0E 00 78 00 0F 00 78 00 10 00 78 00 11 00 78 00 12 00 50 00 13 00 50 00 14 00 5A 00 15 00 5A 00 16 00 5A 00 17 00 5A 00 18 00 5A 00 19 00 +Sort=4097 +ModulesColumns=B4 00 00 00 78 00 01 00 78 00 02 00 78 00 03 00 78 00 04 00 78 00 05 00 78 00 06 00 78 00 07 00 78 00 08 00 78 00 09 00 78 00 0A 00 78 00 0B 00 +ModulesSort=1 +'@ + Set-Content -Path $CFGFile -Value $CFGFileContent + $bluescreenviewexe_path = join-path $ExtractedTools bluescreenview.exe + $bsodViewerExists = 1 + } catch { + # uklid + Remove-Item $DownloadDestination, $ExtractedTools -Confirm:$false -Recurse -Force + throw "Neco se pokazilo.`nChyba:`n$($_.Exception.Message)`nRadek:`n$($_.InvocationInfo.ScriptLineNumber) `n`n..koncim" + } + } + } elseif ($bsodViewerExists) { + # $bsodview existuje, musim upravit konfiguraci, aby fungovalo ziskavani infa ze vzdalenych minidump souboru + $CFGFile = Join-Path (Split-Path $bluescreenviewexe_path) 'BlueScreenView.cfg' + if (Test-Path $CFGFile -ErrorAction SilentlyContinue) { + $content = Get-Content $CFGFile + if ($content -match 'LoadFrom=1') { + Write-Warning "Upravuji $CFGFile, aby slo pracovat s remote minidump soubory!" + $content | Foreach-Object {$_ -replace '^LoadFrom=1$', "LoadFrom=3"} | Set-Content $CFGFile + } + } else { + Set-Content -Path $CFGFile -Value $CFGFileContent + } + } + + # BSOD viewer neni dostupny a uzivatel jej nechtel stahnout + if (!$bsodViewerExists) { + Write-Warning "Obsah BSOD se nezobrazi, BSODViewer neni k dispozici" + } + } + + + + + #kontrola zdali skript bezi s admin pravy + if ($filter -contains "bsod" -and $bsodViewerExists -and ($ComputerName -eq $env:computername) -and !([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + write-warning "BSOD udalosti se nezobrazi! Je nutne spustit s admin pravy." + [void]$Filter.remove('bsod') + } + + $scriptblock = { + param ($Computer, $newest, $Filter, $days_to_search, $maxMinutes, $bluescreenviewexe_path, $VerbosePreference) + if (test-connection -computername $Computer -Count 1 -quiet) { + $AllUnexpectedEvents = @() + $AllUnexpectedEvents = {$AllUnexpectedEvents}.invoke() + [System.Collections.ArrayList]$todelete = @() + $BSODevents = @() + $UnexpectedEvents = @() + $WakeupEvents = @() + $SleepEvents = @() + + #region pomocne funkce + function Get-LogOnOff { + <# + .SYNOPSIS + Fce slouží k vypsání logon/off událostí na vybraných strojích uživatele/ů. + + .DESCRIPTION + Fce vyhledá logon/off eventy na vybraných strojích. + Defaultně vypíše 4 poslední logon/off. + Vyžaduje povolený a modul psasync. + + .PARAMETER ComputerName + Seznam strojů, na kterých zjistím logon/off akce. + + .PARAMETER Newest + Číslo určující kolik logon/off událostí se má vypsat. Výchozí hodnota je 4. + + .PARAMETER UserName + Parametr určující login uživatele, který se má na daných strojích hledat. + + .PARAMETER Type + Seznam určující jaky typ eventu se ma hledat. Moznosti: logon, logoff. + + .PARAMETER After + Parametr určující po jakém datu se mají eventy hledat. + Zadavejte ve formatu: d.M.YYYY pripadne d.M.YYYY H:m, Pr.: 13.5.2015, 13.5.2015 6:00. + Zadáte-li neexistující datum, tak filtr nebude fungovat! + + .PARAMETER Before + Parametr určující před jakým datem se mají eventy hledat. + Zadavejte ve formatu: d.M.YYYY pripadne d.M.YYYY H:m, Pr.: 13.5.2015, 13.5.2015 6:00. + Zadáte-li neexistující datum, tak filtr nebude fungovat! + + .EXAMPLE + $hala | Get-LogOnOff + Na strojích z haly vypíše 4 poslední přihlášení/odhlášení. + + .EXAMPLE + $hala | Get-LogOnOff -username sebela + Vyhledá 4 nejnovější záznamy o přihlášení uživatele sebela na každém stroji v hale. + + .EXAMPLE + $hala | Get-LogOnOff -username sebela -type logon -newest 10 + Vyhledá 10 nejnovějších přihlášení uživatele sebela na každém stroji v hale. + + .EXAMPLE + $hala | Get-LogOnOff -username sebela -type logoff -newest 10 -after '14.1.2015 10:00' -before 20.2.2015 + Vyhledá 10 odhlášení uživatele sebela na každém stroji v hale mezi 14.1.2015 10:00 a 20.2.2015. + + .NOTES + Author: Ondřej Šebela - ztrhgf@seznam.cz + #> + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "zadej jmeno stroje/ů")] + [Alias("c", "CN", "__Server", "IPAddress", "Server", "Computer", "Name", "SamAccountName")] + [ValidateNotNullOrEmpty()] + [String[]] $ComputerName = $env:computername + , + [Parameter(Mandatory = $false, Position = 1)] + [Alias("user", "login")] + [ValidateNotNullOrEmpty()] + [string]$UserName + , + [Parameter(Mandatory = $false, Position = 2)] + [int]$newest + , + [ValidateSet("logon", "logoff")] + [array]$type = @("logon", "logoff") + , + [ValidateScript( { + If (($_ -match '^\d{1,2}\.\d{1,2}\.\d{4}( \d{1,2}:\d{1,2}(:\d{1,2}?)?)?$')) { + $true + } else { + Throw "$_ .Zadavejte ve formatu: d.M.yyyy, d.M.yyyy H:m, d.M.yyyy H:m:s Pr.: 13.5.2015, 13.5.2015 6:00, 13.5.2015 6:00:33" + } + })] + $after + , + [ValidateScript( { + If (($_ -match '^\d{1,2}\.\d{1,2}\.\d{4}( \d{1,2}:\d{1,2}(:\d{1,2}?)?)?$')) { + $true + } else { + Throw "$_ .Zadavejte ve formatu: d.M.yyyy, d.M.yyyy H:m, d.M.yyyy H:m:s Pr.: 13.5.2015, 13.5.2015 6:00, 13.5.2015 6:00:33" + } + })] + $before + ) + + BEGIN { + try { + Import-Module psasync -ErrorAction Stop + } catch { + Write-Error "Nepodařilo se naimportovat psasync modul" + break + } + + # ve vychozim stavu vypise 4 posledni udalosti + if (!$after -and !$newest -and !$before) { + $newest = 4 + } + + $AsyncPipelines = @() + $pool = Get-RunspacePool 20 + + $scriptblock = ` + { + param($computer, $newest, $type, $UserName, $after, $before) + + if (Test-Connection -ComputerName $computer -Count 1 -ErrorAction SilentlyContinue) { + + $UserProperty = @{n = "User"; e = {(New-Object System.Security.Principal.SecurityIdentifier $_.properties[1].value.value).Translate([System.Security.Principal.NTAccount])}} + $TypeProperty = @{n = "Action"; e = {if ($_.ID -eq 7001) {"Logon"} else {"Logoff"}}} + $TimeProperty = @{n = "Time"; e = {$_.TimeCreated}} + $CompName = @{n = "Computer"; e = {$computer}} + + # poskladani prikazu k vykonani + $command = "Get-WinEvent -ComputerName $Computer -ea silentlycontinue -FilterHashTable @{LogName='System'; ProviderName='Microsoft-Windows-Winlogon'" + if ($type -contains "logon" -and $type -contains "logoff") { + $command += "" + } elseif ($type -contains "logon") { + $command += ";id=7001" + } elseif ($type -contains "logoff") { + $command += ";id=7002" + } + if ($after) { + $command += ";starttime=`"$after`"" + } + if ($before) { + $command += ";endtime=`"$before`"" + } + $command += "} " + if ($newest -and !$username) { + $command += "-MaxEvents $Newest " + } + $command += '| select $CompName,$UserProperty,$TypeProperty,$TimeProperty ' + if ($UserName) { + if ($newest) { + $command += '| where {$_.user -like "*$UserName*"} | select -First $Newest' + } else { + $command += '| where {$_.user -like "*$UserName*"}' + } + } + + #vykonani prikazu + Invoke-Expression $command + } else { + Write-Output "$computer nepinga." + } + } + } + + PROCESS { + foreach ($computer in $ComputerName) { + $AsyncPipelines += Invoke-Async -RunspacePool $pool -ScriptBlock $ScriptBlock -Parameters $computer, $newest, $type, $UserName, $after, $before + } + } + + + END { + Receive-AsyncResults -Pipelines $AsyncPipelines -ShowProgress + } + } + + + + # fce pro ziskani podrobnosti ohledne BSOD z DMP souboru, pouziva program bluescreenview + function get-bsodinfo { + param ( + $computer, + $bluescreenviewexe_path, + $bsodtime + ) + + $bsodtime = Get-Date $bsodtime + # ziskam cestu k DMP souboru s BSOD informacemi + $dmpFile = Get-ChildItem \\$computer\C$\Windows\Minidump -File | where { $_.creationtime -gt ($bsodtime).addseconds(-1) -and $_.creationtime -lt ($bsodtime).addseconds(2)} | select -ExpandProperty fullname + if ($dmpFile) { + $CsvFile = [System.IO.Path]::GetTempFileName() + & $bluescreenviewexe_path /singledumpfile $dmpFile /scomma $CsvFile + if ($?) { + $CsvFileFinal = [System.IO.Path]::GetTempFileName() + # pockam nez bude mit soubor nejaky obsah + do { + $CsvFileContent = Get-Content $CsvFile -Force -raw + sleep 1 + ++$x + } + until ($CsvFileContent -or $x -ge 10) + # do finalniho csv souboru pridam hlavicku a obsah csv s bsod informacemi + Add-Content -path $CsvFileFinal -Value "Dump File,Crash Time,Bug Check String,Bug Check Code,Parameter 1,Parameter 2,Parameter 3,Parameter 4,Caused by driver,Caused by address,File description,Product name,Company,File version,Processor,Crash Address,Stack Address 1,Stack Address 2,Stack Address 3,Computer Name,Full path,Processors Count,Major Version,Dump File Size,Dump File Time `r`n$CsvFileContent" -Confirm:$false + # naimportuji obsah + $result = Import-Csv $CsvFileFinal + # smazani tmp souboru + Remove-Item $CsvFile, $CsvFileFinal -Confirm:$false -Force + # vypsani vysledku + $result + } else { + write-output "Nepodarilo se pomoci bluescreenview ziskat bsod informace." + } + } else { + Write-Error "DMP soubor s BSOD infem nenalezen. Cas vytvoreni $(($bsodtime).addseconds(-1)) do $(($bsodtime).addseconds(2))" + } + } + #endregion + + # shutdown | restart + if ($filter -contains "shutdown_or_restart") { + #chystane vypnuti podrobne info (cas vypnuti neodpovida uplne realite = jde o pripravu na vypnuti) + $events += Get-WinEvent -FilterHashtable @{logname = "system"; providername = "User32"; id = 1074}` + -ComputerName $Computer -MaxEvents $newest -ea silentlycontinue | Select-Object @{Name = "Computer"; Expression = {$computer}}, @{Name = "Event"; Expression = {"$($_.Properties[4].Value)" -replace "power off|Napájení vypnuto", "Shutdown" -replace "Restartování", "Restart"}}, @{Name = "Time"; Expression = {$_.TimeCreated}}, @{Name = "Message"; Expression = {"KDO: $($_.properties[6].value), PROC: $($_.properties[5].value) | $($_.properties[2].value), PROCES: $($_.properties[0].value)"}} + } + + # zapnuti + if ($filter -contains "start") { + $events += Get-WinEvent -FilterHashtable @{logname = "system"; providername = "Microsoft-Windows-Kernel-General"; id = 12}` + -ComputerName $Computer -MaxEvents $newest -ea silentlycontinue | Select-Object @{Name = "Computer"; Expression = {$computer}}, @{Name = "Event"; Expression = {"Start"}}, @{Name = "Time"; Expression = {$_.TimeCreated}}, @{Name = "Message"; Expression = {''}} + } + + # Wakeup events + if ($filter -contains "wake_up") { + $events += Get-WinEvent -FilterHashtable @{providername = "Microsoft-Windows-Power-Troubleshooter"; logname = "system"}` + -MaxEvents $Newest -computername $computer -ea silentlycontinue | Select-Object @{Name = "Computer"; Expression = {$computer}}, @{Name = "Event"; Expression = {"WakeUp"}}, @{Name = "Time"; Expression = {$_.TimeCreated}}, @{Name = "Message"; Expression = {$_.User, $_.message}} + } + + # Sleep events + if ($filter -contains "sleep") { + $events += Get-WinEvent -FilterHashtable @{providername = "Microsoft-Windows-Kernel-Power"; logname = "system"; id = 42}` + -MaxEvents $Newest -computername $computer -ea silentlycontinue | Select-Object @{Name = "Computer"; Expression = {$computer}}, @{Name = "Event"; Expression = {"Sleep"}}, @{Name = "Time"; Expression = {$_.TimeCreated}}, @{Name = "Message"; Expression = {$_.User, $_.message}} + } + + # BSOD + # TODO kdyz neni $bsodViewerExists tak ziskat info z logu + if ($filter -contains "bsod" -and $bsodViewerExists) { + # ziskani BSOD z minidump souboru (z event logu ukazuje vic smrtek nez je minidump souboru) + if (test-path "\\$computer\C$\windows\minidump" -ea silentlycontinue) { + $BSODevents = Get-ChildItem -path \\$computer\C$\windows\minidump\* -include *.dmp | sort -descending CreationTime | select -First $newest + if ($BSODevents) { + foreach ($bsod in $BSODevents) { + $message = "" + $message = get-bsodinfo $computer $bluescreenviewexe_path $($bsod.CreationTime) | Out-String # ziskany objekt prevedu na text pro snadnejsi cteni informaci, neocekavam potrebu filtrovani dle jednotlivych parametru + if (!$message) { + $message = "vyskystl se problem pri ziskavani podrobneho BSOD infa." + } + + $allUnexpectedEvents += $bsod | Select-Object @{Name = "Computer"; Expression = {$computer}}, @{Name = "Event"; Expression = {"BSOD"}}, @{Name = "Time"; Expression = {$_.CreationTime}}, @{Name = "Message"; Expression = {$message}} + } + } + } + # # ziskani BSOD z event logu (zalozni moznost) + # $BSODevents = Get-WinEvent -FilterHashtable @{logname="application";providername="Windows Error*";id=1001}` + # -ComputerName $Computer | Select-Object timecreated, properties | where {$_.properties[2].value -eq "BlueScreen"} | Select-Object @{Name="Computer";Expression={$computer}}, @{Name="Event";Expression={"BSOD"}}, @{Name="Time";Expression={$_.TimeCreated}}, @{Name="Message";Expression={$_.message}}, User + # $allUnexpectedEvents += $BSODevents + } + + # unexpected shutdowns + if ($filter -contains "unexpected_shutdown") { + $UnexpectedEvents = Get-WinEvent -FilterHashtable @{logname = "system"; providername = "Microsoft-Windows-Kernel-Power"; id = 41}` + -ComputerName $Computer -MaxEvents $newest -ea silentlycontinue | Select-Object @{Name = "Computer"; Expression = {$computer}}, @{Name = "Event"; Expression = {"Unexpected Shutdown"}}, @{Name = "Time"; Expression = {$_.TimeCreated}}, @{Name = "Message"; Expression = {""}} + + if ($UnexpectedEvents) { + # ULOZENI KDO BYL PRIHLASEN DO USER PROPERTY OBJEKTU + # cas posledniho unexp. shut. + $last_unexpected_shutdown = $UnexpectedEvents | sort time -Descending | select -Last 1 | select -exp time + # posunuti cas dozadu kvuli dohledani tehdy prihlaseneho uzivatele (pokud se prihlasil jeste drive pred unexp. shut. tak se neukaze) + $last_unexpected_shutdown = $last_unexpected_shutdown.adddays( - $days_to_search) + # prevedeni na spravny format data + $last_unexpected_shutdown = (get-date $last_unexpected_shutdown -Format 'd.M.yyyy H:m') + #ziskat logon eventy do tohoto data + $LogonEvents2 = Get-LogOnOff -ComputerName $Computer -type 'logon' -after $last_unexpected_shutdown -Newest 100000 + # upravit message cast kazdeho unexp. shut. s udajem kdo byl prihlasen + foreach ($event in $UnexpectedEvents) { + $logged_user = ($LogonEvents2 | where {$_.action -eq 'logon' -and $_.time -lt $($event.time)} | select -first 1 | select -exp user).value + $event.message = $logged_user + } + write-verbose "do `$allUnexpectedEvents pridavam unexp. shutdown eventy" + $allUnexpectedEvents += $UnexpectedEvents + } + } + + # OSETRENI VARIANTY KDY PRO JEDEN PAR SYSTEMU JSOU V LOGU JAK UNEXP. SHUTDOWN UDALOST TAK BSOD UDALOST + # ZISKANI UNEXP. SHUTDOWNS UDALOSTI, KTERE JSOU V LOGU TESNE PRED BSOD KVULI JEJICH POZDEJSIMU ODSTRANENI + if ($filter -contains "unexpected_shutdown" -and $filter -contains "bsod") { + $allUnexpectedEvents = $allUnexpectedEvents | sort Time -Descending + $allUnexpectedEvents | % {$x = 0} { + $thisEvent = $allUnexpectedEvents[$x] + $nextEvent = $allUnexpectedEvents[$x + 1] + $previousEvent = $allUnexpectedEvents[$x - 1] + # tento IF osetruje variantu, kdy se unexp. zaradil pri sortu pred BSOD i kdyz ma stejny cas a za BSOD je dalsi unexp. ktery by odpovidal filtru = smazal by se nepravy + if (($previousEvent.event -eq "Unexpected Shutdown" -and $thisEvent.event -eq "BSOD") -and ($thisEvent.time -eq $previousEvent.time)) { + $nextEvent = $previousEvent + } + if (($thisEvent.event -eq "BSOD" -and $nextEvent.event -eq "Unexpected Shutdown") -and ($nextEvent.time -gt $thisEvent.time.addminutes( - $maxMinutes))) { + [void]$todelete.add($nextEvent.time) + write-verbose "do seznamu eventu ke smazani jsem pridal unexp. event $($nextEvent.time). Byl max $maxMinutes minut pred BSOD = zrejme jedna udalost."; + } + $x++ + } + + # ODSTRANENI ZISKANYCH UNEXP. SHUTDOWNU + $allUnexpectedEvents = {$allUnexpectedEvents}.invoke() + # smazu unexpected eventy po kterych nasleduje v seznamu BSOD event (realne jde o jednu udalost) + if ($todelete.count -gt 0) { + foreach ($time in $todelete) { + $event = $allUnexpectedEvents | where {$_.time -eq $time -and $_.event -eq "Unexpected Shutdown"} + #write-output "mazu $time tedy $event" + $allUnexpectedEvents.removeat($allUnexpectedEvents.indexof($event)) + } + } + } + # (zeditovane) unexp. eventy spolu s BSOD nahraji k ostatnim udalostem + write-verbose "do seznamu eventu pridavam unexp a bsod eventy" + $events += $allUnexpectedEvents + + + # PRIDANI UPTIME DO MESSAGE PROPERTY DO PRVNIHO START EVENTU + $events = $events | sort Time -Descending + if ($events -and ($events[0]).event -eq "start") { + $Uptime = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer + $LastBootUpTime = $Uptime.ConvertToDateTime($Uptime.LastBootUpTime) + $Time = (Get-Date) - $LastBootUpTime + $Uptime = '{0:00}:{1:00}:{2:00}:{3:00}' -f $Time.Days, $Time.Hours, $Time.Minutes, $Time.Seconds + ($events[0]).message = "Uptime: $Uptime" + } + + # VYPSANI ZISKANYCH UDALOSTI + $ErrorActionPreference = "silentlycontinue" + $events | select -first $newest | select computer, event, Time, message + } else { + # stroj nepinga + $property = @{"Computer" = $computer; "Event" = ""; "Time" = ""; "Message" = "nepinga"} + $object = New-Object -TypeName PSObject -Property $property + $object | select computer, event, Time, message + } + } + } + + PROCESS { + foreach ($Computer in $ComputerName) { + $AsyncPipelines += Invoke-Async -RunspacePool $pool -ScriptBlock $ScriptBlock -Parameters $Computer, $Newest, $Filter, $days_to_search, $maxMinutes, $bluescreenviewexe_path, $VerbosePreference + } + } + + END { + Receive-AsyncResults -Pipelines $AsyncPipelines -ShowProgress + # uklid + # if ($DownloadDestination) { + # Remove-Item $DownloadDestination, $ExtractedTools -Confirm:$false -Recurse -Force + # } + } +} \ No newline at end of file diff --git a/Get-Uptime.ps1 b/Get-Uptime.ps1 new file mode 100644 index 0000000..c74c518 --- /dev/null +++ b/Get-Uptime.ps1 @@ -0,0 +1,40 @@ +Function Get-Uptime { + <# + .SYNOPSIS + Vypise uptime zadaneho stroje. + + .DESCRIPTION + Vypise uptime zadaneho stroje. Podle posledniho casu bootu OS. + + .PARAMETER computerName + Jmeno stroje + + .EXAMPLE + Get-Uptime + + vypise, jak dlouho je lokalni stroj online + + .EXAMPLE + Get-Uptime -ComputerName $hala + + vypise, jak dlouho jsou jednotlive stroje v hale online + #> + + param ( + [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [alias("cn")] + [string[]]$computerName = $env:COMPUTERNAME + ) + + PROCESS { + Invoke-Command2 -computerName $computerName { + $Uptime = Get-WmiObject -Class Win32_OperatingSystem -Property LastBootUpTime + $LastBootUpTime = $Uptime.ConvertToDateTime($Uptime.LastBootUpTime) + $Time = (Get-Date) - $LastBootUpTime + New-Object PSObject -Property @{ + ComputerName = $env:COMPUTERNAME.ToUpper() + Uptime = '{0:00}:{1:00}:{2:00}:{3:00}' -f $Time.Days, $Time.Hours, $Time.Minutes, $Time.Seconds + } + } | Select-Object -property * -excludeProperty PSComputerName, RunspaceId + } +} \ No newline at end of file diff --git a/Get-WinEventArchivedIncluded.ps1 b/Get-WinEventArchivedIncluded.ps1 new file mode 100644 index 0000000..1fd5ed7 --- /dev/null +++ b/Get-WinEventArchivedIncluded.ps1 @@ -0,0 +1,413 @@ +function Get-WinEventArchivedIncluded { + <# + .SYNOPSIS + Slouzi k ziskani udalosti ze systemoveho logu i jeho archivu. + + .DESCRIPTION + Slouzi k ziskani udalosti ze systemoveho logu i jeho archivu + + To jestli se budou udalosti hledat v "zivem" logu i/nebo jeho archivech urcuje inteligentne dle startTime a endTime v filterXML/filterHashTable. + + Hledam vzdy od nejnovejsich udalosti! + + Funkce meni nastaveni culture konzole na en-US, jinak Get-WinEvent nevracel message property udalosti. + Pote je opet vraceno puvodni culture. + .PARAMETER computerName + Na jakem stroji se maji udalosti hledat. + Vychozi je $EventCollector. + + .PARAMETER filterXML + XML filtr pro urceni, ktere zaznamy nas zajimaji. + Slouzi jako hodnota parametru filterxml cmdletu Get-WinEvent. + + Ukazka: + [xml]$xml = @" + + + + + + "@ + + .PARAMETER filterHashTable + Hash filtr pro urceni, ktere zaznamy nas zajimaji. + Slouzi jako hodnota parametru filterHashTable cmdletu Get-WinEvent. + + Ukazka: + @{id=104; logName='forwardedEvents'} + + .PARAMETER maxEvents + Kolik se ma najit udalosti. Jakmile ziskam potrebny pocet, ukoncim prohledavani. + Hledam od nejnovejsich! + Pokud nezadam a ani (v filterXML/filterHashTable) neomezim od kdy do kdy se maji udalosti hledat, tak se projdou vsechny archivovane logy! + + .PARAMETER howOutputPartialResult + Retezec definujici, jak zobrazit mezivysledky (a ze je vubec zobrazovat). + + Tzn. $nalezeneudalosti | + Napriklad: sort timecreated -Descending -Unique | group machinename | sort count -Descending | select Count, Name, @{N="TimeCreated";E={$_.group.timecreated}} + + .EXAMPLE + PS C:\> [xml]$xml = @" + + + + + + "@ + + PS C:\> Get-WinEventArchivedIncluded -filterXML $xml -maxEvents 20 + + Vypise 20 nejnovejsich udalosti odpovidajicich XML filtru ulozenem v $xml. + Pokud jich nebude dost v systemovem logu, projde postupne i archivovane logy. + + .EXAMPLE + PS C:\> [xml]$xml = @" + + + + + + "@ + + PS C:\> Get-WinEventArchivedIncluded -filterXML $xml -howOutputPartialResult 'select id, timecreated, message' + + Vypise vsechny udalosti odpovidajici XML filtru v $xml. + Pokud bude potreba prohledat i archivy, bude vypisovat i mezivysledky ziskane z jednotlivych archivu. + + .EXAMPLE + PS C:\> Get-WinEventArchivedIncluded -filterHashTable @{id=104; logName='forwardedEvents'; startTime='8.25.2017'; endTime='10.13.2017'} + + Vypise vsechny udalosti serazene od nejnovejsich, odpovidajici hash filtru. + Pokud jich nebude dost v systemovem logu, projde postupne i archivovane logy. + + .EXAMPLE + PS C:\> Get-WinEventArchivedIncluded -filterHashTable @{id=104; logName='system'; startTime='8.25.2017'; endTime='10.13.2017'} -maxEvents 150 + + Vypise 150 nejnovejsich udalosti odpovidajich hash filtru. + Pokud jich nebude dost v systemovem logu, projde postupne i archivovane logy. + + .NOTES + Author: Sebela, ztrhgf@seznam.cz. + #> + + [CmdletBinding()] + param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $computerName = $EventCollector + , + [Parameter(Mandatory = $true, ParameterSetName = 'XML')] + [ValidateNotNullOrEmpty()] + [xml] $filterXML + , + [Parameter(Mandatory = $true, ParameterSetName = 'HASH')] + [ValidateNotNullOrEmpty()] + [hashtable] $filterHashTable + , + [int] $maxEvents + , + [string] $howOutputPartialResult + ) + + begin { + if (!$computerName) { + $computerName = $env:computername + } + + if ($filterHashTable -and !$filterhashTable.LogName) { + throw "Parametr filterhashTable musi mit definovan klic logName.`nTzn kde se budou udalosti hledat" + } + + # zjisteni, v jakem logu se budou udalosti hledat + # abych pozdeji mohl prohledat odpovidajici archivy + if ($filterXML) { + $logName = $filterXML.QueryList.Query.Select.Path + } else { + $logName = $filterHashTable.LogName + } + + if (!$logName) { + throw "Nepodarilo se ziskat logName. Uvedli jste jej ve filtru eventu?" + } + + # ulozim si aktualni culture, protoze jej pred pouzitim Get-WinEvent zmenim na en-US, tak abych pak vratil zpatky puvodni + $actualCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture.name + } + + process { + # vytvoreni hashe s parametry pro get-winevent + $params = @{ + erroraction = "silentlycontinue" + errorVariable = "winEventErr" + } + if ($computerName -notin "localhost", $env:computerName) { + $params.ComputerName = $computerName + } + if ($filterXML) { + $params.filterxml = $filterXML + } elseif ($filterHashTable) { + $params.filterHashTable = $filterHashTable + } + if ($maxEvents) { + $params.MaxEvents = $maxEvents + } + if ($VerbosePreference -eq 'continue') { + $params.verbose = $true + } + + Write-Verbose "Ziskavam zaznamy ze stroje $computerName." + + # + # vyextrahuji od kdy do kdy se maji hledat eventy a podle toho nasledne vyfiltruji archivy k prohledani + # + + $startTime = '' + $endTime = '' + # data schvalne ukladam jako datetime objekt, protoze to vynuti US culture tvar, ktery potrebuji pro Get-WinEvent + # u Get-WinEvent potrebuji US culture, protoze jinak se nekdy nezobrazovala Message property udalosti + try { + $ErrorActionPreference = "stop" + if ($filterXML) { + # pri konverzi na [xml] se (nevim proc) < prevede na < atd, proto match vuci < > znakum + $filterText = $filterXML.QueryList.Query.Select.'#text' + + $matches = '' + if ($filterText -match "@SystemTime\s*<=\s*'([^']+)'") { + [datetime]$endTime = $matches[1] + } + $matches = '' + if ($filterText -match "@SystemTime>\s*=\s*'([^']+)'") { + [datetime]$startTime = $matches[1] + } + $matches = '' + if ($filterText -match "\[timediff\(@SystemTime\)\s*<=\s*(\d+)\]") { + [datetime]$startTime = (Get-Date).AddMilliseconds( - $matches[1]) + } + } elseif ($filterHashTable) { + if ($filterHashTable.ContainsKey('startTime')) { + [datetime]$startTime = $filterHashTable.startTime + } + if ($filterHashTable.ContainsKey('endTime')) { + [datetime]$endTime = $filterHashTable.endTime + } + } + } catch { + if ($_ -match "Cannot bind parameter 'Date'") { + throw "Chyba ve tvaru startTime/endTime. Datum je potreba zadat ve tvaru MM.dd.yyyy" + } else { + throw "Pri zpracovani startTime ci endTime se vyskytla chyba:`n$_" + } + } + $ErrorActionPreference = "continue" + + + if ($endTime) { + # upravim cas v endTime datu tak, aby se pouzily i logy z daneho dne + # pokud zadal jen datum bez casu, tak se vezme automaticky od 0:00 + # logicky ale chtel vcetne celeho zadaneho dne + # TODO pri filtrovani udalosti se ale tato zmena neprojevi! doresit.. + if ($endTime.Hour -eq 0 -and $endTime.Minute -eq 0 -and $endTime.Second -eq 0) { + $endTime = $endTime.AddHours(24) + } + } + + Write-Verbose "StartTime: $startTime EndTime: $endTime" + + + + # + # seznam vsech dostupnych archivovanych logu (serazeny od nejnovejsiho) + # + if ($computerName -notin "localhost", $env:computerName) { + $logPath = "\\$computerName\c$\windows\system32\winevt\logs" + } else { + $logPath = "C:\windows\system32\winevt\logs" + } + $archivedLogs = Get-ChildItem -File -Filter "Archive-$logName-*.evtx" -Path $logPath | + select FullName, CreationTime | sort CreationTime -Descending + $archivedLogsCopy = $archivedLogs + + $allArchivedLogsCount = ($archivedLogs.fullname).count + + # jaky nejstarsi zaznam je v systemem pouzivanem forwarded logu (zjistim neprimo dle stari nejstarsiho archivovaneho logu) + $newestArchiveLogCreated = '1.1.1900' # nastavim dummy datum hodne v minulosti + if ($archivedLogs) { + $newestArchiveLogCreated = $archivedLogs | select -First 1 | select -ExpandProperty CreationTime + } + + + + # + # ziskani archivovanych logu, ktere mohou obsahovat hledane udalosti + # + + $searchSystemLog = 1 + # je startTime > vsechny archivy po tomto datu + if ($startTime) { + $archivedLogs = $archivedLogs | where {$_.CreationTime -ge $startTime} #| sort CreationTime # jako prvni chci prohledavat nejstarsi logy? + } + # je endTime > vsechny archivy pred timto datem + eventlog pokud byl vytvoren pred timto datem + if ($endTime) { + $archivedLogs = $archivedLogs | where {$_.CreationTime -le $endTime} + + # vzdy pridam i prvni archiv, ktery vznikl po endTime, obsahuje totiz eventy z doby mezi vznikem posledniho archivu (pred endTime) az po endTime + $archivedLogs = $archivedLogsCopy | where {$_.CreationTime -ge $endTime} | select -Last 1 + + if ($endTime -lt $newestArchiveLogCreated) { + Write-Verbose "Nema smysl prohledavat aktivni log. Obsazene eventy jsou mimo zadane rozsahy." + $searchSystemLog = 0 + } + } + if (!$startTime -and !$endTime -and $archivedLogs) { + Write-Warning "Prohleda se pouze aktivni log, zadne archivy. Nezadali jste totiz startTime ani endTime :)" + } + + $filteredArchivedLogsCount = ($archivedLogs.fullname).count + + + + # + # prohledam aktualni log s forwardovanymi eventy + # + + if ($searchSystemLog) { + Write-Verbose "Prohledavam log $logName" + # ohackovani, aby se nevracela prazdna message property, pokud ma jiny nez en-US culture + [System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US' + [System.Collections.ArrayList] $result = @(Get-WinEvent @params) + [System.Threading.Thread]::CurrentThread.CurrentCulture = $actualCulture + # upozornim na pripadne chyby + if ($winEventErr) { + Write-Warning "Pri hledani udalosti se vyskytla tato chyba:`n$winEventErr" + } + } + + # + # vypsani vysledku (+ dohledani z archivu pripadne) + # + + # mam vse co jsem chtel, vypisi a ukoncim + if ($result -and (($maxEvents -and ($($result.count) -ge $maxEvents) -or (!$maxEvents -and !$archivedLogs)))) { + Write-Verbose "Mam dost udalosti. Ukoncuji" + return $result | Sort-Object TimeCreated -Descending + } else { + # nemam dostatecny pocet udalosti + # zkusim najit a nasledne prohledat i archivovane logy + + # nejsou zadne archivy, vratim co mam + if (!$archivedLogs) { + Write-Warning "Neexistuji zadne vhodne archivovane logy kde bych dohledal zbyle udalosti." + return $result | Sort-Object TimeCreated -Descending + } else { + # mam nejake archivovane logy k prohledani + if ($maxEvents) { + $pocet = ", (pozadovano $maxEvents) -> prohledam archivovane logy" + } + if ($result) { + Write-Host "Mam $($result.count) udalosti$pocet." + } + + # vypisu mezivysledky + if ($result -and $howOutputPartialResult) { + # vytvorim scriptblock, ktery stavajici mezivysledek vypise tak, jak je receno v HowOutputPartialResult + $partialResultOutput = (([scriptblock]::Create("`$result | $howOutputPartialResult")).invoke() | out-string).trim() + Write-Host $partialResultOutput # pouzit Write-Verbose pokud by bylo potreba s vystupem pracovat dal.. + } + + # vypsani informaci o poctu archivu k prohledani + if ($allArchivedLogsCount -eq $filteredArchivedLogsCount -and !$maxEvents) { + Write-Warning "Neomezili jste nijak hledani. Prohledaji se vsechny ($(($archivedLogs.fullname).count)) archivovane logy!" + } else { + # neprohledam vsechny dostupne archivy, nepotrebne jsem odfiltroval + if ($startTime) { + $txt = "Od $startTime " + } + if ($endTime) { + $txt += "do $endTime " + } + if (!$txt) { + $txt = "Existuje" + } else { + $txt += "existuje" + } + + Write-Host "$txt $(($archivedLogs.fullname).count) archivu k prohledani." + } + + Write-Host "`nZiskavam udalosti z:" + + # + # prohledam postupne jednotlive archivovane logy + # + foreach ($path in $archivedLogs.FullName) { + if ($filterXML) { + $filterXML.QueryList.Query.path = "file://$path" + $filterXML.QueryList.Query.select.path = "file://$path" + } elseif ($filterHashTable) { + $filterhashTable["Path"] = $path + } + + # vypisu jmeno aktualne prohledavaneho archivu + if ($archivedLogs[0].fullname -ne $path) { + $newLine = "`n" + } + Write-Host "$newLine - $(Split-Path $path -leaf)" + + # ke stavajicim vysledkum pridam co jsem nasel v archivovanem logu + $partialResult = @() + Write-Verbose "start ziskavani udalosti $(Get-Date -Format T)" + # ohackovani, aby se nevracela prazdna message property, pokud ma jiny nez en-US culture + [System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US' + $winEventErr = '' + $partialResult = Get-WinEvent @params + [System.Threading.Thread]::CurrentThread.CurrentCulture = $actualCulture + # upozornim na pripadne chyby + if ($winEventErr) { + Write-Warning "Pri hledani udalosti se vyskytla tato chyba:`n$winEventErr" + } + Write-Verbose "end ziskavani udalosti $(Get-Date -Format T)" + + if ($partialResult) { + $partialResult | % { $null = $result.add($_) } + } + + # pokud mam dost udalosti, ukoncim hledani + if ($maxEvents -and $($result.count) -ge $maxEvents) { + Write-Host "Uz mam dost udalosti, ukoncuji." + break + } else { + # nemam dost udalosti nebo hledam na zaklade datumu (projdu vsechny dostupne archivy) + + # prohledavam posledni archiv + if ($archivedLogs[-1].fullname -eq $path) { + $t = 'prohledal jsem posledni archiv' + } else { + $t = 'pokracuji' + } + Write-Host "Mam $($result.count) udalosti, $t." + + # chci zobrazovat mezivysledky + if ($howOutputPartialResult) { + # vytvorim scriptblock, ktery stavajici mezivysledek vypise tak jak je receno v HowOutputPartialResult + $partialResultOutput = (([scriptblock]::Create("`$partialResult | $howOutputPartialResult")).invoke() | out-string).Trim() + Write-Host $partialResultOutput # pouzit Write-Verbose pokud by bylo potreba s vystupem pracovat dal.. + } + } + } + } # end mam archivy k prohledani + + if (!$result) { + Write-Verbose "Nenalezl jsem zadne udalosti." + } + + return $result | Sort-Object TimeCreated -Descending + } + } +} \ No newline at end of file diff --git a/Invoke-Command2.ps1 b/Invoke-Command2.ps1 index d4f62c1..6ce4acb 100644 --- a/Invoke-Command2.ps1 +++ b/Invoke-Command2.ps1 @@ -221,43 +221,148 @@ function Invoke-Command2 { [Parameter(ParameterSetName = 'ComputerName')] [Parameter(ParameterSetName = 'Uri')] [string] - ${CertificateThumbprint}) + ${CertificateThumbprint} + ) begin { - # umozneni zadat hostname do ComputerName parametru (jinak konci chybou access denied) - # je potreba hostname nahradit za localhost a povolit parametr EnableNetworkAccess + # povoleni verbose vystupu pokud byl explicitne vyzadan switchem -verbose pri volani teto nebo nadrazene funkce + $MyName = $MyInvocation.MyCommand.Name + $lastCaller = Get-PSCallStack | where {$_.Command -ne $MyName -and $_.command -ne ""} | select -Last 1 + if ($PSBoundParameters.verbose -or $lastCaller.arguments -like '*Verbose=True*') { + Write-Debug "povolim verbose vypis v Invoke-Command2 i uvnitr jim vykonavaneho scriptblocku" + + # povoleni verbose v Invoke-Command + $VerbosePreference = 'continue' + + # povoleni verbose ve scriptblocku, ktery se ma na strojich vykonat + # a to co nejdriv, ale zaroven, abych nerozbil jeho strukturu + # tzn zkusim pridat do begin, process ci end bloku + # pokud ani jeden z nich neni definovan, tak pridam pred text scriptblocku, ale vzdy az za definici parametru + $scriptBlockText = $ScriptBlock.tostring() + $paramBlockText = $scriptBlock.ast.Paramblock.Extent.Text + $dynamicParamBlock = $scriptBlock.ast.DynamicParamblock.Extent.Text + $scriptRequirements = $scriptBlock.ast.ScriptRequirements.Extent.Text + $beginBlockText = $scriptBlock.ast.BeginBlock.Extent.Text + $processBlockText = $scriptBlock.ast.ProcessBlock.Extent.Text + $endBlockText = $scriptBlock.ast.EndBlock.Extent.Text + + if ($beginBlockText) { + # je definovan BEGIN blok, verbose povolim v nem + Write-Debug "verbose povolim v begin bloku" + [regex]$pattern = "(BEGIN)?\s*{" + $string = $pattern.replace($scriptBlockText, "BEGIN {`$VerbosePreference = 'continue'`n", 1) # nahradim pouze prvni vyskyt + #TODO udelat nejak lip, pattern.replace je case sensitive proto pokud je BEGIN napr malym, tak vynecha pri replace == je tam 2x + $string = $string -replace "beginbegin\s*{", "BEGIN {" + #$string = $scriptBlockText -replace "^(BEGIN)?\s*{", "BEGIN {`$VerbosePreference = 'continue'`n" # vim, ze kod musi byt uzavren v {} + } elseif ($processBlockText) { + # neni BEGIN blok, ale je PROCESS blok, verbose povolim v nem + Write-Debug "verbose povolim v process bloku" + [regex]$pattern = "(PROCESS)?\s*{" + $string = $pattern.replace($scriptBlockText, "PROCESS {`$VerbosePreference = 'continue'`n", 1) # nahradim pouze prvni vyskyt + #TODO udelat nejak lip, pattern.replace je case sensitive proto pokud je PROCESS napr malym, tak vynecha pri replace == je tam 2x + $string = $string -replace "processprocess\s*{", "PROCESS {" + } elseif ($endBlockText) { + # je definovan pouze END blok, verbose povolim v nem + Write-Debug "verbose povolim v end bloku" + [regex]$pattern = [Regex]::Escape($scriptRequirements) + $endBlockTextWithoutParam = $pattern.replace($scriptBlockText, '', 1) # nahradim pouze prvni vyskyt + [regex]$pattern = [Regex]::Escape($paramBlockText) + $endBlockTextWithoutParam = $pattern.replace($endBlockTextWithoutParam, '', 1) # nahradim pouze prvni vyskyt + [regex]$pattern = [Regex]::Escape($dynamicParamBlock) + $endBlockTextWithoutParam = $pattern.replace($endBlockTextWithoutParam, '', 1) # nahradim pouze prvni vyskyt + $endBlockTextWithoutParam = $endBlockTextWithoutParam -replace "^;*" -replace "^\s*" + if ($endBlockTextWithoutParam -match '^END|^{') { + # END block je uzavren ve scriptblocku {} + Write-Debug "je uzavren ve scriptblock" + [regex]$pattern = "(END)?\s*{" + $string = $pattern.replace($scriptBlockText, "END {`$VerbosePreference = 'continue'`n", 1) # nahradim pouze prvni vyskyt + #TODO udelat nejak lip, pattern.replace je case sensitive proto pokud je END napr malym, tak vynecha pri replace == je tam 2x + # END tam zaroven byt musi, jinak se bere jako scriptblock a vnitrek se nevykona + $string = $string -replace "endend\s*{", "END {" + } else { + # END blok neni uzavren ve scriptblocku {} + Write-Debug "neni uzavren ve scriptblock" + if ($paramBlockText -or $dynamicParamBlock) { + # END blok zacina param blokem == povoleni verbose musim dat az za nej + Write-Debug "zacina param() blokem" + $string = $scriptRequirements + "`n" + $paramBlockText + "`n" + $dynamicParamBlock + "`n" + '$VerbosePreference = "continue"' + "`n" + $endBlockTextWithoutParam + } else { + # END blok nezacina param blokem a neni uzavren ve scriptblocku {} + Write-Debug "nezacina param() blokem" + $string = '$VerbosePreference = "continue"' + "`n" + $scriptBlockText + } + } + } else { + throw "Invoke-Command2: nemelo by nastat" + } + + Write-Debug "po uprave mam:`n$string" + $PSBoundParameters.scriptBlock = [scriptblock]::Create($string) + } # konec povoleni verbose + if ($PSBoundParameters.computerName) { - $computers = {$PSBoundParameters.computerName.ToLower()}.invoke() # prevedu na pole ze ktereho se daji odstranovat prvky - } - - if ($computers.Count -eq 1 -and $Computers[0] -eq $env:COMPUTERNAME) { - # spoustim pouze vuci sobe samemu - # odstranim parametr computerName, aby fungovalo i na strojich bez povoleneho ps remotingu - - Write-Verbose "ComputerName obsahoval pouze jmeno tohoto stroje, odstranil jsem, aby neskoncilo chybou access denied" - $null = $PSBoundParameters.remove("computerName") - } else { - # spoustim vuci vice strojum nebo vuci nejakemu remote stroji + $computers = {$PSBoundParameters.computerName.ToLower()}.invoke() # prevedu na Collection`1 (umoznuje odstranovat prvky) + # pokud seznam obsahuje i muj hostname (ci jeho fqdn variantu), tak je odstranim a pridam misto toho localhost + povolim enableNetworkAccess - # takto pujde spustit i vuci tomuto stroji (ale nefunguje pokud neni povolen ps remoting!) - - $loopback = $env:COMPUTERNAME.ToLower() - $loopback2 = $env:COMPUTERNAME.ToLower() + ".fi.muni.cz" - $loopback3 = $env:COMPUTERNAME.ToLower() + ".ad.fi.muni.cz" - if ($computers -and ($computers.Contains($loopback) -or $computers.Contains($loopback2) -or $computers.Contains($loopback3))) { - Write-Verbose "ComputerName obsahoval $loopback, nahradim jej za localhost a povolim EnableNetworkAccess" - $null = $computers.Remove($loopback) - $null = $computers.Remove($loopback2) - $null = $computers.Remove($loopback3) + # takto pujde spustit i vuci sobe samemu, jinak by Invoke-Command skoncil chybou Access Denied + # (ale ani po teto uprave nebude fungovat, pokud neni na tomto stroji povolen ps remoting!) + $myself = $env:COMPUTERNAME.ToLower() # kronos + $myself2 = $myself + ".fi.muni.cz" # kronos.fi.muni.cz + $myself3 = $myself + "." + $env:USERDNSDOMAIN # kronos.ad.fi.muni.cz + if ($computers.Contains($myself) -or $computers.Contains($myself2) -or $computers.Contains($myself3)) { + Write-Verbose "ComputerName obsahoval $myself, nahradim jej za localhost" + $null = $computers.Remove($myself) + $null = $computers.Remove($myself2) + $null = $computers.Remove($myself3) $null = $computers.Add('localhost') - $PSBoundParameters.computerName = $computers } - if ($computers -and $computers.Contains('localhost')) { + # spustim pouze vuci pingajicim strojum a upozornim na nepingajici + if (($computers.Count -eq 1 -and $Computers[0] -ne $env:COMPUTERNAME) -or ($computers.Count -gt 1)) { + # spoustim vuci nejakemu remote stroji ci vice strojum + try { + $computersStatus = Test-Connection2 $computers -ErrorAction Stop + } catch { + throw "Prikaz Test-Connection2 neexistuje nebo skoncil chybou:`n$_" + } + + if ($offline = $computersStatus | Where-Object {$_.result -ne 'Success'} | select-Object -ExpandProperty ComputerName) { + if ($offline.count -eq $computers.count) { + Write-Warning "Zadny ze stroju neni online." + return '' + } else { + Write-Warning "Nasledujici stroje jsou offline: $($offline -join ', ')" + } + + # v seznamu ponecham pouze online stroje + $computers = {$computersStatus | Where-Object {$_.result -eq 'Success'} | select-Object -ExpandProperty ComputerName}.invoke() # prevedu na Collection`1 (umoznuje odstranovat prvky) + # ulozim zpet do parametru + $PSBoundParameters.computerName = $computers + } + } + + # nastavim vysledne stroje zpatky do Computername parametru Invoke-Commandu + $PSBoundParameters.computerName = $computers + + if ($computers.Count -eq 1 -and $computers.Contains('localhost') -and $PSBoundParameters.AsJob -ne $true) { + # spoustim pouze vuci sobe samemu a neni pouzit asJob switch + Write-Verbose "ComputerName obsahoval pouze jmeno tohoto stroje, odstranil jsem, aby neskoncilo chybou access denied" + $null = $PSBoundParameters.remove("computerName") + } + + if ($computers.Count -gt 1 -and $computers.Contains('localhost')) { + # spoustim vuci sobe samemu a nejakym dalsim strojum + Write-Verbose "ComputerName obsahuje take localhost = povolim EnableNetworkAccess" $PSBoundParameters.EnableNetworkAccess = $true } - } + # odstranim prepinac HideComputerName pokud spoustim pouze vuci sobe samemu + if ($computers.Count -eq 1 -and $computers.Contains('localhost')) { + Write-Verbose "ComputerName obsahuje pouze localhost = odstranim HideComputerName" + $null = $PSBoundParameters.remove("HideComputerName") + } + } # konec if ($PSBoundParameters.computerName) + try { $outBuffer = $null if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { @@ -273,25 +378,29 @@ function Invoke-Command2 { } process { - try { - $steppablePipeline.Process($_) - } catch { - throw + if ($steppablePipeline) { + try { + $steppablePipeline.Process($_) + } catch { + throw + } } } end { - try { - $steppablePipeline.End() - } catch { - throw + if ($steppablePipeline) { + try { + $steppablePipeline.End() + } catch { + throw + } } } <# - .ForwardHelpTargetName Microsoft.PowerShell.Core\Invoke-Command - .ForwardHelpCategory Cmdlet +.ForwardHelpTargetName Microsoft.PowerShell.Core\Invoke-Command +.ForwardHelpCategory Cmdlet - #> +#> } \ No newline at end of file diff --git a/Invoke-NetworkCapture.ps1 b/Invoke-NetworkCapture.ps1 new file mode 100644 index 0000000..88db75b --- /dev/null +++ b/Invoke-NetworkCapture.ps1 @@ -0,0 +1,245 @@ +function Invoke-NetworkCapture { + <# + .SYNOPSIS + Funkce slouzi k zachyceni sitove komunikace na zadanem stroji. + Vystupem je etl soubor, ktery je mozne otevrit napr v Message Analyzer aplikaci. + Pokud to jde, pouzije Powershell prikazy pro capture, pokud ne tak CMD obdobu tzn. netsh trace start capture. + + .DESCRIPTION + Funkce slouzi k zachyceni sitove komunikace na zadanem stroji. + Vystupem je etl soubor, ktery je mozne otevrit napr v Message Analyzer aplikaci. + Pokud to jde, pouzije Powershell prikazy pro capture, pokud ne tak CMD obdobu tzn. netsh trace start capture. + + .PARAMETER computerName + Jmeno stroje na kterem se ma provest. + + .PARAMETER runTimeMinutes + Jak dlouho ma capture bezet. + + .PARAMETER outputFolder + Kam se ma capture ulozit. + + Vychozi je "C:\temp". + + .PARAMETER ipAddress + Filtrovani dle IP adres. + + .PARAMETER ipProtocol + Filtrovani dle protokolu. + + .PARAMETER maxFileSizeMB + Maximalni velikost capture souboru. + + Vychozi je 1GB + + .EXAMPLE + Invoke-NetworkCapture + + Spusti zachytavani veskere sitove komunikace po dobu 5 minut na tomto stroji do "C:\temp". + + .EXAMPLE + Invoke-NetworkCapture -computerName titan01 -runTimeMinutes 1 + + Spusti zachytavani veskere sitove komunikace po dobu 1 minut na stroji titan01. Vysledne mereni se ulozi do "C:\temp" na tomto stroji. + #> + + [cmdletbinding()] + [Alias("Get-NetworkCapture", "Save-NetworkCapture")] + param ( + $computerName = $env:computername + , + [int] $runTimeMinutes = 5 + , + [string] $outputFolder = "C:\temp" + # , + # [UInt16[]] $TCPPorts + # , + # [UInt16[]] $UDPPorts + , + [string[]] $ipAddress + , + [ValidateSet(4, 6)] + [int] $ipProtocol + , + [int] $maxFileSizeMB = 1000 + ) + + begin { + if ($computerName -contains $env:computername -and ! ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + Throw "Skript je potreba spusti s admin pravy" + } + + if (!(Test-Path $outputFolder -ErrorAction SilentlyContinue)) { + throw "Umisteni $outputFolder, kam se maji ukladat zachycene udaje neexistuje" + } + } + + process { + Write-Output "Nyni pobezi $runTimeMinutes minut zachytavani sitove komunikace na $($computerName -join ', ')" + + $captures = Invoke-Command2 -ComputerName $computerName { + param ( + $maxFileSizeMB + , + $runTimeMinutes + , + $ipAddress + , + $ipProtocol + , + $localhost + , + $outputFolder + , + $verbose + # , + # [UInt16[]] $TCPPorts + # , + # [UInt16[]] $UDPPorts + ) + + $VerbosePreference = $verbose + $sessName = "capture_" + "$(Get-Random)" + $fileName = "_$env:COMPUTERNAME`_$(get-date -f ddmmyyyyhhmm).etl" + + if ($env:COMPUTERNAME -eq $localhost) { + # capture delam na localhostu (ne remote hostu) == etl ulozim rovnou do cilove slozky + $etlFile = Join-Path $outputFolder $fileName + } else { + # capture delam na nejakem remote stroji + $etlFile = "$env:windir\TEMP\$fileName" + } + + $oldPC = !(Get-Command Start-NetEventSession -ErrorAction SilentlyContinue) + + + if ($oldPC) { + # nejsou dostupne PS cmdlety pro capture == pouziji netsh trace + Write-Warning "Na stroji $env:COMPUTERNAME je stara Powershell verze. Pro zachyceni sitove komunikace pouziji CMD prikaz netsh trace start" + try { + if ($ipAddress) { + $params = "ipv4.address=$($ipAddress -join',')" + } + if ($ipProtocol) { + Write-Warning "Protoze se nepouziji nativni Powershell cmdlety, nelze provest filtrovani dle protokolu" + } + + $runningSession = netsh trace show status + if ($runningSession -and $runningSession -match "Status:\s+Running") { + throw "Na $env:COMPUTERNAME jiz existuje aktivni merici session.`n`n$runningSession`n`n Je potreba pockat na ukonceni nebo ukoncit prikazem: netsh trace stop" + } elseif ($runningSession -and $runningSession -match "Status:\s+") { + $null = netsh trace stop + Write-Warning "Na $env:COMPUTERNAME existovala neaktivni merici session. Ukoncil jsem ji, abych mohl pokracovat" + } + + Write-Verbose "Spoustim session $sessName" + $null = netsh trace start capture=yes report=yes tracefile=$etlFile maxsize=$maxFileSizeMB correlation=yes $params + + Start-Sleep -Seconds ($runTimeMinutes * 60) + + # zastaveni capture + $null = netsh trace stop + } catch { + throw "Na $env:COMPUTERNAME capture skoncil chybou:`n$_" + $null = netsh trace stop + } + } else { + # jsou dostupne PS cmdlety pro capture + try { + # invoke-command musi vratit jen cestu k etl souboru, proto vse zacina $null = ... + $ErrorActionPreference = "stop" + + #TODO dodelat podporu pro starsi OS (netsh trace start) + #C:\Windows\system32>netsh trace start capture=yes report=yes maxsize=1024 correlation=yes tracefile=test.etl + #if ((Get-Command Get-NetEventSession -ErrorAction SilentlyContinue).module.name) {} + # zaroven muze bezet jen jedno mereni + # neaktivni ukoncim, na bezici upozornim + $runningSession = Get-NetEventSession + if ($runningSession -and $runningSession.SessionStatus -eq 'NotRunning') { + $null = Remove-NetEventSession -Name ($runningSession.name) + Write-Warning "Na $env:COMPUTERNAME existovala neaktivni merici session. Ukoncil jsem ji, abych mohl pokracovat" + } elseif ($runningSession) { + throw "Na $env:COMPUTERNAME jiz existuje merici session $($runningSession.name) (ve stavu: $($runningSession.SessionStatus)). Je potreba pockat na ukonceni nebo ukoncit prikazem: Stop-NetEventSession; Remove-NetEventSession" + } + + Write-Verbose "Spoustim session $sessName" + $null = New-NetEventSession -Name $sessName -CaptureMode SaveToFile -LocalFilePath $etlFile -MaxFileSize $maxFileSizeMB # MaxFileSize je v MB (pri prekroceni se stare nahradi novymi) + if (!(Get-NetEventSession -Name $sessName)) { + throw "Nepodarilo se vytvorit monitorovaci session" + } + + $null = Add-NetEventProvider -Name "Microsoft-Windows-TCPIP" -SessionName $sessName + # zachytim i SMB + $null = Add-NetEventProvider -Name 'Microsoft-Windows-SMBClient' -SessionName $sessName + + #TODO zakomentovana cast to vzdy rozbije..bug?? + # $null = Add-NetEventWFPCaptureProvider -SessionName $sessName + # if ($TCPPorts) { + # #TODO filtrovani podle portu nefunguje ! + # $null = Set-NetEventWFPCaptureProvider -SessionName $sessName -TCPPorts $TCPPorts + # } + # if ($UDPPorts) { + # #TODO filtrovani podle portu nefunguje ! + # $null = Set-NetEventWFPCaptureProvider -SessionName $sessName -UDPPorts $UDPPorts + # } + + $null = Add-NetEventPacketCaptureProvider -SessionName $sessName + if ($ipAddress) { + $null = Set-NetEventPacketCaptureProvider -SessionName $sessName -IpAddresses $ipAddress + } + if ($ipProtocol) { + $null = Set-NetEventPacketCaptureProvider -SessionName $sessName -IpProtocols $ipProtocol + } + + $null = Start-NetEventSession -Name $sessName + + Start-Sleep -Seconds ($runTimeMinutes * 60) + + # zastaveni capture + $null = Stop-NetEventSession -Name $sessName + $null = Remove-NetEventSession -Name $sessName + } catch { + $null = Stop-NetEventSession -Name $sessName -ErrorAction SilentlyContinue + $null = Remove-NetEventSession -Name $sessName -ErrorAction SilentlyContinue + throw "Na $env:COMPUTERNAME capture skoncil chybou:`n$_" + } + } + + return $etlFile + } -ArgumentList $maxFileSizeMB, $runTimeMinutes, $ipAddress, $ipProtocol, $localhost, $outputFolder, $VerbosePreference #, $TCPPorts, $UDPPorts + } + + end { + if ($captures) { + Write-Output "Do $outputFolder se nyni nakopiruji etl soubory obsahujici zachycenou sitovou komunikaci.`n`t- etl se daji otevrit v aplikaci 'Message Analyzer'.`n" + + # ze stroju zkopiruji zachyceny sitovy provoz + foreach ($etlFile in $captures) { + if ($etlFile -match $env:COMPUTERNAME) { + # lokalne udelany capture rovnou kladam do ciloveho umisteni == netreba nic delat + continue + } + + # zkopiruji capture z remote stroje + $remoteMachine = ([regex]"\\_(\w+)_").Matches($etlFile).captures.groups[1].value + # zmenim cestu na pouziti admin share + $etlFile = $etlFile -replace ':', '$' + try { + Write-Output "Kopiruji $(Split-Path $etlFile -Leaf)" + Copy-Item "\\$remoteMachine\$etlFile" $outputFolder -ErrorAction Stop + } catch { + Write-Error "Zkopirovani se nezdarilo:`n$_" + } + + # smazu jiz nepotrebny etl soubor z remote stroje + try { + Remove-Item "\\$remoteMachine\$etlFile" -Force -ErrorAction Stop + } catch { + Write-Error "Nepodarilo se z $remoteMachine smazat jiz nepotrebny $etlFile" + } + } + } else { + Write-Warning "Nepodarilo se ziskat zadne etl soubory" + } + } +} \ No newline at end of file diff --git a/Reset-KrbtgtAccount.ps1 b/Reset-KrbtgtAccount.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..b16ba3b32a1d48eb84c6ec6a96a1175c8c1e5747 GIT binary patch literal 63936 zcmeI5dv{gGdB)G*U8~^*y*i;e`ASz6N3IeYJ!ci#JL=KSCPo|&vnzMkw)_9ov<{yw=oIhZ`vCx6%P zm-_!e*Pcy&HTkK29_Z>L{p{)Pmy-=$+nem_t|xju(EG=ehkC!S=b!2Ke)?=z|2@{T zw{*|5bl6AWZ(^)oNGt2|pj|+SAF4bjL&8{Y-cQqdye}p9_P~ zh|%KycwN^gTe@RkKYIzYk3_qb^zN_v{9*d#>&gpe{+b%HcmDZTwm(>ePL~7@^2aw>;T10eF_|3 z>+d5$0(9@{^N$5n&D-UCCAL4-2={dNef@gOp_T7ybmS>8`uuQZtK#nG`t*JMA{Q%3 zE}jSy@O>~@)hA$TU%!v_8CZaWVw{kq#|aW7+EjZeDk0O{z0%|s)^STX*wQOl-W44C zTE{`+8O!g1-a~KZgBIKgwsxyEbI;1;^5oiNd$OtjwkJFK|Em7Grcc58d!q1#$;HXf z^#3i*18wuwZOO&@u=oG!amS{n(kokn1^vLXqJl_g}KgYHD3zy zC*oY7-xRgLj# zzamZWNOvHSNFXwIRd=IHuZpHOk`De_SN8SUimsp&c!#F96e9hW<#T#PzMwH9qhpYR zzv#bb>GQY_R{|5i##&%(=(WV2vHIR?#40#MioohKVGn-)I<3%p?z*nM5DvHW*<*3b zW$EHmwIkP&pZ>Km1~Rjql4ix;|R_ARMI{N>3ZI6lzGXjN8gss^v1C(sR#)9Ql{Zfd<-y4SdfcWBG6 z5255a+peKG;a%=VFL~zs;v(RTCo>+W;|#%2QhHp*(BSrWSZ9`1WYjndSq(0MbDkC!7kx9KG51rjD!qBk8jfKa0hb&=ep>GSHn1e z(63?da$QJIXw2X}drR<$^|v)&bUc-nBw=8c z-`4sWyT-w`)(;1Rg{&L5H69iOYkor%`-@(YbMv$HT9#wvzN-nWX%@p3dZzC|PRADB zGt2zDc9e~Ei&(Ea&v$yVHR_gNJrLH2 z%kD_S;c?hjZ={j2tN1J)_s@s(BiHA&mMenEp5Dgf197@N#y{#mbYqTG@_pBmf3U7m z|Ee+8I@q|bRUK%guwAUJhG0#U0|)THf#yNOtq79)X-2r>JN>lR+I{9gPZM{Rc=;~9 zD?b_Ihn9|BgeOA7hljBUSDY12(C9Z0dCgJ+O|V_R(QjMh6A9vVoPA|y0t&8-MT_A1 zjD9yYgL$U(ZXNC26t`Ya(V25ISS)lqUIcIvE5mUm#)IQqpE2K7@>d;=g#S{{wxQAS zMf-Ly?4Pd_g$8ez_Uu^`o<(G(wT4*VwPYV(uN>VLEr`O=B*BHy9U0u$rw{Zuq!G=9 zpGRB=ci~&E%0nS~K$hDyAbvEK(Fl(B@wM;_A`bjUJn>Lt-%Xy_o?;%N>?escB9p zP_X-YbZl-WeTL`qRl<++a;2w*pY7}k^kMZc5(UtMtS(EUR}em8$?@a#nhg^z&JNAn zx^_nJm68irbu)Q22j$oeFN#%T_uI0WWob+cu8Tt+Cc6pe{`k=P5J94iz~+zjdCS6_ zOHlyPdg*&2DLdjFytV7XKG8lA-gfVW{ySN*HQiaR3|T=_pVd9KP4M2D z;xMn=`O>&+bwFn$ozAr34dv|K!xU#k#;u$e8}~@uzbgK2+n#a0UpBqr)!Obalj_KF zJvuqVH-+-qlUa=R_JYkXXh!s+vp3EfqvOcPBU5OQ6>%f7h*l@QMGDA^U>DK7JWm#n zxEWnyZO?r<%a0bqjyMa<6ON$c?zYXJpWhaf@)o@>s1kBfKc|K2A@O*?`-xW26FUOs z`qs77#MBd@=Zi#dss@n_hmzqjWv`HXslz?e_}P03PwY*l`Dvqc?b);?3=3-7+fxV{ zK~r-_>19Aya-_)a?6Nsa#I&4@&LWZ3j#d=W2Ki?4QvH~G`%nX76^8FBE&pZN`?jTQ z*}@#V6QvPRlgo8AlK1H1GEQq}n^shlg+}-*MEa2pdz!Lgcn#gv@>zFneAZpbP$YLt zmg1&hy(|xeIL>i(YlUZYt=1_KC)6Xnjm&1va2!3B*g3S-L}qO&yp7G~iF8jkHXEYY zQKB;>cNXbw$7VG|i$-T-+OHd*)zOiol-y-f)(LIjtO;EgcvI~CtdU$-ClgyzbLz)w zV;HzE3qgLlt@-UU^zFp#);=;G%jcp+V!nBJsTBo$AEUlDpS0|}yLC=~BzROV`2NO% zv-85~jth^*0dI<>m_75;ruzp~tY=*x@d8~xWQWP&EM_CJy+7&bk-EbZK~0w8v8-b5 z382zi_VirSJJ(m8)BT%PJ3VS_*|M;6cNG5+K{~e!{`OV*r0a#AC@@N#Lq508+Y)Df zs_W!P_Eb%=Ri-va4wx)7ok!iL-cf~k92#n6xtct7?^!|aKAc!fytd<3XQ(5`K7_Xz zgZgq*j(EMBa@0|2JU_aMoW%n|{G46U6|tPX1@|BUKsBDBk}kz58e7IzJb-elz*6$$#tm zXQ>O~TVd}r;rlbK{#UAWtqQue$*=2l5m=5`S}rNA!k!|AB2zJf~>Bb%#tmm->mKnt#w*g>kY|j)?DXC<#Ape zm+jPXX*p}ohld(=Px%YwTAu3MOm)G>%!IZnKDQiwao6|wwqUdt; z=QY8FH9_yWyM}B#^NwrJ%d$8_6c6njuUQ(}v#lqbveNU1646?3jMjZ^XQ)<_#VoCI zyVJIto3_6~1=XD))Gp~5rZ(%^J686t`~c&P2)Df|Fv>mdH-47*CB|^plz4ZZm3HO* zNy^tgnrVz${<}Dg_wBV{-DZIk4!%A4z5Ec?9enh=q~T|K{+6=4R}c9w^iSz!l8GpL z!?ra~xrbvT`2$qKp(9-*^gn;8XEr3gjE&}x>pV@B3a@leu;U%^j>_ZyOe>y83IfM> zlqWZa?noohix(KP+~wkuSD)ALDUik*&RO{GJ3Tkc zeYeF)^ala$Ecb);S|Z?2_c4Kyz+hkVn?|$D;b+Y`^m*dD5-9|4)$@b>b@ePiO?b!0 zT^n7=G|YUG>>4;3$H=*~?!}`guX3DoyV`Lm?qE&%r^NAMUQxMQrtbY+#6xOT1+As{ zJIoikwue@uXPtW4Gr|bNk>JG()cLwDpUmm%ZqJ7AFsh*-c@ms_A7X7TZcVCn#lAmdFIaA`#G*YzZ^@Ab$hK<`y68bhr3rpMj6RAl5VVe zM>R4*kHLNYzyaU~I=jGM`|l@TJShe$IL7^E3IB=mA3!& zdhr<VSO*)w4KNh6i27gP3{g$YwwaXPTL-?Soaz60vdz0mg}|c zSXZ`=?)ID!!C%-;IRLP=E(^~!GI?iZmtLkl0_=n;pTT0iOkQucPaChE#9KI{U(cBP zS-*ZYviG+$3wo{Iw@tMnKjWRm`IHLIW~gm=cb!z;AIf6cE5a9Jm+f!#>$q`UegG?E z=IBguW|xlI73FQC3PY!!v*q6k7oVs5nVWnGR~TVce#2_YeymEP)7$N}2MqX#0Ym6H zc2GP>R)T1&4?h%opn66`W5B*Dzp|ULEPeA2b(P$~=Yllm#*6dZ8-kcIR}Z1{4A`kQ z1b&k2Yveg%_C+lA8O^$t#=`HSZ{lg{rn4s&*oF^)H)h428e^tT-lg8f*lE>Zjxo$_ zFcE0&W7)TjF)&A(^mBexvQnc6LG!_RiVJODNVmWTn5~n#6dg^mo1{pN^Ui-S(iPv z(1|rgOIU|+;E`gtY{puF!8}*z&$x;J%lr|32AdzV zX1^yqeBW2|q^*WIBTIAe=g_LN9t*vY_2H$oTKG6@BD%s)z&*@psSUWgl0IFkFD)R| zz-0{F5)6@<3Aw2$pI3AIvRu~)=9T7}n&^+C`WIM}Y$L{Hv!&^cfx_eo$%Ixg$K8Oet$DB5lsd!DhZ}Nc&~!c>HfXoIF!KIvLWk z`=aj+AkF6zPt}^m_AT3J`X}D0)`Z4_e>h<+peSd@-i9|*;@qpluEEFN$9YelDfbNf zrI+N5WseAtlc>BKTZAUWKP4jPv@!lt3t-RsX0n6q6-B?hw=rUs$Sr%vuzlsS0vWV? zq7muntN9w=(~`tYT~4ueuvH7+rNl|V-M7bBZbq~|3mD4i0Y39NY+akhb7f3{)qpeI zGXjR}7mVYjMZJ`S zFW#YjHII+f1GDF_B%%4cyt6J0yAtKO!Z$Eo+t{~Ef^(>sX~_7BJMjKQ7f68DoU<(?Motd^NG zW$+w{rUH7)kNK{9xAOw}~H;Ya4d5=?|{$tqXQszXkYal8Y^U33Z}W$2w;E6NcocjMD}7Vap|`V7fL z!l?^;jdY*|Z(8=>ZQ`7~C1ouwu~OTXS-mXsH}iX0JF4?+>q+Ru=_#Qz zLg$>6f7SJ+T9gURU=1F;)Az2ro<>OakpF}BDm~9}UDw4-t(C3yThPz$3xx%<Ha2P+UF&*IIozxIMJME#4u8X(2rkWgyw>^G@M!|yEm_6pal&%8+)wlg zvgx?NoJFSAHma5}R%p+oTqkln&1=A$;4Xg6$t}!xdAl#Gfd_LIqmBD~p}jNSLZUfQ zpY?>UMB_WpG@CtZ!H;c!S$K3=tH&&k(juQ1*d&8S)yF-~ZJQtdn)w4dmN7`4$@U}8 z1j%@s9qYB+)KZh@IlA}KmR`Kr(o1Rk?VLBr3FNl@V8cE%)h2ZLBk@J#_l$c>W99?? zvkoYu>#$)Sm&g-8q4e7gdf(rV=SXh98GP$UJEmn)Xo`ctbCjcXAXug!Lb2# z@K9OZ{%xXLNjvgUuJpUM5S1#V!4VCdQm%Ela)G`=&(f7B>c;JK~n_#^$SXqUtl{q4h->!0=tfhpsNDnH(# zvp(EWujf*+o?BXP%s)=@f>=AD2%lQ*Db!b8rJg#taK@(_2HP7?`_WY z4xz}+!NqG@%@x5&e2I3tAZlJp(7LL}9QJbXLz&?nJ;N*|Z{F6kT&qvN?e5KJ-!)5@ z$Mm_P%WL?gUB17AJ#k2@``*U85tcFMpEO_Ougf`yD&5PN6RySH`zLo@4AtS5G3S=N zHOG$qS+7a{qRXKE_LVk2OsC|mvt2wU9tFd-jmr9EIyPsOijNboKA&R^<=jYB*=a|| zBWLB^lYBGs$<% zaUcC!Z z6!!KN?K|^`wMJ{N>k}~Jb8+3F0halGuo5e$wG(oP|F%-}0Upd*+4m`Z$&5vC>C=WI zTf-g|e(GJ6wUoJE`gpVl+J$pnGIeiC3drta#rk?ApXgoEeF#6Zdn&eD!}#1hlxZTY z%fdELBjxZZk?~FaH~uYP#6W(inbuNmds`a!iZH+)ox0|7MX)&oKqq}^eb_ZHMrFrS<=G_Y$2Lu;!+c8^ z!v9$lS8b+fA>akSC63!Oz;pZz^8#2QuQLBP!5tS5v>|#+`;T7n>QQM=#VEcHAij%b zL!9PsM~U2->%h*~mBJ3RJ1Yuprd}RDW>Tq}uboBD$ zPJXgB_8r;ThTV-!(hp}BDc?Y8%}kUSx|CNmdF;d}E4JjWTf0z7@8X9Ri4lC6|#mAoJqWMaUWR{RrF2ow%QdPp<1;PDZ+>E~M?wy?NHi#PEc> zpRdRglsh)MD;V;IYkUvgWuLiTSq&+b2Cs`9Vvg+kG0;&w|C|>brpCqq{z`6`XN{)M zo3gf=Tk801d{sjo@~@Xx!<=h=8;TMmhAa6oB;xh1s0GpNyw{IO?^U{_+sWsO;A7oG zwJ$2JWT0;+P0Q0S6iaY!;rYogE6v1R)U~mRQKKa{H(Sh7uBrBvjx(Fo!tQuJ>D=T; z$_tYj-YM)+IS%JQ_4O(_nE3A17B0_MPsLe1#^&;8W9LHD@Thl|5Fh{C-Py>Rm-HX< zn9JuvI!kID%|Bi=ch%ODCy{sRXDd%@mcR5Bhdu7P%8oA;b%9)O%TM=v_wftKEAPsO zFZuDUXP&WEMmb>kfwX;%ce==_cG9iouAOl@+$*$YtJ|}rO3R~f%U@JYOFEv_ehqqH z4+J%k)qeKn$tNivjs2=u#Q8U5aJ?6W_?LVdezGeMK9A;}a_qc6NZvW)_s{wpwi&~o zOSys_!9n$)eOlffWfvcdgE?XKL5gzfHC`3OH&R_SA|PPADoRm9#K--`6M?tS?L{P(`H{r1?&I>c-fMrhj+0c`YLrNP?1XiBI>odq&vjG$S5M7nT zFL+k0VYay=wkzij4CS3gn_B;#FwWU=aUx*kX|4+wQN16-_?94K&%;;ZUwUz0h!RNn zTYMYeef-RaZ|YxL;-vVtg|-Zr+U&OO-WLTz&#tBXee~zjhX|j|KgWph4j;4x8Ou5gF~4>JH%F0{_o}D+xzV#JZ~C=p8@n6n{M*rc zR`7u+x-V^E38Yh+9b|jydh8~w<+8MFK6MRzv@O%dVP_A0;Pf{7wCvy!N6@0qJ9{GflD#%yq8J$F4p`3q~^NE+UGWthq&j>AL-^e{rXdn1J za*fm$>TKi5V`to(n+G#wJ<6R{kF|DsVOb$3-NC!1SChMNrtNI%Kl@HPKg+(>@nuJ+ z>ju0>?;b&)0M9ZpM&AtiZggdwfDtn6&dTiZc^^pD1m2B^m4o9r^D{?-C&6Vy)-?1Y zn0qX^*ePIbIv@7>IX<`xZ4>8FWnTcB@rACqTPkEQH0ak!@=3(=y)+EA|SXlp#qOj-$j*#p+Lq({x8h3D5Ey_F^SVdv25Yz(@U zEDpL8ZGr|Ksvik!#hLHBzp9&~4V|9weG<2>K0f`c}1wXRo^bZ|Di{>|59Mzv_C~=Sd8G^sJh5 zFT*wT@Z%%zo;C&kuO%vizES*)`A8)c{NGwZtorzN?I5E! z<^Aj`Z&62TZGQ+V^w*rTm}B+Rb}qd3djcybvvRW7=CRP&pGxBHN^8N#EevoLMReU} zZVw(nAG9&#{B8N&4efQexFCGNAH5nqYgXi=1-qr9mVCZBZdg7~vim-$9Iwmp7suj6P!|4wASXjO+ zyR?Bgoa1}y7o|0;Z_%-JK6sqGL{*XR zmH}4$3|j&u2Cj+P4Oqf{L%Dp`pk*djZ+Z50Gu+RQ`Tko&PE??0n zAL{k@x`UJZvY&Z7bt+-Sxr;r-T)m;Fa!dSj>u{{=`Yf!!?R|G{-UDYF)_Hsb=kcsRQ- z-3G*{ZCen!-FLMyg}$O z^Axk=GUgzEW<9y76_M{m!_eVK?%F4IyBBYHS|#T0zQ%g_X2O`A9nA-ViEqCpBTGjJ zRf@8YFIGSm6sZl19e9hJOyuRr&CE-0p7~Jth~Cm!dM#R+9{im)A5XV)bzWmzS`uA( zML)zwoWOu(?ekjO!gR48@5?y7)^KEvS~hh|51mXdX&LLzk^lxc>E1Q;u651dB|e{h z1xC0ia5yAy?QCFBr~A4nFXf$a)}OITY6cm`HYR4#UDR<(PFfVst{=ZGDZHj!)y+eW zALs4%)%K_81iV`OqSDU_%pA?T3hI|5XDt_Vv@qMoT-3F@{7_5marz|XO8l)bS@&L% z>>NlBKTi6XzGc>5c2rEmHkfE(BWy&y&P6nnZDkv~({*tvKM_?D|CoQ`WY&z4@Ib%` zy>sqA=ZsLFKj@P`b0=_QDeUw1dtt*@b=Mj5?1^t*9@hM(VYe*-%aB*Jn=`bfwPy8Y zCS$*8N9r>PA`Yd(O!PH9zdB3)!%?{Vrs4#II2)eM#V<8a^k2tGviL--Dq;fG$2Iq- z0UP+wzEk9KN*ea_;if^!&X+Dnh@LSzNIyaWE7 zd00+%x1`A1>@3)gJ`HEvgAQE{d^6=khR$tcl%Xe&D?2+jCw$k>4vl%TwinsnmV6SQ zwynup^7+i=SjE6iU;(;BPBJ(rSJFN_zxTPG*^jp^2YrN|h%ZZ~dkSpP|2epB<1@%u~Cq}?tk+lKu8Pv{A32w5w# zGH<&h_S3$M9B&_IcLY*`g|B(Nl!mY|eAbGBUZ?Li?gtI|aQ%#1jB~u572el=OLz<) zgG?haiR-J*l-H-d2V^a|JtMj?TEwz74aqK-Cj+!)zwP}_j}yzhw_(hXjOR9DC&c)E z(O7r9)}Hlo)-&=tX6dG2-z?E%S)VSw&do!Q(vfM`16`y7-LEFMcq3wx8gH(Tm|yy< zJ6a`VdR3ZQU<=me)*H&cZ1-I)%HwcCnR_bf>aMSlyr>{>KQ=7q4)To~7RZR0H>quO4+>0My4uE1(^HPKPW zI_n(&(YnE6uG|5#DSphA&D^nYv~Gxb=p8V{`rmek(W8ovzbAV`4#Ac2>2{dNqMw0j z>-xa@&}(VCyS~QadhGktEf#pmk^)A)FI{5+eOs5#9|c4Wo2O%kXi=StX3!zOj}cvm^aB+?6-Brm<^bP3m=XuHK?P z*wLb>GOmu?41Vyk`pMXrO4T*`$;c7-e28hBInLLm9avPG;KexJdQ*GGh9aWS4DFRi zWHiQyL`3~>`K&$EenuzP>mnj_c9q%{F{MvLE2}v^aiM<--xzE|tMaOAsH19ja*&Vh zQ4G4&(tVs-&Do$bQ$Q8&<*P`fi!HhuBP#^=w=~}@5nR11!E=5jKZH1PTM!&*rvx)` zu1D9#u&3e^-5I<1^6m!g3H@BJ<3w!oT!EQ(#ddL?HD3(L0+s2Kg496E^E_ui^>UHA z@hqpsEUR``u5z4b#&CUFvj;hg*Vbx_as?I9h~B$z~#%UDu9#e_g!9`|^~uMX1M#aa8Gj?&f~7M10TC2g-Yp zw|+VKqyD4Q?_tWFV?nRzE-co*p2eGtuH`j#j>Z{kd!j%-^_hELCM&srxGVZsIurce zbS5|7Ax|Vg@5+H@9v@>PoyyMsLi(oYJ0OoA-&H_OoNNufwcbTpT3&w}58p%ppN?@X z)dqi~@^^DeQ_v-2b!e{n^RY2@yyiJ(u`;z}nLyeOPy)XZ4n z`4R)+`$mL23pbUVxCs7_Cs)erBCzIU-J$y4b`Ew(>d{8{wn%xm?}exFl^-2;wZ!$jv$pIeL~GIW8Clhm z8_sC-k2KN)y^_PCXBVxG{^S3V literal 0 HcmV?d00001 diff --git a/Search-ADObjectACL.ps1 b/Search-ADObjectACL.ps1 new file mode 100644 index 0000000..a05760d --- /dev/null +++ b/Search-ADObjectACL.ps1 @@ -0,0 +1,226 @@ +#Requires -Modules PowerShellAccessControl +function Search-ADObjectACL { + <# + .SYNOPSIS + Funkce slouzi k najiti ACL odpovidajicich zadani nad yadanymi AD objekty. + Pri spusteni bez dalsich parametru vypise vsechna ACL vsech objektu v domene. + + .DESCRIPTION + Funkce slouzi k najiti ACL odpovidajicich zadani nad yadanymi AD objekty. + Pri spusteni bez dalsich parametru vypise vsechna ACL vsech objektu v domene. + DetailACL pripadne obsahuje, na co se pravo vztahuje (na jaky atribut atd). + + .PARAMETER distinguishedName + Cesta k objektu v AD zadana v distinguished tvaru. + Pokud nezadano, projde se cela AD. + + .PARAMETER recurse + Prepinac rikajici, ze se maji projit i zanorene objekty pod cestou z distinguishedName parametru + + .PARAMETER allPartitions + Prepinac rikajici, ze se maji vyhledat objekty ze vsech AD partition. + Standardne se hleda jen v "Default naming context" partition. + + .PARAMETER objectType + Umoznuje omezit, jake objekty se budou hledat. Min objektu == rychlejsi. + Pokud je zaroven zadan distinguishedName, tak je nutne pouzit s prepinacem -Recurse. + Jinak se nehledaji zanorene objekty a kontroluje se pouze ten jeden zadany. + + Na vyber je "Computer", "User", "Group", "OrganizationUnit" + + .PARAMETER account + Pro vypis pouze prav, ktera ma zadany ucet (ucet/skupina/pocitac) + + .PARAMETER alsoByMembership + Prepinac rikajici, ze se vypisi i prava nalezici skupinam, jichz je ucet definovany v parametru account clenem + + .PARAMETER right + Nazev prava, ktere se ma hledat. + Staci zadat cast nazvu. + Hleda se skutecne konkretni vyskyt prava. Nefunguje tak, ze byste zadali genericRead a nasly se i zaznamy, kde ma uzivatel genericAll (tzn. full control) a je jedno, ze kdyz ma full control, ma tim padem i read. + + Nazvy neodpovidaji 1:1 tomu co je videt v GUI! Idealni je timto prikazem vyje prava k obejktu, kde vite, ze je pravo pouzito a tak ziskat jeho nazev + + .PARAMETER justExplicit + Prepinac rikajici, ze se vypisi pouze explicitni prava (ne zdedena) + + .PARAMETER type + Typ prava. Moznosti jsou Allow, Deny ci vychozi Both + + .EXAMPLE + Search-ADObjectACL -distinguishedName "OU=Management,OU=Skupiny,DC=ad,DC=fi,DC=muni,DC=cz" + + Vypise vsechna prava, ktera jsou definovana na zadanem objektu. + + .EXAMPLE + Search-ADObjectACL -distinguishedName "OU=Management,OU=Skupiny,DC=ad,DC=fi,DC=muni,DC=cz" -recurse + + Vypise vsechna prava, ktera jsou definovana na zadanem objektu a objektech v nem obsazenych + + .EXAMPLE + Search-ADObjectACL -distinguishedName "OU=Management,OU=Skupiny,DC=ad,DC=fi,DC=muni,DC=cz" -justExplicit + + Vypise pouze explicitni prava, ktera jsou definovana na zadanem objektu + + + .EXAMPLE + Search-ADObjectACL -distinguishedName "OU=Management,OU=Skupiny,DC=ad,DC=fi,DC=muni,DC=cz" -right ms-Mcs-AdmPwd -account "domain admins" -alsoByMembership + + Vypise vsechny zaznam prav cist/zapisovat ms-Mcs-AdmPwd (tzn LAPS heslo), ktera ma skupina "Domain Admins" ci skupiny jiz je clenem na zadanem objektu + + + .EXAMPLE + Search-ADObjectACL -distinguishedName "OU=Management,OU=Skupiny,DC=ad,DC=fi,DC=muni,DC=cz" -type Deny + + Vypise vsechna deny prava, ktera jsou definovana na zadanem objektu + + .NOTES + Vyzaduje modul PowerShellAccessControl, konkretne funkci Get-AdObjectAceGuid pro preklad extendedRight a schema prav + #> + + [CmdletBinding()] + param ( + [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [ValidateScript( { + If (($_ -match ',DC=\w+$')) { + $true + } else { + Throw "Zadejte v distinguished name tvaru. Napr.: OU=Skupiny,DC=ad,DC=fi,DC=muni,DC=cz" + } + })] + [string] $distinguishedName + , + [switch] $recurse + , + [switch] $allPartitions + , + [ValidateSet("Computer", "User", "Group", "OrganizationUnit")] + [string[]] $objectType + , + [Parameter(Position = 1, ValueFromPipeline = $true)] + [string] $account + , + [switch] $alsoByMembership + , + [Parameter(Position = 2, ValueFromPipeline = $true)] + [string] $right + , + [switch] $justExplicit + , + [ValidateSet("Allow", "Deny", "Both")] + [string] $type = "Both" + ) + + begin { + if ($objectType -and $distinguishedName -and !$recurse) { + Write-Warning "Filtr objectType se neaplikuje, protoze jste nepouzili -Recurse, takze se prohledaji pouze prava objektu '$distinguishedName'" + } + + if ($allPartitions -and $distinguishedName) { + Write-Warning "Parametr distinguishedName se nepouzije, protoze jste zadali hledani ve vsech partition AD" + $distinguishedName = '' + } + + if ($account) { + $identityFilter = $account + } + + if ($alsoByMembership) { + if ($group = (Get-ADUser $account -Properties memberof | Select-Object -ExpandProperty memberof | % {($_ -split ',')[0] -replace "CN="}) -join '|') { + $identityFilter = "$account|$group" + } + } + + # + # ziskani seznamu objektu z AD, pro ktere pote ziskam jejich ACL + $params = @{ + Properties = 'distinguishedname' + ErrorAction = 'stop' + } + $filter = "*" + if ($objectType) { + $filter = "" + $objectType | % { + if ($filter) { + $filter += " -or " + } + if ($_ -eq 'User') { + $filter += "(ObjectClass -eq `"$_`" -and objectCategory -eq `"Person`")" + + } else { + $filter += "ObjectClass -eq `"$_`"" + } + } + + } + + if ($distinguishedName) { + if ($recurse) { + $params.SearchBase = $distinguishedName + $params.Filter = $filter + } else { + $params.Identity = $distinguishedName + } + } else { + $params.Filter = $filter + } + + if ($allPartitions) { + $params.searchBase = "" + $gc = (Get-ADDomainController -Discover -Service "GlobalCatalog").name + $params.server = "$gc`:3268" + } + + try { + $searchInObject = Get-ADObject @params + } catch { + throw "Pri ziskavani objektu z AD se objevila chyba:`n$_" + } + + if (!$searchInObject) { + Write-Warning "Zadny AD objekt neodpovida zadani. Ukoncuji." + break + } + } + + process { + $searchInObjectCount = $searchInObject.distinguishedname.count + $count = 0 + + # + # pro vsechny zadane AD objekty zjistim jejich ACL + $searchInObject | % { + $dn = $_.distinguishedname + Write-Progress -Activity "Zpracovavam objekt" -Status "$dn" -PercentComplete (( $count / $searchInObjectCount ) * 100) -Id 1 + try { + $acl = Get-Acl -path "AD:\$dn" -errorAction stop | Select-Object -ExpandProperty Access + } catch { + # v AD jsou ruzne podivnosti jako 'CN=\#deprecated\#_InteractiveLogon,OU=Skupiny,DC=ad,DC=fi,DC=muni,DC=cz', ktere konnci chybou 'The object name has bad syntax' + } + + foreach ($a in $acl) { + Write-Progress -Activity "Zpracovavam jeho acl" -Status $a.ActiveDirectoryRights -ParentId 1 + + if ($justExplicit -and $a.isInherited -eq $true) { + continue + } + if ($identityFilter -and $a.IdentityReference.value -notmatch $identityFilter) { + continue + } + if ($type -ne "Both" -and $a.AccessControlType -ne $type) { + continue + } + + $output = $a | Select-Object IdentityReference, @{n = 'DistinguishedName'; e = {$dn}}, ActiveDirectoryRights, AccessControlType, IsInherited, objectType, @{n = 'detailedACL'; e = {Get-AdObjectAceGuid -Guid $a.objectType | select -exp name}} + + if ($right -and !($output.ActiveDirectoryRights -like "*$right*" -or $output.detailedACL -like "*$right*")) { + # pozadovane pravo nenalezeno + continue + } + + $output + } + ++$count + } + } +} diff --git a/Search-GPOSetting.ps1 b/Search-GPOSetting.ps1 new file mode 100644 index 0000000..a33a6d8 --- /dev/null +++ b/Search-GPOSetting.ps1 @@ -0,0 +1,96 @@ +function Search-GPOSetting { + <# + .SYNOPSIS + Slouzi k vyhledani zadaneho stringu v nastavenich vsech AD GPO. + + .DESCRIPTION + Slouzi k vyhledani zadaneho stringu v nastavenich vsech AD GPO. + + Funguje tak, ze si vygeneruje html reporty s nastavenimi kazde GPO v AD a ty pote prohleda. + Pokud je report neaktulni ci chybi, tak se nageneruje znovu. + + Reporty se ukladaji v uzivatelskem profilu a mohou zabirat desitky MB (dle poctu GPO v domene). + + .PARAMETER string + Text, ktery se bude hledat v nastavenich GPO. + + .PARAMETER reports + Cesta k adresari, do ktereho se budou ukladat html reporty jednotlivych GPO. + + .EXAMPLE + Search-GPOSetting -string "Always install with elevated privileges" + + Vyhleda v nagenerovanych HTML reportech s nastavenimi vsech domenovych GPO zadany text. + Pokud nejaky report chybi nebo nebude aktualni, tak se nageneruje znovu. + #> + + [cmdletbinding()] + param ( + [Parameter(Position = 0, Mandatory = $True)] + [string] $string + , + [string] $reports = (Join-Path $env:LOCALAPPDATA "GPOsReports") + ) + + if (!(Test-Path $reports)) { + Write-Verbose "Vytvorim adresar pro ukladani reportu: $reports" + $null = New-Item $reports -itemType directory + # vygeneruji report s nastavenimi + Write-Warning "Nyni se vytvori cache obsahujici reporty s nastavenimi vsech GPO. To muze trvat i 10 minut!`nPote v ni dojde k vyhledani zadaneho retezce." + } + + # zruseni specialniho vyznamu znaku + $string = [regex]::Escape($string) + + Write-Verbose "Reporty se ukladaji do: $reports" + + try { + $GPOs = Get-GPO -All -ErrorAction Stop + } catch { + throw "Nepovedlo se ziskat seznam GPO. Chyba byla $_" + } + + + foreach ($gpo in $GPOs) { + $path = (Join-Path $reports $gpo.id) + ".html" + if ((Test-Path $path -ErrorAction SilentlyContinue) -and (Get-Item $path).lastWriteTime -ge $gpo.modificationtime) { + # report je aktualni + continue + } + + Write-Verbose "Generuji report pro $($gpo.displayName)" + try { + Get-GPOReport -Guid $gpo.id -path $path -ReportType Html -ea Stop + } catch { + Write-Error "Nepovedlo se ziskat nastaveni GPO. Chyba byla $_" + } + } + + $foundReports = @() + foreach ($report in (Get-ChildItem $reports -Filter *.html).FullName) { + Write-Verbose "Kontroluji obsah nastaveni $report" + # po radcich hledam v kazdem reportu zadany string + $match = (Get-Content $report) -split "`n" | Select-String $string -AllMatches + if ($match) { + $guid = (Split-Path $report -Leaf) -replace '.html' + $foundReports += $report + try { + $GPOName = (Get-GPO -Guid $guid).displayName + } catch { + Write-Output "GPO s GUID $guid se nepodarilo dohledat, zrejme jiz v AD neexistuje == html report z cache smazu" + Remove-Item $report -Force + continue + } + + Write-Host "V GPO $GPOName bylo nalezeno zde:" -ForegroundColor Green + Write-Host "$match`n`n" + } + } + + if ($foundReports) { + $a = Read-Host "Chcete dane GPO zobrazit? A|N" + if ($a -eq 'a') { + $foundReports | % {Invoke-Expression $_} + } + } +} \ No newline at end of file diff --git a/Shutdown-Computer.ps1 b/Shutdown-Computer.ps1 new file mode 100644 index 0000000..4e56f38 --- /dev/null +++ b/Shutdown-Computer.ps1 @@ -0,0 +1,286 @@ +#Requires -Modules psasync,SplitPipeline +#TODO: pokud filtruji dle prihlaseneho uzivatele at se provede i kdyz je v disconnected stavu pokud tam uz neni nikdo jinz prihlasen +function Shutdown-Computer { + <# + .Synopsis + Provede na vlastním či vzdáleném pocitaci jednu z vybranych akci: + LogOff, Shutdown, Reboot, ForcedLogOff, ForcedShutdown, ForcedReboot, PowerOff, ForcedPowerOff + + .Description + Provede na vlastním či vzdáleném pocitaci jednu z vybranych akci: + LogOff, Shutdown, Reboot, ForcedLogOff, ForcedShutdown, ForcedReboot, PowerOff, ForcedPowerOff + + Pokud je někdo přihlášen je potřeba použít forced variantu. Forced varianty násilně ukončí běžící procesy - hrozí ztráta dat! + Pokud zadám i parametr username, tak provede akci pouze na strojích, kde je zadaný uživatel aktuálně přihlášený. A to pouze v aktivní session, ne disconnected atp. + + Vyžaduje fci: Get-LoggedOnUser! Která vyfiltruje stroje s prihlášeným $userName. + Vyžaduje moduly: splitPipeline, psasync + + .PARAMETER ComputerName + Parametr udavajici seznam stroju. + + .PARAMETER Type + Typ akce: LogOff, Shutdown, Reboot,ForcedLogOff,ForcedShutdown,ForcedReboot,PowerOff,ForcedPowerOff. + + .PARAMETER UserName + Login uživatele. Akce se proveden pouze na strojích, kde je uživatel přihlášen. + + .PARAMETER DateTime + Nepovinný parametr. Datum a čas kdy se má akce provést. Ve tvaru d.M H:m (13.1 12:25) či H:m (13:35). + + .PARAMETER YourPassword + Povinný parametr, který je třeba pokud je definován parametr DateTime. Heslo uživatele pod kterým běží PS konzole a bude se pouštět scheduled task. + + .PARAMETER TaskPath + Nepovinný parametr udávající cestu, do které se má uložit scheduled task. + + .PARAMETER Comment + Nepovinny parametr udavajici komentar, ktery se zaloguje do sys logu jako duvod vypnuti. + + .PARAMETER TimeOut + Nepovinny parametr udavajici o kolik vterin se ma vypnuti zpozdit. + Vychozi je 0 vterin. + + .Example + Shutdown-Computer -comp $B311 LogOff -username skoleni + U strojů v B311 odhlásí uživatele skoleni. + + .Example + $hala | Shutdown-Computer Shutdown + Vypne stroje v hale. + + .Example + Shutdown-Computer -computername $hala -type Reboot + Restartuje pouze stroje, kde neni prihlasen zadny uzivatel. + + .Example + Shutdown-Computer -computername $hala -type ForcedReboot + Restartuje vsechny stroje v hale. + + .Example + Shutdown-Computer -computername localhost -type PowerOff + Shuts down the computer and turns off the power (if supported by the computer in question). + + .Notes + Více o typech vypnutí http://msdn.microsoft.com/en-us/library/aa394058(v=vs.85).aspx + Author: Ondřej Šebela - ztrhgf@seznam.cz + #> + + [Alias("sdc")] + [CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess = $true, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory = $false, Position = 0, ParameterSetName = "Default", ValueFromPipelinebyPropertyName = $true, ValueFromPipeline = $true, HelpMessage = "zadej jmeno stroje/ů k pingnutí")] + [Parameter(Mandatory = $false, Position = 0, ParameterSetName = "Scheduled", ValueFromPipelinebyPropertyName = $true, ValueFromPipeline = $true, HelpMessage = "zadej jmeno stroje/ů k pingnutí")] + [Alias("c", "name", "dnshostname")] + [ValidateNotNullOrEmpty()] + $ComputerName = $env:computername + , + [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "Default", HelpMessage = "zadej typ akce: LogOff, Shutdown, Reboot, ForcedReboot, ForcedLogOff,...viz help")] + [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "Scheduled", HelpMessage = "zadej typ akce: LogOff, Shutdown, Reboot, ForcedReboot, ForcedLogOff,...viz help")] + [ValidateSet("LogOff", "Shutdown", "Reboot", "ForcedLogOff", "ForcedShutdown", "ForcedReboot", "PowerOff", "ForcedPowerOff")] + $Type + , + [Parameter(Mandatory = $false, Position = 2, ParameterSetName = "Default", HelpMessage = "Zadejte login, aby se akce vykonala pouze na strojích s přihlášeným uživatelem")] + [ValidateNotNullOrEmpty()] + [Alias("login")] + $UserName + , + [Parameter(Mandatory = $true, ParameterSetName = "Scheduled", HelpMessage = "Zadejte datum ve tvaru d.M H:m (13.1 12:25) či H:m (13:35)")] + [Alias("schedule")] + # [ValidateScript({$_.hour -ne 0})] #za urcitych okolnosti i pri zadani hodin se nastavi 00:00, da se zadat i bez hodin-nastavi se 0:0 + # [ValidateScript({try{[DateTime] $date = [DateTime]::ParseExact($_, "d.M. H:m", [System.Globalization.CultureInfo]::InvariantCulture);return $true}catch{return $false}})] + [ValidateScript( { + function Convert-DateString ([String]$Date, [String[]]$Format) { + $result = New-Object DateTime + $convertible = [DateTime]::TryParseExact($Date, $Format, [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::None, [ref]$result) + if ($convertible) {return $true}; else {return $false} + } + Convert-DateString -Date $_ -Format 'd.M. H:m', 'd.M.yy H:m', 'H:m', 'd.M H:m' + })] + [string]$DateTime + # , + # [Parameter(Mandatory=$false,ParameterSetName="Default")] + # [Parameter(ParameterSetName = "Scheduled")] + # $cred + , + [Parameter(Mandatory = $true, ParameterSetName = "Scheduled", HelpMessage = "Zadejte heslo potřebné k vytvoření úkolu v task scheduleru.")] + [SecureString]$YourPassword + , + [Parameter(Mandatory = $false, ParameterSetName = "Scheduled")] + $TaskPath = "\Planned_Shutdowns\" + , + [string] $Comment = 'provedeno prikazem Shutdown-Computer' + , + [int] $TimeOut = 0 + ) + + BEGIN { + # kontrola že je dostupná funkce get-loggedonuser + if ($UserName) { + try { + $null = Get-Command Get-LoggedOnUser -ErrorAction Stop + $null = Get-Command Test-Connection2 -ErrorAction Stop + } catch { + Write-Error "Pro běh tohoto skriptu je zapotřebí funkce: Get-LoggedOnUser a Test-Connection2" + break + } + + # Odfiltrovani nepingajicich stroju + $PingajiciComputerName = Test-Connection2 $ComputerName -JustResponding + # Zjištění, na kterých strojích ze seznamu je přihlášen daný uživatel, pokud nikde = konec + try { + $ComputerName = glu -computername $PingajiciComputerName -UserName $UserName | where {$_.userName -eq $UserName -and $_.state -ne "Disc"} | select -exp computername + } catch { + Write-Output "Uživatel $username není přihlášen na žádném stroji ze seznamu " + break + } + + Write-Output "Uživatel $username je přihlášen na: $ComputerName = zde se provede: $type." + } + + # Převod securestring na plaintext + if ($YourPassword) { + [string]$YourPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($YourPassword)) + } + + switch ($type) { + 'LogOff' {$ShutdownType = "0"} + 'Shutdown' {$ShutdownType = "1"} + 'Reboot' {$ShutdownType = "2"} + 'ForcedLogOff' {$ShutdownType = "4"} + 'ForcedShutdown' {$ShutdownType = "5"} + 'ForcedReboot' {$ShutdownType = "6"} + 'PowerOff' {$ShutdownType = "8"} + 'ForcedPowerOff' {$ShutdownType = "12"} + } + + if ($datetime) { + # aby datum bylo ve správném tvaru (den.měsíc. a ne měsíc.den.) + try { + function Convert-DateString ([String]$Date, [String[]]$Format) { + $result = New-Object DateTime + $convertible = [DateTime]::TryParseExact($Date, $Format, [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::None, [ref]$result) + return $result + } + [datetime]$datetime = Convert-DateString -Date $datetime -Format 'd.M. H:m', 'd.M.yy H:m', 'H:m', 'd.M H:m' + } catch { + Write-Error "Zadaný čas $datetime se nepodařilo převést" + break + } + + # kontrola ze zadane datum neni v minulosti + $date = $DateTime | get-date + if ($date -le (Get-Date)) { + Write-Error "Zadaný čas $date je v minulosti. Funkce se teď ukončí." + break + } + + # + # ZADEFINOVANI VLASTNOSTI SCHEDULED TASKU + # datum musi byt v ' zavorkach proto delam replace + $OriginalCommand = $myinvocation.line -replace "`"", "`'" + # kvůli -replace potřebuji původni argument $datetime a ne ten převedený na [datetime] tvar + $OriginalDate = $psboundparameters.getenumerator() | where {$_.key -match "DateTime"} | select -exp value + # pokud nastavuji scheduledjob tak uz musim prikaz volat bez -datetime jinak by misto provedeni zase udelal jen dalsi scheduledjob :) + $ScheduledCommand = $OriginalCommand -replace "-datetime `'$OriginalDate`'", "" -replace "-datetime $OriginalDate", "" -replace "-password $YourPassword", "" -replace "$YourPassword", "" # replace $YourPassword je pro jistotu aby tam urcite nebylo heslo za zadnych okolnosti + $A = New-ScheduledTaskAction –Execute "powershell.exe" -argument "$ScheduledCommand" + $T = New-ScheduledTaskTrigger -once -at $DateTime + $S = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -RunOnlyIfNetworkAvailable + $D = New-ScheduledTask -Action $A -Trigger $T -Settings $S + } else { + $AsyncPipelines = @() + $pool = Get-RunspacePool 30 + } + + # scriptBlock, ktery provadi vypnuti + $scriptblock = { + param ($computer, $ShutdownType, $comment, $timeOut) + + If (Test-Connection $computer -count 1 -quiet) { + $Error.Clear() + if ($cred -eq $null) { + trap { continue } + + # non-force akce se neprovedou pokud je nekdo prihlasen, informuji o tom + #TODO dodelat, quser ted nespoustim v remote session = chyba + #if ($ShutdownType -in 1, 2, 8) { + # if ((quser.exe).count -ge 2) { + # # neresim jestli jde pouze o disconnected session + # Write-Output "Na $computer je nekdo prihlasen, neprovedu." + # continue + # } + #} + + # + # PROVEDU VYPNUTI + # pozn.: Get-WmiObject nepouzivam, protoze pouziva RPC a zpusobovalo, ze po vypnuti klientu stroj zacal kontaktovat RPC na lokalnich adresach 192. 172. z nejakeho duvodu + $obj = Get-CimInstance win32_operatingsystem -ComputerName $computer + $null = Invoke-CimMethod -InputObject $obj -MethodName Win32ShutdownTracker -Arguments @{Comment = $comment; Flags = $ShutdownType; ReasonCode = 0; Timeout = $timeOut} + Write-Output "Provedeno na $computer" + } + + # if ($cred -eq "other") + # { + # trap { continue } + # $null = (Get-WmiObject win32_operatingsystem -ComputerName $computer -ErrorAction SilentlyContinue -Credential (get-Credential)).Win32Shutdown($ShutdownType) + # Write-Output "Provedeno na $computer" + # } + } else { + Write-Output "$computer nepingá" + } + } + } + + PROCESS { + # ma se vykonat v zadany cas == vytvorim sched. task, ktery prikaz spusti + if ($datetime) { + $dd = $date | Get-Date -Format d.M.yyyy_HH.mm + $TaskName = "$type-$($computername -join ',')-$dd" + $ExistingJobs = Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue + if ($ExistingJobs) { + Write-Warning "`nScheduledTask se jménem $TaskName existuje - bude nahrazen." + } + + try { + # nastaveni uziv. jmena a hesla pro dany task - aby mohl pristupovat i na zdroje na siti (stroje) + $null = Register-ScheduledTask $TaskName -InputObject $D -user "$env:USERDOMAIN\$env:USERNAME" -password $YourPassword -force -erroraction stop -TaskPath $TaskPath + Write-Output "`nByl vytvořen scheduledtask $TaskName." + } catch { + Get-ScheduledTask -TaskName $TaskName | Unregister-ScheduledTask -Confirm:$false + Write-Output "`nPři nastavování scheduled tasku došlo k chybě, proto byl odstraněn.`n Chyba byla: $($_.Exception.Message) " + } + + Write-Output "`nPro zrušení tasku spusťte:`nGet-ScheduledTask -TaskName $TaskName | Unregister-ScheduledTask" + } else { + # ma se vykonat okamzite + if ($UserName) { + while ($choice -notmatch "[A|N]") { + $choice = read-host "Pokračovat? (A|N)" + } + if ($choice -eq "N") { + break + } + } + + foreach ($computer in $ComputerName) { + $AsyncPipelines += Invoke-Async -RunspacePool $pool -ScriptBlock $ScriptBlock -Parameters $computer, $ShutdownType, $comment, $timeOut + } + } + } + + END { + # ma se vykonat v zadany cas == jen zkontroluji, ze se sched. task vytvoril + if ($datetime) { + try { + $null = Get-ScheduledTask –TaskName "$TaskName" -erroraction stop #-TaskPath "\Microsoft\Windows\PowerShell\ScheduledJobs\" + } catch { + Write-Output "Task $TaskName nebyl vytvořen! Zkontrolujte Task Scheduler" + } + } else { + # ma se vykonat okamzite == jiz jsem spustil == nyni ziskam vysledek + Receive-AsyncResults -Pipelines $AsyncPipelines -ShowProgress + } + } +} + +Set-Alias sdc Shutdown-Computer diff --git a/Start-TCPPortListener.ps1 b/Start-TCPPortListener.ps1 new file mode 100644 index 0000000..db83b41 --- /dev/null +++ b/Start-TCPPortListener.ps1 @@ -0,0 +1,118 @@ +function Start-TCPPortListener { + <# + .SYNOPSIS + Funkce spusti naslouchani na zadanem TCP portu na zadanem pocitaci. + Dobre pro testovani pruchodnosti komunikace skrze firewall. + + .DESCRIPTION + Funkce spusti naslouchani na zadanem TCP portu na zadanem pocitaci. + Dobre pro testovani pruchodnosti komunikace skrze firewall. + + .PARAMETER Computer + IP ci DNS jmeno stroje, na kterem chceme spustit naslouchani na portu + + .PARAMETER PORT + Cislo TCP portu + + .PARAMETER ListenSeconds + Jak dlouho, se bude naslouchat. + Vychozi je 600 sekund + + .PARAMETER KeepAlive + Vychozi jsou 2 vteriny + + .EXAMPLE + Start-TCPPortListener -computer 147.251.48.186 -PORT 4000 + + .NOTES + https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Listen-TCP-Port-0bf882c2 + #> + + [CmdletBinding()] + Param( + [Parameter()] + [ValidateNotNull()] + [String] $Computer = $env:COMPUTERNAME, + + [Parameter(Mandatory = $True)] + [ValidateNotNull()] + [Int] $PORT, + + [Parameter()] + [ValidateNotNull()] + [Int] $ListenSeconds = 600, + + [Parameter()] + [ValidateNotNull()] + [Int] $KeepAlive = 2 + ) + + # zadal IP, ziskam z ni DNS + # hostname potrebuji kvuli kerb. autentizaci pouzivane v Invoke-Command + if ($Computer -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") { + try { + [system.net.IPAddress]$Computer | Out-Null + } catch { + throw "Parametr IP musi obsahovat validni IP adresu" + } + $dnsName = ([System.Net.Dns]::gethostentry($Computer)).hostname # kvuli kerberos autentizaci potrebuju dns jmeno a ne IP + $dnsName = ($dnsName).split('\.')[0] + if (!$dnsName) { + throw "Nepodarilo se z $computer ziskat DNS jmeno. Je zadana IP spravna?" + } + $IP = $Computer + } else { + # zadal DNS jmeno, ziskam z nej IP + $IP = [System.Net.Dns]::GetHostAddresses($Computer).ipaddresstostring | select -Last 1 # prednostne chci pouzivat IPv4 adresy pred IPv6 + if (!$IP) { + throw "Nepodarilo se ziskat IP adresu stroje $computer, zkuste ji rovnou zadat do parametru Computer." + } + } + + if ($Computer -eq $env:COMPUTERNAME) { + Write-Warning "Pokud budete dostupnost zkouset z localhostu prikazem Test-Port, pouzijte v nem v parametru computerName hodnotu $computer (a ne localhost)" + } + + Write-Output "Zkusim jestli uz na danem portu nahodou neco nebezi" + if ((Test-Port -ComputerName $Computer -Port $PORT).result) { + Write-Host "Na $Computer`:$PORT jiz neco bezi"; Break + } + + $scriptBlock = { + param ($IP, $port, $ListenSeconds, $KeepAlive) + + $ListenSecondst = New-TimeSpan -Seconds $ListenSeconds + $TIME = [diagnostics.stopwatch]::StartNew() + $EP = new-object System.Net.IPEndPoint ([system.net.IPAddress]::Parse($IP), $PORT) + $LSTN = new-object System.Net.Sockets.TcpListener $EP + $LSTN.server.ReceiveTimeout = 300 + $LSTN.start() + + try { + Write-Host "`n$(Get-Date -f hh:mm) START naslouchani $IP`:$port po dobu $ListenSeconds vterin.`nCTRL + C pro ukonceni" # tolik prazdnych radku je schvalne, kvuli vypisu progresu test-netconnection, aby neprekryval tento text + Write-Warning "Je take potreba mit port $port povolen na danem firewallu!" + + While ($TIME.elapsed -lt $ListenSecondst) { + if (!$LSTN.Pending()) {Start-Sleep -Seconds 1; continue; } + $CONNECT = $LSTN.AcceptTcpClient() + $CONNECT.client.RemoteEndPoint | Add-Member -NotePropertyName Date -NotePropertyValue (get-date) -PassThru | Add-Member -NotePropertyName Status -NotePropertyValue Connected -PassThru | select Status, Date, Address, Port + Start-Sleep -Seconds $KeepAlive; + $CONNECT.client.RemoteEndPoint | Add-Member -NotePropertyName Date -NotePropertyValue (get-date) -PassThru -Force | Add-Member -NotePropertyName Status -NotePropertyValue Disconnected -PassThru -Force | select Status, Date, Address, Port + $CONNECT.close() + } + } catch { + Write-Error $_ + } finally { + $LSTN.stop(); $end = get-date; Write-host "`n$end - ukonceno" + } + } # konec scriptblock + + $params = @{ + scriptBlock = $scriptBlock + computerName = $Computer + ArgumentList = $IP, $port, $ListenSeconds, $KeepAlive + } + + Invoke-Command2 @params + +} \ No newline at end of file diff --git a/Test-Connection2.ps1 b/Test-Connection2.ps1 new file mode 100644 index 0000000..c5d5651 --- /dev/null +++ b/Test-Connection2.ps1 @@ -0,0 +1,154 @@ +Function Test-Connection2 { + <# + .SYNOPSIS + Funkce k otestovani dostupnosti stroju. + + .DESCRIPTION + Funkce k otestovani dostupnosti stroju. Pouziva asynchronni ping. + + .PARAMETER Computername + List of computers to test connection + + .PARAMETER DetailedTest + Prepinac. Pomalejsi metoda testovani vyzadujici modul psasync. + Krome pingu otestuje i dostupnost c$ sdileni a RPC. + + Aby melo smysl, je potreba mit na danych strojich prava pro pristup k c$ sdileni! + + .PARAMETER Repeat + Prepinac. Donekonecna bude pingat vybrane stroje. + Neda se pouzit spolu s DetailedTest + + .PARAMETER JustResponding + Vypise jen stroje, ktere odpovidaji + + .PARAMETER JustNotResponding + Vypise jen stroje, ktere neodpovidaji + + .NOTES + Vychazi ze skriptu Test-ConnectionAsync od Boe Prox + + .EXAMPLE + Test-Connection2 -Computername server1,server2,server3 + + Computername Result + ------------ ------ + Server1 Success + Server2 TimedOut + Server3 No such host is known + + .EXAMPLE + $offlineStroje = Test-Connection2 -Computername server1,server2,server3 -JustNotResponding + + .EXAMPLE + if (Test-Connection2 bumpkin -JustResponding) {"Bumpkin bezi"} + #> + + [cmdletbinding(DefaultParameterSetName = 'Default')] + Param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipelinebyPropertyName = $true, ValueFromPipeline = $true)] + [string[]] $Computername + , + [Parameter(Mandatory = $false, ParameterSetName = "Online")] + [switch] $JustResponding + , + [Parameter(Mandatory = $false, ParameterSetName = "Offline")] + [switch] $JustNotResponding + , + [switch] $DetailedTest + , + [Alias('t')] + [switch] $Repeat + ) + + Begin { + if ($DetailedTest -and $Repeat) { + Write-Warning "Prepinac detailed, se neda pouzit v kombinaci s repeat." + $DetailedTest = $false + } + + if ($DetailedTest) { + if (! (Get-Module psasync)) { + throw "Pro detailni otestovani dostupnosti je potreba psasync modul" + } + + $AsyncPipelines = @() + $pool = Get-RunspacePool 30 + $scriptblock = ` + { + param($computer, $JustResponding, $JustNotResponding) + # vytvorim si objekt s atributy + $Object = [pscustomobject] @{ + ComputerName = $computer + Result = "" + } + + if (Test-Connection $computer -count 1 -quiet) { + if (! (Get-WmiObject win32_computersystem -ComputerName $Computer -ErrorAction SilentlyContinue)) { + $Object.Result = "RPC not available" + } elseif (Test-Path \\$computer\c$) { + $Object.Result = "Success" + } else { + $Object.Result = "c$ share not available" + } + } else { + $Object.Result = "TimedOut" + } + + if (($JustResponding -and $Object.Result -eq 'Success') -or ($JustNotResponding -and $Object.Result -ne 'Success')) { + $Object.ComputerName + } elseif (!$JustResponding -and !$JustNotResponding) { + $Object + } + } + } + } + + Process { + if ($DetailedTest) { + foreach ($computer in $ComputerName) { + $AsyncPipelines += Invoke-Async -RunspacePool $pool -ScriptBlock $ScriptBlock -Parameters $computer, $JustResponding, $JustNotResponding + } + } + } + + End { + if ($DetailedTest) { + Receive-AsyncResults -Pipelines $AsyncPipelines -ShowProgress + } else { + while (1) { + $job = Test-Connection -ComputerName $computername -AsJob -Count 1 + $job | Wait-Job | Receive-Job | % { + if ($_.responseTime -ge 0) { + $result = "Success" + } elseif ($_.PrimaryAddressResolutionStatus -ne 0) { + $result = "Unknown hostname" + } else { + $result = "TimedOut" + } + + if (($JustResponding -and $result -eq 'Success') -or ($JustNotResponding -and $result -ne 'Success')) { + # vratim pouze hostname stroje + $_.address + } elseif (!$JustResponding -and !$JustNotResponding) { + [pscustomobject]@{ + ComputerName = $_.address + Result = $result + } + } + } + + Remove-Job $job + + # ukoncim while cyklus pokud neni receno, ze se maji vysledky neustale vypisovat + if (!$Repeat) { + break + } else { + sleep 1 + } + } # end while + } + } +} +Set-Alias pc Test-Connection2 +Set-Alias ping2 Test-Connection2 \ No newline at end of file diff --git a/Test-Path2.ps1 b/Test-Path2.ps1 new file mode 100644 index 0000000..3681c6a --- /dev/null +++ b/Test-Path2.ps1 @@ -0,0 +1,60 @@ +function Test-Path2 { + <# + .Synopsis + Fce slouží ke zjištění, zdali existuje zadaná cesta. + .Description + .PARAMETER $ComputerName + seznam stroju u kterych zjistim prihlasene uzivatele + .PARAMETER $path + Parametr určující jaká cesta se bude testovat. + .EXAMPLE + test-path2 $hala -p C:\temp + #> + + [CmdletBinding()] + param ( + [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,HelpMessage="zadej jmeno stroje/ů")] + [Alias("c","CN","__Server","IPAddress","Server","Computer","Name","SamAccountName")] + [String[]] $ComputerName + , + [Parameter(Mandatory=$true,Position=1,HelpMessage="zadej cestu např.: C:\TEMP")] + [Alias("p")] + [string] $path + ) + + BEGIN { + # adresa ke kontrole + $path = $path -replace ":", "$" -Replace("`"","") + # nazev souboru + $filename = $path.substring($path.lastindexofany("\") +1 ) + + $AsyncPipelines = @() + $pool = Get-RunspacePool 20 + + $scriptblock = { + param($Computer,$Path) + if (Test-Connection -ComputerName $computer -Count 1 -quiet -ErrorAction SilentlyContinue) { + If(test-Path "\\$computer\$path") { + write-output "na $computer $filename nalezen" + } else { + write-output "na $computer $filename nenalezen" + } + } else { + write-output "$computer nepingá" + } + } + } + + PROCESS { + foreach ($computer in $ComputerName) { + $AsyncPipelines += Invoke-Async -RunspacePool $pool -ScriptBlock $ScriptBlock -Parameters $Computer,$Path + } + } + + END { + Receive-AsyncResults -Pipelines $AsyncPipelines -ShowProgress + } +} + +# NASTAVENI ALIASU +Set-Alias tp test-path2 diff --git a/Test-Port.ps1 b/Test-Port.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..716df8f770dcf7e1e1f5d2db552a03ffef4a7220 GIT binary patch literal 16440 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 W*LNr;&)7_ + + [cmdletbinding()] + param ( + [Parameter(Position = 0)] + [string] $computerName = $env:COMPUTERNAME + , + [Parameter(Position = 1, Mandatory = $true)] + [ValidateScript( { + If ($_ -match '^[\w-/]+;?[\d, ]*;?[\w, -]*$') { + $true + } else { + Throw "Zadavejte ve formatu: 'logName;eventId; provider' Pr.: 'security;50' nebo 'security;50,100' nebo 'security;50,100;Microsoft-Windows-Security-Auditing' nebo 'security;;Microsoft-Windows-Security-Auditing'" + } + })] + [string[]] $eventToSearch + , + [int] $sleep = 60 + , + [ValidateScript( { + If (($_.getType().name -eq "string" -and [DateTime]::Parse($_)) -or ($_.getType().name -eq "dateTime")) { + $true + } else { + Throw "From zadejte ve formatu dle vaseho culture. Pro cs-CZ napr.: 15.2.2019 15:00. Pro en-US pak prohodit den a mesic." + } + })] + $searchFrom + , + [ValidateScript( { + If ($_ -gt 0) { + $true + } else { + Throw "stopAfter musi byt kladna hodnota." + } + })] + [int] $stopAfter + ) + + if ($searchFrom) { + if ($searchFrom.getType().name -eq "string") { + $searchFrom = [DateTime]::Parse($searchFrom) + } + + if ($searchFrom -gt (Get-Date)) { + throw "searchFrom musi byt v minulosti" + } + } + + $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") + + # nastavim odstup hledani (aby se mi nestalo, ze mi utece udalost, ktera se vyskytla ve stejne vterina, kdy jsem udelal mereni) + # tzn vypisi udalosti do aktualni cas - vterina + $delay = 1 + + # + # VYTVORENI XML FILTRU DLE ZADANI + $filter = @" + + +"@ + + # ze zadani vyextrahuji jake udalosti se maji hledat a vytvorim odpovidajici xml filtr + $eventToSearch | % { + $s = $_ -split ';' + + $logName = $s[0] + + if ($logName -match 'security' -and !$isAdmin -and $computerName -eq $env:COMPUTERNAME) { + throw "Pro prohlizeni security logu je potreba spustit s admin pravy" + } + + if ($logName -match "forwardedEvents" -and !$delay) { + # nastavim odstup hledani (60 vterin by melo staci, eventy se typicky forwarduji z klientu na server do 30 vterin) + $delay = 60 + Write-Warning "Nastavuji zpozdeni hledani o $delay vterin, protoze se hleda ve forwardovanych eventech.`nAby se nestalo, ze se nejake eventy prehlednou, protoze se objevily az po prohledani daneho casoveho rozsahu daneho parametrem sleep. (Jejich TimeCreated atribut odpovida vytvoreni na puvodnim klientovi a ne kdy se objevi ve forwardedEvents logu)" + } + + $id = $s[1] + if ($id) { + $id = $id -split ',' -replace "\s+" + } + + $provider = $s[2] + if ($provider) { + $provider = $provider -split ',' -replace "\s+" + } + + $idFilter = "" + $id | ? {$_} | % { + if ($idFilter) { $idFilter += " or "} + $idFilter += "EventID=$_" + } + $providerFilter = "" + $provider | ? {$_} | % { + if ($providerFilter) { $providerFilter += " or "} + $providerFilter += "@Name=`'$_`'" + } + + $row = "`n" + $filter += $row + } + + $filter += @" +`n + +"@ + + Write-Verbose $filter + + # poznacim si spusteni skriptu, abych jej mohl pripadne po x hodinach ukoncit + $start = Get-Date + + if ($searchFrom -and $delay -and $searchFrom.AddSeconds($delay) -gt (Get-Date)) { + Write-Warning "Pockam $delay vterin kvuli moznemu zpozdeji eventu ve forwardedEvents logu a provedu prvni hledani" + Start-Sleep -Seconds $delay + } + + + # + # hledani udalosti + while (1) { + if (!$searchFrom) { + Write-Warning "Pockam $($sleep + $delay) vterin a provedu prvni hledani" + $searchFrom = Get-Date + Start-Sleep -Seconds ($sleep + $delay) + Write-Warning "Hledam.." + continue + } + + if ($stopAfter -and $start.AddHours($stopAfter) -lt (Get-Date)) { + Write-Warning "Skript uz bezi $stopAfter hodin. Ukoncuji" + break + } + + $from = $searchFrom + $to = (Get-Date).AddSeconds(-$delay) + Write-Verbose "Hledam od $(Get-Date $from -Format "HH:mm:ss") do $(Get-Date $to -Format "HH:mm:ss")" + # do searchFrom ulozim cas, po ktery ted budu hledat udalosti, abych priste od tohoto casu zacal hledat dalsi + $searchFrom = $to + # prevedu datum na format pouzivany v xml filtru event logu (vcetne nutne korekce casu o hodinu) + $from = Get-Date (Get-Date $from).AddHours(-1) -Format s + $to = Get-Date (Get-Date $to).AddHours(-1) -Format s + + # ve filtru musim pro kazdou iteraci while cyklu nastavit znovu od kdy a do kdy se maji hledat udalosti + $xmlFilter = $filter -replace "DUMBFILTERTIME", "`@SystemTime>=`'$from`' and `@SystemTime<=`'$to`'" + + Write-Verbose $xmlFilter + + $params = @{ + FilterXml = $xmlFilter + ErrorAction = "Stop" + } + if ($computerName -ne $env:COMPUTERNAME) { + $params.computerName = $computerName + } + + # najdu a vypisu udalosti odpovidajici zadani + try { + Get-WinEvent @params + } catch { + if ($_ -notmatch "^No events were found that match the specified selection criteria") { + throw $_ + } else { + Write-Verbose "Nenalezen odpovidajici event" + } + } + + Write-Warning "Cekam" + Start-Sleep -Seconds $sleep + } +} \ No newline at end of file diff --git a/Write-Log.ps1 b/Write-Log.ps1 new file mode 100644 index 0000000..44b38dc --- /dev/null +++ b/Write-Log.ps1 @@ -0,0 +1,555 @@ +Function Write-Log { + <# + .SYNOPSIS + Zapise predany text do konzole i log souboru. + + .DESCRIPTION + Zapise predany text do konzole i log souboru. + Lze jej zapsat i do systemoveho event logu (ToEventLog) ci poslat mailem (SendEmail). + Pokud log soubor prekroci velikost 5 MB, original se prejmenuje a vytvori se novy s identickym jmenem. + Podrobnosti ohledne vysledne cesty k log souboru a jeho pojmenovani v popisu parametru Path. + + TIP: + Pokud Write-Log budete volat v nejake fci s nejakymi explicitnimi parametry (napr zadanou cestou k log souboru), + muzete pro danou scope nastavit vychozi parametry teto funkce pomoci PS promenne $PSDefaultParameterValues takto: + $PSDefaultParameterValues = @{'Write-Log:Path'= '.\output.log'} + a pak se pri kazdem volani Write-Log nastavi v parametru Path hodnota '.\output.log' + + .PARAMETER Message + Text, ktery se ma vypsat. + + Pokud dojde k predani neceho jineho nez stringu, tak se provede prevod pomoci Out-String + + .PARAMETER Level + Typ zpravy. Moznosti jsou: Error, Warning, Host (Info), Verbose a Output. Dle toho, jaky Write-X se ma pouzit a jaka systomova udalost se ma zapsat do logu. + + Vychozi je Host (tedy Write-Host). + + .PARAMETER NoConsoleOut + Prepinac zpusobi, ze se zprava jen zaloguje do souboru (pripadne systemoveho logu), ale do konzole se nevypise. + + .PARAMETER ConsoleForeground + Specifikuje, jaka barva textu se ma pouzit. Da se nastavit pouze pro level = Host. + Tento level totiz pro vypis pouziva cmdlet Write-Host. + + .PARAMETER Indent + Pocet mezer, kterymi se ma odsadit text v log souboru. + + .PARAMETER Path + Cesta k souboru, do ktereho se ma zalogovat message. Napr.: C:\temp\MyLog.log + + Pokud nezadano, tak se postupuje nasledovne: + Pokud bude volano z konzole, tak bude pojmenovan: psscript.log jinak dle skriptu/modulu, ze ktereho je volano napr.: scripts.ps1.log. + Pokud je navic volano ve funkci umistene v skriptu/modulu, tak vysledne pojmenovani bude ve tvaru jmenoskriptu_jmenofunkce.log (napr. scripts.ps1_get-process.log) + + Umisti se do slozky Logs, umistene v adresari ve kterem je skript/modul, ze ktereho se volalo nebo do aktualniho pracovniho adresare (pri volani z konzole). + Pokud se nepodari ulozit do slozky volaneho skriptu, tak se ulozi do uzivatelova TEMP\Logs adresare ($env:TEMP) + + .PARAMETER OverWrite + Prepinac rikajici, ze se existujici log prepise. Defaultne se text jen prida. + + .PARAMETER ToEventLog + Prepinac rikajici, ze se ma vystup zalogovat i do systemoveho logu. + Vychozi je zapis do logu 'Application', source WSH. ID udalosti dle nastaveni Level parametru. + + .PARAMETER EventLogName + Jmeno systemoveho logu, do ktereho se ma zalogovat napr. 'System'. + + Vychozi je 'Application'. + + .PARAMETER EventSource + Source, jaky se ma pouzit pri vytvareni udalosti v systemovem logu. + Pokud takovy Source nebude existovat, tak jej skript zkusi vytvorit a pouzit. Na to vsak musi byt spusten s admin pravy! (jinak skonci chybou) + + Vychozi je 'WSH' protoze jde o jediny Source, pod kterym se da zapisovat do Application logu. + A do nej chci zapisovat, protoze typicky ma velkou velikost a udalosti v nem vydrzi i nekolik mesicu narozdil napr. od Windows Powershell logu. + Jeste lepsi by byl System log, ale do nej ne-admin nemuze zapisovat... + + .PARAMETER EventID + ID, jake se ma pouzit pri vytvareni udalosti v systemovem logu. + + Vychozi je 9999. + + .PARAMETER LogEncoding + Jak se ma kodovat obsah log souboru. + Vychozi je UTF8. + + .PARAMETER ErrorRecord + Do tohoto parametru mohu predat pouze objekt typu [System.Management.Automation.ErrorRecord] tzn. typicky zabudovanou powershell $Error promennou. + Kazdy zaznam obsazeny v $Error se rozparsuje, ulozi do souboru/posle mailem a vypise do konzole pod urovni Error + Pokud chci vypsat jen konkretni chybu, musim ji predat ve tvaru: $Error[0]. + + .PARAMETER SendEmail + Prepinac rikajici, ze se dana zprava ma poslat i emailem. + Urceno typicky pro pripady, kdy dojde k neocekavane chybe a ja to krome zalogovani ji chci i dostat mailem. + Pouzije se k tomu custom fce Send-Email (vetsina parametru Send-Email je default). + Tzn email se posle na aaa@bbb.cz z monitoring@fi.muni.cz pres relay.fi.muni.cz. + Subjekt je ve tvaru jmeno funkce, ze ktere se Write-Log volal a jmena pocitace. + Body obsahuje to co se ma zapsat do log souboru. + + .PARAMETER To + Seznam prijemcu emailu. + + Vychozi je dle nastaveni Send-Email (aaa@bbb.cz). + + .PARAMETER Subject + Subject emailu. + + Vychozi je ve tvaru "co na jakem pc" + + .PARAMETER AttachLog + Prepinac rikajici, ze se k emailu ma prilozit i log soubor, do ktereho aktualne loguji. + + .EXAMPLE + Write-Log -Message "OK" + + Zpravu vypise jak do konzole, tak do souboru. Jeho umisteni a jmeno zalezi na okolnostech viz help k parametru Path. + + .EXAMPLE + Write-Log -Message "OK" -Path C:\temp\MyLog.log -OverWrite -ForegroundColor Green + + Zpravu vypise jak do konzole (zelenou barvou), tak do C:\temp\MyLog.log. Pokud jiz existuje, tak jej prepise. + + .EXAMPLE + Write-Log -Message "Neco se nepovedlo" -Path C:\temp\MyLog.log -Level Warning + + Zpravu vypise jak do konzole (skrze Write-Warning), tak do C:\temp\MyLog.log. + + .EXAMPLE + Write-Log -Message "Objevila se chyba!" -Path C:\temp\MyLog.log -Level Error -ErrorRecord $Error[0] + + Zpravu vypise jak do konzole (skrze Write-Error), tak do C:\temp\MyLog.log a to vec tne podrobnosti ziskanych z $Error objektu. + + .EXAMPLE + Write-Log -Message "NOK" -Path C:\temp\MyLog.log -ToEventLog -Level Verbose + + Zpravu vypise jak do konzole (skrze Write-Verbose), tak do C:\temp\MyLog.log a systemoveho logu, konkretne logu Application, s ID 9999 a Source 'WSH'. + + .EXAMPLE + Write-Log -Message "NOK" -Path C:\temp\MyLog.log -SendEmail -Level Warning -AttachLog + + Zpravu vypise jak do konzole (skrze Write-Warning), tak do C:\temp\MyLog.log a posle ji emailem na aaa@bbb.cz (vychozi adresa ve fci Send-Email). + + .EXAMPLE + Write-Log -Message "NOK" -Path C:\temp\MyLog.log -SendEmail -Level Error -to sebela@fi.muni.cz + + Zpravu vypise jak do konzole (skrze Write-Error), tak do C:\temp\MyLog.log a posle ji emailem na uvedenou adresu. + + .NOTES + Pri pouziti SendEmail prepinace musi byt k dispozici custom funkce Send-Email! + #> + + [cmdletbinding()] + Param( + [Parameter(ValueFromPipeline = $True, Position = 0)] + $Message + , + [Parameter(Position = 1)] + [ValidateSet("Error", "Warning", "Host", "Output", "Verbose", "Info")] + [string] $Level = "Host" + , + [Parameter(Position = 2)] + [ValidateSet("Black", "DarkBlue", "DarkGreen", "DarkCyan", "DarkRed", "DarkMagenta", "DarkYellow", "Gray", "DarkGray", "Blue", "Green", "Cyan", "Red", "Magenta", "Yellow", "White")] + [Alias("ConsoleForeground")] + [String] $ForegroundColor = 'White' + , + [Parameter(Position = 3)] + [ValidateRange(1, 30)] + [Int16] $Indent = 0 + , + [Parameter()] + [Switch] $NoConsoleOut + , + [Parameter()] + [ValidateScript( {Test-Path $_ -IsValid})] + [ValidateScript( {$_ -match '\.\w+'})] # melo by jit o cestu k souboru, ocekavam ve tvaru .txt ci neco podobneho + [string] $Path + , + [Parameter()] + [Switch] $OverWrite + , + [Parameter()] + [Switch] $ToEventLog + , + [Parameter()] + [ValidateNotNullOrEmpty()] + [String] $EventLogName = 'Application' + , + [Parameter()] + [ValidateNotNullOrEmpty()] + [String] $EventSource = 'WSH' + , + [Parameter()] + [Int32] $EventID + , + [Parameter()] + [String] $LogEncoding = "UTF8" + , + [Parameter()] + [System.Management.Automation.ErrorRecord[]] $ErrorRecord + , + [Parameter()] + [Switch] $SendEmail + , + [Parameter()] + [string] $To + , + [Parameter()] + [string] $Subject + , + [Parameter()] + [switch] $AttachLog + ) + + Begin { + if (!$Message -and !$ErrorRecord) { + throw "Nezadali jste ani message ani ErrorRecord!" + } + + # pokud bude omylem predano neco jineho nez string (typicky objekt), tak prevedu + if ($Message -and $Message.gettype() -ne 'String') { + $Message = $Message | Out-String + } + + if (($To -or $Subject -or $AttachLog) -and !$SendEmail) { + throw "Zadali jste to, subject ci attachlog, ale nezadali SendEmail. Nedava smysl" + } + + # odvozeni pojmenovani logu z volaneho skriptu/funkce/modulu + if (!$Path) { + # + # ziskam umisteni log souboru + # + + # jmeno sebe sama (Write-Log) + $MyName = $MyInvocation.MyCommand.Name + # poznacim si, ze cestu k logu sestavuji ja, nebyla zadana uzivatelem + ++ $calculatedPath + + # co zavolalo Write-Log (ze seznamu volajicich odeberu jmeno teto funkce (Write-Log) a + # a vyberu uplne posledniho volajiciho + $lastCaller = Get-PSCallStack | where {$_.Command -ne $MyName -and $_.command -ne ""} | select -Last 1 + + # zkusim zjistit cestu skriptu/modulu odkud je write-log volano + $scriptName = ($lastCaller).scriptName + if ($scriptName) { + try { $ScriptDirectory = Split-Path $scriptName -Parent -ea Stop } catch {} + } + # neni volano ze skriptu/modulu pouziji cestu aktualniho pracovniho adresare + if (!$ScriptDirectory) { $ScriptDirectory = (Get-Location).path } + # nepodarilo se ziskat aktualni pracovni adresar, ulozim do slozky kde je umistena tato funkce + if (!$ScriptDirectory) { $ScriptDirectory = $PSScriptRoot } + + # + # ziskam pojmenovani log souboru + # + + # vychozi pojmenovani logu, pokud nenajdu presnejsi pojmenovani + $CallerName = 'psscript' + # volam li write-log z nadrazene fce, tak jeji jmeno pouziji pro pojmenovani log souboru + if ($lastCaller) { + # jmeno funkce/skriptu/modulu, ze ktereho se Write-Log zavolalo + $CallerName = $lastCaller | select -exp command -Last 1 + $command = $lastCaller.command + + # volano ze souboru + if ($scriptName) { + $filename = (split-path $scriptName -Leaf) + if ($command -ne $filename) { + $CallerName = $($filename -replace "\.\w+$") + '_' + $command # odeberu koncovku souboru + } else { + $CallerName = $command + } + } else { + # volano z konzole/funkce + $CallerName = $command + } + } + + # aby pri volani z konzole nelogovalo do Write-Log.log + if (!$CallerName -or $CallerName -eq $MyName) { + $CallerName = 'psscript' + } + + $Path = Join-Path $ScriptDirectory "Logs\$CallerName.log" + } + + # hodnotu 'Host' pouzivam pouze aby bylo zrejme, ze se pouzije Write-Host, pozdeji s hodnotou Level ale pracuji a vic se mi hodi 'Info' + if ($Level -eq 'Host') { $Level = 'Info' } + } + + Process { + try { + + # + # vypisu do konzole + # + + $ErrorActionPreference = 'continue' + + if ($NoConsoleOut -eq $False) { + if ($Message) { + switch ($Level) { + 'Error' { Write-Error $Message } + 'Warning' { Write-Warning $Message } + 'Info' { Write-Host $Message -ForegroundColor $ForegroundColor -NoNewline} + 'Output' { Write-Output $Message } + 'Verbose' { Write-Verbose $Message } + } + } + + $ErrorActionPreference = 'stop' # musi byt az za Write-Error jinak se ukoncil tento try blok :) + + # vypisi i predane errory + # nevypisuji pomoci write-host abych $Error neplnil duplicitnimi chybami + if ($ErrorRecord -and $Message) { + # vypisu jen text chyby + $ErrorRecord | % { Write-Output $('{0}, {1}' -f $_.Exception.Message, $_.FullyQualifiedErrorId) } + } elseif ($ErrorRecord -and !$Message) { + # vypisu krome chyby i uvodni text + $ErrorRecord | % { Write-Output $('{0}{1}, {2}' -f "Objevily se chyby:`n", $_.Exception.Message, $_.FullyQualifiedErrorId) } + } + } + + # + # zapisu text do log souboru + # + + # vytvoreni finalniho textu, ktery se zapise do log souboru a pripadne i posle mailem + $Message = $Message.TrimEnd() + if ($Message -and $Message.contains("`n")) { + # vsechny radky message budou odsazeny, ne jen prvni + $Message = @($Message -split "`n") + # prvni radek + $msg = '{0}{1} : {2} : {3}' -f (" " * $Indent), (Get-Date -Format s), $Level.ToUpper(), $Message[0] + # nasledujici radky message + for ($i = 1; $i -le $Message.count; $i++) { + $msg += '{0}{1}{2}' -f "`n", (" " * $Indent), $Message[$i] + } + } else { + # message neni viceradkovy + $msg = '{0}{1} : {2} : {3}' -f (" " * $Indent), (Get-Date -Format s), $Level.ToUpper(), $Message + } + + # pokud predal i $Error objekt, tak jej rozparsuji a pridam do vypisu + if ($ErrorRecord) { + $ErrorRecord | % { + $msg += "`r`n" + '{0}{1} : {2} : {3}: {4}:{5} char:{6}' -f (" " * $Indent), (Get-Date -Format s), 'ERROR', $_.Exception.Message, + $_.FullyQualifiedErrorId, + $_.InvocationInfo.ScriptName, + $_.InvocationInfo.ScriptLineNumber, + $_.InvocationInfo.OffsetInLine + } + } + + # mutex kvuli ochrane pred chybami pri pokusu o simultani zapis vice procesu do stejneho souboru + try { + $mutex = New-Object -TypeName 'Threading.Mutex' -ArgumentList $false, 'MyInterprocMutex' -ErrorAction Stop + } catch { + # uz se mi stalo, ze koncilo chybou Access Denied, ale s jinym jmenem mutexu vytvorit slo + $mutex = New-Object -TypeName 'Threading.Mutex' -ArgumentList $false, 'MyInterprocMutex2' + } + + $CommandParameters = @{ + FilePath = $Path + Encoding = $LogEncoding + ErrorAction = 'stop' + } + + if ($OverWrite) { + $CommandParameters.Add("Force", $true) + } else { + $CommandParameters.Add("Append", $true) + } + + # vytvoreni log souboru + # pokud cestu nezadal uzivatel, mel by se log zapsat do adresare se skriptem, kam ale nemusi mit uzivatel pravo zapisu + # v tom pripade zkusim zapsat log do uzivatelova temp adresare (catch blok) + if ($calculatedPath) { + # uzivatel nezadal cestu k logu, zkousim ulozit v adresari se skriptem, ze ktereho se Write-Log volalo, v pripade chyby zkusim vytvorit log jeste v $env:TEMP + $ErrorActionPreference = 'silentlycontinue' + } else { + # uzivatel zadal cestu k log souboru, pokud nepujde ulozit, tak nebudu zkouset jej ulozit jinam + $ErrorActionPreference = 'stop' + } + + # pokud se ma log ulozit do dosud neexistujiciho adresare, zkusim jej vytvorit + [Void][System.IO.Directory]::CreateDirectory($(Split-Path $Path -Parent)) + if (!(Test-Path $Path)) { + New-Item -Path $Path -ItemType File -Force | Out-Null + } elseif (!$OverWrite) { + # log soubor jiz existuje a nema se prepsat + ++$Exists + } + + # overim, ze mam pravo zapisu + if (Test-Path $Path) { + try { + Write-Verbose "Cesta $Path existuje, otestuji jestli mam write pravo" + $oldErrPref = $ErrorActionPreference + $ErrorActionPreference = 'stop' + [io.file]::OpenWrite($Path).close() # bacha pokud by soubor neexistoval, tak jej vytvori + } catch { + Write-Verbose "nemam" + ++$accessDenied + } + $ErrorActionPreference = $oldErrPref + } + $ErrorActionPreference = 'stop' + + if (!(Test-Path $Path) -or $accessDenied) { + Write-Verbose "Log se nepodarilo ulozit do $Path, zkusim to do $env:TEMP\Logs" + # nepodarilo se vytvorit log v adresari se skriptem, ze ktereho byl spusten + # zkusim to znovu, ale s cestou do uzivatelova TEMP adresare, kam by mel mit pravo zapisu + $calculatedLogName = Split-Path $Path -leaf + $Path = Join-Path "$env:TEMP\Logs" $calculatedLogName + + # vytvorim v danem adresari slozku Logs, do ktere pote ulozim vysledny log soubor + [Void][System.IO.Directory]::CreateDirectory($(Split-Path $Path -Parent)) + + $CommandParameters.FilePath = $Path + + if (!(Test-Path $Path -ErrorAction SilentlyContinue)) { + New-Item -Path $Path -ItemType File -Force | Out-Null + } elseif (!$OverWrite) { + # log soubor jiz existuje a nema se prepsat + ++$Exists + } + } + + # pockam az bude mozne do souboru zapisovat + Write-Verbose "Pockam nez se uvolni pripadny lock na souboru" + try { + $mutex.waitone() | Out-Null + } catch { + Write-Verbose "nepovedlo se" + } + # log je jiz prilis velky, prejmenuji jej a vytvorim novy se stejnym jmenem + # (aby logy nerostly neumerne a nebyl problem s jejich ctenim/posilani mailem) + if ($Exists -and ((Get-Item $Path).Length / 1MB -gt 5)) { + Rename-Item $Path ((Get-Item $Path).BaseName + '_' + (Get-Date -Format yyyyMMddHHmm) + (Get-Item $Path).Extension) + } + # zapisu info do logu + Write-Verbose "zapisuji" + $msg | Out-File @CommandParameters + $mutex.ReleaseMutex() | Out-Null + + # zkontroluji ze se povedlo zapsat do log souboru + if (!(Test-Path $Path -ErrorAction SilentlyContinue)) { + throw "Nepovedlo se vytvorit/zapsat do log souboru $path" + } + + + # + # zapsani do event logu + # + if ($ToEventLog) { + # otestuji existenci zadaneho Source (ktery jakoze vytvari ten zaznam) + if (-not [Diagnostics.EventLog]::SourceExists($EventSource)) { + # neexistuje, tak otestuji jestli mam pravo jej vytvorit + if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + throw "Bez admin prav neni mozne vytvaret EventSource. Zadejte existujici EventSource nebo spustte s admin pravy." + } + # vytvorim zadany Source + [Diagnostics.EventLog]::CreateEventSource($EventSource, $EventLogName) + } + + # dle zadane urovne nastavim pomocne promenne pro pouziti k zapisu do systemoveho logu + switch ($Level) { + "Error" { $lvl = 1; $entryType = 'Error' } + "Warning" { $lvl = 2; $entryType = 'Warning' } + "Info" { $lvl = 4; $entryType = 'Information' } + "Output" { $lvl = 4; $entryType = 'Information' } + "Verbose" { $lvl = 4; $entryType = 'Information' } + } + + # uzivatel nezadal source, pod kterym se ma event zapsat + # zapisu do aplikacniho logu pod source WSH a ID dle typu udalosti + if ($EventSource -eq 'WSH') { + # vytvorim objekt wscript shellu + $WSH = New-Object -com WScript.Shell + + # zapisu udalost do application systemoveho logu kde zdrojem je WSH (wscript shell), jde o jediny source, pod kterym muze do application zapisovat bezny uzivatel + # navic takto zalozka General v event logu u daneho eventu bude obsahovat pouze predany message a ne kecy typu "The description for Event ID 4 from source WSH cannot be found...." + $WSH.LogEvent($lvl, $msg.TrimStart()) | out-null + + # zaloguji i predane chyby + if ($ErrorRecord) { + $WSH.LogEvent(1, $(($ErrorRecord | Out-String).TrimStart())) | out-null + } + } else { + # uzivatel zmenil source, pod kterym se ma event zapsat + + # uzivatel nezadal EventID, nastavim vlastni + if (!$EventID) { $EventID = 9999 } + + # tento postup by mel fungovat i na systemech, kde neni write-eventlog cmdlet + # vytvorim objekt systemove udalosti + $log = New-Object System.Diagnostics.EventLog + $log.set_log($EventLogName) + $log.set_source($EventSource) + # zapisu udalost do systemoveho logu + $log.WriteEntry($Message, $entryType, $EventID) + + # zaloguji i predane chyby + if ($ErrorRecord) { + $log.WriteEntry($(($ErrorRecord | Out-String).TrimStart()), 'Error', $EventID) + } + + if (! $?) { + # byla chyba + throw $error[0] + } + } + } + + + # + # poslani emailem + # + if ($SendEmail) { + if (! (Get-Command Send-Email -ea SilentlyContinue)) { + throw "Neni k dispozici prikaz Send-Email pro poslani emailove zpravy!" + } + + # nastavim parametry odeslani emailu + # zamerne neumoznuji nic moc nastavit, aby tvar emailu byl standardizovan kvuli prehlednosti + if (!$Subject) { + $Subject = "$CallerName na $($env:COMPUTERNAME)" + } + $Params = @{ + Subject = $Subject + Body = $msg.TrimStart() # pripadny indent zde nema smysl + } + + # ma se prilozit i log soubor + if ($AttachLog) { + <# kontroly velikosti nejsou potreba, protoze pri prekroceni velikosti 5MB vytvorim novy log soubor, pokud bych zrusil, tak odkomentovat + $LogSize = (Get-Item $Path).length/1MB + $MaxSize = 5 + # emaily mohou obsahovat jen omezenou velikost souboru, pro jistotu povoluji poslat max 5MB soubory (lepsi aby email prisel bez logu nez vubec) + if ($LogSize -le $MaxSize) { + #> + $Params.Add("Attachment", $Path) + + <# + } else { + # log soubor je prilis velky pro poslani emailem + # informuji o tom prijemce + $Params.Body += "`n`n`nMel se poslat i log soubor, ale je prilis velky. Najdete jej zde: $Path." + } + #> + } + + # uzivatel explicitne zadal, komu se ma email poslat + if ($To) { $Params.Add("To", $To) } + + Send-Email @params -ea stop + } + } catch { + throw "Objevila se chyba: $_." + } + } #End Process + + End {} +} \ No newline at end of file