master c8b39d0b1f9c cached
143 files
6.1 MB
1.6M tokens
15 symbols
1 requests
Download .txt
Showing preview only (6,440K chars total). Download the full file or copy to clipboard to get everything.
Repository: ztrhgf/useful_powershell_functions
Branch: master
Commit: c8b39d0b1f9c
Files: 143
Total size: 6.1 MB

Directory structure:
gitextract_uv8sk41q/

├── Active Directory/
│   ├── Get-ADGroupMemberAddDate.ps1
│   ├── Get-ADGroupMemberChangesHistory.ps1
│   ├── Get-ADGroupMemberRecursive.ps1
│   ├── Invoke-ADPasswordsAudit.ps1
│   └── Reset-KrbtgtAccount.ps1
├── Azure/
│   ├── Add-AzureADAppUserConsent.ps1
│   └── Get-AzureDevOpsOrganizationOverview.ps1
├── CHANGELOG.md
├── Confluence/
│   └── fill_confluence_table_with_aws_dns_records.ps1
├── ConvertFrom-HTMLTable.ps1
├── ConvertFrom-XML.ps1
├── Copy-Item2.ps1
├── Export-ScheduledTask.ps1
├── Export-ScriptsToModule.ps1
├── FTP/
│   └── install_FTP_server.ps1
├── Get-AccountFromSID.ps1
├── Get-AdministrativeEvents.ps1
├── Get-ComputerInfo.ps1
├── Get-CurrentLoad.ps1
├── Get-FailedScheduledTask.ps1
├── Get-FirewallLog.ps1
├── Get-FirewallRules.ps1
├── Get-FolderSize.ps1
├── Get-InstalledSoftware.ps1
├── Get-LogOnOff.ps1
├── Get-LoggedOnUser.ps1
├── Get-NetworkCapture.ps1
├── Get-PSLog.ps1
├── Get-PendingReboot.ps1
├── Get-ReliabilityHistory.ps1
├── Get-SFCLogEvent.ps1
├── Get-SIDFromAccount.ps1
├── Get-Shutdown.ps1
├── Get-Uptime.ps1
├── Get-WinEventArchivedIncluded.ps1
├── INTUNE/
│   ├── Connect-Graph.ps1
│   ├── ConvertFrom-MDMDiagReport.ps1
│   ├── ConvertFrom-MDMDiagReportXML.ps1
│   ├── Get-ClientIntunePolicyResult.ps1
│   ├── Get-ClientStatusReport.ps1
│   ├── Get-IntuneDeviceComplianceStatus.ps1
│   ├── Get-IntuneEnrollmentStatus.ps1
│   ├── Get-IntuneLog.ps1
│   ├── Get-IntuneOverallComplianceStatus.ps1
│   ├── Get-IntuneReport.ps1
│   ├── Get-MDMClientData.ps1
│   ├── Invoke-IntuneScriptRedeploy.ps1
│   ├── Invoke-IntuneWin32AppRedeploy.ps1
│   ├── Invoke-MDMReenrollment.ps1
│   ├── New-GraphAPIAuthHeader.ps1
│   ├── Reset-HybridADJoin.ps1
│   ├── Reset-IntuneEnrollment.ps1
│   └── Win32App/
│       └── SetBitLockerPin/
│           ├── BitlockerIsEnabledAndNotSet.ps1
│           ├── DetectBitLockerPin.ps1
│           ├── Popup.ps1
│           └── SetBitLockerPin.ps1
├── Invoke-AsLoggedUser.ps1
├── Invoke-AsSystem.ps1
├── Invoke-Command2.ps1
├── Invoke-NetworkCapture.ps1
├── JIRA/
│   └── New-JIRATicket.ps1
├── OSD/
│   ├── OSDComputerName_via_ServiceTag/
│   │   └── Set-CMTSStep_ServiceTag2OSDComputerName.ps1
│   └── OfflineDomainJoin/
│       └── Set-CMDeviceDJoinBlobVariable.ps1
├── Quote-String.ps1
├── README.md
├── Read-FromClipboard.ps1
├── SCCM/
│   ├── Connect-SCCM.ps1
│   ├── Get-CMLog.ps1
│   ├── Invoke-CMAdminServiceQuery.ps1
│   ├── Invoke-CMComplianceEvaluation.ps1
│   └── Update-CMClientPolicy.ps1
├── SCVMM/
│   └── New-VMFromTemplate.ps1
├── Search-ADObjectACL.ps1
├── Search-GPOSetting.ps1
├── Shutdown-Computer.ps1
├── Start-TCPPortListener.ps1
├── Test-Connection2.ps1
├── Test-Path2.ps1
├── Test-Port.ps1
├── Unlock-File.ps1
├── Watch-EventLog.ps1
├── Write-Log.ps1
├── environmental variables/
│   ├── Get-EnvVariable.ps1
│   ├── Remove-EnvVariable.ps1
│   └── Set-EnvVariable.ps1
├── event subscriptions/
│   ├── Get-EventSubscription.ps1
│   ├── Get-EventSubscriptionStatus.ps1
│   ├── New-EventSubscription.ps1
│   ├── Remove-EventSubscription.ps1
│   └── Set-EventSubscription.ps1
└── modules/
    ├── AdmPwd.PS/
    │   ├── AdmPwd.PS.format.ps1xml
    │   ├── AdmPwd.PS.psd1
    │   └── en-US/
    │       └── AdmPwd.PS.dll-Help.xml
    ├── AutoItX/
    │   └── AutoItX.psd1
    ├── ConfluencePS/
    │   └── 2.5.0/
    │       ├── CHANGELOG.md
    │       ├── ConfluencePS.Types.cs
    │       ├── ConfluencePS.format.ps1xml
    │       ├── ConfluencePS.psd1
    │       ├── ConfluencePS.psm1
    │       ├── LICENSE
    │       ├── PSGetModuleInfo.xml
    │       ├── README.md
    │       └── en-US/
    │           ├── ConfluencePS-help.xml
    │           └── about_ConfluencePS.help.txt
    ├── PSScriptAnalyzer/
    │   └── 1.17.1/
    │       ├── PSScriptAnalyzer.cat
    │       ├── PSScriptAnalyzer.psd1
    │       ├── PSScriptAnalyzer.psm1
    │       ├── ScriptAnalyzer.format.ps1xml
    │       ├── ScriptAnalyzer.types.ps1xml
    │       └── Settings/
    │           ├── CmdletDesign.psd1
    │           ├── CodeFormatting.psd1
    │           ├── CodeFormattingAllman.psd1
    │           ├── CodeFormattingOTBS.psd1
    │           ├── CodeFormattingStroustrup.psd1
    │           ├── DSC.psd1
    │           ├── PSGallery.psd1
    │           ├── ScriptFunctions.psd1
    │           ├── ScriptSecurity.psd1
    │           ├── ScriptingStyle.psd1
    │           ├── core-6.0.2-linux.json
    │           ├── core-6.0.2-macos.json
    │           ├── core-6.0.2-windows.json
    │           ├── desktop-3.0-windows.json
    │           ├── desktop-4.0-windows.json
    │           └── desktop-5.1.14393.206-windows.json
    ├── SplitPipeline/
    │   ├── LICENSE.txt
    │   ├── README.htm
    │   ├── Release-Notes.htm
    │   ├── SplitPipeline.nuspec
    │   ├── SplitPipeline.psd1
    │   ├── [Content_Types].xml
    │   ├── _rels/
    │   │   └── .rels
    │   ├── en-US/
    │   │   ├── SplitPipeline.dll-Help.xml
    │   │   └── about_SplitPipeline.help.txt
    │   ├── package/
    │   │   └── services/
    │   │       └── metadata/
    │   │           └── core-properties/
    │   │               └── 9ffd50a1389e4b6d8b6c79579467ccd3.psmdcp
    │   └── tools/
    │       └── SplitPipeline/
    │           ├── LICENSE.txt
    │           ├── README.htm
    │           ├── Release-Notes.htm
    │           ├── SplitPipeline.psd1
    │           └── en-US/
    │               ├── SplitPipeline.dll-Help.xml
    │               └── about_SplitPipeline.help.txt
    └── psasync/
        ├── psasync.psd1
        └── psasync.psm1

================================================
FILE CONTENTS
================================================

================================================
FILE: Active Directory/Get-ADGroupMemberAddDate.ps1
================================================
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
}

================================================
FILE: Active Directory/Get-ADGroupMemberChangesHistory.ps1
================================================
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
        }
    }
}

================================================
FILE: Active Directory/Get-ADGroupMemberRecursive.ps1
================================================
function Get-ADGroupMemberRecursive {
    <#
    .SYNOPSIS
    Function for outputting members login (samAccountName) of given AD group and its nested groups.
    By default output only users.

    .DESCRIPTION
    Function for outputting members login (samAccountName) of given AD group and its nested groups.
    By default output only users.

    Does not need AD module.

    .PARAMETER name
    AD group name.

    .PARAMETER distinguishedName
    AD group distinguishedName.

    .PARAMETER justGroup
    Instead of member users, returns name of members groups.

    .PARAMETER userAndGroup
    Outputs member users and groups.

    .EXAMPLE
    Get-ADGroupMemberRecursive 'domain admins'

    Returns samAccountName of members of given AD group.

    .EXAMPLE
    Get-ADGroupMemberRecursive "CN=Domain Admins, CN=Users, DC=master, DC=contoso, DC=com"

    Returns samAccountName of members of given AD group.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param (
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Default")]
        [ValidateScript( {
                If ($_ -match "=") {
                    throw "$_ is in DN format, use regular AD group name"
                } else {
                    $true
                }
            })]
        [ArgumentCompleter( {
                param ($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams)

                $ADDN = ([ADSI]"LDAP://RootDSE").rootDomainNamingContext
                $searcher = [adsisearcher]"(objectCategory=group)"
                $searcher.PageSize = 500
                $searcher.PropertiesToLoad.AddRange('name')
                $searcher.searchRoot = [adsi]"LDAP://$ADDN"
                ($searcher.FindAll() | ? { $_.properties.name -like "*$WordToComplete*" }).properties.name
                $searcher.Dispose()
            })]
        [string] $name
        ,
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "DN")]
        [ValidateScript( {
                If ($_ -match "^CN=") {
                    $true
                } else {
                    throw "$_ is not in DN format (example: CN=Domain Admins, CN=Users, DC=contoso, DC=com)"
                }
            })]
        [string] $distinguishedName
        ,
        [switch] $justGroup
        ,
        [switch] $userAndGroup
    )

    (Get-Variable name).Attributes.Clear()
    (Get-Variable distinguishedName).Attributes.Clear()

    if ($env:USERDOMAIN -eq $env:COMPUTERNAME) {
        throw "Run under domain user. Local users does not have right to query AD"
    }

    if ($justGroup -and $userAndGroup) {
        throw "You cannot use both justGroup and userAndGroup"
    }

    if ($name) {
        $ADDN = ([ADSI]"LDAP://RootDSE").rootDomainNamingContext
        $distinguishedName = (New-Object System.DirectoryServices.DirectorySearcher((New-Object System.DirectoryServices.DirectoryEntry("LDAP://$ADDN")) , "(&(objectCategory=group)(cn=$name))")).FindAll() | ForEach-Object { $_.Properties.distinguishedname }
        Write-Verbose "Name $name was translated to DN $distinguishedName"
        if (!$distinguishedName) {
            Write-Warning "Group with name $name doesn't exist."
            return
        }
    }

    $adobject = [adsi]"LDAP://$distinguishedName"
    if ($adobject.properties) {
        $adobject.properties.item("member") | % {
            $objMembermod = $_.replace("/", "\/")
            $objAD = [adsi]"LDAP://$objmembermod"
            $attObjClass = $objAD.properties.item("objectClass")
            if ($attObjClass -eq "group") {
                Write-Verbose "$($objAD.name) is group"
                if ($justGroup -or $userAndGroup) {
                    $objAD.name
                }

                $params = $PSBoundParameters
                $null = $params.remove("name")
                $params.distinguishedName = $_
                Get-ADGroupMemberRecursive @params
            } else {
                Write-Verbose "$($objAD.name) is account"
                if (!($justGroup) -or $userAndGroup) {
                    $objAD.sAMAccountName
                }
            }
        }
    } else {
        Write-Warning "Group with DN $distinguishedName doesn't exist."
        return
    }
}



================================================
FILE: Active Directory/Invoke-ADPasswordsAudit.ps1
================================================
function Invoke-ADPasswordsAudit {
    <#
    .SYNOPSIS
    Function for offline audit of AD user passwords using DSinternals and haveibeenpwnd password database.

    .DESCRIPTION
    Function for offline audit of AD user passwords using DSinternals and haveibeenpwnd password database.

    Function will:
    - check if there is newer version of haveibeenpwnd database
    - export ntds.dit and syskey on random DC
    - creates VM on node core-01 (without network connection)
    - to cluster node core-01 copy
        - exported ntds and syskey
        - DSinternals and haveibeenpwnd database
    - delete ntds.dit and syskey on DC
    - from cluster node to VM copy
        - exported ntds and syskey
        - DSinternals and haveibeenpwnd database
    - delete this copied data on cluster node
        - ntds.dit and syskey
        - deprecated haveibeenpwnd databases
    - run AD audit inside the VM
    - get the results
    - remove VM

    Result will be outputted to console and saved to resultDestination as txt file.

    .PARAMETER pwnedPasswordsNTLMOrdered
    Path to downloaded database of haveibeenpwnd password hashes.
    Download it from https://haveibeenpwned.com/Passwords and !always! pick NTLM ordered by hash version!

    .PARAMETER weakPasswordsFile
    Path to txt file with your custom plaintext passwords to check.
    Each on new line!

    .PARAMETER DSInternals
    Path to DSInternals PS module.
    If not specified but found on system, uses that path.

    .PARAMETER VMName
    Name of VM, that will be created and where the magic happens.

    .PARAMETER VMHostName
    Name of Hyper-V cluster node, which will host the VM.

    .PARAMETER VMMServerName
    Name of your SCVMM server.

    .PARAMETER resultDestination
    Path, where the results should be saved in txt form.

    .EXAMPLE
    Invoke-ADPasswordsAudit -pwnedPasswordsNTLMOrdered "C:\AD_audit\pwned-passwords-ntlm-ordered-by-hash-v7.txt" -DSInternals "C:\AD_audit\modules\DSInternals"

    Check passwords of all AD users against pwned database.

    .EXAMPLE
    Invoke-ADPasswordsAudit -pwnedPasswordsNTLMOrdered "C:\AD_audit\pwned-passwords-ntlm-ordered-by-hash-v7.txt" -weakPasswordsFile "C:\AD_audit\weakPasswordsFile.txt" -DSInternals "C:\AD_audit\DSInternals"

    Check passwords of all AD users against pwned database and weakPasswordsFile.txt.
    #>

    [Alias("Check-ADPasswords")]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateScript( {
                If ($_ -match "pwned-passwords-ntlm-ordered-by-hash.+\.txt" -and (Test-Path -Path $_ -PathType Leaf)) {
                    $true
                } else {
                    Throw "$_ is not a path to pwned-passwords-ntlm-ordered-by-hash-v5.txt file"
                }
            })]
        [string] $pwnedPasswordsNTLMOrdered
        ,
        [ValidateScript( {
                If ($_ -match "weakPasswordsFile\.txt$" -and (Test-Path -Path $_ -PathType Leaf)) {
                    $true
                } else {
                    Throw "$_ is not a path to existing weakPasswordsFile.txt file"
                }
            })]
        [string] $weakPasswordsFile
        ,
        [ValidateScript( {
                If ($_ -match "DSInternals" -and (Test-Path -Path $_ -PathType Container)) {
                    $true
                } else {
                    Throw "$_ is not a path to PS module DSInternals"
                }
            })]
        [string] $DSInternals = (Get-Module "DSInternals" -ListAvailable | select -exp ModuleBase)
        ,
        [string] $VMName = "AD_audit"
        ,
        [Parameter(Mandatory = $true)]
        [string] $VMHostName = (Read-Host "Enter name of one of your Hyper-V cluster servers")
        ,
        [Parameter(Mandatory = $true)]
        [string] $VMMServerName
        ,
        [ValidateScript( {
                If (Test-Path -Path $_ -PathType Container) {
                    $true
                } else {
                    Throw "$_ doesn't exists"
                }
            })]
        [string] $resultDestination = ""
    )

    BEGIN {
        If ((Invoke-Command -ComputerName $VMMServerName -arg $VMName { Get-SCVirtualMachine $args[0] })) {
            Throw "VM $VMName already exists"
        }

        #
        #region functions
        #
        function Remove-ItemSecure {
            <#
            .SYNOPSIS
            Function for secure overwrite and deletion of file(s).
            It will overwrite file(s) in a secure way by using a cryptographically strong sequence of random values using .NET functions.

            .DESCRIPTION
            Function for secure overwrite and deletion of file(s).
            It will overwrite file(s) in a secure way by using a cryptographically strong sequence of random values using .NET functions.

            .PARAMETER item
            Path to file or folder that should be securely deleted.

            .EXAMPLE
            Remove-FileSecure C:\temp\passwords.txt

            Securely overwrite content of passwords.txt and than delete it.

            .EXAMPLE
            Remove-FileSecure C:\temp

            Securely overwrite all files in C:\temp and than delete whole folder.

            .EXAMPLE
            Get-ChildItem $path -Filter *.txt | Remove-FileSecure

            Securely overwrite all txt files in given folder and than delete them.

            .NOTES
            https://gallery.technet.microsoft.com/scriptcenter/Secure-File-Remove-by-110adb68
            #>

            [CmdletBinding()]
            [Alias("Remove-FileSecure")]
            param (
                [Parameter(Mandatory = $true)]
                [ValidateScript( {
                        If (Test-Path -Path $_) {
                            $true
                        } else {
                            Throw "$_ doesn't exist"
                        }
                    })]
                [string] $item
            )

            function _Remove-FileSecure {
                <#
                .SYNOPSIS
                Function for secure overwrite and deletion of file(s).
                It will overwrite file(s) in a secure way by using a cryptographically strong sequence of random values using .NET functions.

                .DESCRIPTION
                Function for secure overwrite and deletion of file(s).
                It will overwrite file(s) in a secure way by using a cryptographically strong sequence of random values using .NET functions.

                .PARAMETER File
                Path to file that should be overwritten.

                .OUTPUTS
                Boolean. True if successful else False.

                .NOTES
                https://gallery.technet.microsoft.com/scriptcenter/Secure-File-Remove-by-110adb68
                #>

                [CmdletBinding()]
                [OutputType([boolean])]
                param(
                    [Parameter(Mandatory = $true, ValueFromPipeline = $true )]
                    [System.IO.FileInfo] $File
                )

                BEGIN {
                    $r = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
                }

                PROCESS {
                    $retObj = $null

                    if ((Test-Path $file -PathType Leaf) -and $pscmdlet.ShouldProcess($file)) {
                        $f = $file
                        if ( !($f -is [System.IO.FileInfo]) ) {
                            $f = New-Object System.IO.FileInfo($file)
                        }

                        $l = $f.length

                        $s = $f.OpenWrite()

                        try {
                            $w = New-Object system.diagnostics.stopwatch
                            $w.Start()

                            Write-Progress -Activity $f.FullName -Status "Write" -PercentComplete 0 -CurrentOperation ""

                            [long]$i = 0
                            $b = New-Object byte[](1024 * 1024)
                            while ( $i -lt $l ) {
                                $r.GetBytes($b)

                                $rest = $l - $i

                                if ( $rest -gt (1024 * 1024) ) {
                                    $s.Write($b, 0, $b.length)
                                    $i += $b.LongLength
                                } else {
                                    $s.Write($b, 0, $rest)
                                    $i += $rest
                                }

                                [double]$p = [double]$i / [double]$l

                                [long]$remaining = [double]$w.ElapsedMilliseconds / $p - [double]$w.ElapsedMilliseconds

                                Write-Progress -Activity $f.FullName -Status "Write" -PercentComplete ($p * 100) -CurrentOperation "" -SecondsRemaining ($remaining / 1000)
                            }
                            $w.Stop()
                        } finally {
                            $s.Close()

                            $null = Remove-Item $f.FullName -Force -Confirm:$false -ErrorAction Stop
                        }
                    } else {
                        Write-Warning "$($f.FullName) wasn't found"
                        return $false
                    }

                    return $true
                }
            }

            if ((Get-Item $item).PSIsContainer) {
                # is directory
                # remove files securely
                Get-ChildItem $item -Recurse -File | % {
                    $ok = _Remove-FileSecure $_.FullName
                    if (!$ok) {
                        throw "Secure deletion of $($_.FullName) failed"
                    }
                }
                # remove the folder itself
                Remove-Item $item -Recurse -Force -Confirm:$false
            } else {
                # is file

                # remove file securely
                $ok = _Remove-FileSecure $item
                if (!$ok) {
                    throw "Secure deletion of $item failed"
                }
            }
        }
        #endregion functions

        $allFunctionDefs = "function Remove-ItemSecure { ${function:Remove-ItemSecure} }"

        # generate VM Administrator credentials, that will be used to create VM and than connect to it
        # (when creating VM, just password will be used, because login will be always Administrator)
        $u = "$VMName\Administrator"
        [securestring] $p = ConvertTo-SecureString (Generate-Password -length 30 -outputToConsole -ea stop) -AsPlainText -Force
        [pscredential] $VMadminCredential = New-Object System.Management.Automation.PSCredential ($u, $p)

        # check invoker permissions
        $domainAdmins = Get-ADGroupMemberRecursive -name "Domain Admins"
        if ($env:USERNAME -notin $domainAdmins) {
            Throw "Insufficient rights. Run as Domain Admin."
        }

        # check newest password database from haveibennpwnd
        $url = "https://haveibeenpwned.com/Passwords"
        $web = Invoke-WebRequest $url
        $webPwndVer = $web.ParsedHtml.getelementsbytagname('a') | ? { $_.textContent -match "cloudflare" -and $_.nameProp -match "pwned-passwords-ntlm-ordered-by-hash" } | select -exp nameProp
        $webPwndVer = [System.IO.Path]::GetFileNameWithoutExtension($webPwndVer)
        $usedPwndVer = [System.IO.Path]::GetFileNameWithoutExtension($pwnedPasswordsNTLMOrdered)
        if ($webPwndVer -and $usedPwndVer -notmatch [regex]::Escape($webPwndVer)) {
            throw "On $url is newer version on pwnedPasswordsNTLMOrdered database ($webPwndVer). Download and use NTLM (ordered by hash) version!"
        }

        # path where data will be saved (on DC, cluster node even VM)
        $IFMPath = "C:\Windows\x89u111k890dnkfdjk3o2hrnfds9"
        $DC = ((Get-ADDomain | Select-Object -exp PDCEmulator) -split "\.")[0] # to get DC with PDCEmulator role, because why not
        Write-Warning "In case this function will end unexpectedly, make sure that folder '$IFMPath' is deleted on $DC, $VMHostName and VM $VMName on SCVMM server!`nUse function Remove-ItemSecure for secure deletion."
    }

    PROCESS {
        #
        #region export ntds and syskey on DC
        #

        "Exporting NTDS and syskey on $DC"

        Invoke-Command $DC {
            param ($IFMPath)

            if (Test-Path $IFMPath -ea SilentlyContinue) { Remove-Item $IFMPath -Recurse -Force }

            $IFM = ntdsutil "activate instance ntds" ifm "create full `"$IFMPath`"" q q

            if (!($IFM -like "*IFM media created successfully*")) {
                Remove-Item $IFMPath -Recurse -Force # just for sure
                throw "Export failed`n`n$IFM"
            }
        } -ArgumentList $IFMPath
        #endregion export ntds and syskey on DC

        #
        #region create VM (nondomain, without network, with local admin)
        #
        "Creating VM $VMName (in background)"
        TODO
        $null = New-VMFromTemplate -VMName $VMName -VMHostName $VMHostName -VMAdminPass $VMadminCredential -JoinWorkgroup -NoNetwork -asJob -Tier 0
        #endregion create VM

        #
        #region copy ntds to cluster node, that hosts VM
        #
        # it has to be on that node because of powershell direct

        # ensure, that we don't fill all disk space on cluster node
        $disk = Get-WmiObject Win32_LogicalDisk -ComputerName $VMHostName -Filter "DeviceID='C:'" | Select-Object FreeSpace
        if ($disk.FreeSpace / 1gb -lt 30) {
            throw "$VMHostName has too little free space $($disk.FreeSpace/1gb)GB. Unsafe to continue.`n`nRemove folder $IFMPath from $DC. It contains sensitive data!"
        }

        $VMnodeDest = Join-Path "\\$VMHostName\c$" (Split-Path $IFMPath -NoQualifier)
        "Copying NTDS and syskey from $DC to $VMHostName"
        $r = Copy-Folder (Join-Path "\\$DC\c$" (Split-Path $IFMPath -NoQualifier)) $VMnodeDest
        if ($r.failures) { throw "Copy failed" }
        #endregion copy ntds to cluster node, that hosts VM

        # delete ntds and syskey from DC
        "Deleting exported NTDS and syskey from $DC"
        Invoke-Command $DC {
            param ($IFMPath)
            Remove-Item $IFMPath -Recurse -Force
        } -ArgumentList $IFMPath

        #
        #region copy necessary tools to cluster node, that hosts VM
        #
        "Copying DSInternals module to $VMHostName"
        $r = Copy-Folder $DSInternals "$VMnodeDest\DSInternals"
        if ($r.failures) { throw "Copy failed" }

        "Copying haveibeenpwnd password database to $VMHostName"
        $null = xcopy $pwnedPasswordsNTLMOrdered $VMnodeDest /d # to copy only if the file is newer
        if ($weakPasswordsFile) {
            "Copying weak password database to $VMHostName"
            $null = xcopy $weakPasswordsFile $VMnodeDest /d # to copy only if the file is newer
        }
        #endregion copy necessary tools to cluster node, that hosts VM

        "Waiting for establishing session to VM (minimum wait time is 5 minutes)"
        # waiting, because VM restarts itself after creation and I don't want the connection to hit this online window
        Start-Sleep -Seconds 300

        Invoke-Command -ComputerName $VMHostName {
            param ($VMName, $VMadminCredential)

            while (!$VMsession) {
                try {
                    # session to VM through Powershell Direct
                    $VMsession = New-PSSession -VMName $VMName -Credential $VMadminCredential -ErrorAction Stop
                } catch {
                    Write-Host "." -NoNewline
                }

                Start-Sleep 5
            }

            Remove-PSSession $VMsession
        } -ArgumentList $VMName, $VMadminCredential

        #
        #region copy ntds and tools from cluster node to VM and run audit
        #
        "Copying necessary data to VM $VMName"
        $result = Invoke-Command -ComputerName $VMHostName {
            param ($VMName, $IFMPath, $VMadminCredential, $allFunctionDefs)

            foreach ($functionDef in $allFunctionDefs) {
                . ([ScriptBlock]::Create($functionDef))
            }

            # session to VM through Powershell Direct
            $VMsession = $null
            while (!$VMsession) {
                try {
                    $VMsession = New-PSSession -VMName $VMName -Credential $VMadminCredential -ErrorAction Stop
                } catch {
                    Write-Host "." -NoNewline
                }

                Start-Sleep 5
            }

            # copy folder with all data to VM
            # remove existing folder
            # in case folder exists, copy-item copy content to the same named subfolder ..bleh
            Invoke-Command -Session $VMsession -ScriptBlock {
                param ($IFMPath, $allFunctionDefs)

                foreach ($functionDef in $allFunctionDefs) {
                    . ([ScriptBlock]::Create($functionDef))
                }

                try {
                    Remove-ItemSecure $IFMPath -ea SilentlyContinue
                } catch {}
            } -ArgumentList $IFMPath, $allFunctionDefs
            Copy-Item -ToSession $VMsession -Path $IFMPath -Destination $IFMPath -Recurse

            # delete sensitive data from cluster node
            (Join-Path $IFMPath "Active Directory"), (Join-Path $IFMPath "registry") | % {
                $toDel = $_
                try {
                    Write-Warning "Removing sensitive data '$toDel' from $env:COMPUTERNAME"
                    Remove-ItemSecure $toDel -ea Stop
                } catch {
                    Write-Error "Deletion of sensitive data '$toDel' on $env:COMPUTERNAME failed. Delete it manually!`n`nError was: $_"
                }
            }

            # delete deprecated haveibeenpwnd databases
            $pwnedPasswords = Get-ChildItem $IFMPath -Filter "pwned-passwords-ntlm-ordered-by-hash*.txt" | sort lastwrite | select -Skip 1 -exp fullname | % {
                Write-Warning "Removing deprecated pwd database $_"
                Remove-ItemSecure $_
            }


            #
            #region run AD audit on data stored in VM
            #
            Write-Warning "Running security checks on $VMName"
            $result = Invoke-Command -Session $VMsession -ScriptBlock {
                param ($IFMPath)

                Import-Module (Join-Path $IFMPath "DSinternals") -ErrorAction Stop

                $pwnedPasswords = Get-ChildItem $IFMPath -Filter "pwned-passwords-ntlm-ordered-by-hash*.txt" | select -Last 1 | select -exp fullname
                if (!$pwnedPasswords) { throw "Couldn't find pwned-passwords-ntlm-ordered-by-hash*.txt" }

                $weakPasswords = Get-ChildItem $IFMPath -Filter "*weakPasswordsFile.txt" | select -Last 1 | select -exp fullname
                if (!$weakPasswords) { Write-Warning "Couldn't find weakPasswords.txt" }

                $key = Get-BootKey -SystemHivePath (Join-Path $IFMPath 'registry\SYSTEM')
                if (!$key) { throw "Couldn't get syskey" }

                $params = @{
                    WeakPasswordHashesSortedFile = $pwnedPasswords
                }
                if ($weakPasswords) {
                    $params.WeakPasswordsFile = $weakPasswords
                }

                $result = Get-ADDBAccount -All -DatabasePath (Join-Path $IFMPath 'Active Directory\ntds.dit') -BootKey $key | Test-PasswordQuality @params

                return ($result | Out-String) # to get nice human readable output
            } -ArgumentList $IFMPath
            #endregion run AD audit on data stored in VM

            Remove-PSSession $VMsession -ErrorAction SilentlyContinue

            return $result
        } -ArgumentList $VMName, $IFMPath, $VMadminCredential, $allFunctionDefs
        #endregion copy ntds and tools from cluster node to VM and run audit
    }

    END {
        #
        #region save the results
        #
        if ($result) {
            $rFileName = "$(Get-Date -Format 'dd.MM.yyyy_HH.mm')_ADpwdAudit.txt"
            # output the result to console
            $result | Out-String
            # save output to given destination
            $rFile = Join-Path $resultDestination $rFileName
            "Saving output to $rFile"
            $result | Out-String | Out-File $rFile -Force # file isn't encrypted, because I don't want to copy&use any binaries on our cluster servers
        } else {
            Write-Error "Something went wrong."
        }
        #endregion save the results

        #
        #region cleanup
        #

        # Deleting VM
        "Deleting VM $VMName"
        try {
            Invoke-Command -ComputerName $VMMServerName {
                param ($VMName)

                $ErrorActionPreference = "Stop"
                Stop-SCVirtualMachine $VMName -Force | Out-Null
                Get-SCVirtualMachine $VMName | Remove-SCVirtualMachine | Out-Null
            } -ArgumentList $VMName -ErrorAction Stop
        } catch {
            throw "Deletion of $VMName failed: $_`n`n`nDelete it manually. It contains extremely sensitive data!"
        }

        # Deleting exported NTDS and syskey from DC
        Invoke-Command $DC {
            param ($IFMPath)

            if (Test-Path $IFMPath) {
                "Deleting exported NTDS and syskey from $env:COMPUTERNAME"
                Remove-Item $IFMPath -Recurse -Force
            }
        } -ArgumentList $IFMPath

        # Deleting sensitive data from cluster node
        Invoke-Command -ComputerName $VMHostName {
            param ($IFMPath, $allFunctionDefs)

            foreach ($functionDef in $allFunctionDefs) {
                . ([ScriptBlock]::Create($functionDef))
            }

            (Join-Path $IFMPath "Active Directory"), (Join-Path $IFMPath "registry") | % {
                $toDel = $_
                if (Test-Path $toDel) {
                    try {
                        "Removing sensitive data '$toDel' from $env:COMPUTERNAME"
                        Remove-ItemSecure $toDel -ea Stop
                    } catch {
                        Write-Error "Deletion of sensitive data '$toDel' on $env:COMPUTERNAME failed. Delete it manually!`n`nError was: $_"
                    }
                }
            }
        } -ArgumentList $IFMPath, $allFunctionDefs
        #endregion cleanup
    }
}

================================================
FILE: Azure/Add-AzureADAppUserConsent.ps1
================================================
#Requires -Module Microsoft.Graph.Authentication, Microsoft.Graph.Applications, Microsoft.Graph.Users, Microsoft.Graph.Identity.SignIns
function Add-AzureADAppUserConsent {
    <#
    .SYNOPSIS
    Function for granting consent on behalf of a user to chosen application over selected resource(s) (enterprise app(s)) and permission(s) and assign the user default app role to be able to see the app in his 'My Apps'.

    .DESCRIPTION
    Function for granting consent on behalf of a user to chosen application over selected resource(s) (enterprise app(s)) and permission(s) and assign the user default app role to be able to see the app in his 'My Apps'.

    Consent can be explicitly specified or copied from some existing one.

    .PARAMETER clientAppId
    ID of application you want to grant consent on behalf of a user.

    .PARAMETER consent
    Hashtable where:
    - key is objectId of the resource (enterprise app) you are granting permissions to
    - value is list of permissions strings (scopes)

    Both can be found at Permissions tab of the enterprise app in Azure portal, when you select particular permission.

    For example:
    $consent = @{
        "02ad85cd-02ce-4902-a319-1af611526021" = "User.Read", "Contacts.ReadWrite", "Calendars.ReadWrite", "Mail.Send", "Mail.ReadWrite", "EWS.AccessAsUser.All"
    }

    .PARAMETER copyExistingConsent
    Switch for getting consent details (resource ObjectId and permissions) from existing user consent.
    You will be asked for confirmation before proceeding.

    .PARAMETER userUpnOrId
    User UPN or ID.

    .EXAMPLE
    $consent = @{
        "88690023-f9e1-4728-9028-cdcc6bf67d22" = "User.Read"
        "02ad85cd-02ce-4902-a319-1af611526021" = "User.Read", "Contacts.ReadWrite", "Calendars.ReadWrite", "Mail.Send", "Mail.ReadWrite", "EWS.AccessAsUser.All"
    }

    Add-AzureADAppUserConsent -clientAppId "00b263e4-3497-4650-b082-3197cfdfdd7c" -consent $consent -userUpnOrId "dealdesk@contoso.onmicrosoft.com"

    Grants consent on behalf of the "dealdesk@contoso.onmicrosoft.com" user to application "Salesforce Inbox" (00b263e4-3497-4650-b082-3197cfdfdd7c) and given permissions on resource (ent. application) "Office 365 Exchange Online" (02ad85cd-02ce-4902-a319-1af611526021) and "Windows Azure Active Directory" (88690023-f9e1-4728-9028-cdcc6bf67d22).

    .EXAMPLE
    Add-AzureADAppUserConsent -clientAppId "00b263e4-3497-4650-b082-3197cfdfdd7c" -copyExistingConsent -userUpnOrId "dealdesk@contoso.onmicrosoft.com"

    Grants consent on behalf of the "dealdesk@contoso.onmicrosoft.com" user to application "Salesforce Inbox" (00b263e4-3497-4650-b082-3197cfdfdd7c) based on one of the existing consents.

    .NOTES
    https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/grant-consent-single-user
    #>

    [CmdletBinding()]
    param (
        # The app for which consent is being granted
        [Parameter(Mandatory = $true)]
        [string] $clientAppId,

        [Parameter(Mandatory = $true, ParameterSetName = "explicit")]
        [hashtable] $consent,

        [Parameter(ParameterSetName = "copyConsent")]
        [switch] $copyExistingConsent,

        [Parameter(Mandatory = $true)]
        # The user on behalf of whom access will be granted. The app will be able to access the API on behalf of this user.
        [string] $userUpnOrId
    )

    $ErrorActionPreference = "Stop"

    #region connect to Microsoft Graph PowerShell
    # we need User.ReadBasic.All to get
    # users' IDs, Application.ReadWrite.All to list and create service principals,
    # DelegatedPermissionGrant.ReadWrite.All to create delegated permission grants,
    # and AppRoleAssignment.ReadWrite.All to assign an app role.
    # WARNING: These are high-privilege permissions!

    Import-Module Microsoft.Graph.Authentication
    Import-Module Microsoft.Graph.Applications
    Import-Module Microsoft.Graph.Users
    Import-Module Microsoft.Graph.Identity.SignIns

    Connect-AzureAD -asYourself

    $null = Connect-MgGraph -Scopes ("User.ReadBasic.All", "Application.ReadWrite.All", "DelegatedPermissionGrant.ReadWrite.All", "AppRoleAssignment.ReadWrite.All")
    #endregion connect to Microsoft Graph PowerShell

    $clientSp = Get-MgServicePrincipal -Filter "appId eq '$($clientAppId)'"
    if (-not $clientSp) {
        throw "Enterprise application with Application ID $clientAppId doesn't exist"
    }

    # prepare consent from the existing one
    if ($copyExistingConsent) {
        $consent = @{}

        Get-AzureADServicePrincipalOAuth2PermissionGrant -ObjectId $clientSp.id -All:$true | group resourceId | select @{n = 'ResourceId'; e = { $_.Name } }, @{n = 'ScopeToGrant'; e = { $_.group | select -First 1 | select -ExpandProperty scope } } | % {
            $consent.($_.ResourceId) = $_.ScopeToGrant
        }

        if (!$consent.Keys) {
            throw "There is no existing user consent that can be cloned. Use parameter consent instead."
        } else {
            "Following consent(s) will be added:"
            $consent.GetEnumerator() | % {
                $resourceSp = Get-MgServicePrincipal -Filter "id eq '$($_.key)'"
                if (!$resourceSp) {
                    throw "Resource with ObjectId $($_.key) doesn't exist"
                }
                " - resource '$($resourceSp.DisplayName)' permission: $(($_.value | sort) -join ', ')"
            }

            $choice = ""
            while ($choice -notmatch "^[Y|N]$") {
                $choice = Read-Host "`nContinue? (Y|N)"
            }
            if ($choice -eq "N") {
                break
            }
        }
    }

    #region create a delegated permission that grants the client app access to the API, on behalf of the user.
    $user = Get-MgUser -UserId $userUpnOrId
    if (!$user) {
        throw "User $userUpnOrId doesn't exist"
    }

    foreach ($item in $consent.GetEnumerator()) {
        $resourceId = $item.key
        $scope = $item.value

        if (!$scope) {
            throw "You haven't specified any scope for resource $resourceId"
        }

        $resourceSp = Get-MgServicePrincipal -Filter "id eq '$resourceId'"
        if (!$resourceSp) {
            throw "Resource with ObjectId $resourceId doesn't exist"
        }

        # convert scope string (perm1 perm2) i.e. permission joined by empty space (returned by Get-AzureADServicePrincipalOAuth2PermissionGrant) into array
        if ($scope -match "\s+") {
            $scope = $scope -split "\s+" | ? { $_ }
        }

        $scopeToGrant = $scope

        # check if user already granted some permissions to this app for such resource
        # and skip such permissions to avoid errors
        $scopeAlreadyGranted = Get-MgOauth2PermissionGrant -Filter "principalId eq '$($user.Id)' and clientId eq '$($clientSp.Id)' and resourceId eq '$resourceId'" | select -ExpandProperty Scope
        if ($scopeAlreadyGranted) {
            Write-Verbose "Some permission(s) ($($scopeAlreadyGranted.trim())) are already granted to an app '$($clientSp.Id)' and resourceId '$resourceId'"
            $scopeAlreadyGrantedList = $scopeAlreadyGranted.trim() -split "\s+"

            $scopeToGrant = $scope | ? { $_ } | % {
                if ($_ -in $scopeAlreadyGrantedList) {
                    Write-Warning "Permission '$_' is already granted. Skipping"
                } else {
                    $_
                }
            }

            if (!$scopeToGrant) {
                Write-Warning "All permissions for resource $resourceId are already granted. Skipping"
                continue
            }
        }

        Write-Warning "Grant user consent on behalf of '$userUpnOrId' for application '$($clientSp.DisplayName)' to have following permission(s) '$(($scopeToGrant.trim() | sort) -join ', ')' over API '$($resourceSp.DisplayName)'"

        $grant = New-MgOauth2PermissionGrant -ResourceId $resourceSp.Id -Scope ($scopeToGrant -join " ") -ClientId $clientSp.Id -ConsentType "Principal" -PrincipalId $user.Id
    }
    #endregion create a delegated permission that grants the client app access to the API, on behalf of the user.

    #region assign the app to the user.
    # this ensures that the user can sign in if assignment is required, and ensures that the app shows up under the user's My Apps.
    $userAssignableRole = $clientSp.AppRoles | ? { $_.AllowedMemberTypes -contains "User" }
    if ($userAssignableRole) {
        Write-Warning "A default app role assignment cannot be created because the client application exposes user-assignable app roles ($($userAssignableRole.DisplayName -join ', ')). You must assign the user a specific app role for the app to be listed in the user's My Apps access panel."
    } else {
        if (Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $clientSp.Id -Property AppRoleId, PrincipalId | ? PrincipalId -EQ $user.Id) {
            # user already have some app role assigned
            Write-Verbose "User already have some app role assigned. Skipping default app role assignment."
        } else {
            # the app role ID 00000000-0000-0000-0000-000000000000 is the default app role
            # indicating that the app is assigned to the user, but not for any specific app role.
            Write-Verbose "Assigning default app role to the user"
            $assignment = New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $clientSp.Id -ResourceId $clientSp.Id -PrincipalId $user.Id -AppRoleId "00000000-0000-0000-0000-000000000000"
        }
    }
    #endregion assign the app to the user.
}

================================================
FILE: Azure/Get-AzureDevOpsOrganizationOverview.ps1
================================================
#Requires -Module MSAL.PS
function Get-AzureDevOpsOrganizationOverview {
    <#
    .SYNOPSIS
    Function for getting list of all Azure DevOps organizations that uses your AzureAD directory.

    .DESCRIPTION
    Function for getting list of all Azure DevOps organizations that uses your AzureAD directory.
    It is the same data as downloaded csv from https://dev.azure.com/<organizationName>/_settings/organizationAad.

    Function uses MSAL to authenticate (requires MSAL.PS module).

    .PARAMETER tenantId
    (optional) ID of your Azure tenant.
    Of omitted, tenantId from MSAL auth. ticket will be used.

    .EXAMPLE
    Get-AzureDevOpsOrganizationOverview

    Returns all DevOps organizations in your Azure tenant.

    .NOTES
    PowerShell module AzSK.ADO > ContextHelper.ps1 > GetCurrentContext
    https://stackoverflow.com/questions/56355274/getting-oauth-tokens-for-azure-devops-api-consumption
    https://stackoverflow.com/questions/52896114/use-azure-ad-token-to-authenticate-with-azure-devops
    #>

    [CmdletBinding()]
    param (
        [string] $tenantId = $_tenantId
    )

    if (!(Get-Module "MSAL.PS" -ListAvailable -ea SilentlyContinue)) {
        throw "Module MSAL.PS is missing.`n`nYou can install it via: Install-Module MSAL.PS -Scope CurrentUSer"
    }

    $clientId = "872cd9fa-d31f-45e0-9eab-6e460a02d1f1" # Visual Studio
    $adoResourceId = "499b84ac-1321-427f-aa17-267ca6975798" # Azure DevOps app ID

    $msalToken = Get-MsalToken -Scopes "$adoResourceId/.default" -ClientId $clientId -ErrorAction Stop

    if (!$tenantId) {
        $tenantId = $msalToken.tenantId
        Write-Verbose "Set TenantId to $tenantId (retrieved from MSAL token)"
    }

    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "", $msalToken.accessToken)))

    # URL retrieved thanks to developer mod at page https://dev.azure.com/<organizationName>/_settings/organizationAad
    Invoke-WebRequest -Uri "https://aexprodweu1.vsaex.visualstudio.com/_apis/EnterpriseCatalog/Organizations?tenantId=$tenantId" -Method get -ContentType "application/json" -Headers @{Authorization = ("Basic {0}" -f $base64AuthInfo) } | select -ExpandProperty content | ConvertFrom-Csv
}

================================================
FILE: CHANGELOG.md
================================================
# ${project-name} Change Log

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## 1.0.0

- initial

================================================
FILE: Confluence/fill_confluence_table_with_aws_dns_records.ps1
================================================
####################  !MODIFY TO MATCH YOUR ORGANIZATION!  ####################
# $baseUri = 'https://contoso.atlassian.net/wiki'
$baseUri = Read-Host "Enter base URL of your Confluence wiki (something like 'https://contoso.atlassian.net/wiki'"

# $pageID = "2920906911"
$pageID = Read-Host "Enter ID of the Confluence page you want to work with (its the number part from page URL you want to work with i.e. 2920906911 for URL https://contoso.atlassian.net/wiki/spaces/IT/pages/2920906911/Edge+Network+Overview)"

# !MODIFY TO MATCH THE ZONE ID OF THE DNS ZONE YOU WANT TO WORK WITH!
# $domainZoneID = "Z01320692HE4O8HBIG967"
$domainZoneID = Read-Host "Enter ID of the AWS Zone Domain you want the records from (something like Z01320692HE4O8HBIG967)"
################################################################################


<#

Script will get DNS records from given AWS DNZ Zone and use them to create HTML table and save it to given Confluence wiki page.
In case there is already some HTML table there, extra information from it will be retained.

More information at https://doitpsway.com/how-to-createupdateread-html-table-on-confluence-wiki-page-using-powershell

#>


# confluence user account and its API key (NOT password!), that has appropriate permissions on given Confluence page
$confluenceCredential = Get-Credential -Message "Enter user login and his API key (NOT PASSWORD)"

Import-Module ConfluencePS -ErrorAction Stop

# authenticate to your Confluence space
Set-ConfluenceInfo -BaseURi $baseUri -Credential $confluenceCredential

Add-Type -AssemblyName System.Web

#region functions
function _getAWSDNSZoneRecord {
    # you have to use account, that has READ permissions over DNS zone you want to read records from
    param (
        [Parameter(Mandatory = $true)]
        [string] $domainZoneID
        ,
        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential] $credential
    )

    # to download this modules use:
    # Install-Module -Name AWS.Tools.Installer -Force
    # Install-AWSToolsModule AWS.Tools.Common,AWS.Tools.Route53 -CleanUp
    Import-Module "$PSScriptRoot\AWS.Tools.Common" -ea stop
    Import-Module "$PSScriptRoot\AWS.Tools.Route53" -ea stop

    $accessKey = $credential.UserName
    $secretKey = $credential.GetNetworkCredential().password


    Set-AWSCredential -AccessKey $accessKey -SecretKey $secretKey

    # because results are returned by 100 items, you have to iterate (there is maxItem parameter but is limited to 300)
    # https://forums.aws.amazon.com/message.jspa?messageID=463427
    $nextIdentifier = $null
    $nextType = $null
    $nextName = $null

    [System.Collections.ArrayList] $result = @()

    do {
        $recordSet = Get-R53ResourceRecordSet -HostedZoneId "/hostedzone/$domainZoneID" -StartRecordIdentifier $nextIdentifier -StartRecordName $nextName -StartRecordType $nextType

        $recordSet.ResourceRecordSets | select @{n = "name"; e = { $name = $_.name; if ([string]::IsNullOrEmpty($name)) { "@" } else { $name } } }, type , @{n = "value"; e = { $_.ResourceRecords.value } } | % {
            $name = $_.name
            $type = $_.type
            if ($_.value.getType().name -ne "String") {
                # for each value create separate object
                $_.value | % {
                    [void] $result.add(
                        [PSCustomObject]@{
                            name  = $name
                            type  = $type
                            value = _optimizeValue $_
                        }
                    )
                }
            } else {
                # value is string, there is no need to expand it
                [void] $result.add(
                    [PSCustomObject]@{
                        name  = $name
                        type  = $type
                        value = _optimizeValue $_.value
                    }
                )
            }
        }

        # set up for the next call
        if ($recordSet.IsTruncated) {
            $nextIdentifier = $recordSet.NextRecordIdentifier
            $nextType = $recordSet.NextRecordType
            $nextName = $recordSet.NextRecordName
        }
    } while ($recordSet.IsTruncated)

    return $result
}

function _convertFromHTMLTable {
    # function convert html object to PS object
    # expects object returned by (Invoke-WebRequest).parsedHtml as input
    param ([System.__ComObject]$table)

    $columnName = $table.getElementsByTagName("th") | % { $_.innerText -replace "^\s*|\s*$" }

    $table.getElementsByTagName("tr") | % {
        # per row I read cell content and returns object
        $columnValue = $_.getElementsByTagName("td") | % { $_.innerText -replace "^\s*|\s*$" }
        if ($columnValue) {
            $property = [ordered]@{ }
            $i = 0
            $columnName | % {
                $property.$_ = $columnValue[$i]
                ++$i
            }

            New-Object -TypeName PSObject -Property $property
        } else {
            # row doesn't contain <td>, its probably headline
        }
    }
}

function _optimizeValue {
    param (
        [string] $text
        ,
        [int] $lengthLimit = 50
        ,
        [switch] $replaceNewLine
    )
    # replace | because it is delimiter in confluence
    $result = $text -replace "\|", "!" -join " "
    # TXT recoeds can be in quotes, so replace them, just in case
    $result = $text -replace '^"' -replace '"$'
    if ($replaceNewLine) {
        # multiline values are returned with \n on places where were linebreaks
        $result = $result -replace "\B\\n|\\n\s|\\n\B"
    }
    $result = $result.trim()
    if ($result.Length -gt $lengthLimit) {
        $result = $result.substring(0, $lengthLimit) + "..."
    }
    return $result
}

function _getCorrespondingData {
    param ($item)

    $resultByName = $confluenceContent | ? { $_.Name -eq $item.Name -and $_.Type -eq $item.Type }
    $resultByValue = $confluenceContent | ? { $_.Value -eq $item.Value -and $_.Type -eq $item.Type }
    $resultByNameAndValue = $confluenceContent | ? { $_.Name -eq $item.Name -and $_.Value -eq $item.Value -and $_.Type -eq $item.Type }

    if ($resultByNameAndValue) {
        if ( @($resultByNameAndValue).count -eq 1) {
            return $resultByNameAndValue
        } else {
            throw "There are multiple rows with same name '$($item.Name)' and value '$($item.Value)' of type '$($item.Type)' on $atlassianPage. Page sync cannot continue until you solve this duplicity."
        }
    }

    if ($resultByName -and @($resultByName).count -eq 1) {
        return $resultByName
    }
    if ($resultByValue -and @($resultByValue).count -eq 1) {
        return $resultByValue
    }

    Write-Warning "DNS record with name '$($item.Name)', value '$($item.Value)' and type '$($item.Type)' wasn't found on $atlassianPage.`nIt's new record or there was change of name or value in the existing one, that removed possibility to uniquely identify it.`n`nOwner and description will be therefore `$null."
}
#endregion functions

#region get data from AWS
$awsCredential = Get-Credential -Message "Enter credentials for AWS account"

$registratorContent = _getAWSDNSZoneRecord -domainZoneID $domainZoneID -credential $awsCredential
# filter non interesting records
$registratorContent = $registratorContent | ? { $_.type -notin "SOA", "NS" }

if (!$registratorContent) { throw "unable to receive DNS records" }
#endregion get data from AWS

#region get data from confluence page (table)
# authenticate to Confluence page
$Headers = @{"Authorization" = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($confluenceCredential.UserName + ":" + [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($confluenceCredential.Password)) ))) }

# Invoke-WebRequest instead of Get-ConfluencePage to be able to use ParsedHtml
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
try {
    $confluencePageContent = Invoke-WebRequest -Method GET -Headers $Headers -Uri "$baseUri/rest/api/content/$pageID`?expand=body.storage" -ea stop
} catch {
    if ($_.exception -match "The response content cannot be parsed because the Internet Explorer engine is not available") {
        throw "Error was: $($_.exception)`n Run following command on $env:COMPUTERNAME to solve this:`nSet-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Internet Explorer\Main' -Name DisableFirstRunCustomize -Value 2"
    } else {
        throw $_
    }
}

# from confluence page get content of the first html table
$table = $confluencePageContent.ParsedHtml.GetElementsByTagName('table')[0]

# convert HTML table to PS object
$confluenceContent = @(_convertFromHTMLTable $table)
#endregion get data from confluence page (table)

# merging registrator information with confluence user inputs
$mergedContent = $registratorContent | % {
    $item = $_
    $correspondingData = _getCorrespondingData $item
    $item | select Name, Type, Value, @{n = "Description"; e = { _optimizeValue $correspondingData.description -lengthLimit 1000 -replaceNewLine } }, @{ n = "Owner"; e = { $correspondingData.owner } }
}

# save the result back to confluence page
$body = $mergedContent | ConvertTo-ConfluenceTable | ConvertTo-ConfluenceStorageFormat
Set-ConfluencePage -PageID $pageID -Body $body

================================================
FILE: ConvertFrom-HTMLTable.ps1
================================================
function ConvertFrom-HTMLTable {
    <#
    .SYNOPSIS
    Function for converting ComObject HTML object to common PowerShell object.

    .DESCRIPTION
    Function for converting ComObject HTML object to common PowerShell object.
    ComObject can be retrieved by (Invoke-WebRequest).parsedHtml or IHTMLDocument2_write methods.

    In case table is missing column names and number of columns is:
    - 2
        - Value in the first column will be used as object property 'Name'. Value in the second column will be therefore 'Value' of such property.
    - more than 2
        - Column names will be numbers starting from 1.

    .PARAMETER table
    ComObject representing HTML table.

    .PARAMETER tableName
    (optional) Name of the table.
    Will be added as TableName property to new PowerShell object.

    .EXAMPLE
    $pageContent = Invoke-WebRequest -Method GET -Headers $Headers -Uri "https://docs.microsoft.com/en-us/mem/configmgr/core/plan-design/hierarchy/log-files"
    $table = $pageContent.ParsedHtml.getElementsByTagName('table')[0]
    $tableContent = @(ConvertFrom-HTMLTable $table)

    Will receive web page content >> filter out first table on that page >> convert it to PSObject

    .EXAMPLE
    $Source = Get-Content "C:\Users\Public\Documents\MDMDiagnostics\MDMDiagReport.html" -Raw
    $HTML = New-Object -Com "HTMLFile"
    $HTML.IHTMLDocument2_write($Source)
    $HTML.body.getElementsByTagName('table') | % {
        ConvertFrom-HTMLTable $_
    }

    Will get web page content from stored html file >> filter out all html tables from that page >> convert them to PSObjects
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.__ComObject] $table,

        [string] $tableName
    )

    $twoColumnsWithoutName = 0

    if ($tableName) { $tableNameTxt = "'$tableName'" }

    $columnName = $table.getElementsByTagName("th") | % { $_.innerText -replace "^\s*|\s*$" }

    if (!$columnName) {
        $numberOfColumns = @($table.getElementsByTagName("tr")[0].getElementsByTagName("td")).count
        if ($numberOfColumns -eq 2) {
            ++$twoColumnsWithoutName
            Write-Verbose "Table $tableNameTxt has two columns without column names. Resultant object will use first column as objects property 'Name' and second as 'Value'"
        } elseif ($numberOfColumns) {
            Write-Warning "Table $tableNameTxt doesn't contain column names, numbers will be used instead"
            $columnName = 1..$numberOfColumns
        } else {
            throw "Table $tableNameTxt doesn't contain column names and summarization of columns failed"
        }
    }

    if ($twoColumnsWithoutName) {
        # table has two columns without names
        $property = [ordered]@{ }

        $table.getElementsByTagName("tr") | % {
            # read table per row and return object
            $columnValue = $_.getElementsByTagName("td") | % { $_.innerText -replace "^\s*|\s*$" }
            if ($columnValue) {
                # use first column value as object property 'Name' and second as a 'Value'
                $property.($columnValue[0]) = $columnValue[1]
            } else {
                # row doesn't contain <td>
            }
        }
        if ($tableName) {
            $property.TableName = $tableName
        }

        New-Object -TypeName PSObject -Property $property
    } else {
        # table doesn't have two columns or they are named
        $table.getElementsByTagName("tr") | % {
            # read table per row and return object
            $columnValue = $_.getElementsByTagName("td") | % { $_.innerText -replace "^\s*|\s*$" }
            if ($columnValue) {
                $property = [ordered]@{ }
                $i = 0
                $columnName | % {
                    $property.$_ = $columnValue[$i]
                    ++$i
                }
                if ($tableName) {
                    $property.TableName = $tableName
                }

                New-Object -TypeName PSObject -Property $property
            } else {
                # row doesn't contain <td>, its probably row with column names
            }
        }
    }
}

================================================
FILE: ConvertFrom-XML.ps1
================================================
function ConvertFrom-XML {
    <#
    .SYNOPSIS
    Function for converting XML object (XmlNode) to PSObject.

    .DESCRIPTION
    Function for converting XML object (XmlNode) to PSObject.

    .PARAMETER node
    XmlNode object (retrieved like: [xml]$xmlObject = (Get-Content C:\temp\file.xml -Raw))

    .EXAMPLE
    [xml]$xmlObject = (Get-Content C:\temp\file.xml -Raw)
    ConvertFrom-XML $xmlObject

    .NOTES
    Based on https://stackoverflow.com/questions/3242995/convert-xml-to-psobject
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline)]
        [System.Xml.XmlNode] $node
    )

    #region helper functions

    function ConvertTo-PsCustomObjectFromHashtable {
        param (
            [Parameter(
                Position = 0,
                Mandatory = $true,
                ValueFromPipeline = $true,
                ValueFromPipelineByPropertyName = $true
            )] [object[]]$hashtable
        );

        begin { $i = 0; }

        process {
            foreach ($myHashtable in $hashtable) {
                if ($myHashtable.GetType().Name -eq 'hashtable') {
                    $output = New-Object -TypeName PsObject;
                    Add-Member -InputObject $output -MemberType ScriptMethod -Name AddNote -Value {
                        Add-Member -InputObject $this -MemberType NoteProperty -Name $args[0] -Value $args[1];
                    };
                    $myHashtable.Keys | Sort-Object | % {
                        $output.AddNote($_, $myHashtable.$_);
                    }
                    $output
                } else {
                    Write-Warning "Index $i is not of type [hashtable]";
                }
                $i += 1;
            }
        }
    }
    #endregion helper functions

    $hash = @{}

    foreach ($attribute in $node.attributes) {
        $hash.$($attribute.name) = $attribute.Value
    }

    $childNodesList = ($node.childnodes | ? { $_ -ne $null }).LocalName

    foreach ($childnode in ($node.childnodes | ? { $_ -ne $null })) {
        if (($childNodesList.where( { $_ -eq $childnode.LocalName })).count -gt 1) {
            if (!($hash.$($childnode.LocalName))) {
                Write-Verbose "ChildNode '$($childnode.LocalName)' isn't in hash. Creating empty array and storing in hash.$($childnode.LocalName)"
                $hash.$($childnode.LocalName) += @()
            }
            if ($childnode.'#text') {
                Write-Verbose "Into hash.$($childnode.LocalName) adding '$($childnode.'#text')'"
                $hash.$($childnode.LocalName) += $childnode.'#text'
            } else {
                Write-Verbose "Into hash.$($childnode.LocalName) adding result of ConvertFrom-XML called upon '$($childnode.Name)' node object"
                $hash.$($childnode.LocalName) += ConvertFrom-XML($childnode)
            }
        } else {
            Write-Verbose "In ChildNode list ($($childNodesList -join ', ')) is only one node '$($childnode.LocalName)'"

            if ($childnode.'#text') {
                Write-Verbose "Into hash.$($childnode.LocalName) set '$($childnode.'#text')'"
                $hash.$($childnode.LocalName) = $childnode.'#text'
            } else {
                Write-Verbose "Into hash.$($childnode.LocalName) set result of ConvertFrom-XML called upon '$($childnode.Name)' $($childnode.Value) object"
                $hash.$($childnode.LocalName) = ConvertFrom-XML($childnode)
            }
        }
    }

    Write-Verbose "Returning hash ($($hash.Values -join ', '))"
    return $hash | ConvertTo-PsCustomObjectFromHashtable
}

================================================
FILE: Copy-Item2.ps1
================================================
function Copy-Item2 {
    <#
    .SYNOPSIS
    Fce slouzi k chytremu kopirovani souboru/adresaru. Umoznuje i zabaleni zdroje a kopirovani ZIP souboru misto originalu.

    .DESCRIPTION
    Pro kopirovani velkeho mnozstvi malych souboru na vic stroju je lepsi pouzit prepinac copyZipped. Kdy se zdroj nejdrive zabali, kopiruje se dany ZIP a na cilovych strojich se pote opet rozbali.

    .PARAMETER ComputerName
    Seznam stroju na ktere budu kopirovat.

    .PARAMETER Source
    Uvadi zdroj odkud se bude kopirovat. Musi byt zadano jako UNC cesta. Napr. \\titan01\temp ci \\titan01\temp\program.exe

    .PARAMETER Destination
    Uvadi kam se bude kopirovat. Pokud adresar neexistuje, tak se automaticky vytvori. Musi byt zadano jako lokalni cesta. Napr. C:\temp.

    .PARAMETER giveUsersModifyPerm
    Prepínač rikajici, ze na cilovem objektu se jeste nastavi pro skupinu Users Modify NTFS prava (vcetne dedeni na podobjekty).

    .PARAMETER copyZipped
    Prepinac rikajici, ze kopirovana slozka se nejdrive zabali na zdrojovem stroji, zip archiv se skopiruje na cil a tam se opet rozbali.
    Efektivni pouze u adresaru s vetsim poctem souboru a kopirovani na vetsi pocet stroju.
    Prepinac se neda pouzit v kombinaci s kopirovanim souboru.

    .PARAMETER noConfirm
    Prepinac rikajici, ze neni potreba potvrzovat akci kopirovani.

    .PARAMETER EmailReport
    Prepinac rikajici, ze pokud se behem kopirovani vyskytnou chyby, tak budou zaslany na $emailAddress.

    .PARAMETER emailAddress
    Parametr udavajici adresu, na kterou budou zaslany pripadne chyby, ktere se objevily pri kopirovani.

    .EXAMPLE
    $hala | copy-item2 -s "\\titan01\c$\qtsdk" -d "C:\qtsdk"
    Do C:\qtsdk na kazdem stroji v hale nakopiruje obsah adresare "\\titan01\c$\qtsdk".

    .EXAMPLE
    copy-item2 -c "titan05","titan06" -s "\\titan01\c$\qtsdk" -d "C:\qtsdk"
    Do C:\qtsdk na titan05,titan06 nakopiruje obsah adresare "\\titan01\c$\qtsdk".

    .EXAMPLE
    $hala | ogv2 | copy-item2 -s "\\titan01\c$\qtsdk\rad.log" -d "C:\temp"
    Na vybrané stroje z haly nakopíruje do C:\temp soubor rad.log

    .NOTES
    Author: Ondřej Šebela - ztrhgf@seznam.cz
    Povoleni CredSSP by melo byt reseno skrze GPO: Windows Remote Management a Enable CredSSP.
    Vice zde http://dustinhatch.tumblr.com/post/24589312635/enable-powershell-remoting-with-credssp-using-group
    #>

    #[CmdletBinding(SupportsShouldProcess=$true)]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "zadej jmeno stroje/ů")]
        [Alias("c", "CN", "__Server", "IPAddress", "Server", "Computer", "Name", "SamAccountName")]
        [ValidateNotNullOrEmpty()]
        [String[]] $ComputerName
        ,
        [Parameter(Mandatory = $true, Position = 1, HelpMessage = "zadej cestu ke zdroji v UNC tvaru!")]
        [ValidateScript( {$_ -match "^\\\\[.\w]+\\\w+"})] # kontrola jestli jde o UNC cestu a to vcetne tecek v prvni casti adresy
        #	[ValidateScript({Test-Path $_})] # kontrola jestli $source existuje
        [Alias("s")]
        [string] $source
        ,
        [Parameter(Mandatory = $true, Position = 2, HelpMessage = "zadej cilovy adresar (jako lokalni cestu!)")]
        #		[ValidateScript({$_ -match "^[a-z]:\\"})]
        [Alias("d")]
        [string] $destination
        ,
        [switch] $giveUsersModifyPerm
        ,
        [ValidateScript( {
                If (Get-Command zip-folder, unzip-file -errorAction Stop) {
                    $true
                } else {
                    Throw "Jedna z potrebnych funkci (zip-folder,unzip-file) v prostredi chybi."
                }
            })]
        [ValidateScript( {
                If ($source -match "\\[^\.]+\.[\w]{2,4}$") {
                    Throw "Snazite se kopirovat soubor! Pro soubory neni kopirovani ZIP podporovano."
                } else {
                    $true
                }
            })]
        [switch] $copyZipped
        ,
        [switch] $noConfirm
        ,
        [switch] $emailReport
        ,
        $emailAddress = "sebela@fi.muni.cz"

    )

    BEGIN {
        $error.clear()

        #region preruseni skriptu pokud kopiruji soubor a dal jsem copyZipped (duplicitni kontrola, protoze pokud $source zadam jako pozicni a ne named parametr tak se validace par. nespusti)
        If ($source -match "\\[^\.]+\.[\w]{2,4}$" -and $copyZipped) {
            Throw "Snazite se kopirovat soubor! Pro soubory neni kopirovani ZIP podporovano."
        }
        #endregion

        #region pomocne promenne
        $originalSource = $source
        $SourceCompName = $source.Split("\\")[2]
        $SourceObjectName = Split-Path $source -Leaf
        $sourceIsFolder = test-path $source -pathType container
        $destinationIsFile = $destination -match "\\[^\.]+\.[\w]{2,4}$"
        $destinationObjectName = Split-Path $destination -Leaf
        $destinationFolderName = Split-Path $destination -Parent
        [array]$global:CompletedSources = ($SourceCompName)
        $jobs = @()
        #endregion

        #region potvrzeni akce
        function chcete-pokracovat {
            while ($choice -notmatch "[A|N]") {
                $choice = read-host "Pokračovat? (A|N)"
            }
            if ($choice -eq "N") {
                break
            }
        }

        if ($sourceIsFolder) {
            write-output "Obsah adresáře $source se nakopíruje do adresáře $(Split-Path $destination -Leaf)"
            if (!$noConfirm) {
                chcete-pokracovat
            }
        }

        if (!$sourceIsFolder) {
            if ($destinationIsFile) {
                Write-Output "Soubor $SourceObjectName se nakopíruje do adresáře $destinationFolderName se jménem $destinationObjectName"
                if (!$noConfirm) {
                    chcete-pokracovat
                }
            } else {
                Write-Output "Soubor $SourceObjectName se nakopíruje do adresáře $destination"
                if (!$noConfirm) {
                    chcete-pokracovat
                }
            }
        }
        #endregion

        #region info o zaslani emailu
        if ($emailReport) {
            write-output "Na $emailAddress dojde na konci k zaslani vysledku kopirovani."
        }
        #endregion

        #region odstraneni zdrojoveho pc ze seznamu cilu
        # do pole matches ulozim cast X:\
        $null = $source -match "[a-z]{1}\$\\[\w\\.]+"
        #pokud pristupuji primo na disk$
        if ($matches) {
            # v ziskanem matchi nahradim $ za : a \ na konci za prazdny retezec
            $SourceLocalPath = $matches[0] -replace "\$", ":" -replace "\\$", ""
            # v destination nahradim \ na konci za prazdny retezec
            $DestinationLocalPath = $destination -replace "\\$", ""
            if ($ComputerName -like "*$SourceCompName*" -and ($SourceLocalPath -eq $DestinationLocalPath)) {
                $ComputerName = $ComputerName -replace "$SourceCompName", $null | ? {$_}  # ? {$_} zahodi prazdne radky
                Write-Warning "Ze seznamu cilu byl odstranen stroj $SourceCompName protoze se shodovala zdrojova a cilova slozka"
            }
        } elseif ($ComputerName -like "*$SourceCompName*") {
            # pokud pristupuji na klasickou UNC cestu bez pismene disku (nepoznam jestli se shoduji = radeji odstranim)
            $ComputerName = $ComputerName -replace "$SourceCompName", $null | ? {$_}
            Write-Warning "Ze seznamu cilu byl odstranen stroj $SourceCompName protoze je zdrojem dat"
        }
        #endregion

        #region scriptblock pro invoke-command
        $ScriptBlock = {
            [CmdletBinding()]
            param
            (
                $VerbosePreference
                ,
                $source
                ,
                $SourceObjectName
                ,
                $destination
                ,
                $destinationIsFile
                ,
                $destinationObjectName
                ,
                $destinationFolderName
                ,
                $computer
                ,
                $global:CompletedSources
                ,
                $giveUsersModifyPerm
                ,
                $copyZipped
                ,
                $UnzipFileFunctionDef
            )

            # nastaveni verbose
            if ($VerbosePreference.value) {
                $VerbosePreference = $VerbosePreference.value
            }

            #region kdyz zdrojem je adresar
            $sourceIsFolder = test-path $source -pathType container
            if ($sourceIsFolder) {
                # pokud je zdrojem adresar a zaroven cilova cesta existuje, tak se osetri tvar $source. Kvuli chovani cmdletu copy-item, kdy pokud zdrojova adresa nekonci \* a zadany cilovy adresar existuje, tak se v nem vytvori subfolder s obsahem zdroje coz nechceme
                if (test-path $destination)	{
                    switch -regex ($source)	{
                        '\\$' {$source = "$source*"; break} # kdyz konci "\" tak se prida "*"
                        '\w$' {$source = "$source\*"; break} # kdyz konci alfanumerickym znakem tak se prida "\*"
                        default {break}
                    }
                }
            }
            #endregion

            #region kdyz zdrojem je soubor
            else {
                # oprava toho, ze pokud posledni cast $destination adresy neexistuje, tak copy-item misto aby ho vytvoril, vytvori soubor s jeho nazvem a obsahem $source souboru

                # pokud $destination je soubor a adresar ve kterem by se mel vytvorit neexistuje, tak jej vytvorim
                if ($destinationIsFile -and !(Test-Path $destinationFolderName) -and !($copyZipped)) {
                    Write-output "na stroji $computer vytvarim adresar $destinationFolderName abych do nej mohl nakopirovat zdrojovy soubor"
                    $null = New-Item -Path $destinationFolderName -ItemType directory -Confirm:$false -Force
                }

                # pokud $destination je adresar a zaroven neexistuje
                if (!($destinationIsFile) -and !(Test-Path $destination) -and !($copyZipped)) {
                    Write-output "na stroji $computer vytvarim adresar $destination , abych do nej mohl nakopirovat zdrojovy soubor"
                    $null = New-Item -Path $destination -ItemType directory -Confirm:$false -Force
                }
            }
            #endregion

            #region uprava destination adresy kvuli kopirovani ZIP souboru
            if ($copyZipped) {
                # jelikoz budu kopirovat ZIP archiv a ne puvodni data, tak do $ZipDestination ulozim originalni $destination abych ho nasledne mohl zmenit
                $ZipDestination = $destination
                # $destination zmenim na root disku kde mel byt puvodni $destination + nazev ZIP archivu
                if ($destination -match "^\\\\[.\w]+\\\w+") {
                    # destination je v UNC tvaru
                    if ($destination -match "^\\\\[.\w]+\\[a-z]{1}\$") {
                        # destination je v UNC tvaru a obsahuje pismeno disku
                        $destination = Join-Path -Path $Matches[0] -ChildPath "archive_to_copy.zip"
                    } else {
                        # destination je v UNC tvaru a neobsahuje pismeno disku
                        $destination = "\\" + "$computer" + "\c$\" + "archive_to_copy.zip"
                    }
                } else {
                    # destination je v lokalnim tvaru
                    $destination = Join-Path -Path (Split-Path $destination -Qualifier) -ChildPath "archive_to_copy.zip"
                }
                Write-Verbose "Puvodni cestu $ZipDestination jsem upravil na $destination kvuli nakopirovani ZIP souboru"
            }
            #endregion

            if ($VerbosePreference -eq "Continue" -or $VerbosePreference -eq 2) {
                Write-Output "Kopíruji na $computer z $source do $destination"
            } else {
                Write-Output "Kopíruji na $computer"
            }

            Copy-Item $source $destination -recurse -force

            #region nastaveni opravneni
            if (!$copyZipped) {
                # pokud kopiruji ZIP tak opravneni zmenim az po rozbaleni
                if ($giveUsersModifyPerm) {
                    # pokud jsem kopiroval ZIP, tak jsem upravil $destination, musim vratit zpet
                    if ($copyZipped) {
                        $destination = $ZipDestination
                    }
                    Write-Verbose "Ziskavam aktualni opravneni na $destination."
                    $Acl = Get-Acl $destination
                    $inheritance = [int]([System.Security.AccessControl.InheritanceFlags]::ContainerInherit) + [int]([System.Security.AccessControl.InheritanceFlags]::ObjectInherit)
                    $propagation = [System.Security.AccessControl.PropagationFlags]::None
                    if ($destinationIsFile) {
                        $AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("Users", "Modify", "Allow")
                    } else {
                        $AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("Users", "Modify", $inheritance, $propagation, "Allow")
                    }
                    $Acl.SetAccessRule($AccessRule)
                    Write-Verbose "Upravuji opravneni na $destination."

                    Set-Acl $destination $Acl
                }
            }
            #endregion
        } # konec scriptblock
        #endregion

        if ($copyZipped) {
            #region vytvoreni definice funkci pro invoke-command
            $ZipFileFunctionDef = "function zip-folder { ${function:zip-folder} }"
            $UnzipFileFunctionDef = "function unzip-file { ${function:unzip-file} }"
            #endregion

            #region definice scriptblocku pro vytvoreni ZIP archivu
            $ScriptBlock2 = {
                Param( $ZipFileFunctionDef, $source, $SourceDriveLetter, $SourceCompName )
                # z definice predane jako argument opetovne vytvorim funkce a nactu pomoci dot source (tecka)
                . ([ScriptBlock]::Create($ZipFileFunctionDef))

                # dle tvaru source adresy zvolim
                if ($SourceDriveLetter) {
                    # pokud source v nazvu obsahuje i pismeno disku, tak ZIP bude v rootu daneho disku
                    $ZipDestination = $SourceDriveLetter + ":\" + "archive_to_copy.zip"
                } else {
                    # pokud source neobsahuje pismeno disku, tak umistim ZIP archiv do rootu C:\
                    $ZipDestination = "C:\archive_to_copy.zip"
                }
                Zip-Folder $source $ZipDestination -IncludeBaseFolder:$true
            }
            #endregion

            #region vytvoreni ZIP archivu na zdrojovem stroji v rootu disku, na kterem je umisten $source + uprava $source adresy
            try {
                # match automaticky ulozi do pole $matches vsechny shody umistene v ()
                try {
                    $null = $source -match '^(\\\\([\w]+)[^$]+([a-z])\$).*'
                    $SourceDriveLetter = $matches[3]
                    $SourceCompNameWithLetter = $matches[1]
                    #				$SourceCompName = $matches[2]
                } catch {}

                # vytvorim na stroji v rootu ZIP archiv
                Write-Output "na $SourceCompName vytvarim ZIP archiv $ZipDestination"
                Invoke-Command -ComputerName $SourceCompName -ScriptBlock $ScriptBlock2 -ArgumentList $ZipFileFunctionDef, $source, $SourceDriveLetter, $SourceCompName -ErrorAction Stop

                #region uprava $source adresy (nepracuji s originalnim zdrojem ale ZIP archivem)
                # upravim $source adresu aby odpovidala umisteni ZIP archivu
                if ($SourceDriveLetter) {
                    # pokud source v nazvu obsahuje i pismeno disku, tak ZIP bude v rootu daneho disku
                    $source = Join-Path -path $SourceCompNameWithLetter -child "archive_to_copy.zip"
                } else {
                    # pokud source neobsahuje pismeno disku, tak umistim ZIP archiv do rootu C:\
                    $source = "\\" + $SourceCompName + "\" + "c$\archive_to_copy.zip"
                }
                #endregion

            } catch {
                Write-Output "Pri vytvareni archivu se vyskytla chyba"
                Write-Error -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
                #TODO zvolit vhodne reseni problemu..
            }
            #endregion
        }

        #region definice scriptblocku pro akce provadene po nakopirovani ZIP archivu (rozbaleni, nastaveni prav,...)
        if ($copyZipped) {
            $CopyZippedScriptblock = {
                param (
                    $VerbosePreference,
                    $UnzipFileFunctionDef,
                    $destination,
                    $SourceObjectName,
                    $DestinationObjectName,
                    $giveUsersModifyPerm,
                    $copyzipped
                )
                #region uprava destination adresy kvuli kopirovani ZIP souboru
                # jelikoz budu kopirovat ZIP archiv a ne puvodni data, tak do $ZipDestination ulozim originalni $destination abych ho nasledne mohl zmenit
                $ZipDestination = $destination
                # $destination zmenim na root disku kde mel byt puvodni $destination + nazev ZIP archivu
                if ($destination -match "^\\\\[.\w]+\\\w+") {
                    # destination je v UNC tvaru
                    if ($destination -match "^\\\\[.\w]+\\[a-z]{1}\$") {
                        # destination je v UNC tvaru a obsahuje pismeno disku
                        $destination = Join-Path -Path $Matches[0] -ChildPath "archive_to_copy.zip"
                    } else {
                        # destination je v UNC tvaru a neobsahuje pismeno disku
                        $destination = "\\" + "$computer" + "\c$\" + "archive_to_copy.zip"
                    }
                } else {
                    # destination je v lokalnim tvaru
                    $destination = Join-Path -Path (Split-Path $destination -Qualifier) -ChildPath "archive_to_copy.zip"
                }
                Write-Verbose "Puvodni cestu $ZipDestination jsem upravil na $destination kvuli nakopirovani ZIP souboru"
                #endregion

                #region rozbaleni nakopirovaneho ZIP archivu
                # z definice predane jako argument opetovne vytvorim funkci a nactu pomoci dot source (tecka)
                . ([ScriptBlock]::Create($UnzipFileFunctionDef))
                $ZipDestinationFolder = $ZipDestination | Split-Path -Parent
                if (!(Test-Path $ZipDestinationFolder)) {
                    Write-Output "vytvarim adresar $ZipDestinationFolder protoze neexistuje"
                    $null = New-Item -ItemType "directory" -Path $ZipDestinationFolder -Confirm:$false
                }

                Write-Verbose "rozbaluji $destination do $ZipDestinationFolder"
                unzip-file $destination $ZipDestinationFolder
                #endregion

                #region prejmenovani rozbaleneho adresare aby odpovidal zadani
                # rozbaleny adresar ma stejny nazev jako ten zdrojovy (ze ktereho byl ZIP archiv vytvoren), proto je potreba jej rozbalit pokud $source a $destination adresare nejsou shodne
                if ($SourceObjectName -ne $DestinationObjectName) {
                    $ActualZipDestinationFolderName = $ZipDestination -replace $DestinationObjectName, $SourceObjectName
                    $CorrectZipDestinationFolderName = Join-Path (Split-Path $ActualZipDestinationFolderName -parent) $DestinationObjectName
                    # pokud jiz na stroji existuje adresar s cilovym jmenem, tak jej musim pred prejmenovanim smazat
                    if (Test-Path $CorrectZipDestinationFolderName -ErrorAction SilentlyContinue) {
                        try {
                            Write-Verbose "Adresar $CorrectZipDestinationFolderName jiz existoval, smazal jsem."
                            Remove-Item $CorrectZipDestinationFolderName -Force -Recurse -Confirm:$false
                        } catch {
                            Write-Warning "Pri mazani $CorrectZipDestinationFolderName na $computer se vyskytla chyba. Je tam tedy jak puvodni, tak neprejmenovany novy. Akci prejmenovani tedy musim preskocit, stejne by skoncila chybou."
                            continue
                        }
                    }

                    Write-Verbose "V adresari $ActualZipDestinationFolderName prejmenuji $SourceObjectName na $DestinationObjectName"
                    Rename-Item $ActualZipDestinationFolderName $DestinationObjectName -Force -Confirm:$false -ErrorAction Stop
                }
                #endregion

                #region nastaveni opravneni
                if ($giveUsersModifyPerm) {
                    # pokud jsem kopiroval ZIP, tak jsem upravil $destination, musim vratit zpet
                    if ($copyZipped) {
                        $destination = $ZipDestination
                    }
                    Write-Verbose "Ziskavam aktualni opravneni na $destination."
                    $Acl = Get-Acl $destination
                    $inheritance = [int]([System.Security.AccessControl.InheritanceFlags]::ContainerInherit) + [int]([System.Security.AccessControl.InheritanceFlags]::ObjectInherit)
                    $propagation = [System.Security.AccessControl.PropagationFlags]::None
                    if ($destinationIsFile) {
                        $AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("Users", "Modify", "Allow")
                    } else {
                        $AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("Users", "Modify", $inheritance, $propagation, "Allow")
                    }
                    $Acl.SetAccessRule($AccessRule)
                    Write-Verbose "Upravuji opravneni na $destination."

                    Set-Acl $destination $Acl
                }
                #endregion

                #TODO: pokud source pinga, pokud ne tak z nej udelat source?
                #region smazani ZIP archivu z ciloveho stroje
                Write-Verbose "mazu ZIP soubor $destination"
                sleep -Milliseconds 200
                Remove-Item $destination -Confirm:$false -Force
                #endregion
            }
        }
        #endregion

        #region promenne, ktere je potreba definovat az na konci BEGIN ci obnovit jejich hodnoty
        $sourceIsFolder = test-path $source -pathType container
        $destinationIsFile = $destination -match "\\[^\.]+\.[\w]{2,4}$"
        $destinationObjectName = Split-Path $destination -Leaf
        $destinationFolderName = Split-Path $destination -Parent
        #endregion
    }

    PROCESS {
        foreach ($computer in $ComputerName) {
            if (Test-Connection -ComputerName $computer -Count 2 -ErrorAction SilentlyContinue) {
                #region vybrani nahodneho zdroje z dostupnych a uprava $source adresy dle potreby
                if (!$copyZipped) {
                    #TODO: nezajistuji ze se otestuji vsechny polozky v $CompletedSources (get-random v kombinaci s poctem prvku v poli)
                    $NewSourceCompName = ""
                    $PreviousSourceCompName = ""
                    $CompletedSourcesCount = $global:CompletedSources.count
                    $private:PocetPokusu = 0
                    $private:destination = $destination

                    #TODO zmena source funguje chybne ..opravit
                    # Write-Verbose "Vyberu jiny zdroj dat a zmenim source adresu"
                    # z pole stroju, kam jsem uspesne nakopiroval data nahodne vyberu jeden a upravim source adresu
                    # uvazuji i variantu kdy source a destination neukazuji na stejny adresar + vyberu po case za source zase originalni zdroj
                    # do {
                    #     $NewSourceCompName = get-random $global:CompletedSources -Count 1
                    #     $PreviousSourceCompName = $source.Split("\\")[2]

                    #     # kopiruji z originalniho zdroje
                    #     if ($NewSourceCompName -eq $SourceCompName) {
                    #         $source = $originalSource
                    #         Write-Verbose "	Novy source je $source (kopiruji z puvodniho zdroje)"
                    #     } else {
                    #         # kopiruji z NEoriginalniho zdroje
                    #         $oldSource = $source
                    #         $NewSourceCompName
                    #         $destination
                    #         $source = (join-path "\\$NewSourceCompName" $destination) -replace ":", "$"
                    #         Write-Verbose "	Novy source je $source (puvodne $oldSource)"
                    #     }

                    #     $private:PocetPokusu++
                    # } until ((Test-Connection $NewSourceCompName -Count 1 -Quiet) -or ($private:PocetPokusu = $CompletedSourcesCount))

                    # ukonceni skriptu pokud nemohu pouzit zadny z dostupnych zdroju
                    if ($private:PocetPokusu -eq $CompletedSourcesCount -and ($NewSourceCompName -ne $PreviousSourceCompName)) {
                        Write-Warning "Neni dostupny zadny zdroj dat!"
                        break
                    }
                }
                #endregion

                #region vytvoreni hashe s parametry pro invoke-command
                $InvokeCommandParams = @{
                    ScriptBlock  = $ScriptBlock
                    ErrorAction  = "Stop"
                    ArgumentList = $VerbosePreference, $source, $SourceObjectName, $destination, $destinationIsFile,
                    $destinationObjectName, $destinationFolderName, $computer, $global:CompletedSources, $giveUsersModifyPerm, $copyZipped, $UnzipFileFunctionDef
                }
                #endregion

                try {
                    # upravim cesty aby byly v UNC tvaru
                    $InvokeCommandParams2 = $InvokeCommandParams.clone()
                    $InvokeCommandParams2.ArgumentList[3] = (Join-Path "\\$computer" $destination) -replace ":", "$"
                    $InvokeCommandParams2.ArgumentList[6] = (Join-Path "\\$computer" $destinationFolderName) -replace ":", "$"
                    Invoke-Command @InvokeCommandParams2
                    # v pripade uspesneho zkopirovani pridam cil do seznamu potencialnich zdroju
                    if (!$copyZipped) {
                        if ($? -and $global:CompletedSources -notlike "*$computer*") {
                            $global:CompletedSources += $computer
                        }
                    }
                } catch {
                    Write-Warning "Nepovedlo se ani klasicke kopirovani iniciovane z lokalniho stroje"
                    Write-Warning "Chyba byla: $($_.Exception.Message) Cislo radku: $($_.InvocationInfo.ScriptLineNumber)"
                }
            } else {
                Write-Warning "$computer nepingá"
            }
        }
    }

    END {
        if ($copyZipped) {
            #region smazani ZIP archivu ze zdrojoveho stroje
            Write-Verbose "mazu zdrojovy ZIP soubor $source"
            Remove-Item $source -Confirm:$false -Force
            #endregion

            #region ziskani vysledku akce: rozbaleni, nastaveni prav,..
            if ($jobs) {
                Write-Output "Ziskavam vysledky jobu (rozbaleni, nastaveni prav,...)"
                Wait-Job -Job $jobs | Out-Null
                foreach ($job in $jobs) {
                    if ($job.State -eq 'Failed') {
                        Write-Host "Job na $($job.location) skoncil neuspechem: $($job.ChildJobs[0].JobStateInfo.Reason.Message)" -ForegroundColor Red # pozuti radeji $job.ChildJobs[0].Error ?
                    } else {
                        Write-Host "Job na $($job.location) skoncil uspechem $(Receive-Job $job)" -ForegroundColor Green
                        Write-Verbose ($job | fl *)
                    }
                }
            }
            #endregion
        }

        if ($emailReport) {
            #vytvorim si hash s parametry
            $CommandParameters = @{
                From    = "copy@fi.muni.cz"
                To      = $emailAddress
                ReplyTo = $emailAddress
            }
            # nastavim zbyle parametry
            if ($Error) {
                $CommandParameters.Add("Body", "PŘÍKAZ:`n$($myinvocation.line) `n`nCHYBY:`n$Error")
                $CommandParameters.Add("Subject", 'Chyby z copy-item2')
            } else {
                $CommandParameters.Add("Body", "PŘÍKAZ:`n$($myinvocation.line) `n`nskončil bez chyb.")
                $CommandParameters.Add("Subject", 'copy-item2')
            }

            Send-Email @CommandParameters
        }
    }
}

================================================
FILE: Export-ScheduledTask.ps1
================================================
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
            }
        }	
    }
}


================================================
FILE: Export-ScriptsToModule.ps1
================================================
function Export-ScriptsToModule {
    <#
    .SYNOPSIS
        Function for generating Powershell module from ps1 scripts (that contains definition of functions) that are stored in given folder.
        Generated module will also contain function aliases (no matter if they are defined using Set-Alias or [Alias("Some-Alias")].
        Every script file has to have exactly same name as function that is defined inside it (ie Get-LoggedUsers.ps1 contains just function Get-LoggedUsers).
        If folder with ps1 script(s) contains also module manifest (psd1 file), it will be added as manifest of the generated module.
        In console where you call this function, font that can show UTF8 chars has to be set.

    .PARAMETER configHash
        Hash in specific format, where key is path to folder with scripts and value is path to which module should be generated.

        eg.: @{"C:\temp\scripts" = "C:\temp\Modules\Scripts"}

    .PARAMETER enc
        Which encoding should be used.

        Default is UTF8.

    .PARAMETER includeUncommitedUntracked
        Export also functions from modified-and-uncommited and untracked files.
        And use modified-and-untracked module manifest if necessary.

    .PARAMETER dontCheckSyntax
        Switch that will disable syntax checking of created module.

    .PARAMETER dontIncludeRequires
        Switch that will lead to ignoring all #requires in scripts, so generated module won't contain them.
        Otherwise just module #requires will be added.

    .PARAMETER markAutoGenerated
        Switch will add comment '# _AUTO_GENERATED_' on first line of each module, that was created by this function.
        For internal use, so I can distinguish which modules was created from functions stored in scripts2module and therefore easily generate various reports.

    .EXAMPLE
        Export-ScriptsToModule @{"C:\DATA\POWERSHELL\repo\scripts" = "c:\DATA\POWERSHELL\repo\modules\Scripts"}

    .NOTES
        Author: Ondřej Šebela - ztrhgf@seznam.cz
    #>

    [CmdletBinding()]
    param (
        [ValidateNotNullOrEmpty()]
        [hashtable] $configHash
        ,
        [ValidateNotNullOrEmpty()]
        [string] $enc = 'utf8'
        ,
        [switch] $includeUncommitedUntracked
        ,
        [switch] $dontCheckSyntax
        ,
        [switch] $dontIncludeRequires
        ,
        [switch] $markAutoGenerated
    )

    if (!(Get-Command Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue) -and !$dontCheckSyntax) {
        Write-Warning "Syntax won't be checked, because function Invoke-ScriptAnalyzer is not available (part of module PSScriptAnalyzer)"
    }
    function _generatePSModule {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty()]
            $scriptFolder
            ,
            [Parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty()]
            $moduleFolder
            ,
            [switch] $includeUncommitedUntracked
        )

        if (!(Test-Path $scriptFolder)) {
            throw "Path $scriptFolder is not accessible"
        }

        $moduleName = Split-Path $moduleFolder -Leaf
        $modulePath = Join-Path $moduleFolder "$moduleName.psm1"
        $function2Export = @()
        $alias2Export = @()
        # contains function that will be exported to the module
        # the key is name of the function and value is its text definition
        $lastCommitFileContent = @{ }
        $location = Get-Location
        Set-Location $scriptFolder
        $unfinishedFile = @()
        try {
            # uncommited changed files
            $unfinishedFile += @(git ls-files -m --full-name)
            # untracked files
            $unfinishedFile += @(git ls-files --others --exclude-standard --full-name)
        } catch {
            throw "It seems GIT isn't installed. I was unable to get list of changed files in repository $scriptFolder"
        }
        Set-Location $location

        #region get last commited content of the modified untracked or uncommited files
        if ($unfinishedFile) {
            # there are untracked and/or uncommited files
            # instead just ignoring them try to get and use previous version from GIT
            [System.Collections.ArrayList] $unfinishedFile = @($unfinishedFile)

            # helper function to be able to catch errors and all outputs
            # dont wait for exit
            function _startProcess {
                [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() # cannot be used otherwise if git show HEAD:$file returned something, process stuck
                $p.StandardOutput.ReadToEnd()
                if ($err = $p.StandardError.ReadToEnd()) {
                    Write-Error $err
                }
            }

            $unfinishedScriptFile = $unfinishedFile.Clone() | ? { $_ -like "*.ps1" }

            if (!$includeUncommitedUntracked) {
                Set-Location $scriptFolder

                $unfinishedScriptFile | % {
                    $file = $_
                    $lastCommitContent = $null
                    $fName = [System.IO.Path]::GetFileNameWithoutExtension($file)

                    try {
                        $lastCommitContent = _startProcess git "show HEAD:$file" -ErrorAction Stop
                    } catch {
                        Write-Verbose "GIT error: $_"
                    }

                    if (!$lastCommitContent -or $lastCommitContent -match "^fatal: ") {
                        Write-Warning "$fName has uncommited changes. Skipping, because no previous file version was found in GIT"
                    } else {
                        Write-Warning "$fName has uncommited changes. For module generating I will use content from its last commit"
                        $lastCommitFileContent.$fName = $lastCommitContent
                        $unfinishedFile.Remove($file)
                    }
                }

                Set-Location $location
            }

            # unix / replace by \
            $unfinishedFile = $unfinishedFile -replace "/", "\"

            $unfinishedScriptFileName = $unfinishedScriptFile | % { [System.IO.Path]::GetFileName($_) }

            if ($includeUncommitedUntracked -and $unfinishedScriptFileName) {
                Write-Warning "Exporting changed but uncommited/untracked functions: $($unfinishedScriptFileName -join ', ')"
                $unfinishedFile = @()
            }
        }
        #endregion get last commited content of the modified untracked or uncommited files

        # in ps1 files to export leave just these in consistent state
        $script2Export = (Get-ChildItem (Join-Path $scriptFolder "*.ps1") -File).FullName | where {
            $partName = ($_ -split "\\")[-2..-1] -join "\"
            if ($unfinishedFile -and $unfinishedFile -match [regex]::Escape($partName)) {
                return $false
            } else {
                return $true
            }
        }

        if (!$script2Export -and $lastCommitFileContent.Keys.Count -eq 0) {
            Write-Warning "In $scriptFolder there is none usable function to export to $moduleFolder. Exiting"
            return
        }

        #region cleanup old module folder
        if (Test-Path $modulePath -ErrorAction SilentlyContinue) {
            Write-Verbose "Removing $moduleFolder"
            Remove-Item $moduleFolder -Recurse -Confirm:$false -ErrorAction Stop
            Start-Sleep 1
            [Void][System.IO.Directory]::CreateDirectory($moduleFolder)
        }
        #endregion cleanup old module folder

        Write-Verbose "Functions from the '$scriptFolder' will be converted to module '$modulePath'"

        #region fill $lastCommitFileContent hash with functions content
        $script2Export | % {
            $script = $_
            $fName = [System.IO.Path]::GetFileNameWithoutExtension($script)
            if ($fName -match "\s+") {
                throw "File $script contains space in name which is nonsense. Name of file has to be same to the name of functions it defines and functions can't contain space in it's names."
            }

            # add function content only in case it isn't added already (to avoid overwrites)
            if (!$lastCommitFileContent.containsKey($fName)) {
                # check, that file contain just one function definition and nothing else
                $ast = [System.Management.Automation.Language.Parser]::ParseFile("$script", [ref] $null, [ref] $null)
                # just END block should exist
                if ($ast.BeginBlock -or $ast.ProcessBlock) {
                    throw "File $script isn't in correct format. It has to contain just function definition (+ alias definition, comment or requires)!"
                }

                # get funtion definition
                $functionDefinition = $ast.FindAll( {
                        param([System.Management.Automation.Language.Ast] $ast)

                        $ast -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
                        # Class methods have a FunctionDefinitionAst under them as well, but we don't want them.
                        ($PSVersionTable.PSVersion.Major -lt 5 -or
                        $ast.Parent -isnot [System.Management.Automation.Language.FunctionMemberAst])
                    }, $false)

                if ($functionDefinition.count -ne 1) {
                    throw "File $script doesn't contain any function or contain's more than one."
                }

                #TODO pouzivat pro jmeno funkce jeji skutecne jmeno misto nazvu souboru?.
                # $fName = $functionDefinition.name

                # use function definition obtained by AST to generating module
                # this way no possible dangerous content will be added
                $content = ""
                if (!$dontIncludeRequires) {
                    # adding module requires
                    $requiredModules = $ast.scriptRequirements.requiredModules.name
                    if ($requiredModules) {
                        $content += "#Requires -Modules $($requiredModules -join ',')`n`n"
                    }
                }
                # replace invalid chars for valid (en dash etc)
                $functionText = $functionDefinition.extent.text -replace [char]0x2013, "-" -replace [char]0x2014, "-"

                # add function text definition
                $content += $functionText

                # add aliases defined by Set-Alias
                $ast.EndBlock.Statements | ? { $_ -match "^\s*Set-Alias .+" } | % { $_.extent.text } | % {
                    $parts = $_ -split "\s+"

                    $content += "`n$_"

                    if ($_ -match "-na") {
                        # alias set by named parameter
                        # get parameter value
                        $i = 0
                        $parPosition
                        $parts | % {
                            if ($_ -match "-na") {
                                $parPosition = $i
                            }
                            ++$i
                        }

                        # save alias for later export
                        $alias2Export += $parts[$parPosition + 1]
                        Write-Verbose "- exporting alias: $($parts[$parPosition + 1])"
                    } else {
                        # alias set by positional parameter
                        # save alias for later export
                        $alias2Export += $parts[1]
                        Write-Verbose "- exporting alias: $($parts[1])"
                    }
                }

                # add aliases defined by [Alias("Some-Alias")]
                $innerAliasDefinition = $ast.FindAll( {
                        param([System.Management.Automation.Language.Ast] $ast)

                        $ast -is [System.Management.Automation.Language.AttributeAst]
                    }, $true) | ? { $_.parent.extent.text -match '^param' } | Select-Object -ExpandProperty PositionalArguments | Select-Object -ExpandProperty Value -ErrorAction SilentlyContinue # filter out aliases for function parameters

                if ($innerAliasDefinition) {
                    $innerAliasDefinition | % {
                        $alias2Export += $_
                        Write-Verbose "- exporting 'inner' alias: $_"
                    }
                }

                $lastCommitFileContent.$fName = $content
            }
        }
        #endregion fill $lastCommitFileContent hash with functions content

        if ($markAutoGenerated) {
            "# _AUTO_GENERATED_" | Out-File $modulePath $enc
            "" | Out-File $modulePath -Append $enc
        }

        #region save all functions content to the module file
        # store name of every function for later use in Export-ModuleMember
        $lastCommitFileContent.GetEnumerator() | Sort-Object Name | % {
            $fName = $_.Key
            $content = $_.Value

            Write-Verbose "- exporting function: $fName"
            $function2Export += $fName

            $content | Out-File $modulePath -Append $enc
            "" | Out-File $modulePath -Append $enc
        }
        #endregion save all functions content to the module file

        #region set what functions and aliases should be exported from module
        # explicit export is much faster than use *
        if (!$function2Export) {
            throw "There are none functions to export! Wrong path??"
        } else {
            if ($function2Export -match "#") {
                Remove-Item $modulePath -Recurse -Force -Confirm:$false
                throw "Exported function contains unnaproved character # in it's name. Module was removed."
            }

            $function2Export = $function2Export | Select-Object -Unique | Sort-Object

            "Export-ModuleMember -function $($function2Export -join ', ')" | Out-File $modulePath -Append $enc
            "" | Out-File $modulePath -Append $enc
        }

        if ($alias2Export) {
            if ($alias2Export -match "#") {
                Remove-Item $modulePath -Recurse -Force -Confirm:$false
                throw "Exported alias contains unapproved character # in it's name. Module was removed."
            }

            $alias2Export = $alias2Export | Select-Object -Unique | Sort-Object

            "Export-ModuleMember -alias $($alias2Export -join ', ')" | Out-File $modulePath -Append $enc
        }
        #endregion set what functions and aliases should be exported from module

        #region process module manifest (psd1) file
        $manifestFile = (Get-ChildItem (Join-Path $scriptFolder "*.psd1") -File).FullName

        if ($manifestFile) {
            if ($manifestFile.count -eq 1) {
                $partName = ($manifestFile -split "\\")[-2..-1] -join "\"
                if ($partName -in $unfinishedFile -and !$includeUncommitedUntracked) {
                    Write-Warning "Module manifest file '$manifestFile' is modified but not commited."

                    $choice = ""
                    while ($choice -notmatch "^[Y|N]$") {
                        $choice = Read-Host "Continue? (Y|N)"
                    }
                    if ($choice -eq "N") {
                        break
                    }
                }

                try {
                    Write-Verbose "Processing '$manifestFile' manifest file"
                    $manifestDataHash = Import-PowerShellDataFile $manifestFile -ErrorAction Stop
                } catch {
                    Write-Error "Unable to process manifest file '$manifestFile'.`n`n$_"
                }

                if ($manifestDataHash) {
                    # customize manifest data
                    Write-Verbose "Set manifest RootModule key"
                    $manifestDataHash.RootModule = "$moduleName.psm1"
                    Write-Verbose "Set manifest FunctionsToExport key"
                    $manifestDataHash.FunctionsToExport = $function2Export
                    Write-Verbose "Set manifest AliasesToExport key"
                    if ($alias2Export) {
                        $manifestDataHash.AliasesToExport = $alias2Export
                    } else {
                        $manifestDataHash.AliasesToExport = @()
                    }

                    # create final manifest file
                    Write-Verbose "Generating module manifest file"
                    # create empty one and than update it because of the bug https://github.com/PowerShell/PowerShell/issues/5922
                    New-ModuleManifest -Path (Join-Path $moduleFolder "$moduleName.psd1")
                    Update-ModuleManifest -Path (Join-Path $moduleFolder "$moduleName.psd1") @manifestDataHash
                    if ($manifestDataHash.PrivateData.PSData) {
                        # bugfix because PrivateData parameter expect content of PSData instead of PrivateData
                        Update-ModuleManifest -Path (Join-Path $moduleFolder "$moduleName.psd1") -PrivateData $manifestDataHash.PrivateData.PSData
                    }
                }
            } else {
                Write-Warning "Module manifest file won't be processed because more then one were found."
            }
        } else {
            Write-Verbose "No module manifest file found"
        }
        #endregion process module manifest (psd1) file
    } # end of _generatePSModule

    $configHash.GetEnumerator() | % {
        $scriptFolder = $_.key
        $moduleFolder = $_.value

        $param = @{
            scriptFolder = $scriptFolder
            moduleFolder = $moduleFolder
            verbose      = $VerbosePreference
        }
        if ($includeUncommitedUntracked) {
            $param["includeUncommitedUntracked"] = $true
        }

        _generatePSModule @param

        if (!$dontCheckSyntax -and (Get-Command Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue)) {
            # check generated module syntax
            $syntaxError = Invoke-ScriptAnalyzer $moduleFolder -Severity Error
            if ($syntaxError) {
                Write-Warning "In module $moduleFolder was found these problems:"
                $syntaxError
            }
        }
    }
}

================================================
FILE: FTP/install_FTP_server.ps1
================================================
<#
script will set up new Windows Server as FTP server:
- install FTP, IIS roles
- create FTP site
- create READ and WRITE users and grant them permissions to IIS
- set Window Updates
- if finds RAW disk, format and assign E: letter and use it as FTP root otherwise 'C:\FTP_Root'
- in FTP root creates WRITE and READ folders and set appropriate NTFS permissions
- change ports for passive FTP to 60000-65535
- enable FTPS (using self-signed certificate)
- enable FTP on firewall

inspired by:
https://blogs.iis.net/jaroslad/windows-firewall-setup-for-microsoft-ftp-publishing-service-for-iis-7-0
http://fabriccontroller.net/passive-ftp-and-dynamic-ports-in-iis8-and-windows-azure-virtual-machines/
https://4sysops.com/archives/install-and-configure-an-ftp-server-with-powershell/
#>

param (
    $FTPSiteName = (Read-Host 'Enter FTP site name')
    ,
    $readFTPuser = (Read-Host "Enter name of READ FTP account")
    ,
    $writeFTPuser = (Read-Host "Enter name of WRITE FTP account")
)

if (! ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
    throw "Run with administrator rights"
}

Write-Warning "Public IP of this server has to remain the same, so in AWS use elastic IP, otherwise after each start, VM gets new IP!!!`n"
Write-Warning "For FTP in passive mode to work correctly, public IP of this computer will be set in IIS. In case this computer doesn't have it's final IP (you will set Elastic IP etc), DO NOT CONTINUE!"
$choice = ""
while ($choice -notmatch "^[Y|N]$") {
    $choice = Read-Host "Continue? (Y|N)"
}
if ($choice -eq "N") {
    break
}

$dnsName = Read-Host "Enter DNS name of this FTP server (e.g. ftp.contoso.com). It will be used for certificate creation."

# create user accounts
$readFTPuser, $writeFTPuser | % {
    $Password = Read-Host "Password for '$_' user account" -AsSecureString
    $null = New-LocalUser $_ -Password $Password -FullName $_ -Description $_
    $null = Set-LocalUser -Name $_ -PasswordNeverExpires:$true -UserMayChangePassword:$false
}
Add-LocalGroupMember -Group "IIS_IUSRS" -Member $writeFTPuser

# nastavit autoinstalaci updatu + restart
# https://docs.microsoft.com/en-us/windows-server/administration/server-core/server-core-servicing

# Stop-Service wuauserv
# cmd /c "%systemroot%\system32\Cscript %systemroot%\system32\scregedit.wsf /AU 4"
# # $WUregistryPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
# # New-ItemProperty -Path $WUregistryPath -Name ScheduledInstallDay -Value 0 -PropertyType DWORD -Force | Out-Null # kazdy den
# # New-ItemProperty -Path $WUregistryPath -Name ScheduledInstallTime -Value 3 -PropertyType DWORD -Force | Out-Null # ve 3 rano
# # New-ItemProperty -Path $WUregistryPath -Name NoAutoRebootWithLoggedOnUsers -Value 0 -PropertyType DWORD -Force | Out-Null # reboot i kdyz je nekdo prihlasen
# Start-Service wuauserv

#region create sched task for Windows Update
$scriptBlock = {
    # find updates
    $ScanResult = Invoke-CimMethod -Namespace "root/Microsoft/Windows/WindowsUpdate" -ClassName "MSFT_WUOperations" -MethodName ScanForUpdates -Arguments @{SearchCriteria = "IsInstalled=0 AND Type='Software'" } # s AutoSelectOnWebSites=1 koncilo chybou instalace updatu
    # apply updates
    if ($ScanResult.Updates) {
        $result = Invoke-CimMethod -Namespace "root/Microsoft/Windows/WindowsUpdate" -ClassName "MSFT_WUOperations" -MethodName InstallUpdates -Arguments @{Updates = $ScanResult.Updates }
    }
    $pendingReboot = Invoke-CimMethod -Namespace "root/Microsoft/Windows/WindowsUpdate" -ClassName "MSFT_WUSettings" -MethodName IsPendingReboot | select -exp pendingReboot
    if ($pendingReboot) {
        shutdown /r /t 30 /c "restarting because of newly installed updates"
    }
}

$bytes = [System.Text.Encoding]::Unicode.GetBytes($scriptBlock.ToString())
$encodedString = [Convert]::ToBase64String($bytes)

$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument "-ExecutionPolicy Bypass -NoProfile -encodedcommand $encodedString"

$trigger = New-ScheduledTaskTrigger -Daily -At 3am

Register-ScheduledTask -User "SYSTEM" -Action $action -Trigger $trigger -TaskName "WindowsUpdate" -Description "regular updating of Windows" -Force
#endregion create sched task for Windows Update

#region format raw disk and assign it E letter
$rawDisk = Get-Disk | ? { $_.partitionstyle -eq "raw" }
$rawDisk | Initialize-Disk -PartitionStyle MBR
$rawDisk | New-Partition -DriveLetter E -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel "DATA" -Confirm:$false
#endregion format raw disk and assign E letter

#region install FTP, IIS roles
Install-WindowsFeature Web-FTP-Server -IncludeAllSubFeature -IncludeManagementTools
Install-WindowsFeature Web-Server -IncludeAllSubFeature -IncludeManagementTools
#endregion install FTP, IIS roles

#region set FTP
Import-Module WebAdministration -ea Stop # creates IIS: drive too

#region create the FTP site
if ($rawDisk) {
    $FTPRootDir = 'E:\FTP_Root'
} else {
    $FTPRootDir = 'C:\FTP_Root'
}
$FTPPort = 21
$null = New-Item $FTPRootDir -ItemType Directory
New-WebFtpSite -Name $FTPSiteName -Port $FTPPort -PhysicalPath $FTPRootDir
#endregion create the FTP site

# enable basic authentication
$FTPSitePath = "IIS:\Sites\$FTPSiteName"
$BasicAuth = 'ftpServer.security.authentication.basicAuthentication.enabled'
Set-ItemProperty -Path $FTPSitePath -Name $BasicAuth -Value $True

#region set authorization rules for FTP users
# READ-WRITE
$writeFTPuser | % {
    $Param = @{
        Filter   = "/system.ftpServer/security/authorization"
        Value    = @{
            accessType  = "Allow"
            Users       = "$_"
            permissions = 3
        }
        PSPath   = 'IIS:\'
        Location = $FTPSiteName
    }
    Add-WebConfiguration @param
}
# READ
$readFTPuser | % {
    $Param = @{
        Filter   = "/system.ftpServer/security/authorization"
        Value    = @{
            accessType  = "Allow"
            Users       = "$_"
            permissions = 1
        }
        PSPath   = 'IIS:\'
        Location = $FTPSiteName
    }
    Add-WebConfiguration @param
}
#endregion set authorization rules for FTP users

#region set custom NTFS to FTP_Root\WRITE
$NoneSID = ((New-Object System.Security.Principal.NTAccount("", "none")).Translate([System.Security.Principal.SecurityIdentifier])).Value

$writeFTPSDDL = ""
$writeFTPuser | % {
    $SID = ((New-Object System.Security.Principal.NTAccount("", "$_")).Translate([System.Security.Principal.SecurityIdentifier])).Value
    $writeFTPSDDL += "(A;OICI;0x1301bf;;;$SID)"
}
$readFTPSDDL = ""
$readFTPuser | % {
    $SID = ((New-Object System.Security.Principal.NTAccount("", "$_")).Translate([System.Security.Principal.SecurityIdentifier])).Value
    $readFTPSDDL += "(A;;0x1200a9;;;$SID)"
}
$sddl = "O:BAG:$NoneSID`D:PAI(A;OICI;FA;;;SY)(A;OICI;0x1301bf;;;NS)(A;OICI;FA;;;BA)$writeFTPSDDL$readFTPSDDL"

$_ClientWRITE = Join-Path $FTPRootDir "WRITE"
$null = New-Item $_ClientWRITE -ItemType Directory
$securityDescriptor = Get-Acl -Path $_ClientWRITE
$securityDescriptor.SetSecurityDescriptorSddlForm($sddl)
Set-Acl -Path $_ClientWRITE -AclObject $securityDescriptor
#endregion set custom NTFS to FTP_Root\WRITE

#region set custom NTFS to FTP_Root\READ
$writeFTPSDDL = ""
$writeFTPuser | % {
    $SID = ((New-Object System.Security.Principal.NTAccount("", "$_")).Translate([System.Security.Principal.SecurityIdentifier])).Value
    $writeFTPSDDL += "(A;OICIIO;0x1301bf;;;$SID)(A;;0x1200ad;;;$SID)"
}
$readFTPSDDL = ""
$readFTPuser | % {
    $SID = ((New-Object System.Security.Principal.NTAccount("", "$_")).Translate([System.Security.Principal.SecurityIdentifier])).Value
    $readFTPSDDL += "(A;OICI;0x1200a9;;;$SID)"
}
$sddl = "O:BAG:$NoneSID`D:PAI(A;OICI;FA;;;SY)(A;OICIIO;0x1301bf;;;NS)(A;OICI;FA;;;BA)$writeFTPSDDL$readFTPSDDL"

$_ClientREAD = Join-Path $FTPRootDir "READ"
$null = New-Item $_ClientREAD -ItemType Directory
$securityDescriptor = Get-Acl -Path $_ClientREAD
$securityDescriptor.SetSecurityDescriptorSddlForm($sddl)
Set-Acl -Path $_ClientREAD -AclObject $securityDescriptor
#endregion set custom NTFS to FTP_Root\READ

#region set SSL (FTPS)

# force FTPS
Set-ItemProperty -Path $FTPSitePath -Name ftpServer.security.ssl.controlChannelPolicy -Value 1
Set-ItemProperty -Path $FTPSitePath -Name ftpServer.security.ssl.dataChannelPolicy -Value 1
$newCert = New-SelfSignedCertificate -FriendlyName "FTP Server" -CertStoreLocation "Cert:\LocalMachine\My" -DnsName $dnsName -NotAfter (Get-Date).AddMonths(120)
#TODO use LetsEncrypt i.e. https://github.com/win-acme/win-acme

# https://stackoverflow.com/questions/32390097/powershell-set-ssl-certificate-on-https-binding
# bind certificate to FTP site
Set-ItemProperty -Path $FTPSitePath -Name ftpServer.security.ssl.serverCertHash -Value $newCert.GetCertHashString()

# SSL has to be set identically per SITE and per SERVER (in IIS) http://www.vsysad.com/2013/06/install-and-configure-ftp-over-ssl-ftps-in-iis-7-5/
# otherwise error:
# Response: 534 Local policy on server does not allow TLS secure connections.
# Error: Critical error
# Error: Could not connect to server

# set server public IP because of passive FTP(S)
$publicIP = Invoke-RestMethod http://ipinfo.io/json | Select-Object -exp ip
if (!$publicIP) { $publicIP = Read-Host "Enter !PUBLIC! IP address of this FTP server" }
Set-ItemProperty -Path $FTPSitePath -Name ftpServer.firewallSupport.externalIp4Address -Value $publicIP
#endregion set SSL (FTPS)

# change range of ports for passive FTP to 60000-65535 (default contains even 3389 i.e. RDP!)
cmd /c "$env:windir\System32\inetsrv\appcmd set config /section:system.ftpServer/firewallSupport /lowDataChannelPort:60000 /highDataChannelPort:65535"
#endregion set FTP

# restart site to apply the changes
Restart-WebItem "IIS:\Sites\$FTPSiteName" -Verbose

# set FW
# default FTP rule seems to not work...
New-NetFirewallRule -Name "FTP 21" -DisplayName "FTP 21" -Description "default rule seems to not work" -Profile private, public, domain -Direction Inbound -Action Allow -Protocol TCP -LocalPort 21 -Program "%windir%\system32\svchost.exe"
netsh advfirewall set global Statefulftp disable
Restart-Service ftpsvc -Force

Write-Warning "Don't forget to:`n - set FW (Security Groups) in AWS (use existing 'FTP server')`n - set Elastic IP of this server in it's DNS record"

Write-Warning "Check NTFS permission on $FTPRootDir if it suit your needs!"

================================================
FILE: Get-AdministrativeEvents.ps1
================================================
function Get-AdministrativeEvents {
    <#
	.SYNOPSIS
        Fce slouží k vypsani Warning, Error a Critical eventu z vybranych logu.
        Seznam logu by mel vicemene odpovidat view Administrative Events.

    .DESCRIPTION
        Fce slouží k vypsani Warning, Error a Critical eventu z vybranych logu.
        Seznam logu by mel vicemene odpovidat view Administrative Events.

        Defaultně se vypíší eventy za posledních 24 hodin.
        Ignoruji se eventy z logu ForwardedEvents!

    .PARAMETER ComputerName
        Seznam strojů, ze kterých vytahnu chybova hlaseni.

    .PARAMETER Newest
        Kolik událostí se má ziskat.

    .PARAMETER After
        Po jakém datu se mají eventy hledat.

    .PARAMETER Before
        Před jakým datem se mají eventy hledat.

    .PARAMETER LogName
        Tyto logy budou pridany ke standardne zobrazovanym.

    .PARAMETER JustLogNames
        Prepinac slouzici k vypsani nazvu logu, ze kterych by se na danem stroji vypisovaly chybove udalosti.

    .PARAMETER severity
        Jake typy eventu se maji vypsat.
        Vychozi jsou vsechny.

        1 = critical
        2 = error
        3 = warning

    .EXAMPLE
        Get-AdministrativeEvents $hala
        vypise chybove eventy na vsech strojich v hale

    .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", "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 += "<Select Path=`"$_`">*[System[($severityFilter)]]</Select>" }

            # zakladni XML dotaz
            [xml] $xml = "
			<QueryList>
                <Query Id=`"0`" Path=`"Application`">
                    $filterLogs
                </Query>
			</QueryList>
			"
            # 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
    }
}

================================================
FILE: Get-ComputerInfo.ps1
================================================
<#
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 {
    }
}

================================================
FILE: Get-CurrentLoad.ps1
================================================
function Get-CurrentLoad {
    <#
    .SYNOPSIS
        Function for realtime outputting values of basic performance counters (CPU, RAM, GPU, HDD, NETWORK) to console.

	.DESCRIPTION
        Function for realtime outputting values of basic performance counters (CPU, RAM, GPU, HDD, NETWORK) to console.
        On Windows Server OS, you have to enable HDD counter by running "diskperf -Y" first!

	.PARAMETER computerName
	 	Name of the remote computer from which you want to get performance data.

    .PARAMETER includeGPU
        Switch for outputting also GPU counters
        This is little more CPU intense, so not by default included.

    .PARAMETER topProcess
        Changes output just to top 5 processes, that make the most load in specified domain.
        Possible domain values: CPU, GPU, HDD, RAM, NIC.

    .PARAMETER detailed
        Add more detailed counters for given domain.
        Possible values: HDD.

        For HDD it shows Read/Write load on every disk.
        For Tiered Storage queue etc.

    .PARAMETER updateSpeed
        How often to collect the perf. counters
        Default is 1 second.

    .PARAMETER measure
        What to measure.
        By default: CPU, RAM, HDD, NIC

    .PARAMETER captureOutput
        Switch for capturing output to csv file.
        Path to such csv is defined in capturePath parameter.

    .PARAMETER capturePath
        Path to csv file.
        Default is C:\Windows\Temp\hostname_date.csv.

        To show csv content: Import-Csv $capturePath -Delimiter ";"

    .EXAMPLE
        Get-CurrentLoad

        Output load on localhost.

	.EXAMPLE
        Get-CurrentLoad -computername titan01

        Output load on remote computer titan01.

    .EXAMPLE
        Get-CurrentLoad -topProcess CPU

        Output top 5 CPU heavy processes on localhost.

    .EXAMPLE
        Get-CurrentLoad -measure CPU, HDD

        Output CPU and HDD load on localhost.

    .EXAMPLE
        Get-CurrentLoad -measure CPU, HDD -detailed HDD

        Output CPU and HDD load on localhost. Moreover outputs Read/Write load of every disk here.

    .EXAMPLE
        Get-CurrentLoad -captureOutput

        Output load and save it to csv file too (C:\Windows\TEMP\<hostname>_<date>.csv)

	.NOTES
	 	Author: Ondřej Šebela - ztrhgf@seznam.cz
        https://github.com/ztrhgf
    #>

    [cmdletbinding()]
    [Alias("top")]
    param (
        [string] $computerName = $env:COMPUTERNAME
        ,
        [switch] $includeGPU
        ,
        [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 "Enter path like: C:\temp\result.csv"
                }
            })]
        [string] $capturePath = ''
    )

    begin {
        if ($includeGPU) {
            $measure += 'GPU'
        }
    }

    process {
        $param = @{
            ArgumentList = $measure, $topProcess, $updateSpeed, $detailed, $captureOutput, $capturePath, $env:computername
            ScriptBlock  = {
                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) {
                    # run on remote computer i.e. capturing CTRL + C isn't possible i.e. output this information right now
                    $capturePathUNC = "\\$env:COMPUTERNAME\" + ($capturePath -replace ":", "$")
                    Write-Warning "Captured output will be in $capturePathUNC.`nFor import to console use: Import-Csv `"$capturePathUNC`" -Delimiter `";`""
                    Start-Sleep 3
                }

                # name of the counter is language specific!
                # 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
                if ($osLanguage -eq 'en-US') {
                    $sent = 'sent'
                    $process = 'Process'
                    $IDprocess = 'ID Process'
                    $percentProcessorTime = '% Processor Time'
                    $workingSet = 'Working Set'
                    $IODataOperationsSec = 'IO Data Operations/sec'
                    $GPUEngine = 'GPU Engine'
                    $utilizationPercentage = 'Utilization Percentage'
                    $processor = 'Processor'
                    $physicalDisk = 'PhysicalDisk'
                    $percentDiskTime = '% Disk Time'
                    $percentDiskReadTime = '% Disk Read Time'
                    $percentDiskWriteTime = '% Disk Write Time'
                    $memory = 'Memory'
                    $availableMBytes = 'Available MBytes'
                    $networkInterface = 'Network Interface'
                    $bytesSentSec = 'Bytes Sent/sec'
                    $bytesReceivedSec = 'Bytes Received/sec'
                } elseif ($osLanguage -eq 'cs-CZ') {
                    $sent = 'odeslané'
                    $process = 'Proces'
                    $IDprocess = 'ID procesu'
                    $percentProcessorTime = '% času procesoru'
                    $workingSet = 'pracovní sada'
                    $IODataOperationsSec = 'Vstupně-výstupní datové operace/s'
                    $GPUEngine = 'GPU engine'
                    $utilizationPercentage = 'Utilization Percentage'
                    $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'
                    $networkInterface = 'Rozhraní sítě'
                    $bytesSentSec = 'Bajty odeslané/s'
                    $bytesReceivedSec = 'Bajty přijaté/s'
                } else {
                    throw "this language ($osLanguage) is not supported (just 'en-US' and 'cs-CZ')"
                }

                # set counters
                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 "undefined" }
                    }

                    # on Hyper-V server I want for vmwp process the corresponding VM
                    # therefore I find the name in counters and corresponding PID, so I can later pair it
                    $isHyperVServer = (Get-WmiObject -Namespace "root\virtualization\v2" -Query 'select elementname, caption from Msvm_ComputerSystem where caption = "Virtual Machine"' -ErrorAction SilentlyContinue | select ElementName).count # if there are some VM, consider it as 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 {
                    # list of counter to monitor
                    [System.Collections.ArrayList] $counterList = @()
                    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"
                    }

                    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 ('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)
                    }

                    # counters for network adapter are added per adapter
                    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 | % { # '(' replace for '[', because such are used in counter name
                            $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'
                    }
                }

                # if have just one counter, convert to string because of Get-Counter
                if ($counterList.Count -eq 1) {
                    [string] $counterList = $counterList[0]
                }

                # modify CTRL + C shortcut behaviour, so I can output path to csv file
                if ($captureOutput -and $env:COMPUTERNAME -eq $computerName) {
                    [console]::TreatControlCAsInput = $true
                }

                while (1) {
                    # if output to csv, than after CTRL + C print the csv path to console
                    if ($captureOutput -and $env:COMPUTERNAME -eq $computerName -and [console]::KeyAvailable) {
                        # running locally i.e. capturing CTRL + C will work
                        $key = [system.console]::readkey($true)
                        if (($key.modifiers -band [consolemodifiers]"control") -and ($key.key -eq "C")) {
                            [console]::TreatControlCAsInput = $false
                            Write-Warning "Captured output will be saved in $capturePath.`nTo import it into console: Import-Csv `"$capturePath`" -Delimiter `";`""
                            break
                        }
                    }
                    # get counter results
                    $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 } }
                    }

                    if (!$actualResults) { throw "there are no results, does the counter: $($CounterList -join ', ') exists on computer: $env:COMPUTERNAME`?" }

                    Clear-Host

                    try {
                        $result = [ordered] @{date = Get-Date }
                    } catch {
                        # older PS version don't support [ordered]
                        $result = @{date = Get-Date }
                    }

                    if ($topProcess) {
                        if ($topProcess -in 'HDD', 'NIC') {
                            "contains !all! types of process IO operations (HDD + NIC + ...)"
                        }

                        $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 ($topProcess -eq 'RAM') {
                                $value = ([math]::Round($_.value / 1MB, 2)).tostring() + ' MB'
                            } elseif ($topProcess -eq 'GPU') {
                                # GPU counter shows process PID, convert it to process name
                                $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
                            }

                            # show what VM corresponds to vmwp process
                            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)"
                            }

                            $name = $name -replace '\(|\)'

                            "{0}: {1}" -f $name, $value

                            if ($captureOutput) {
                                if ($subResult) { $subResult += ", " }
                                $subResult += $name, "$value%" -join ' '
                            }
                        }

                        if ($captureOutput) { $result['topProcess'] = $subResult }
                    } else {
                        # output load of CPU, HDD, RAM, ...

                        # GPU load
                        $GPUTotal = 0

                        # if it is not array, convert, to be able to use getenumerator()
                        if ($actualResults.GetType().basetype.name -ne 'Array') {
                            $actualResults = @(, $actualResults)
                        }

                        $actualResults.GetEnumerator() | % {
                            $item = $_
                            switch -Wildcard ($_.name) {
                                "*\$percentProcessorTime" {
                                    $core = ([regex]"\(([^)]+)\)").Matches($_).Value
                                    $name = "CPU $core %: "
                                    $value = [math]::Round($item.Value, 2)

                                    $name + $value

                                    if ($captureOutput) { $result[$name] = $value }
                                }

                                "*\$availableMBytes" {
                                    $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 }
                                    $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*" {
                                    $nName = ([regex]"\(([^)]+)\)").Matches($_).Value

                                    if ($item.name -like "*$sent*") {
                                        $action = 'sent'
                                    } else {
                                        $action = 'received'
                                    }

                                    $name = "NIC $nName $action MB: "
                                    $value = [math]::Round($item.Value / 1MB, 2)

                                    $name + $value

                                    if ($captureOutput) { $result[$name] = $value }
                                }

                                "*$GPUEngine*" {
                                    # GPU doesn't have summarizing _total counter, I will sum all received values
                                    $GPUTotal += $item.Value
                                }

                                Default {
                                    #$item.name + ": " + [math]::Round($item.Value / 1MB, 2)
                                    throw "undefined counter"
                                }
                            }
                        } # end of foreach

                        if ($GPUTotal) {
                            $name = "GPU %: "
                            $value = [math]::Round($GPUTotal, 2)

                            $name + $value

                            if ($captureOutput) { $result[$name] = $value }
                        }
                    } # end of else

                    # export results to CSV
                    if ($captureOutput) {
                        New-Object -TypeName PSObject -Property $result | Export-Csv $capturePath -Append -NoTypeInformation -Delimiter ';' -Force -Encoding UTF8
                    }

                    Start-Sleep $updateSpeed
                } # end of while
            }
        }
        if ($computerName -and $computerName -ne $env:computername) {
            $param.ComputerName = $computerName
        }
        Invoke-Command @param
    }
}

================================================
FILE: Get-FailedScheduledTask.ps1
================================================
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"
        }
    }
}


================================================
FILE: Get-FirewallLog.ps1
================================================
#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", 
Download .txt
gitextract_uv8sk41q/

├── Active Directory/
│   ├── Get-ADGroupMemberAddDate.ps1
│   ├── Get-ADGroupMemberChangesHistory.ps1
│   ├── Get-ADGroupMemberRecursive.ps1
│   ├── Invoke-ADPasswordsAudit.ps1
│   └── Reset-KrbtgtAccount.ps1
├── Azure/
│   ├── Add-AzureADAppUserConsent.ps1
│   └── Get-AzureDevOpsOrganizationOverview.ps1
├── CHANGELOG.md
├── Confluence/
│   └── fill_confluence_table_with_aws_dns_records.ps1
├── ConvertFrom-HTMLTable.ps1
├── ConvertFrom-XML.ps1
├── Copy-Item2.ps1
├── Export-ScheduledTask.ps1
├── Export-ScriptsToModule.ps1
├── FTP/
│   └── install_FTP_server.ps1
├── Get-AccountFromSID.ps1
├── Get-AdministrativeEvents.ps1
├── Get-ComputerInfo.ps1
├── Get-CurrentLoad.ps1
├── Get-FailedScheduledTask.ps1
├── Get-FirewallLog.ps1
├── Get-FirewallRules.ps1
├── Get-FolderSize.ps1
├── Get-InstalledSoftware.ps1
├── Get-LogOnOff.ps1
├── Get-LoggedOnUser.ps1
├── Get-NetworkCapture.ps1
├── Get-PSLog.ps1
├── Get-PendingReboot.ps1
├── Get-ReliabilityHistory.ps1
├── Get-SFCLogEvent.ps1
├── Get-SIDFromAccount.ps1
├── Get-Shutdown.ps1
├── Get-Uptime.ps1
├── Get-WinEventArchivedIncluded.ps1
├── INTUNE/
│   ├── Connect-Graph.ps1
│   ├── ConvertFrom-MDMDiagReport.ps1
│   ├── ConvertFrom-MDMDiagReportXML.ps1
│   ├── Get-ClientIntunePolicyResult.ps1
│   ├── Get-ClientStatusReport.ps1
│   ├── Get-IntuneDeviceComplianceStatus.ps1
│   ├── Get-IntuneEnrollmentStatus.ps1
│   ├── Get-IntuneLog.ps1
│   ├── Get-IntuneOverallComplianceStatus.ps1
│   ├── Get-IntuneReport.ps1
│   ├── Get-MDMClientData.ps1
│   ├── Invoke-IntuneScriptRedeploy.ps1
│   ├── Invoke-IntuneWin32AppRedeploy.ps1
│   ├── Invoke-MDMReenrollment.ps1
│   ├── New-GraphAPIAuthHeader.ps1
│   ├── Reset-HybridADJoin.ps1
│   ├── Reset-IntuneEnrollment.ps1
│   └── Win32App/
│       └── SetBitLockerPin/
│           ├── BitlockerIsEnabledAndNotSet.ps1
│           ├── DetectBitLockerPin.ps1
│           ├── Popup.ps1
│           └── SetBitLockerPin.ps1
├── Invoke-AsLoggedUser.ps1
├── Invoke-AsSystem.ps1
├── Invoke-Command2.ps1
├── Invoke-NetworkCapture.ps1
├── JIRA/
│   └── New-JIRATicket.ps1
├── OSD/
│   ├── OSDComputerName_via_ServiceTag/
│   │   └── Set-CMTSStep_ServiceTag2OSDComputerName.ps1
│   └── OfflineDomainJoin/
│       └── Set-CMDeviceDJoinBlobVariable.ps1
├── Quote-String.ps1
├── README.md
├── Read-FromClipboard.ps1
├── SCCM/
│   ├── Connect-SCCM.ps1
│   ├── Get-CMLog.ps1
│   ├── Invoke-CMAdminServiceQuery.ps1
│   ├── Invoke-CMComplianceEvaluation.ps1
│   └── Update-CMClientPolicy.ps1
├── SCVMM/
│   └── New-VMFromTemplate.ps1
├── Search-ADObjectACL.ps1
├── Search-GPOSetting.ps1
├── Shutdown-Computer.ps1
├── Start-TCPPortListener.ps1
├── Test-Connection2.ps1
├── Test-Path2.ps1
├── Test-Port.ps1
├── Unlock-File.ps1
├── Watch-EventLog.ps1
├── Write-Log.ps1
├── environmental variables/
│   ├── Get-EnvVariable.ps1
│   ├── Remove-EnvVariable.ps1
│   └── Set-EnvVariable.ps1
├── event subscriptions/
│   ├── Get-EventSubscription.ps1
│   ├── Get-EventSubscriptionStatus.ps1
│   ├── New-EventSubscription.ps1
│   ├── Remove-EventSubscription.ps1
│   └── Set-EventSubscription.ps1
└── modules/
    ├── AdmPwd.PS/
    │   ├── AdmPwd.PS.format.ps1xml
    │   ├── AdmPwd.PS.psd1
    │   └── en-US/
    │       └── AdmPwd.PS.dll-Help.xml
    ├── AutoItX/
    │   └── AutoItX.psd1
    ├── ConfluencePS/
    │   └── 2.5.0/
    │       ├── CHANGELOG.md
    │       ├── ConfluencePS.Types.cs
    │       ├── ConfluencePS.format.ps1xml
    │       ├── ConfluencePS.psd1
    │       ├── ConfluencePS.psm1
    │       ├── LICENSE
    │       ├── PSGetModuleInfo.xml
    │       ├── README.md
    │       └── en-US/
    │           ├── ConfluencePS-help.xml
    │           └── about_ConfluencePS.help.txt
    ├── PSScriptAnalyzer/
    │   └── 1.17.1/
    │       ├── PSScriptAnalyzer.cat
    │       ├── PSScriptAnalyzer.psd1
    │       ├── PSScriptAnalyzer.psm1
    │       ├── ScriptAnalyzer.format.ps1xml
    │       ├── ScriptAnalyzer.types.ps1xml
    │       └── Settings/
    │           ├── CmdletDesign.psd1
    │           ├── CodeFormatting.psd1
    │           ├── CodeFormattingAllman.psd1
    │           ├── CodeFormattingOTBS.psd1
    │           ├── CodeFormattingStroustrup.psd1
    │           ├── DSC.psd1
    │           ├── PSGallery.psd1
    │           ├── ScriptFunctions.psd1
    │           ├── ScriptSecurity.psd1
    │           ├── ScriptingStyle.psd1
    │           ├── core-6.0.2-linux.json
    │           ├── core-6.0.2-macos.json
    │           ├── core-6.0.2-windows.json
    │           ├── desktop-3.0-windows.json
    │           ├── desktop-4.0-windows.json
    │           └── desktop-5.1.14393.206-windows.json
    ├── SplitPipeline/
    │   ├── LICENSE.txt
    │   ├── README.htm
    │   ├── Release-Notes.htm
    │   ├── SplitPipeline.nuspec
    │   ├── SplitPipeline.psd1
    │   ├── [Content_Types].xml
    │   ├── _rels/
    │   │   └── .rels
    │   ├── en-US/
    │   │   ├── SplitPipeline.dll-Help.xml
    │   │   └── about_SplitPipeline.help.txt
    │   ├── package/
    │   │   └── services/
    │   │       └── metadata/
    │   │           └── core-properties/
    │   │               └── 9ffd50a1389e4b6d8b6c79579467ccd3.psmdcp
    │   └── tools/
    │       └── SplitPipeline/
    │           ├── LICENSE.txt
    │           ├── README.htm
    │           ├── Release-Notes.htm
    │           ├── SplitPipeline.psd1
    │           └── en-US/
    │               ├── SplitPipeline.dll-Help.xml
    │               └── about_SplitPipeline.help.txt
    └── psasync/
        ├── psasync.psd1
        └── psasync.psm1
Download .txt
SYMBOL INDEX (15 symbols across 1 files)

FILE: modules/ConfluencePS/2.5.0/ConfluencePS.Types.cs
  class Icon (line 9) | public class Icon {
    method ToString (line 14) | public override string ToString() {
  class User (line 19) | public class User {
    method ToString (line 24) | public override string ToString() {
  class Version (line 29) | public class Version {
    method ToString (line 36) | public override string ToString() {
  class Space (line 41) | public class Space {
    method ToString (line 49) | public override string ToString() {
  class Page (line 54) | public class Page {
    method ToString (line 64) | public override string ToString() {
  class Label (line 69) | public class Label {
    method ToString (line 73) | public override string ToString() {
  class ContentLabelSet (line 78) | public class ContentLabelSet {
  class Attachment (line 83) | public class Attachment {
    method ToString (line 95) | public override string ToString() {
Condensed preview — 143 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (6,702K chars).
[
  {
    "path": "Active Directory/Get-ADGroupMemberAddDate.ps1",
    "chars": 2702,
    "preview": "function Get-ADGroupMemberAddDate {\n    <#\n\t\t.SYNOPSIS\n            Vypise kdy byl dany uzivatel/skupina pridan do skupin"
  },
  {
    "path": "Active Directory/Get-ADGroupMemberChangesHistory.ps1",
    "chars": 3157,
    "preview": "Function Get-ADGroupMemberChangesHistory {\n    <#\n\t\t.SYNOPSIS\n            Vypise historii zmen ve clenstvi dane AD skupi"
  },
  {
    "path": "Active Directory/Get-ADGroupMemberRecursive.ps1",
    "chars": 4295,
    "preview": "function Get-ADGroupMemberRecursive {\n    <#\n    .SYNOPSIS\n    Function for outputting members login (samAccountName) of"
  },
  {
    "path": "Active Directory/Invoke-ADPasswordsAudit.ps1",
    "chars": 22459,
    "preview": "function Invoke-ADPasswordsAudit {\n    <#\n    .SYNOPSIS\n    Function for offline audit of AD user passwords using DSinte"
  },
  {
    "path": "Azure/Add-AzureADAppUserConsent.ps1",
    "chars": 9589,
    "preview": "#Requires -Module Microsoft.Graph.Authentication, Microsoft.Graph.Applications, Microsoft.Graph.Users, Microsoft.Graph."
  },
  {
    "path": "Azure/Get-AzureDevOpsOrganizationOverview.ps1",
    "chars": 2235,
    "preview": "#Requires -Module MSAL.PS\nfunction Get-AzureDevOpsOrganizationOverview {\n    <#\n    .SYNOPSIS\n    Function for getting "
  },
  {
    "path": "CHANGELOG.md",
    "chars": 261,
    "preview": "# ${project-name} Change Log\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based "
  },
  {
    "path": "Confluence/fill_confluence_table_with_aws_dns_records.ps1",
    "chars": 9416,
    "preview": "####################  !MODIFY TO MATCH YOUR ORGANIZATION!  ####################\n# $baseUri = 'https://contoso.atlassian"
  },
  {
    "path": "ConvertFrom-HTMLTable.ps1",
    "chars": 4176,
    "preview": "function ConvertFrom-HTMLTable {\n    <#\n    .SYNOPSIS\n    Function for converting ComObject HTML object to common Power"
  },
  {
    "path": "ConvertFrom-XML.ps1",
    "chars": 3629,
    "preview": "function ConvertFrom-XML {\n    <#\n    .SYNOPSIS\n    Function for converting XML object (XmlNode) to PSObject.\n\n    .DES"
  },
  {
    "path": "Copy-Item2.ps1",
    "chars": 29297,
    "preview": "function Copy-Item2 {\n    <#\n    .SYNOPSIS\n    Fce slouzi k chytremu kopirovani souboru/adresaru. Umoznuje i zabaleni zd"
  },
  {
    "path": "Export-ScheduledTask.ps1",
    "chars": 2123,
    "preview": "function Export-ScheduledTasks {\n    <#\n\t\t.SYNOPSIS\n\t\t\tExportuje scheduled tasky ve formě XML souborů.\n\n\t\t.DESCRIPTION\n"
  },
  {
    "path": "Export-ScriptsToModule.ps1",
    "chars": 19014,
    "preview": "function Export-ScriptsToModule {\n    <#\n    .SYNOPSIS\n        Function for generating Powershell module from ps1 scrip"
  },
  {
    "path": "FTP/install_FTP_server.ps1",
    "chars": 10504,
    "preview": "<#\nscript will set up new Windows Server as FTP server:\n- install FTP, IIS roles\n- create FTP site\n- create READ and WRI"
  },
  {
    "path": "Get-AdministrativeEvents.ps1",
    "chars": 9527,
    "preview": "function Get-AdministrativeEvents {\n    <#\n\t.SYNOPSIS\n        Fce slouží k vypsani Warning, Error a Critical eventu z vy"
  },
  {
    "path": "Get-ComputerInfo.ps1",
    "chars": 24477,
    "preview": "<#\nTODO:\ndodelat propertyset pro snadne filtrovani informaci\n#>\nFunction Get-ComputerInfo {\n    <#\n    .SYNOPSIS\n    Fc"
  },
  {
    "path": "Get-CurrentLoad.ps1",
    "chars": 21035,
    "preview": "function Get-CurrentLoad {\r\n    <#\r\n    .SYNOPSIS\r\n        Function for realtime outputting values of basic performance "
  },
  {
    "path": "Get-FailedScheduledTask.ps1",
    "chars": 8058,
    "preview": "function Get-FailedScheduledTask {\n    <#\n    .SYNOPSIS\n    Vypise scheduled tasky, ktere skoncily neuspechem.\n    \n    "
  },
  {
    "path": "Get-FirewallLog.ps1",
    "chars": 14834,
    "preview": "#TODO log muze byt i najinem miste, dokonce kazdy fw profil jej muze mit jinde, zjistit prikazem: netsh advfirewall show"
  },
  {
    "path": "Get-FirewallRules.ps1",
    "chars": 7128,
    "preview": "#TODO dodelat propertysety na name a direction, ktere nemohou byt spolu!\n#TODO displayName a action take nemohou byt spo"
  },
  {
    "path": "Get-FolderSize.ps1",
    "chars": 6075,
    "preview": "# TODO: fce neumí přistupovat do systémových adresářů jako System Volume Information, ošetřit.\nfunction Get-FolderSize {"
  },
  {
    "path": "Get-InstalledSoftware.ps1",
    "chars": 4489,
    "preview": "function Get-InstalledSoftware {\n    <#\n    .SYNOPSIS\n    Fce pro zjištění nainstalovaného software.\n\n    .DESCRIPTION\n "
  },
  {
    "path": "Get-LogOnOff.ps1",
    "chars": 8688,
    "preview": "function Get-LogOnOff {\n    <#\n\t.SYNOPSIS\n\t \tFce slouží k vypsání logon/off událostí na vybraných strojích uživatele/ů.\n"
  },
  {
    "path": "Get-LoggedOnUser.ps1",
    "chars": 2930,
    "preview": "function Get-LoggedOnUser {\n    <#\n\t\t.Synopsis\n\t\t\tFce pro zjištění, kdo je na stroji přihlášen.\n\n\t\t.Description\n\t\t\tKe zj"
  },
  {
    "path": "Get-NetworkCapture.ps1",
    "chars": 7266,
    "preview": "function Get-NetworkCapture {\r\n    [cmdletbinding()]\r\n    param (\r\n        $computerName = $env:computername\r\n        ,\r"
  },
  {
    "path": "Get-PSLog.ps1",
    "chars": 11699,
    "preview": "#TODO zrychlit cast, kdyz a dresaru zjistuji logy odpovidajici from/to (joby?)\nfunction Get-PSLog {\n    <#\n    .SYNOPSIS"
  },
  {
    "path": "Get-PendingReboot.ps1",
    "chars": 2811,
    "preview": "function Get-PendingReboot {\n    <#\n \t.SYNOPSIS\n        The PowerShell script which can be used to check if the server i"
  },
  {
    "path": "Get-ReliabilityHistory.ps1",
    "chars": 7261,
    "preview": "function Get-ReliabilityHistory {\n    <#\n    .SYNOPSIS\n    Vypise stroje ze seznamu, ktere maji prumerny index spolehliv"
  },
  {
    "path": "Get-SFCLogEvent.ps1",
    "chars": 1860,
    "preview": "function Get-SFCLogEvent {\n    <#\n    .SYNOPSIS\n    Function for outputting SFC related lines from CBS.log.\n\n    .DESCRI"
  },
  {
    "path": "Get-SIDFromAccount.ps1",
    "chars": 2290,
    "preview": "function Get-SIDFromAccount {\n    <#\n.SYNOPSIS\nFce pro zjisteni SID zadaneho uzivatele ci skupiny.\n.DESCRIPTION\nVe vycho"
  },
  {
    "path": "Get-Shutdown.ps1",
    "chars": 30510,
    "preview": "#requires -modules psasync\n\n<#\nTODO:\n- pridat moznost filtrovat dle zadaneho data\n- vyresit ze nekdy je vic restartu za "
  },
  {
    "path": "Get-Uptime.ps1",
    "chars": 1220,
    "preview": "Function Get-Uptime {\n    <#\n\t.SYNOPSIS\n    Vypise uptime zadaneho stroje.\n    \n\t.DESCRIPTION\n    Vypise uptime zadaneho"
  },
  {
    "path": "Get-WinEventArchivedIncluded.ps1",
    "chars": 17465,
    "preview": "function Get-WinEventArchivedIncluded {\n    <#\n\t\t.SYNOPSIS\n\t\t\tSlouzi k ziskani udalosti ze systemoveho logu i jeho archi"
  },
  {
    "path": "INTUNE/Connect-Graph.ps1",
    "chars": 1832,
    "preview": "function Connect-Graph {\n    <#\n    .SYNOPSIS\n    Function for connecting to Microsoft Graph.\n\n    .DESCRIPTION\n    Func"
  },
  {
    "path": "INTUNE/ConvertFrom-MDMDiagReport.ps1",
    "chars": 2957,
    "preview": "function ConvertFrom-MDMDiagReport {\n    <#\n    .SYNOPSIS\n    Function for converting MDMDiagReport.html to PowerShell "
  },
  {
    "path": "INTUNE/ConvertFrom-MDMDiagReportXML.ps1",
    "chars": 45511,
    "preview": "function ConvertFrom-MDMDiagReportXML {\n    <#\n    .SYNOPSIS\n    Function for converting Intune XML report generated by"
  },
  {
    "path": "INTUNE/Get-ClientIntunePolicyResult.ps1",
    "chars": 86595,
    "preview": "function Get-ClientIntunePolicyResult {\n    <#\n        .SYNOPSIS\n        Function for getting gpresult/rsop like report"
  },
  {
    "path": "INTUNE/Get-ClientStatusReport.ps1",
    "chars": 12514,
    "preview": "\n\n#Requires -Module ActiveDirectory\nfunction Get-ClientStatusReport {\n    <#\n    .SYNOPSIS\n    Function will gather cli"
  },
  {
    "path": "INTUNE/Get-IntuneDeviceComplianceStatus.ps1",
    "chars": 3748,
    "preview": "function Get-IntuneDeviceComplianceStatus {\n    <#\n    .SYNOPSIS\n    Function for getting device compliance status from"
  },
  {
    "path": "INTUNE/Get-IntuneEnrollmentStatus.ps1",
    "chars": 6737,
    "preview": "function Get-IntuneEnrollmentStatus {\n    <#\n    .SYNOPSIS\n    Function for checking whether computer is managed by Int"
  },
  {
    "path": "INTUNE/Get-IntuneLog.ps1",
    "chars": 5184,
    "preview": "function Get-IntuneLog {\n    <#\n    .SYNOPSIS\n    Function for Intune policies debugging on client.\n    - opens Intune l"
  },
  {
    "path": "INTUNE/Get-IntuneOverallComplianceStatus.ps1",
    "chars": 4968,
    "preview": "function Get-IntuneOverallComplianceStatus {\n    <#\n    .SYNOPSIS\n    Function for getting overall device compliance st"
  },
  {
    "path": "INTUNE/Get-IntuneReport.ps1",
    "chars": 10050,
    "preview": "function Get-IntuneReport {\n    <#\n    .SYNOPSIS\n    Function for getting Intune Reports data. As zip file (csv) or PS "
  },
  {
    "path": "INTUNE/Get-MDMClientData.ps1",
    "chars": 45575,
    "preview": "#Requires -Module ActiveDirectory\nfunction Get-MDMClientData {\n    <#\n    .SYNOPSIS\n    Function for getting client mana"
  },
  {
    "path": "INTUNE/Invoke-IntuneScriptRedeploy.ps1",
    "chars": 19045,
    "preview": "function Invoke-IntuneScriptRedeploy {\n    <#\n    .SYNOPSIS\n    Function for forcing redeploy of selected Script(s) dep"
  },
  {
    "path": "INTUNE/Invoke-IntuneWin32AppRedeploy.ps1",
    "chars": 12309,
    "preview": "function Invoke-IntuneWin32AppRedeploy {\n    <#\n    .SYNOPSIS\n    Function for forcing redeploy of selected Win32App de"
  },
  {
    "path": "INTUNE/Invoke-MDMReenrollment.ps1",
    "chars": 6608,
    "preview": "function Invoke-MDMReenrollment {\n    <#\n    .SYNOPSIS\n    Function for resetting device Intune management connection.\n"
  },
  {
    "path": "INTUNE/New-GraphAPIAuthHeader.ps1",
    "chars": 1988,
    "preview": "function New-GraphAPIAuthHeader {\n    <#\n    .SYNOPSIS\n    Function for generating header that can be used for authenti"
  },
  {
    "path": "INTUNE/Reset-HybridADJoin.ps1",
    "chars": 14116,
    "preview": "function Reset-HybridADJoin {\n    <#\n    .SYNOPSIS\n    Function for resetting Hybrid AzureAD join connection.\n\n    .DES"
  },
  {
    "path": "INTUNE/Reset-IntuneEnrollment.ps1",
    "chars": 42290,
    "preview": "function Reset-IntuneEnrollment {\n    <#\n    .SYNOPSIS\n    Function for resetting device Intune management connection.\n"
  },
  {
    "path": "INTUNE/Win32App/SetBitLockerPin/BitlockerIsEnabledAndNotSet.ps1",
    "chars": 290,
    "preview": "# prerequisite for showing up GUI for entering Bitlocker PIN\nif (Get-BitLockerVolume -MountPoint $env:SystemDrive -ea si"
  },
  {
    "path": "INTUNE/Win32App/SetBitLockerPin/DetectBitLockerPin.ps1",
    "chars": 122,
    "preview": "Write-Output $(Get-BitLockerVolume -MountPoint $env:SystemDrive).KeyProtector | Where { $_.KeyProtectorType -eq 'TpmPin'"
  },
  {
    "path": "INTUNE/Win32App/SetBitLockerPin/Popup.ps1",
    "chars": 128233,
    "preview": "# Author: Oliver Kieselbach (oliverkieselbach.com)\n# Date: 08/01/2019\n# Description: Creates a Windows Forms Dialog for"
  },
  {
    "path": "INTUNE/Win32App/SetBitLockerPin/SetBitLockerPin.ps1",
    "chars": 2201,
    "preview": "# https://oliverkieselbach.com/2019/08/02/how-to-enable-pre-boot-bitlocker-startup-pin-on-windows-with-intune/\n# Author:"
  },
  {
    "path": "Invoke-AsLoggedUser.ps1",
    "chars": 32651,
    "preview": "function Invoke-AsLoggedUser {\n    <#\n    .SYNOPSIS\n    Function for running specified code under all logged users (imp"
  },
  {
    "path": "Invoke-AsSystem.ps1",
    "chars": 8264,
    "preview": "function Invoke-AsSystem {\n    <#\n    .SYNOPSIS\n    Function for running specified code under SYSTEM account.\n\n    .DES"
  },
  {
    "path": "Invoke-Command2.ps1",
    "chars": 21116,
    "preview": "function Invoke-Command2 {\r\n    [CmdletBinding(DefaultParameterSetName = 'InProcess', HelpUri = 'https://go.microsoft.co"
  },
  {
    "path": "Invoke-NetworkCapture.ps1",
    "chars": 10834,
    "preview": "function Invoke-NetworkCapture {\n    <#\n    .SYNOPSIS\n    Funkce slouzi k zachyceni sitove komunikace na zadanem stroji."
  },
  {
    "path": "JIRA/New-JIRATicket.ps1",
    "chars": 4642,
    "preview": "function New-JIRATicket {\n    <#\n    .SYNOPSIS\n    Function for creating ticket.\n\n    THIS IS JUST A proof-of-concept! "
  },
  {
    "path": "OSD/OSDComputerName_via_ServiceTag/Set-CMTSStep_ServiceTag2OSDComputerName.ps1",
    "chars": 5817,
    "preview": "function Set-CMTSStep_ServiceTag2OSDComputerName {\n    <#\n    .SYNOPSIS\n    Function for setting Task Sequence Step, th"
  },
  {
    "path": "OSD/OfflineDomainJoin/Set-CMDeviceDJoinBlobVariable.ps1",
    "chars": 5452,
    "preview": "function Set-CMDeviceDJoinBlobVariable {\n    <#\n    .SYNOPSIS\n    Function for enabling Offline Domain Join in OSD proc"
  },
  {
    "path": "Quote-String.ps1",
    "chars": 3101,
    "preview": "function Quote-String {\n    <#\n    .SYNOPSIS\n    Function for splitting given text by delimiter and enclosing the resul"
  },
  {
    "path": "README.md",
    "chars": 711,
    "preview": "*NOTICE: most of these functions were migrated to PSH modules https://github.com/ztrhgf/useful_powershell_modules (are a"
  },
  {
    "path": "Read-FromClipboard.ps1",
    "chars": 8506,
    "preview": "function Read-FromClipboard {\n    <#\n    .SYNOPSIS\n    Read text from clipboard and tries to convert it to OBJECT.\n\n   "
  },
  {
    "path": "SCCM/Connect-SCCM.ps1",
    "chars": 4432,
    "preview": "function Connect-SCCM {\n    <#\n    .SYNOPSIS\n    Helper function for making session to SCCM server, to be able to call "
  },
  {
    "path": "SCCM/Get-CMLog.ps1",
    "chars": 52850,
    "preview": "function Get-CMLog {\n    <#\n    .SYNOPSIS\n    Function for easy opening of SCCM logs.\n\n    You have two options to defi"
  },
  {
    "path": "SCCM/Invoke-CMAdminServiceQuery.ps1",
    "chars": 13895,
    "preview": "function Invoke-CMAdminServiceQuery {\n    <#\n    .SYNOPSIS\n    Function for retrieving information from SCCM Admin Serv"
  },
  {
    "path": "SCCM/Invoke-CMComplianceEvaluation.ps1",
    "chars": 2579,
    "preview": "function Invoke-CMComplianceEvaluation {\n    <#\n    .SYNOPSIS\n    Function triggers evaluation of available SCCM complia"
  },
  {
    "path": "SCCM/Update-CMClientPolicy.ps1",
    "chars": 4059,
    "preview": "function Update-CMClientPolicy {\n    <#\n    .SYNOPSIS\n    Function for invoking update of SCCM client policy.\n\n    .DES"
  },
  {
    "path": "SCVMM/New-VMFromTemplate.ps1",
    "chars": 19099,
    "preview": "function New-VMFromTemplate {\n    <#\n    .SYNOPSIS\n    Function for creation of VM from existing VM template through SC"
  },
  {
    "path": "Search-ADObjectACL.ps1",
    "chars": 8482,
    "preview": "#Requires -Modules PowerShellAccessControl\nfunction Search-ADObjectACL {\n    <#\n    .SYNOPSIS\n    Funkce slouzi k najiti"
  },
  {
    "path": "Search-GPOSetting.ps1",
    "chars": 3348,
    "preview": "function Search-GPOSetting {\n    <#\n    .SYNOPSIS\n    Slouzi k vyhledani zadaneho stringu v nastavenich vsech AD GPO.\n\n "
  },
  {
    "path": "Shutdown-Computer.ps1",
    "chars": 14250,
    "preview": "#Requires -Modules psasync,SplitPipeline\n#TODO: pokud filtruji dle prihlaseneho uzivatele at se provede i kdyz je v disc"
  },
  {
    "path": "Start-TCPPortListener.ps1",
    "chars": 4529,
    "preview": "function Start-TCPPortListener {\n    <#\n    .SYNOPSIS\n    Funkce spusti naslouchani na zadanem TCP portu na zadanem poci"
  },
  {
    "path": "Test-Connection2.ps1",
    "chars": 5435,
    "preview": "Function Test-Connection2 {\n    <#\n        .SYNOPSIS\n            Funkce k otestovani dostupnosti stroju.\n\n        .DESCR"
  },
  {
    "path": "Test-Path2.ps1",
    "chars": 1603,
    "preview": "function Test-Path2 {\n\t<#\n\t.Synopsis\n\t Fce slouží ke zjištění, zdali existuje zadaná cesta. \t \n\t.Description\n\t.PARAMETER"
  },
  {
    "path": "Unlock-File.ps1",
    "chars": 3823,
    "preview": "function Unlock-File {\n    <#\n    .SYNOPSIS\n    Function for closing open handles for specified file.\n\n    .DESCRIPTION\n"
  },
  {
    "path": "Watch-EventLog.ps1",
    "chars": 8567,
    "preview": "function Watch-EventLog {\n    <#\n    .SYNOPSIS\n    Funkce pro realtime sledovani vybranych udalosti v sys logu.\n    Nale"
  },
  {
    "path": "Write-Log.ps1",
    "chars": 24179,
    "preview": "Function Write-Log {\n    <#\n\t.SYNOPSIS\n\t\tZapise predany text do konzole i log souboru.\n\n\t.DESCRIPTION\n\t\tZapise predany t"
  },
  {
    "path": "environmental variables/Get-EnvVariable.ps1",
    "chars": 4789,
    "preview": "function Get-EnvVariable {\n    <#\n\t\t.SYNOPSIS\n\t\tFce slouzi k ziskani obsahu vybrane systemove/uzivatelske promenne. \n\n\t\t"
  },
  {
    "path": "environmental variables/Remove-EnvVariable.ps1",
    "chars": 3491,
    "preview": "function Remove-EnvVariable {\n    <#\n\t.SYNOPSIS\n\tMaze cesty z sys. promennych/cele sys. promenne. Pouziva se napr. pro u"
  },
  {
    "path": "environmental variables/Set-EnvVariable.ps1",
    "chars": 5380,
    "preview": "function Set-EnvVariable {\n    <#\n\t.SYNOPSIS\n\tNastavuje hodnoty v jedne ze systemovych/uzivatelskych promennych (PATH, T"
  },
  {
    "path": "event subscriptions/Get-EventSubscription.ps1",
    "chars": 2696,
    "preview": "function Get-EventSubscription {\n    <#\n\t\t.SYNOPSIS\n\t\t\tFce pro vypsani event subskripci. Pripadne nastaveni nejake konkr"
  },
  {
    "path": "event subscriptions/Get-EventSubscriptionStatus.ps1",
    "chars": 7619,
    "preview": "function Get-EventSubscriptionStatus {\n    <#\n\t\t.SYNOPSIS\n\t\t\tFce pro vypsani stavu event subskripce/i.\n\n        .DESCRIP"
  },
  {
    "path": "event subscriptions/New-EventSubscription.ps1",
    "chars": 7855,
    "preview": "function New-EventSubscription {\n    <#\n    .SYNOPSIS\n        Fce pro vytvoreni event subskripce. Akceptuje bud soubor s"
  },
  {
    "path": "event subscriptions/Remove-EventSubscription.ps1",
    "chars": 418,
    "preview": "function Remove-EventSubscription {\n    [cmdletbinding()]\n    param (\n        [ValidateNotNullOrEmpty()]\n        [string"
  },
  {
    "path": "event subscriptions/Set-EventSubscription.ps1",
    "chars": 2601,
    "preview": "function Set-EventSubscription {\n    [cmdletbinding()]\n    param (\n        [Parameter(Mandatory = $true)]\n        [Valid"
  },
  {
    "path": "modules/AdmPwd.PS/AdmPwd.PS.format.ps1xml",
    "chars": 5237,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<Configuration>\n  <ViewDefinitions>\n    <View>\n      <Name>ExtendedRightsInfo</"
  },
  {
    "path": "modules/AdmPwd.PS/en-US/AdmPwd.PS.dll-Help.xml",
    "chars": 39875,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><helpItems xmlns=\"http://msh\" schema=\"maml\">\n<command:command xmlns:maml=\"http://"
  },
  {
    "path": "modules/ConfluencePS/2.5.0/CHANGELOG.md",
    "chars": 9411,
    "preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Chang"
  },
  {
    "path": "modules/ConfluencePS/2.5.0/ConfluencePS.Types.cs",
    "chars": 2514,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Collections;\n// using System.Linq;\n\nnamespace ConfluencePS\n"
  },
  {
    "path": "modules/ConfluencePS/2.5.0/ConfluencePS.format.ps1xml",
    "chars": 6338,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<!--<Configuration xmlns=\"http://schemas.microsoft.com/PowerShell/FormatData/200"
  },
  {
    "path": "modules/ConfluencePS/2.5.0/ConfluencePS.psd1",
    "chars": 4142,
    "preview": "#\n# Module manifest for module 'ConfluencePS'\n#\n# Generated by: Brian Bunke\n#\n# Generated on: 11/22/2015\n#\n\n@{\n\n    # S"
  },
  {
    "path": "modules/ConfluencePS/2.5.0/ConfluencePS.psm1",
    "chars": 95424,
    "preview": "#region Dependencies\n# Load the ConfluencePS namespace from C#\nif (!(\"ConfluencePS.Space\" -as [Type])) {\n    Add-Type -P"
  },
  {
    "path": "modules/ConfluencePS/2.5.0/LICENSE",
    "chars": 1077,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Brian Bunke\n\nPermission is hereby granted, free of charge, to any person obtai"
  },
  {
    "path": "modules/ConfluencePS/2.5.0/README.md",
    "chars": 6176,
    "preview": "---\nlayout: module\npermalink: /module/ConfluencePS/\n---\n# [ConfluencePS](https://atlassianps.org/module/ConfluencePS)\n\n["
  },
  {
    "path": "modules/ConfluencePS/2.5.0/en-US/ConfluencePS-help.xml",
    "chars": 327569,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<helpItems schema=\"maml\" xmlns=\"http://msh\">\n  <command:command xmlns:maml=\"http"
  },
  {
    "path": "modules/ConfluencePS/2.5.0/en-US/about_ConfluencePS.help.txt",
    "chars": 4172,
    "preview": "TOPIC\n    about_confluenceps\n\nSHORT DESCRIPTION\n    Interact with your Confluence wiki environments from PowerShell. Cr"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/PSScriptAnalyzer.psd1",
    "chars": 16360,
    "preview": "#\n# Module manifest for module 'PSScriptAnalyzer'\n#\n\n@{\n\n# Author of this module\nAuthor = 'Microsoft Corporation'\n\n# Scr"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/PSScriptAnalyzer.psm1",
    "chars": 15519,
    "preview": "#\n# Script module for module 'PSScriptAnalyzer'\n#\nSet-StrictMode -Version Latest\n\n# Set up some helper variables to make"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/ScriptAnalyzer.format.ps1xml",
    "chars": 19095,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<Configuration>\n<ViewDefinitions>\n  <View>\n    <Name>PSScriptAnalyzerView</Name"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/ScriptAnalyzer.types.ps1xml",
    "chars": 17016,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<Types>\n  <Type>\n    <Name>Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic."
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/CmdletDesign.psd1",
    "chars": 13390,
    "preview": "@{\n    IncludeRules=@('PSUseApprovedVerbs',\n                   'PSReservedCmdletChar',\n                   'PSReservedPa"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/CodeFormatting.psd1",
    "chars": 14158,
    "preview": "@{\n    IncludeRules = @(\n        'PSPlaceOpenBrace',\n        'PSPlaceCloseBrace',\n        'PSUseConsistentWhitespace',\n "
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/CodeFormattingAllman.psd1",
    "chars": 14159,
    "preview": "@{\n    IncludeRules = @(\n        'PSPlaceOpenBrace',\n        'PSPlaceCloseBrace',\n        'PSUseConsistentWhitespace',\n "
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/CodeFormattingOTBS.psd1",
    "chars": 14159,
    "preview": "@{\n    IncludeRules = @(\n        'PSPlaceOpenBrace',\n        'PSPlaceCloseBrace',\n        'PSUseConsistentWhitespace',\n "
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/CodeFormattingStroustrup.psd1",
    "chars": 14194,
    "preview": "# Inspired by https://eslint.org/docs/rules/brace-style#stroustrup\n@{\n    IncludeRules = @(\n        'PSPlaceOpenBrace',\n"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/DSC.psd1",
    "chars": 13043,
    "preview": "@{\n    IncludeRules=@('PSDSC*')\n}\n# SIG # Begin signature block\n# MIIkNgYJKoZIhvcNAQcCoIIkJzCCJCMCAQExDzANBglghkgBZQMEA"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/PSGallery.psd1",
    "chars": 14179,
    "preview": "@{\n    IncludeRules=@('PSUseApprovedVerbs',\n                   'PSReservedCmdletChar',\n                   'PSReservedPar"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/ScriptFunctions.psd1",
    "chars": 13470,
    "preview": "@{\n    IncludeRules=@('PSAvoidUsingCmdletAliases',\n                   'PSAvoidUsingWMICmdlet',\n                   'PSAv"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/ScriptSecurity.psd1",
    "chars": 13298,
    "preview": "@{\n    IncludeRules=@('PSAvoidUsingPlainTextForPassword',\n                   'PSAvoidUsingComputerNameHardcoded',\n     "
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/ScriptingStyle.psd1",
    "chars": 13101,
    "preview": "@{\n    IncludeRules=@('PSProvideCommentHelp',\n                   'PSAvoidUsingWriteHost')\n}\n# SIG # Begin signature blo"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/core-6.0.2-linux.json",
    "chars": 136801,
    "preview": "{\n  \"Modules\": [\n    {\n      \"Name\": \"Microsoft.PowerShell.Archive\",\n      \"Version\": \"1.1.0.0\",\n      \"ExportedCommands"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/core-6.0.2-macos.json",
    "chars": 136801,
    "preview": "{\n  \"Modules\": [\n    {\n      \"Name\": \"Microsoft.PowerShell.Archive\",\n      \"Version\": \"1.1.0.0\",\n      \"ExportedCommands"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/core-6.0.2-windows.json",
    "chars": 179586,
    "preview": "{\n  \"SchemaVersion\": \"0.0.1\",\n  \"Modules\": [\n    {\n      \"Name\": \"CimCmdlets\",\n      \"Version\": \"1.0.0.0\",\n      \"Export"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/desktop-3.0-windows.json",
    "chars": 1083240,
    "preview": "{\n    \"Modules\":  [\n                    {\n                        \"Name\":  \"AppLocker\",\n                        \"Versio"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/desktop-4.0-windows.json",
    "chars": 1213065,
    "preview": "{\n    \"Modules\":  [\n                    {\n                        \"Name\":  \"AppLocker\",\n                        \"Versio"
  },
  {
    "path": "modules/PSScriptAnalyzer/1.17.1/Settings/desktop-5.1.14393.206-windows.json",
    "chars": 1821748,
    "preview": "{\n    \"Modules\":  [\n                    {\n                        \"Name\":  \"AppBackgroundTask\",\n                        "
  },
  {
    "path": "modules/SplitPipeline/LICENSE.txt",
    "chars": 614,
    "preview": "SplitPipeline - Parallel Data Processing in PowerShell\nCopyright (c) 2011-2015 Roman Kuzmin\n\nLicensed under the Apache "
  },
  {
    "path": "modules/SplitPipeline/README.htm",
    "chars": 2851,
    "preview": "<html><title>README</title><body>\n<h1>Parallel Data Processing in PowerShell</h1>\n<p>PowerShell module for parallel dat"
  },
  {
    "path": "modules/SplitPipeline/Release-Notes.htm",
    "chars": 5785,
    "preview": "<html><title>Release-Notes</title><body>\n<h1>SplitPipeline Release Notes</h1>\n<h2>v1.5.2</h2>\n<p>Fixed #12 <code>Verbos"
  },
  {
    "path": "modules/SplitPipeline/SplitPipeline.nuspec",
    "chars": 1252,
    "preview": "<?xml version=\"1.0\"?>\n<package xmlns=\"http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd\">\n  <metadata>\n    <id>S"
  },
  {
    "path": "modules/SplitPipeline/SplitPipeline.psd1",
    "chars": 355,
    "preview": "@{\n\tAuthor = 'Roman Kuzmin'\n\tModuleVersion = '1.5.2'\n\tDescription = 'SplitPipeline - Parallel Data Processing in PowerSh"
  },
  {
    "path": "modules/SplitPipeline/[Content_Types].xml",
    "chars": 683,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Defa"
  },
  {
    "path": "modules/SplitPipeline/_rels/.rels",
    "chars": 496,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationship"
  },
  {
    "path": "modules/SplitPipeline/en-US/SplitPipeline.dll-Help.xml",
    "chars": 15050,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<helpItems xmlns=\"http://msh\" schema=\"maml\">\n<command:command xmlns:maml=\"http:/"
  },
  {
    "path": "modules/SplitPipeline/en-US/about_SplitPipeline.help.txt",
    "chars": 519,
    "preview": "\nTOPIC\n    about_SplitPipeline\n\nSHORT DESCRIPTION\n    SplitPipeline - Parallel Data Processing in PowerShell\n\nLONG DESC"
  },
  {
    "path": "modules/SplitPipeline/package/services/metadata/core-properties/9ffd50a1389e4b6d8b6c79579467ccd3.psmdcp",
    "chars": 988,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><coreProperties xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http:/"
  },
  {
    "path": "modules/SplitPipeline/tools/SplitPipeline/LICENSE.txt",
    "chars": 614,
    "preview": "SplitPipeline - Parallel Data Processing in PowerShell\nCopyright (c) 2011-2015 Roman Kuzmin\n\nLicensed under the Apache "
  },
  {
    "path": "modules/SplitPipeline/tools/SplitPipeline/README.htm",
    "chars": 2851,
    "preview": "<html><title>README</title><body>\n<h1>Parallel Data Processing in PowerShell</h1>\n<p>PowerShell module for parallel dat"
  },
  {
    "path": "modules/SplitPipeline/tools/SplitPipeline/Release-Notes.htm",
    "chars": 5785,
    "preview": "<html><title>Release-Notes</title><body>\n<h1>SplitPipeline Release Notes</h1>\n<h2>v1.5.2</h2>\n<p>Fixed #12 <code>Verbos"
  },
  {
    "path": "modules/SplitPipeline/tools/SplitPipeline/SplitPipeline.psd1",
    "chars": 355,
    "preview": "@{\n\tAuthor = 'Roman Kuzmin'\n\tModuleVersion = '1.5.2'\n\tDescription = 'SplitPipeline - Parallel Data Processing in PowerSh"
  },
  {
    "path": "modules/SplitPipeline/tools/SplitPipeline/en-US/SplitPipeline.dll-Help.xml",
    "chars": 15050,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<helpItems xmlns=\"http://msh\" schema=\"maml\">\n<command:command xmlns:maml=\"http:/"
  },
  {
    "path": "modules/SplitPipeline/tools/SplitPipeline/en-US/about_SplitPipeline.help.txt",
    "chars": 519,
    "preview": "\nTOPIC\n    about_SplitPipeline\n\nSHORT DESCRIPTION\n    SplitPipeline - Parallel Data Processing in PowerShell\n\nLONG DESC"
  },
  {
    "path": "modules/psasync/psasync.psd1",
    "chars": 2681,
    "preview": "#\n# Module manifest for module 'psasync'\n#\n# Generated by: Jon Boulineau\n#\n# Created on: 19 April 2012\n#\n \n@{\n \n    # S"
  },
  {
    "path": "modules/psasync/psasync.psm1",
    "chars": 4715,
    "preview": "<#\n\tModule file for psasync.\n#>\nAdd-Type @'\npublic class AsyncPipeline\n{\n    public System.Management.Automation.PowerS"
  }
]

// ... and 7 more files (download for full content)

About this extraction

This page contains the full source code of the ztrhgf/useful_powershell_functions GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 143 files (6.1 MB), approximately 1.6M tokens, and a symbol index with 15 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!