├── .gitignore ├── Add-RsToSubVs.ps1 ├── AudioCodesCdr.ps1 ├── AudioCodesIPPhone.ps1 ├── Compare-FileHash.ps1 ├── CryptoTools.ps1 ├── ExDagMaintenance └── ExDagMaintenance.psm1 ├── ExVirtualDir.ps1 ├── Exchange_TimeBasedInboxRule.ps1 ├── Find-UniqueIP.ps1 ├── Get-AdAuditReport.ps1 ├── Get-DiskSpaceReport.ps1 ├── Get-ExVirtualDirectory.ps1 ├── Get-InventoryInfo.ps1 ├── Get-TeamsNetAssessmentStats.ps1 ├── GitHub.ps1 ├── Invoke-DiskSpd.ps1 ├── Invoke-SEFAUtil.ps1 ├── Invoke-VocabTrainer.ps1 ├── M01Stats.ps1 ├── Microsoft.PowerShell_profile.ps1 ├── Microsoft.PowerShell_profile_osx.ps1 ├── New-FirewallRule.ps1 ├── New-SfBBackup.ps1 ├── README.md ├── Read-GPG.ps1 ├── Remove-LogFile.ps1 ├── Restore-VMPermission.ps1 ├── Restore-VmPermission.Tests.ps1 ├── Send-MailJetMail.ps1 ├── Send-SplunkEvent.ps1 ├── Start-ArchiveLog.ps1 ├── Start-OutlookBackup.ps1 ├── Start-TestWebServer.Tests.ps1 ├── Start-TestWebServer.ps1 ├── Telegram.ps1 ├── Test-GroupMembership.ps1 ├── Untitled-2.ps1 ├── Untitled-4.ps1 ├── Update-DashboardInfo.ps1 ├── WordPressApiDemo.ps1 ├── bv-calc.ps1 ├── chromecast.ps1 ├── compare-directory.ps1 ├── ews.ps1 ├── exchange.ps1 ├── get-sfbconnections.ps1 ├── ifttt.ps1 ├── kraken-stats.ps1 ├── logmytime.ps1 ├── new-cap.ps1 ├── nostr.ps1 ├── npslog.ps1 ├── prtg.ps1 ├── pskeybase.ps1 ├── qos.ps1 ├── rgsreport-xml.ps1 ├── sqltools.ps1 ├── tak.Import-NPSLog.ps1 ├── test-exchangeautodisco.ps1 ├── uccapilog.ps1 └── vmware.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /Add-RsToSubVs.ps1: -------------------------------------------------------------------------------- 1 | function Add-RsToSubVs { 2 | <# 3 | .Synopsis 4 | Add a Real Server to one or more sub virtual services on a KEMP load balancer. 5 | .DESCRIPTION 6 | This function calls Send-LBMessage to add a Real Server to sub virtual services. 7 | A filter for the virtual services nickname can be used in order to add the real server 8 | to multiple sub VS matching the filter. Alternatively, the function takes objects returned 9 | from Get-VirtualService and adds the real server to each returned VS. 10 | .EXAMPLE 11 | Add-RsToSubVs -SubVsFilter "Exchange 2013*" -RealServer 192.168.1.1 -Port 443 12 | 13 | This example adds RS 192.168.1.1:443 to all sub virutal services matching the name "Exchange 2013*" 14 | .EXAMPLE 15 | Get-VirtualService | Where-Object {$_.nickname -like "Exchange 2013*"} | Add-RsToSubVs -RealServer 192.168.1.1 -Port 443 -Weight 2000 16 | 17 | This example adds RS 192.168.1.1:443 to all virtual services returned by Get-VirtualService and the following filter. 18 | Please note: Get-VirtualService returns only sub virtual services, no addtional filtering is done. 19 | .INPUTS 20 | This cmdlet takes input objects from Get-VirtualService 21 | #> 22 | [CmdletBinding(SupportsShouldProcess=$true, 23 | ConfirmImpact='High')] 24 | param( 25 | [Parameter(Mandatory=$true, 26 | ValueFromPipelineByPropertyName=$true, 27 | ParameterSetName='Index')] 28 | [int] 29 | $Index, 30 | 31 | [Parameter(Mandatory=$true, 32 | ParameterSetName='NamePrefix')] 33 | [string] 34 | $SubVsFilter, 35 | 36 | [Parameter(Mandatory=$true)] 37 | [ipaddress] 38 | $RealServer, 39 | 40 | [Parameter(Mandatory=$true)] 41 | [ValidateRange(1,65535)] 42 | [int] 43 | $Port, 44 | 45 | [Parameter(Mandatory=$false)] 46 | [ValidateRange(1,65535)] 47 | [int] 48 | $Weight=1000 49 | ) 50 | 51 | if ($PSCmdlet.ParameterSetName -eq "Index") { Write-Verbose "VS from Pipeline, Index $Index" 52 | if ($pscmdlet.ShouldProcess("VS $Index", "Add RS $RealServer`:$Port")) { 53 | Send-LBMessage -command addrs -ParameterValuePair @{"vs"=$Index ; "rs"=$RealServer; "rsport"=$Port; "weight"=$Weight } 54 | } } else { 55 | Write-Verbose "Getting VS from Filter $SubVsFilter" 56 | $subVSGroup = Get-VirtualService | Where-Object {$_.nickname -like $SubVsFilter -and $_.mastervs -eq 0 -and (-not$_.subvs)} 57 | foreach ($subVs in $subVSGroup) { 58 | if ($pscmdlet.ShouldProcess($subVs.nickname, "Add RS $RealServer`:$Port")) { 59 | Send-LBMessage -command addrs -ParameterValuePair @{"vs"=$subVs.Index ; "rs"=$RealServer; "rsport"=$Port; "weight"=$Weight } 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /AudioCodesCdr.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Get-Cdr { 3 | [cmdletbinding()] 4 | param( 5 | [Parameter(Mandatory,ValueFromPipeline)] 6 | [System.IO.FileInfo] 7 | $Path, 8 | [Parameter(Mandatory)] 9 | [ValidateSet("MEDIA","SBC")] 10 | [string]$Type 11 | ) 12 | process { 13 | $Pattern = "\|CALL_[START|CONNECT|END]" 14 | if($type -eq "MEDIA") { 15 | $Pattern = "\|MEDIA_[START|UPDATE|END]" 16 | } 17 | if(Test-Path $Path) { 18 | Write-Verbose "Get CDR from: $Path" 19 | (Select-String -Pattern $Pattern -Path $Path | Select-Object -ExpandProperty line).TrimEnd() -replace "\|","," -replace "\s*,","," -replace "BYE(.*)","BYE" -replace "CANCEL(.*)","CANCEL" -replace "q.850(.*)","Q.850" 20 | } 21 | } 22 | } 23 | 24 | function Split-Cdr { 25 | [CmdletBinding()] 26 | param( 27 | [Parameter(Mandatory,ValueFromPipeline)] 28 | [System.IO.FileInfo] 29 | $Path, 30 | [System.IO.FileInfo] 31 | $OutputFolder = $env:temp 32 | ) 33 | # create output folder if it does not exist 34 | New-Item -Path $OutputFolder -ItemType Directory -ErrorAction SilentlyContinue 35 | $MediaPath = Join-path -Path $OutputFolder -ChildPath media.txt 36 | $SBCPath = Join-path -Path $OutputFolder -ChildPath sbc.txt 37 | # delete existing target files if they exist 38 | Remove-Item $MediaPath,$SBCPath -ErrorAction SilentlyContinue 39 | foreach($f in $path) { 40 | Get-Cdr -Path $f -Type SBC | Add-Content $SBCPath 41 | Get-Cdr -Path $f -Type MEDIA | Add-Content $MediaPath 42 | } 43 | } 44 | 45 | function Import-Cdr { 46 | <# 47 | .SYNOPSIS 48 | Import CDR from file. 49 | .DESCRIPTION 50 | This function uses Import-Csv to import CDR information form a file. 51 | .EXAMPLE 52 | PS C:\> Import-Cdr -Path .\CDR-2018-10-03-08.log -Header $Header 53 | This example imports Media CDRs. 54 | .INPUTS 55 | [system.io.fileinfo] 56 | .OUTPUTS 57 | [psobject] 58 | .NOTES 59 | Author: @torggler 60 | #> 61 | [CmdletBinding()] 62 | param( 63 | [Parameter(Mandatory,ValueFromPipeline)] 64 | [System.IO.FileInfo] 65 | $Path, 66 | [Parameter()] 67 | [string[]]$Header 68 | ) 69 | process { 70 | if(Test-Path $Path) { 71 | Import-Csv -Path $Path -Delimiter "," -Header $Header | Select-Object *,@{n="Jitter";e={$_.RTPjitter -as [int]}},@{n="Delay";e={$_.RTPdelay -as [int]}} -ExcludeProperty RTPjitter,RTPdelay 72 | } 73 | } 74 | } 75 | 76 | 77 | function Get-CdrTitle { 78 | <# 79 | .SYNOPSIS 80 | Extract CSV header from CDR file. 81 | .DESCRIPTION 82 | This function extracts the header information from a CDR file. This can later be used, to import the CDR using Import-Csv. 83 | .EXAMPLE 84 | PS C:\> Get-CdrTitle .\CDR-2018-10-03-08.log -Type SBC 85 | This example extracts the header for SBCReport. 86 | .EXAMPLE 87 | PS C:\> Get-CdrTitle .\CDR-2018-10-03-08.log -Type MEDIA 88 | This example extracts the header for MediaReport. 89 | .INPUTS 90 | [system.io.fileinfo] 91 | .OUTPUTS 92 | [string] 93 | .NOTES 94 | Author: @torggler 95 | #> 96 | [CmdletBinding()] 97 | param( 98 | [Parameter(Mandatory,ValueFromPipeline)] 99 | [System.IO.FileInfo] 100 | $Path, 101 | [Parameter(Mandatory)] 102 | [ValidateSet("MEDIA","SBC","GW")] 103 | [string]$Type 104 | ) 105 | switch ($type) { 106 | 'media' { $Pattern = "MediaReportType" } 107 | 'gw' { $Pattern = "GWReportType" } 108 | 'sbc' { $Pattern = "SBCReportType" } 109 | } 110 | 111 | if(Test-Path $Path) { 112 | #$title = Select-String -Path $path -Pattern $Pattern | Select-Object -ExpandProperty line | Select-Object -First 1 113 | #$out = $title -replace "^.*?\|","Timestamp|" -replace " ","" -split "\|" -replace "\(\w+\)","" -replace $Pattern,"ReportType" 114 | } 115 | if($Type -eq "MEDIA" -and -not($out)){ 116 | $out = @('Timestamp','ReportType','SIPCallId','SessionId','Cid','MediaType','Coder','Intrv','LocalRtpIp','LocalRtpPort','RemoteRtpIp','RemoteRtpPort','InPackets','OutPackets','LocalPackLoss','RemotePackLoss','RTPdelay','RTPjitter','TxRTPssrc','RxRTPssrc','LocalRFactor','RemoteRFactor','LocalMosCQ','RemoteMosCQ','TxRTPIPDiffServ','LatchedRtpIp','LatchedRtpPort','LatchedT38Ip','LatchedT38Port','CoderTranscoding','LegId') 117 | } elseif($Type -eq "SBC" -and -not($out)) { 118 | $out = @('Timestamp','ReportType','EPTyp','SIPCallId','SessionId','Orig','SourceIp','SourcePort','DestIp','DestPort','TransportType','SrcURI','SrcURIBeforeMap','DstURI','DstURIBeforeMap','Durat','TrmSd','TrmReason','TrmReasonCategory','SetupTime','ConnectTime','ReleaseTime','RedirectReason','RedirectURINum','RedirectURINumBeforeMap','TxSigIPDiffServ','IPGroup','SrdId','SIPInterfaceId','ProxySetId','IpProfileId','MediaRealmId','DirectMedia','SIPTrmReason','SipTermDesc') 119 | } elseif ($Type -eq "GW" -and -not($out)) { 120 | $out = @('Timestamp','ReportType','Cid','SessionId','LegId','Trunk','BChan','ConId','TG','EPTyp','Orig','SourceIp','DestIp','SrcTON','SrcNPI','SrcPhoneNum','SrcNumBeforeMap','DstTON','DstNPI','DstPhoneNum','DstNumBeforeMap','Duration','Coder','Intrv','RtpIp','Port','TrmSd','TrmReason','Fax','InPackets','OutPackets','PackLoss','RemotePackLoss','SIPCallId','SetupTime','ConnectTime','ReleaseTime','RTPdelay','RTPjitter','RTPssrc','RemoteRTPssrc','RedirectReason','TON','NPI','RedirectPhonNum','MeteringPulses','SrcHost','SrcHostBeforeMap','DstHost','DstHostBeforeMap','IPG (name)','LocalRtpIp','LocalRtpPort','Amount','Mult','TrmReasonCategory','RedirectNumBeforeMap','SrdId (name)','SIPInterfaceId (name)','ProxySetId (name)','IpProfileId (name)','MediaRealmId (name)','SigTransportType','TxRTPIPDiffServ','TxSigIPDiffServ','LocalRFactor','RemoteRFactor','LocalMosCQ','RemoteMosCQ','SigSourcePort','SigDestPort','MediaType','AMD','Percent','SIPTrmReason','SIPTer') 121 | } 122 | Write-Output $out 123 | } 124 | 125 | 126 | <# 127 | function Get-BadStream { 128 | [CmdletBinding()] 129 | param ( 130 | [Parameter(Mandatory)] 131 | [System.IO.FileInfo] 132 | $Path, 133 | [Parameter()] 134 | [ValidateSet("Jitter","Delay","PackLoss")] 135 | $Type = "Jitter", 136 | [int] 137 | $Count 138 | ) 139 | process { 140 | switch($Type){ 141 | 'Jitter' { $fs = {$_.RTPjitter -gt 1} } 142 | 'Delay' { $fs = {$_.RTPdelay -gt 1} } 143 | 'PackLoss' { $fs = {$_.RemotePackLoss -gt 1 -or $_.LocalPackLoss -gt 1}} 144 | } 145 | if($count) { 146 | ### Add Header! 147 | Import-Cdr -Path $Path | Where-Object -FilterScript $fs | Select-Object -First $Count 148 | } else { 149 | Import-Cdr -Path $Path | Where-Object -FilterScript $fs 150 | } 151 | 152 | } 153 | } 154 | #> 155 | 156 | function Get-NrFromUri { 157 | [CmdletBinding()] 158 | param ( 159 | [Parameter(Mandatory,ValueFromPipeline)] 160 | $InputObject 161 | ) 162 | process { 163 | foreach($i in $InputObject) { 164 | $i -replace "[@|;].*$","" 165 | } 166 | } 167 | } 168 | 169 | function Get-DateFromTime { 170 | [CmdletBinding()] 171 | param ( 172 | [Parameter(Mandatory,ValueFromPipeline)] 173 | $InputObject 174 | ) 175 | process { 176 | foreach($i in $InputObject) { 177 | $J = $i -split "UTC" 178 | Get-Date (($J[1] -replace "(\w{3})(\w{3})(\d{2})(\d{4})","`$1 `$2 `$3 `$4"),$J[0] -join " ") 179 | } 180 | } 181 | } 182 | 183 | function Get-HostFromPath { 184 | [CmdletBinding()] 185 | param ( 186 | [Parameter(Mandatory,ValueFromPipeline)] 187 | [System.IO.FileInfo] 188 | $InputObject 189 | ) 190 | process { 191 | # split the Path at \ and try to cast each part as [ipaddress]. Only returns valid casts. 192 | $InputObject.DirectoryName -split "\\" | ForEach-Object { $_ -as [ipaddress] } | Select-Object -ExpandProperty IPAddressToString 193 | } 194 | } 195 | 196 | function Get-MediaCdr { 197 | [cmdletbinding()] 198 | param($path) 199 | $header = Get-CdrTitle -Path $path -Type MEDIA 200 | $tempfile = Join-path -Path $env:temp -ChildPath "$((get-date).ticks).txt" 201 | Get-Cdr $path -Type MEDIA | Set-Content $tempfile 202 | Import-Cdr -Header $header -Path $tempfile 203 | } 204 | 205 | function Get-SbcCdr { 206 | [cmdletbinding()] 207 | param($path) 208 | $header = Get-CdrTitle -Path $path -Type SBC 209 | $tempfile = Join-path -Path $env:temp -ChildPath "$((get-date).ticks).txt" 210 | Get-Cdr $path -Type SBC | Set-Content $tempfile 211 | Import-Cdr -Header $header -Path $tempfile 212 | } 213 | 214 | function Get-GWCdr { 215 | [cmdletbinding()] 216 | param($path) 217 | $header = Get-CdrTitle -Path $path -Type gw 218 | $tempfile = Join-path -Path $env:temp -ChildPath "$((get-date).ticks).txt" 219 | Get-Cdr $path -Type sbc | Set-Content $tempfile 220 | Import-Cdr -Header $header -Path $tempfile 221 | } 222 | -------------------------------------------------------------------------------- /AudioCodesIPPhone.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Get-IPPCfg { 3 | <# 4 | .SYNOPSIS 5 | Get phone cfg from AudioCodes IP Phone Manager (Express). 6 | .DESCRIPTION 7 | This function uses Invoke-WebRequest to retreive configuration information from an AudioCodes IP Phone Manager. 8 | .EXAMPLE 9 | PS C:\> Get-IPPCfg -ComputerName acipp01.uclab.eu -Phone 450HD -Tenant uclab_de 10 | This example gets the config template file for 450HD phones in the uclab_de tenant. 11 | .INPUTS 12 | None. 13 | .OUTPUTS 14 | [psobject] 15 | .NOTES 16 | Author: @torggler 17 | #> 18 | [CmdletBinding()] 19 | param( 20 | # FQDN of the IP Phone Manager 21 | [Parameter(Mandatory)] 22 | [string]$ComputerName, 23 | # IP Phone Type 24 | [Parameter(Mandatory)] 25 | [ValidateSet("450HD","445HD","440HD","420HD")] 26 | [string]$Phone, 27 | # MAC Address of an IP Phone to get a specific config file 28 | [Parameter()] 29 | [string]$MacAddress = "00065BBC7AC7", 30 | # Tenant Name as configured on IP Phone Manager 31 | [Parameter()] 32 | [string]$Tenant = "Default" 33 | ) 34 | $uri = "http://{0}/ipp/tenant/{1}/{2}.cfg" -f $ComputerName,$Tenant,$MacAddress 35 | $ua = "AUDC-IPPhone-{0}_UC_0.0.0.0/0" -f $Phone 36 | $result = Invoke-WebRequest -Uri $uri -UserAgent $ua 37 | New-Object -TypeName psobject -Property ([ordered]@{ 38 | TemplateName = $result.headers["X-Template-Name"] 39 | Content = ConvertFrom-ByteArray($result.content) 40 | }) 41 | 42 | } 43 | function ConvertFrom-ByteArray($i) { 44 | $enc = [System.Text.Encoding]::ASCII 45 | $enc.GetString($i) 46 | } 47 | 48 | 49 | 50 | function Set-DhcpProvServ { 51 | <# 52 | .SYNOPSIS 53 | Set DHCP Option 160 (provisioning server) for a scope on one or more DHCP servers. 54 | .DESCRIPTION 55 | This function can be used to set the Provisioning Server DHCP Option (160) 56 | .EXAMPLE 57 | PS C:\> Set-DhcpProvServ -ComputerName "dhcp01.example.com" -ScopeId "192.168.1.0" -Uri "http://192.168.1.10/firmwarefiles;ipp/tenant/uclab_de" 58 | 59 | This example sets the provisioning server option for scope 192.168.1.0 on server dhcp01. 60 | .EXAMPLE 61 | PS C:\> $c = New-CimSession -ComputerName "dhcp01.example.com","dhcp02.example.com" 62 | PS C:\> Get-DhcpServerv4Scope -ScopeId 192.168.1.0,192.168.2.0 -CimSession $c | Set-DhcpProvServ -Uri "http://192.168.1.10/firmwarefiles;ipp/tenant/uclab_de" -PassThru 63 | 64 | OptionId Name Type Value VendorClass UserClass PolicyName 65 | -------- ---- ---- ----- ----------- --------- ---------- 66 | 160 Provisioning... String {http://192.168.1... 67 | 160 Provisioning... String {http://192.168.1... 68 | 160 Provisioning... String {http://192.168.1... 69 | 160 Provisioning... String {http://192.168.1... 70 | 71 | In this example, we create a new CIM session to two remote DHCP Servers and then we use Get-DhcpServerv4Scope to retrieve two scopes from each Server. 72 | We use the scopes as input for the Set-DhcpProvServ function and set the Option 160 to the same URI on all scopes. 73 | 74 | .INPUTS 75 | [Microsoft.Management.Infrastructure.CimInstance] 76 | .OUTPUTS 77 | None. 78 | .NOTES 79 | Author: @torggler 80 | #> 81 | [CmdletBinding( 82 | SupportsShouldProcess=$true, 83 | DefaultParameterSetName="ByComputerName" 84 | )] 85 | param( 86 | [Parameter(Mandatory,ParameterSetName="ByComputerName")] 87 | [string[]] 88 | $ComputerName, 89 | [Parameter(ParameterSetName="ByObject",ValueFromPipeline=$true)] 90 | [Microsoft.Management.Infrastructure.CimInstance] 91 | $InputObject, 92 | [Parameter()] 93 | [System.Net.IPAddress[]] 94 | $ScopeId, 95 | [Parameter(Mandatory)] 96 | [string] 97 | $Uri, 98 | [Parameter()] 99 | [switch] 100 | $PassThru 101 | ) 102 | process { 103 | if($InputObject) { 104 | Write-Verbose "Information by input object" 105 | $ComputerName = $InputObject.PSComputerName 106 | $ScopeId = $InputObject.ScopeId 107 | } 108 | foreach ($dhcpSrv in $ComputerName) { 109 | Write-Verbose "DHCP Server is $dhcpSrv" 110 | foreach ($scope in $ScopeId) { 111 | Write-Verbose "ScopeId is $scope" 112 | if ($pscmdlet.ShouldProcess("$dhcpSrv\$scope", "Set Option 160 to $Uri")) { 113 | Set-DhcpServerv4OptionValue -ScopeId $scope -OptionId 160 -Value $Uri -ComputerName $dhcpSrv -PassThru:$PassThru 114 | } 115 | } 116 | } 117 | } 118 | } 119 | 120 | 121 | 122 | function Get-DhcpProvServ { 123 | <# 124 | .SYNOPSIS 125 | Get DHCP Option 160 (provisioning server) for a scope from one or more DHCP servers. 126 | .DESCRIPTION 127 | This function can be used to get the Provisioning Server DHCP Option (160). 128 | .EXAMPLE 129 | PS C:\> Get-DhcpProvServ -ComputerName "dhcp01.example.com" -ScopeId "192.168.1.0" 130 | 131 | This example gets the provisioning server option for scope 192.168.1.0 from server dhcp01. 132 | .EXAMPLE 133 | PS C:\> $c = New-CimSession -ComputerName "dhcp01.example.com","dhcp02.example.com" 134 | PS C:\> Get-DhcpServerv4Scope -ScopeId 192.168.1.0,192.168.2.0 -CimSession $c | Get-DhcpProvServ 135 | 136 | In this example, we create a new CIM session to two remote DHCP Servers and then we use Get-DhcpServerv4Scope to retrieve two scopes from each Server. 137 | We use the scopes as input for the Get-DhcpProvServ function and get configured value for each scope. 138 | 139 | .INPUTS 140 | [Microsoft.Management.Infrastructure.CimInstance] 141 | .OUTPUTS 142 | [psobject] 143 | .NOTES 144 | Author: @torggler 145 | #> 146 | [CmdletBinding(DefaultParameterSetName="ByComputerName")] 147 | param( 148 | [Parameter(ParameterSetName="ByComputerName")] 149 | [string[]] 150 | $ComputerName, 151 | [Parameter(ParameterSetName="ByObject",ValueFromPipeline=$true)] 152 | [Microsoft.Management.Infrastructure.CimInstance] 153 | $InputObject, 154 | [Parameter()] 155 | [System.Net.IPAddress[]] 156 | $ScopeId, 157 | [Parameter()] 158 | [int] 159 | $optionId = 160 160 | ) 161 | process { 162 | if($InputObject) { 163 | $ComputerName = $InputObject.PSComputerName 164 | $ScopeId = $InputObject.ScopeId 165 | } 166 | foreach ($s in $ComputerName) { 167 | Write-Verbose "Server is $s" 168 | foreach($scope in $ScopeId){ 169 | Write-Verbose "Scope is $scope" 170 | $o = Get-DhcpServerv4OptionValue -ScopeId $scope -OptionId $optionId -ComputerName $s -ErrorAction SilentlyContinue 171 | New-Object -TypeName psobject -Property ([ordered]@{ 172 | DhcpServer = $s 173 | Scopde = $scope 174 | ProvisioningServer = $o.Value 175 | }) 176 | } 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /Compare-FileHash.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | function Compare-FileHash { 4 | [CmdletBinding()] 5 | param ( 6 | [ValidateScript({Test-Path $_ -PathType Container})] 7 | $Source, 8 | [ValidateScript({Test-Path $_ -PathType Container})] 9 | $Destination 10 | ) 11 | 12 | try { 13 | $sourceFiles = Get-ChildItem -Path $Source -Recurse -File -ErrorAction Stop 14 | $destinationFiles = Get-ChildItem -Path $Destination -Recurse -File -ErrorAction Stop 15 | } catch { 16 | Write-Warning "Could not get files: $_" 17 | break 18 | } 19 | $outArr = @() 20 | foreach($file in $sourceFiles) { 21 | $h1 = Get-FileHash -Path $file.FullName 22 | 23 | if ($destinationFiles.Where{$_.Name -eq $file.Name}) { 24 | $h2 = Get-FileHash -Path $destinationFiles.Where{$_.Name -eq $file.Name}.FullName 25 | 26 | if($h1.Hash -eq $h2.Hash) { 27 | Write-Verbose "$($file.FullName) matches $($h2.path)" 28 | } else { 29 | $out = @{ 30 | SourceFile = $h1.Path; 31 | SourceFileHash = $h1.Hash; 32 | DestinationFile = $h2.Path; 33 | DestinationFileHash = $h2.Hash; 34 | } 35 | Write-Output (New-Object -TypeName psobject -Property $out) 36 | } 37 | } else { 38 | Write-Verbose "Could not fild file $($file.Name) in $Destination" 39 | 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /CryptoTools.ps1: -------------------------------------------------------------------------------- 1 | function Convert-BchAddress { 2 | <# 3 | .SYNOPSIS 4 | Converts Bitcoin Cash Address formats. 5 | .DESCRIPTION 6 | This function uses https://cashaddr.bitcoincash.org to convert Bitcoin Cash address formats. It supports legacy and bitcoincash: address formats. 7 | The outout is a custom object containing all address formats and links to block explorers to for convenience. 8 | .EXAMPLE 9 | PS C:\> Convert-BchAddress -Address "1BppmEwfuWCB3mbGqah2YuQZEZQGK3MfWc" 10 | 11 | This example converts a legacy address to the new bitcoincash format. 12 | .EXAMPLE 13 | PS C:\> Convert-BchAddress -Address "bitcoincash:qpmtetdtqpy5yhflnmmv8s35gkqfdnfdtywdqvue4p" 14 | 15 | This example converts a new address to the legacy format. 16 | .INPUTS 17 | [string] 18 | .OUTPUTS 19 | [PSCustomObject] 20 | .NOTES 21 | More information: https://www.bitcoinabc.org/cashaddr 22 | #> 23 | [CmdletBinding()] 24 | param( 25 | [Parameter(ValueFromPipeline=$true, 26 | ValueFromPipelineByPropertyName=$true)] 27 | [ValidateNotNullOrEmpty()] 28 | $Address 29 | ) 30 | $uri = "https://cashaddr.bitcoincash.org/convert?address=$Address" 31 | try { 32 | $out = Invoke-RestMethod -Uri $uri -ErrorAction Stop 33 | } catch { 34 | Write-Warning "Could not connect." 35 | } 36 | $out | Add-Member -MemberType NoteProperty -Name "Blockchair" -Value "https://blockchair.com/bitcoin-cash/address/$($out.cashaddr.replace(":","%3A"))" 37 | $out | Add-Member -MemberType NoteProperty -Name "Blockdozer" -Value "https://blockdozer.com/address/$($out.cashaddr.replace(":","%3A"))" 38 | Write-Output $out 39 | } 40 | 41 | enum CryptoCurrencySymbol { 42 | eth 43 | btc 44 | ltc 45 | bch 46 | } 47 | 48 | class CryptoCurrency { 49 | [string]$address 50 | } 51 | 52 | class ETH : CryptoCurrency { 53 | ETH ($obj) { 54 | $this.address = $obj.address 55 | $this.balance = $obj.balance / 1000000000000000000 56 | $this.sent = $obj.total_sent / 1000000000000000000 57 | $this.received = $obj.total_received / 1000000000000000000 58 | } 59 | [double]$balance 60 | [double]$sent 61 | [double]$received 62 | } 63 | 64 | class BTC : CryptoCurrency { 65 | BTC ($obj) { 66 | $this.address = $obj.address 67 | $this.balance = $obj.balance / 100000000 68 | $this.sent = $obj.total_sent / 100000000 69 | $this.received = $obj.total_received / 100000000 70 | } 71 | [double]$balance 72 | [double]$sent 73 | [double]$received 74 | } 75 | function Get-BlockCypherAddress { 76 | [CmdletBinding()] 77 | param( 78 | [Parameter( 79 | Mandatory=$true, 80 | Position=0 81 | )] 82 | [string] 83 | $Address, 84 | [string] 85 | $Version = "v1" 86 | ) 87 | $Currency = Get-CurrencyFromAddress -Address $Address 88 | $Address = $Address.Replace("0x","") 89 | Write-Verbose "Currency is $Currency" 90 | Invoke-RestMethod https://api.blockcypher.com/$version/$currency/main/addrs/$Address 91 | } 92 | 93 | function Get-BlockCypherBalance { 94 | [CmdletBinding()] 95 | param( 96 | [Parameter( 97 | Mandatory=$true, 98 | Position=0 99 | )] 100 | [string] 101 | $Address, 102 | [string] 103 | $Version = "v1" 104 | ) 105 | $Currency = Get-CurrencyFromAddress -Address $Address 106 | $Address = $Address.Replace("0x","") 107 | Write-Verbose "Currency is $Currency" 108 | $r = Invoke-RestMethod https://api.blockcypher.com/$version/$currency/main/addrs/$Address/balance 109 | if ($r) { 110 | switch($Currency) { 111 | "eth" { 112 | [ETH]::new($r) 113 | } 114 | Default { 115 | [BTC]::new($r) 116 | } 117 | } 118 | } 119 | } 120 | 121 | function Get-BlockCypherTransaction { 122 | [CmdletBinding(DefaultParameterSetName="ByTxHash")] 123 | param( 124 | [Parameter( 125 | Position=0, 126 | ParameterSetName="ByTxHash", 127 | Mandatory=$True 128 | )] 129 | [string] 130 | $Hash, 131 | [Parameter( 132 | ParameterSetName="ByAddress", 133 | Mandatory=$True 134 | )] 135 | [string] 136 | $Address, 137 | [string] 138 | $Version = "v1", 139 | [Parameter(ParameterSetName="ByTxHash")] 140 | [CryptoCurrencySymbol] 141 | $Currency = "eth" 142 | ) 143 | 144 | # provide two possibilites of using the function, 145 | # either get a specific tx by hash or get all tx for a certain address 146 | 147 | if($Hash) { 148 | $hash = $hash.Replace("^0x","") 149 | Invoke-RestMethod https://api.blockcypher.com/$version/$currency/main/txs/$Hash 150 | } elseif ($Address) { 151 | Get-BlockCypherAddress -Address $Address | Select-Object -ExpandProperty txrefs 152 | } 153 | } 154 | 155 | function Get-CurrencyFromAddress { 156 | [CmdletBinding()] 157 | param($Address) 158 | switch ($Address) { 159 | { $_ -match "^[13][a-zA-Z0-9]{27,34}$" } { "btc" } 160 | { $_ -match "^L[a-km-zA-HJ-NP-Z1-9]{26,33}$" } { "ltc" } 161 | { $_ -match "^(0x)?[0-9a-f]{40}$" } { "eth" } 162 | } 163 | } 164 | 165 | 166 | function Get-BchBalance { 167 | [CmdletBinding()] 168 | param( 169 | $Address 170 | ) 171 | 172 | $result = Invoke-RestMethod "https://api.blockchair.com/bitcoin-cash/dashboards/address/$address" 173 | if($result){ 174 | $result.data 175 | } 176 | 177 | } 178 | 179 | function Get-BchTransaction { 180 | [CmdletBinding()] 181 | param( 182 | $Hash 183 | ) 184 | $result = Invoke-RestMethod "https://api.blockchair.com/bitcoin-cash/transactions?q=hash($hash)" 185 | if($result){ 186 | $result.data 187 | } 188 | } 189 | 190 | 191 | #https://explorer.bitcoin.com/api/btc/addr/14hbuMuFCGSCzQr3TCVqPUKGkDGbepEoe2 192 | #https://explorer.bitcoin.com/api/bch/txs/?address=14hbuMuFCGSCzQr3TCVqPUKGkDGbepEoe2 193 | #https://explorer.bitcoin.com/api/bch/txs/?hash=ffe712907e11479bdca14445f66b5fbabd4a5e2c73bc7d7f1511429a470be036 194 | 195 | 196 | function Get-ChainfeedTx { 197 | [CmdletBinding()] 198 | param ( 199 | [string] 200 | $Hash 201 | ) 202 | process { 203 | $Response = Invoke-RestMethod https://chainfeed.org/tx/$Hash 204 | New-Object -TypeName psobject -Property (@{ 205 | Sender = $Response.Sender 206 | Text = [System.Text.Encoding]::ASCII.GetString($Response.data[-1].buf.data) 207 | }) 208 | } 209 | } 210 | 211 | 212 | 213 | function get-unixtime { 214 | param([datetime]$date) 215 | if(-not $date) { 216 | $date = ([datetime]::UtcNow).AddHours(-1) 217 | } 218 | $ts = New-TimeSpan -Start (Get-Date 01.01.1970) -End $date 219 | [int]$ts.TotalSeconds 220 | } 221 | function invoke-whalealertapi { 222 | param( 223 | $endpoint = "status", 224 | $apikey = $whalealertapi, 225 | $baseUrl = "https://api.whale-alert.io/v1", 226 | $currency, 227 | $minvalue, 228 | $start = (get-unixtime) 229 | ) 230 | $header = @{ 231 | "X-WA-API-KEY" = $apikey 232 | } 233 | $uri = $baseUrl,$endpoint -join "/" 234 | $uri += "?start=$start" 235 | if($currency){ 236 | $uri += "¤cy=$currency" 237 | } 238 | if($minvalue){ 239 | $uri += "&min_value=$minvalue" 240 | } 241 | $r = Invoke-RestMethod -Uri $uri -Headers $header 242 | $r.transactions 243 | } 244 | function convertto-ether { 245 | param($i) 246 | $i/1000000000000000000 247 | } 248 | 249 | function invoke-etherscanio { 250 | param( 251 | [Parameter()] 252 | [ValidateSet("mainnet","goerli","rinkeby")] 253 | $network = "mainnet", 254 | [Parameter()] 255 | [ValidateSet("account","transaction")] 256 | $module = "account", 257 | $apikey = $etherscanapi 258 | ) 259 | if($network -eq "mainnet"){$url = ""} else {$url = "-$network"} 260 | $baseUrl = "https://api{0}.etherscan.io/api?module={1}" -f $url,$module 261 | 262 | if($address){ 263 | $baseUrl += "&action=balance&address=$address" 264 | } 265 | 266 | $baseUrl += "&apikey=$apikey" 267 | 268 | $r = Invoke-RestMethod -Uri $baseUrl 269 | $r 270 | } 271 | 272 | function Get-EtherScanBalance { 273 | param( 274 | $Address, 275 | $apikey = "N9XZ94DDT1NBD18PJ8DZKXU76ZUNDVRQ83" 276 | ) 277 | $out=invoke-etherscanio -address $Address 278 | convertto-ether -i $out.result 279 | } -------------------------------------------------------------------------------- /ExDagMaintenance/ExDagMaintenance.psm1: -------------------------------------------------------------------------------- 1 | ### 2 | # This is based on the following scripts by Justin Beeden 3 | # I made some adjustments for myself and merged the functions into a Module. 4 | # This is a work in progress and not yet fit for production use. 5 | # https://gallery.technet.microsoft.com/office/Exchange-2013-DAG-3ac89826 6 | ### 7 | 8 | function Start-ExDagMaintenance { 9 | <# 10 | .NOTES 11 | Written by Justin Beeden 12 | V2.1 2016-09-16 13 | 2.1 @torggler: Added -StopTransport switch to stop transport services. 14 | 2.0 Added logic to confirm DAG File Share Witness is operational to maintain quorum 15 | Added logic to confirm QueueTargetFQDN is a FQDN, will attempt to resolve to FQDN if hostname is entered 16 | Added logic to confirm mail queues have been moved to QueueTargetFQDN 17 | Added logic to confirm all active database copies have been moved to another DAG member 18 | 1.1 Corrected Spelling error in one of the parameters 19 | 1.0 Initial 20 | .SYNOPSIS 21 | Puts Exchange 2013 DAG nodes into maintenance mode. 22 | .DESCRIPTION 23 | Puts Exchange 2013 DAG nodes into maintenance mode. 24 | http://technet.microsoft.com/en-us/library/dd298065%28v=exchg.150%29.aspx#Pm 25 | .PARAMETER Server 26 | Specifies the DAG node Server name to be put into maintenance mode. 27 | .PARAMETER QueueTargetFQDN 28 | Specifies the target Exchange 2013 mailbox server FQDN to move the mail queue to from the Server to be put into maintenance mode. 29 | .PARAMETER StopTransport 30 | Stops the MSExchangeFrontendTransport and MSExchangeTransport services. 31 | .EXAMPLE 32 | PS> .\Start2013DagServerMaintenance.ps1 -Server Server1 -QueueTargetFQDN Server2.contoso.com 33 | Puts Server1 into maintenace mode and moves all queued mail to Server2 for delivery 34 | .EXAMPLE 35 | PS> .\Start2013DagServerMaintenance.ps1 -Server Server1 -QueueTargetFQDN Server2.contoso.com -StopTransport 36 | Puts Server1 into maintenace mode, moves all queued mail to Server2 for delivery and stops transport service on Server1 37 | #> 38 | 39 | #Requires -version 3.0 40 | 41 | [CmdletBinding()] 42 | Param( 43 | [Parameter(Position=0, Mandatory = $true, 44 | HelpMessage="Enter the name of the DAG Server to put into Maintenance mode.")] 45 | [string]$Server, 46 | 47 | [Parameter(Position=1, Mandatory = $true, 48 | HelpMessage="Enter FQDN of server to move mail queue to.")] 49 | [string]$QueueTargetFQDN, 50 | 51 | # Stop Transport Services 52 | [Parameter(Position=2, Mandatory = $false)] 53 | [switch]$StopTransport 54 | ) 55 | Begin 56 | { 57 | try { 58 | #If QueueTargetFQDN is not enterend as a FQDN will attempt to resolve it to a FQDN 59 | $TargetServer = ([System.Net.Dns]::GetHostByName($QueueTargetFQDN)).Hostname 60 | } 61 | catch { 62 | #If above does not resolve to a valid FQDN script will throw error and quit script 63 | Throw "Could not resolve ServerFQDN: $QueueTargetFQDN hostname needs to be resolvable FQDN." 64 | } 65 | } 66 | Process 67 | { 68 | ### Moved out creation of supporting functions. Rename! 69 | 70 | Write-Verbose "Checking DAG File Share Witness" 71 | CheckFSW 72 | 73 | Write-Verbose "Begining the process of draining the transport queues" 74 | Set-ServerComponentState $Server -Component HubTransport -State Draining -Requester Maintenance 75 | 76 | Write-verbose "Begining the process of draining all Unified Messaging calls" 77 | Set-ServerComponentState $Server -Component UMCallRouter –State Draining –Requester Maintenance 78 | 79 | Write-Verbose "Redirecting messages pending delivery in the local queues to $QueueTargetFQDN" 80 | Redirect-Message -Server $Server -Target $TargetServer -Confirm:$false 81 | 82 | Write-Verbose "Pausing the cluster node, which prevents the node from being and becoming the PrimaryActiveManager" 83 | Suspend-ClusterNode $Server 84 | 85 | Write-Verbose "Moving all active databases currently hosted on $Server to other DAG members" 86 | Set-MailboxServer $Server -DatabaseCopyActivationDisabledAndMoveNow $True 87 | 88 | Write-Verbose "Preventing $Server from hosting active database copies" 89 | Set-MailboxServer $Server -DatabaseCopyAutoActivationPolicy Blocked 90 | 91 | Write-Verbose "Placing $Server into maintenance mode" 92 | Set-ServerComponentState $Server -Component ServerWideOffline -State Inactive -Requester Maintenance 93 | } 94 | End 95 | { 96 | CheckQueues 97 | 98 | CheckActiveDatabase 99 | 100 | Write-Host "$Server is fully in maintenance mode and ready for maintenance." -ForegroundColor Green 101 | } 102 | } 103 | 104 | function Stop-ExDagMaintenance { 105 | 106 | <# 107 | .NOTES 108 | Written by Justin Beeden 109 | V1.2 11.16.2013 110 | .SYNOPSIS 111 | Removes Exchange 2013 DAG nodes out of maintenance mode. 112 | .DESCRIPTION 113 | Removes Exchange 2013 DAG nodes out of maintenance mode. 114 | http://technet.microsoft.com/en-us/library/dd298065%28v=exchg.150%29.aspx#Pm 115 | .PARAMETER Server 116 | Specifies the DAG node Server name to be removed from maintenance mode. 117 | .EXAMPLE 118 | PS> .\Stop2013DagServerMaintenance.ps1 -Server Server1 119 | #> 120 | 121 | #Requires -version 3.0 122 | 123 | [CmdletBinding()] 124 | Param( 125 | [Parameter(Mandatory = $true, 126 | HelpMessage="Enter the name of DAG Server to remove from Maintenance mode.")] 127 | [string]$Server 128 | ) 129 | 130 | #Designates that the server is out of maintenance mode 131 | Write-Verbose "Taking $Server out of maintenance mode" 132 | Set-ServerComponentState $Server -Component ServerWideOffline -State Active -Requester Maintenance 133 | 134 | #Allows the server to accept Unified Messaging calls 135 | Write-Verbose "$Server can now accept Unified Messaging calls." 136 | Set-ServerComponentState $Server -Component UMCallRouter –State Active –Requester Maintenance 137 | 138 | #Resumes the node in the cluster and enables full cluster functionality for the server 139 | Write-Verbose "Resuming the cluster node and enabling full cluster functionality." 140 | Resume-ClusterNode $Server 141 | 142 | #Allows databases to become active on the server 143 | Write-Verbose "$Server can now host active database copies." 144 | Set-MailboxServer $Server -DatabaseCopyActivationDisabledAndMoveNow $False 145 | 146 | #Removes the automatic activation blocks 147 | Write-Verbose "$Server can now automatically host active database copies." 148 | Set-MailboxServer $Server -DatabaseCopyAutoActivationPolicy Unrestricted 149 | 150 | #Resumes the transport queues and allows the server to accept and process messages 151 | Write-Verbose "Transport Queues on $Server are now active." 152 | Set-ServerComponentState $Server -Component HubTransport -State Active -Requester Maintenance 153 | 154 | Write-Host "$Server is now fully out of maintenance mode. You should now redistribute active database copies in the DAG." -ForegroundColor Green 155 | 156 | 157 | 158 | } 159 | 160 | function Get-ExDagMaintenance { 161 | <# 162 | .NOTES 163 | Written by Justin Beeden 164 | V1.3 12.05.2013 165 | .SYNOPSIS 166 | Checks Exchange 2013 DAG nodes maintenance mode settings. 167 | .DESCRIPTION 168 | Checks Exchange 2013 DAG nodes maintenance mode settings. 169 | http://technet.microsoft.com/en-us/library/dd298065%28v=exchg.150%29.aspx#Pm 170 | .PARAMETER Server 171 | Specifies the DAG node Server name to checked for maintenance mode settings. 172 | .EXAMPLE 173 | PS> .\Get2013DagServerMaintenance.ps1 -Server Server1 174 | .EXAMPLE 175 | PS> .\Get2013DagServerMaintenance.ps1 Server1 176 | #> 177 | 178 | #Requires -version 3.0 179 | 180 | [CmdletBinding()] 181 | Param( 182 | [Parameter(Position=0, Mandatory = $true, 183 | HelpMessage="Enter the name of DAG Server to check for Maintenance mode.")] 184 | [string]$Server 185 | ) 186 | 187 | #Shows if the server has been placed into maintenance mode 188 | Get-ServerComponentState $Server | Where {$_.Component -ne "Monitoring" -and $_.Component -ne "RecoveryActionsEnabled"} | ft Component,State -Autosize 189 | 190 | #Shows if the server is not hosting any active database copies 191 | Get-MailboxServer $Server | ft DatabaseCopy* -Autosize 192 | 193 | #Shows if that the cluster node is paused 194 | Get-ClusterNode $Server | fl 195 | 196 | #Shows that all transport queues have been drained 197 | Get-Queue -Server $Server 198 | 199 | } 200 | 201 | #Function to check all transport queues except Poison and Shadow queues are empty 202 | function CheckQueues() { 203 | $MessageCount = Get-Queue -Server $Server | Where {$_.Identity -notlike "*\Poison" -and $_.Identity -notlike"*\Shadow\*"} | Select MessageCount | Where {$_.MessageCount -ne 0} 204 | if($MessageCount){ 205 | Write-Host "$Server still has messages in transport queue, will check again in 30 seconds..." -ForegroundColor Yellow 206 | Start-Sleep -s 30 207 | CheckQueues 208 | } elseif($StopTransport){ 209 | Write-Host "Transport queues are empty, trying to stop transport services." -ForegroundColor Green 210 | try { 211 | Stop-Service MSExchangeFrontendTransport,MSExchangeTransport 212 | } 213 | catch { 214 | Write-Warning "Could not Stop Transport Services! Try stopping manually?" 215 | } 216 | } else{ 217 | Write-Host "Transport queues are empty." -ForegroundColor Green 218 | } 219 | } 220 | 221 | #Function to check all active database copies have been moved to another member of the DAG 222 | function CheckActiveDatabase() { 223 | $ActiveDatabase = Get-MailboxDatabaseCopyStatus -Server $Server | Where {$_.Status -eq 'Mounted'} 224 | if($ActiveDatabase){ 225 | Write-Host "$Server is still hosting active database copies, will check again in 30 seconds..." -ForegroundColor Yellow 226 | Start-Sleep -s 30 227 | CheckActiveDatabase 228 | } else{ 229 | Write-Host "All active database copies have been moved." -ForegroundColor Green 230 | } 231 | } 232 | 233 | #Function to check on DAGs File Share Witness if needed by DAG 234 | function CheckFSW(){ 235 | $FSW = Get-DatabaseAvailabilityGroup -Identity $Server.DatabaseAvailabilityGroup -Status | Where {$_.WitnessShareInUse -eq 'InvalidConfiguration'} 236 | if($FSW){ 237 | Throw "There is an issue with this DAGs File Share Witness, fix BEFORE doing node maintenance." 238 | } else { 239 | Write-Host "DAG File Share Witness OK or not in use." -ForegroundColor Green 240 | } 241 | } -------------------------------------------------------------------------------- /ExVirtualDir.ps1: -------------------------------------------------------------------------------- 1 | function Get-VirtualDir { 2 | param( 3 | $ExchangeServer, 4 | [switch]$AdProp 5 | ) 6 | 7 | foreach($server in $ExchangeServer) { 8 | 9 | $param = @{ 10 | 'Server' = $server; 11 | 'ADPropertiesOnly' = $true; 12 | 'ErrorAction' = 'Stop'; 13 | } 14 | 15 | try { 16 | $owa = Get-OwaVirtualDirectory @param 17 | $ews = Get-WebServicesVirtualDirectory @param 18 | $oab = Get-OabVirtualDirectory @param 19 | $mapi = Get-MapiVirtualDirectory @param 20 | $autodiscover = Get-ClientAccessService -Identity $server 21 | $oa = Get-OutlookAnywhere @param 22 | $eas = Get-ActiveSyncVirtualDirectory @param 23 | 24 | $out = @{ 25 | 'internal' = @{ 26 | 'Server' = $server 27 | 'OWA' = $owa.InternalUrl 28 | 'EWS' = $ews.InternalUrl 29 | 'OAB' = $oab.InternalUrl 30 | 'EAS' = $eas.InternalUrl 31 | 'Mapi' = $mapi.InternalUrl 32 | 'OutlookAnywhere' = $oa.InternalHostname 33 | 'Autodiscover' = $autodiscover.AutoDiscoverServiceInternalUri 34 | }; 35 | 'external' = @{ 36 | 'Server' = $server 37 | 'OWA' = $owa.ExternalUrl 38 | 'Ews' = $ews.ExternalUrl 39 | 'OAB' = $oab.ExternalUrl 40 | 'EAS' = $eas.ExternalUrl 41 | 'Mapi' = $mapi.ExternalUrl 42 | 'OutlookAnywhere' = $oa.ExternalHostname 43 | } 44 | } 45 | 46 | $out 47 | } catch { 48 | Write-Warning "Could not connect to $_" 49 | } 50 | 51 | } 52 | 53 | } 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Exchange_TimeBasedInboxRule.ps1: -------------------------------------------------------------------------------- 1 | # just testing... 2 | # connect to exchange online 3 | # Connect-Exchange -Online -Credential (Get-Credential) 4 | # 5 | # update rule daily (scheduled task) 6 | # Get-InboxRule -Mailbox tom -Identity move_night | 7 | # Set-InboxRule 8 | #-ExceptIfReceivedAfterDate (Get-Date 06:00) 9 | #-ExceptIfReceivedBeforeDate (Get-Date 15:00) 10 | # 11 | #Get-Mailbox tom | New-OffHoursFolder -Name "_offhours" -Verbose 12 | #Get-Mailbox tom | Get-OffHoursFolder -Name night 13 | #Get-Mailbox tom | Get-OffHoursRule -Name _offhours 14 | #Get-Mailbox tom | New-OffHoursRule -Name move_offhours -MoveToFolder _offhours -StartTime 08:00 -EndTime 17:00 -Verbose 15 | # 16 | 17 | function Connect-Exchange 18 | { 19 | [CmdletBinding()] 20 | Param 21 | ( 22 | # Credential used for connection 23 | [Parameter(Mandatory=$true)] 24 | [pscredential] 25 | $Credential 26 | ) 27 | $params = @{ 28 | ConnectionUri = "https://outlook.office365.com/powershell-liveid/"; 29 | ConfigurationName = "Microsoft.Exchange"; 30 | Credential = $Credential; 31 | Authentication = "Basic"; 32 | AllowRedirection = $true; 33 | } 34 | try { 35 | Write-Verbose "Trying to connect to $($params.ConnectionUri)" 36 | $sExch = New-PSSession @params -ErrorAction Stop -ErrorVariable ExchangeSessionError -Verbose:$false 37 | Import-PSSession $sExch 38 | } catch { 39 | Write-Warning "Could not connect to Exchange $($ExchangeSessionError.ErrorRecord)" 40 | } 41 | } 42 | 43 | # Returns Inbox Rule for a Mailbox 44 | # If rulename or mailbox not found, returns nothing 45 | function Get-OffHoursRule { 46 | [CmdletBinding()] 47 | param ( 48 | [Parameter(ValueFromPipeline=$true)] 49 | [ValidateNotNullOrEmpty()] 50 | [string] 51 | $Mailbox, 52 | [ValidateNotNullOrEmpty()] 53 | [string] 54 | $Name 55 | ) 56 | process { 57 | Get-InboxRule -Mailbox $mailbox -Identity $Name -ErrorAction SilentlyContinue 58 | } 59 | } 60 | 61 | function New-OffHoursRule { 62 | [CmdletBinding()] 63 | param ( 64 | [Parameter(ValueFromPipeline=$true)] 65 | [ValidateNotNullOrEmpty()] 66 | $Mailbox, 67 | [ValidateNotNullOrEmpty()] 68 | [string] 69 | $Name, 70 | [ValidateNotNullOrEmpty()] 71 | [string] 72 | $MoveToFolder, 73 | $StartTime, 74 | $EndTime 75 | ) 76 | process { 77 | # Check if target folder already exists 78 | $mbxFolder = Get-OffHoursFolder -Mailbox $Mailbox -Name $MoveToFolder 79 | # Create folder if doesn't exist 80 | if(-not($mbxFolder)){ 81 | $mbxFolder = New-OffHoursFolder -Mailbox $Mailbox -Name $MoveToFolder 82 | } 83 | # create folder id string in format: mailbox:\foldername 84 | $folderId = $Mailbox.Identity,$mbxFolder.Name -join ":\" 85 | Write-Verbose "Folder Id is $folderId" 86 | # Create Rule 87 | if(-not(Get-OffHoursRule -Mailbox $Mailbox -Name $Name)){ 88 | $params = @{ 89 | Mailbox = $Mailbox; 90 | Name = $Name; 91 | ExceptIfReceivedAfterDate = (Get-Date $StartTime).ToUniversalTime(); 92 | ExceptIfReceivedBeforeDate = (Get-Date $EndTime).ToUniversalTime(); 93 | MoveToFolder = $folderId; 94 | } 95 | Write-Verbose "Create Rule $Name in Mailbox $Mailbox" 96 | Write-Verbose "Move mails before $StartTime and after $EndTime to folder: $FolderId" 97 | New-InboxRule @params 98 | } 99 | } 100 | } 101 | 102 | function Update-OffHoursRule { 103 | [CmdletBinding()] 104 | param ( 105 | [Parameter(ValueFromPipeline=$true)] 106 | [ValidateNotNullOrEmpty()] 107 | $Mailbox, 108 | $Name, 109 | $StartTime, 110 | $EndTime 111 | ) 112 | process { 113 | $params = @{ 114 | ExceptIfReceivedAfterDate = (Get-Date $StartTime).ToUniversalTime(); 115 | ExceptIfReceivedBeforeDate = (Get-Date $EndTime).ToUniversalTime(); 116 | } 117 | Get-OffHoursRule -Mailbox $Mailbox -Name $Name | Set-InboxRule @params 118 | } 119 | } 120 | 121 | function Get-OffHoursFolder { 122 | [CmdletBinding()] 123 | param ( 124 | [Parameter(ValueFromPipeline=$true)] 125 | [ValidateNotNullOrEmpty()] 126 | [string] 127 | $Mailbox, 128 | [ValidateNotNullOrEmpty()] 129 | [string] 130 | $Name 131 | ) 132 | process { 133 | Get-MailboxFolder -Identity "$Mailbox`:\$Name" -ErrorAction SilentlyContinue 134 | } 135 | } 136 | 137 | function New-OffHoursFolder { 138 | [CmdletBinding()] 139 | param ( 140 | [Parameter(ValueFromPipeline=$true)] 141 | [ValidateNotNullOrEmpty()] 142 | [string] 143 | $Mailbox, 144 | [ValidateNotNullOrEmpty()] 145 | [string] 146 | $Name 147 | ) 148 | process { 149 | if(Get-MailboxFolder -Identity "$Mailbox`:\$Name" -ErrorAction SilentlyContinue) { 150 | Write-Verbose "Folder $Name exists in Mailbox $($Mailbox.Identity)" 151 | } else { 152 | New-MailboxFolder -Parent $Mailbox -Name $Name 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Find-UniqueIP.ps1: -------------------------------------------------------------------------------- 1 | function Find-UniuqeClientIP { 2 | <# 3 | .Synopsis 4 | Find and list unique client IPs in IIS logs. 5 | .DESCRIPTION 6 | This function was created for the Scripting Games 2013. 7 | This function uses regular expression to find IP addresses withing one or more IIS log files. 8 | You need to specify a folder containing one or more logfiles with a specified extension. The Filter parameter 9 | can be used to specify a file extension like *.log, *.txt. 10 | The Pattern 11 | parameter can be used to select a subset of client IP addresses. 12 | .EXAMPLE 13 | Find-UniqueClientIP -Path C:\temp\LogFiles 14 | 15 | This example finds unique client IP addresses in all .log files within C:\temp\LogFile 16 | .EXAMPLE 17 | Find-UniqueClientIP -Path C:\temp\LogFiles -Filter *.txt 18 | 19 | This example finds unique client IP addresses in all .txt files within C:\temp\LogFile. 20 | .EXAMPLE 21 | Find-UniqueClientIP -Path C:\temp\LogFiles -Pattern "192.168.*" -Verbose 22 | 23 | This example finds unique client IP addresses in all .log files within C:\temp\LogFile. 24 | Only IP addresses starting with "192.168." are included in the output. 25 | .OUTPUTS 26 | [string] 27 | #> 28 | [CmdletBinding(PositionalBinding=$true, 29 | ConfirmImpact='Medium')] 30 | [OutputType([string])] 31 | Param 32 | ( 33 | # Specify a folder containing one or more IIS log files with *.log extension. 34 | [Parameter(Mandatory=$true, 35 | ValueFromPipeline=$true, 36 | ValueFromPipelineByPropertyName=$true, 37 | Position=0)] 38 | [ValidateScript({Test-Path -Path $PSItem -PathType Container})] 39 | [Alias("LogFolder")] 40 | [System.IO.DirectoryInfo] 41 | $Path, 42 | 43 | # Specify a string filter to select an subset of client IP addresses. 44 | [Parameter(Mandatory=$false, 45 | Position=1)] 46 | [ValidateNotNull()] 47 | [ValidateNotNullOrEmpty()] 48 | [Alias("IpAddressPattern")] 49 | [string] 50 | $Pattern = "*", 51 | 52 | # specify which file extensions to include, like *.txt. Default to *.log. 53 | [Parameter(Mandatory=$false, 54 | Position=2)] 55 | [ValidateNotNull()] 56 | [ValidateNotNullOrEmpty()] 57 | [ValidatePattern('^\*\.\w{3}$')] 58 | [Alias("Extension")] 59 | [string] 60 | $Filter = "*.log" 61 | ) 62 | 63 | # regex used to match ip addersses in logfiles 64 | $RegExPattern = '\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b' 65 | 66 | # initialize variable and build the path used with select-string 67 | $filtered = @() 68 | $LogPath = Join-Path -Path $Path -ChildPath $Filter 69 | Write-Verbose "Path is: $LogPath" 70 | 71 | # use -matchall to get all IPs, first match is "s-ip"; second match is "c-ip"; using strings makes it easier 72 | $PatternMatches = Select-String -Path $LogPath -Pattern $RegExPattern -AllMatches | Select-Object -Property @{n="ServerIP";e={$PSItem.matches[0].toString()}},@{n="ClientIP";e={$PSItem.matches[1].toString()}} 73 | 74 | Write-Verbose "Applying filter pattern: $Pattern" 75 | 76 | foreach ($match in $PatternMatches) { 77 | # filter the ClientIP, this is 50% faster than using where-object 78 | if ($match.clientIP -like $Pattern) { 79 | # save all objects witch matching ClientIP in a container 80 | $filtered += $match 81 | } 82 | } 83 | # writing string values to the pipeline 84 | $filtered.clientIP | select -Unique 85 | } -------------------------------------------------------------------------------- /Get-AdAuditReport.ps1: -------------------------------------------------------------------------------- 1 | function Get-ADAuditReport { 2 | <# 3 | .Synopsis 4 | Get 20 random users from Active Directory. 5 | .DESCRIPTION 6 | This function gets a number of random users from Active Directory and displays the following information about them: SamAccountName, Department, Title, LastLogonTimeStamp, PasswordLastSet, Enabled, LockedOut. 7 | The number of users to query can be specified using the -ResultSetSize parameter, the number of random users to select out of the result set can be specified using the -Count parameter. 8 | The function creates an HTML report at a location spcified by -Path. The report will be opened using the default browser. 9 | If the ActiveDirectory PowerShell Module cannot be loaded locally, the function tries to create a PSRemoting session with a DC found by FindDomainController(). The -DomainController and -Credential parameters 10 | can be used to connect to a spcifiy Domain Contoller using specified credentials. 11 | If the ActiveDirectory PowerShell Module is available locally, it will always be preferd and -DomainController is ignored. 12 | .EXAMPLE 13 | Get-AdAuditReport -Path C:\temp\report.htm 14 | 15 | This example gets 512 users from Active Directory, it then randomly selects 20 of them and creates an audit report at c:\temp\report.htm. The report will be opened using the default browser. 16 | .EXAMPLE 17 | Get-AdAuditReport -Path C:\temp\report.htm -Count 40 -PassThru 18 | 19 | This example gets 512 users from Active Directory, it then randomly selects 40 of them and creates an audit report at c:\temp\report.htm. The report will be opened using the default browser. 20 | It also writes psobjects to the pipeline. 21 | .EXAMPLE 22 | Get-AdAuditReport -Path C:\temp\report.htm -DomainController dc01 -Credential (Get-Credential) -ResultSetSize 1000 -Count 10 23 | 24 | If the ActiveDirectory Module cannot be imported, this example tries to establish a PsRemoting session with dc01 using the credentials submitted by Get-Credential. 25 | It will then get 1000 users from AD and randomly select 10 of them. The report will again be generated at C:\temp\report.htm and be opened using the default browser. 26 | .OUTPUTS 27 | [psobject] 28 | .NOTES 29 | This function was created for the Scripting Games 2013. 30 | Author: thomas torggler; @torggler 31 | Date: 2013-05-20 32 | #> 33 | [CmdletBinding()] 34 | [OutputType([psobject])] 35 | Param 36 | ( 37 | # Specify a path for the HTML report, filename must end in html or htm. 38 | [Parameter(Mandatory=$true, 39 | Position=0)] 40 | [ValidatePattern("\.html$|\.htm$")] 41 | [Alias("Report")] 42 | [System.IO.FileInfo] 43 | $Path, 44 | 45 | # Specify the number of random accounts to get from AD. Defaults to 20. 46 | [Parameter(Position=1)] 47 | [ValidateRange(1,1000)] 48 | [int] 49 | $Count = 20, 50 | 51 | # Specify Domain Controller to connect to, uses FindDomainController() as a default value. 52 | [Parameter(Position=2)] 53 | [ValidateNotNull()] 54 | [ValidateNotNullorEmpty()] 55 | [Alias("DC")] 56 | [string] 57 | $DomainController = [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain().FindDomainController().Name, 58 | 59 | # Specify credentials to connect to the Domain Controller. 60 | [Parameter(Mandatory=$false)] 61 | [ValidateNotNull()] 62 | [ValidateNotNullOrEmpty()] 63 | [System.Management.Automation.Credential()] 64 | $Credential = [System.Management.Automation.PSCredential]::Empty, 65 | 66 | # Specify ResultSetSize for Get-ADUser command. Defaults to 512. 67 | [Parameter(Position=3)] 68 | [ValidateRange(1,4000)] 69 | [Alias("Set")] 70 | [int] 71 | $ResultSetSize = 512, 72 | 73 | # Speficy ResultPageSize for Get-ADUser command. Defaults to 256. 74 | [Parameter(Position=4)] 75 | [ValidateRange(1,2000)] 76 | [Alias("Page")] 77 | [int] 78 | $ResultPageSize = 256, 79 | 80 | # Output objects to the pipeline 81 | [switch] 82 | $PassThru 83 | ) 84 | 85 | ### no use for begin, process, end blocks as there is nothing piped in here. 86 | 87 | ### try to create an empty new repot file to check permissions 88 | try { 89 | New-Item -Path $Path -ItemType File -Force -ErrorAction Stop -ErrorVariable CreatePathError | Out-Null 90 | } catch { 91 | Write-Warning -Message "Could not create file: $($CreatePathError.ErrorRecord) Report will not be available" 92 | } 93 | 94 | ### try importing ActiveDirectory Module 95 | if ((Get-Module -ListAvailable ActiveDirectory) -ne $null) { 96 | Write-Verbose "ActiveDirectory Module found, trying to import" 97 | try { 98 | Import-Module ActiveDirectory -ErrorAction Stop -ErrorVariable ImportModuleError 99 | } catch { 100 | Write-Warning "Could not import ActiveDirectory PowerShell Module $($ImportModuleError.ErrorRecord)" 101 | } 102 | } else { 103 | Write-Verbose "Active Directory Module not found, trying to connect to $DomainController" 104 | try { 105 | $ADSession = New-PSSession -Name AD -ComputerName $DomainController -Credential $Credential -ErrorAction Stop -ErrorVariable PSSessionError 106 | Invoke-Command -Session $ADSession -ScriptBlock {Import-Module ActiveDirectory} 107 | Import-PSSession $ADSession -CommandName Get-ADUser | Out-Null 108 | } catch { 109 | Write-Warning "Could not connect to $DomainController $($PSSessionError.ErrorRecord)" 110 | } 111 | } 112 | 113 | if (Get-Command Get-ADUser -ErrorAction SilentlyContinue) { 114 | Write-Verbose "Get-ADUser command found, getting $ResultSetSize users" 115 | 116 | ### Getting AD Users with needed properties, using LastLogonTimeStamp because it is replicated between DomainControllers. 117 | ### LastLogon would provide more up to date information, but is only updated on the validating DC. 118 | 119 | $getAdUser = @{ 120 | 'Filter'='*'; 121 | 'ResultSetSize'=$ResultSetSize; 122 | 'ResultPageSize'=$ResultPageSize 123 | } 124 | ### Getting users from AD 125 | $RandomUsers = Get-ADUser @getAdUser -Properties Department, Title, LockedOut, LastLogonTimeStamp, PasswordLastSet | Get-Random -Count $Count 126 | $AuditReport = @() 127 | 128 | foreach ($User in $RandomUsers) { 129 | $AuditInfo = [ordered]@{ 130 | 'Username' = $User.SamAccountName; 131 | 'Department' = $User.Department; 132 | 'Title' = $User.Title; 133 | 'LastLogonTime' = [datetime]::FromFileTime($User.LastLogonTimeStamp); 134 | 'PasswordLastSet' = $User.PasswordLastSet; 135 | 'AccountDisabled' = -not $User.Enabled; 136 | 'AccountLockedOut' = $User.LockedOut 137 | } 138 | if ($User.LastLogonTimeStamp -eq $null) { 139 | $AuditInfo.LastLogonTime = "never" 140 | } 141 | if ($User.PasswordLastSet -eq $null) { 142 | $AuditInfo.PasswordLastSet = "never" 143 | } 144 | 145 | ### Add all user information objects to a single object 146 | $AuditReport += New-Object -TypeName PsObject -Property $AuditInfo 147 | 148 | } # end foreach $user 149 | 150 | ### if PassThru parameter is set, write objects to the pipeline 151 | if ($PassThru){ 152 | Write-Output $AuditReport 153 | } 154 | 155 | ### parameters for ConvertTo-Html 156 | $htmlParam = @{ 157 | 'Title'='Active Directory Audit Report' 158 | 'PreContent'="

Audit Report for $($Count) users out of $($ResultSetSize)

"; 159 | 'PostContent'="
$(Get-Date)" 160 | } 161 | 162 | ### convert the collection to HTML and write it to a file 163 | $AuditReport | ConvertTo-Html @htmlParam | Add-Content -Path $Path 164 | 165 | ### open the HTML report in the default browser 166 | Start-Process $Path 167 | } # end if 168 | 169 | } # end function Get-AdAuditReport -------------------------------------------------------------------------------- /Get-DiskSpaceReport.ps1: -------------------------------------------------------------------------------- 1 | function Get-DiskSpaceReport { 2 | <# 3 | .Synopsis 4 | Get information about free disk space from remote computers and create a HTML report. 5 | .DESCRIPTION 6 | This function uses WMI to query one or more remote computers for disk space information. A HTML report will be 7 | created for each Computer that could be reached. The -ReportPath parameter specifies a folder where the reports will 8 | be generated. The -ErrorLog parameter can be used to wirte unreachable computers to a separate file. 9 | .INPUTS 10 | [String[]] 11 | .OUTPUTS 12 | [none] 13 | .NOTES 14 | This function was created for the Scripting Games 2013. 15 | Author: thomas torggler; @torggler 16 | Date: 2013-05-12 17 | .EXAMPLE 18 | Get-DiskSpaceReport -ComputerName srv1 -ReportPath C:\temp 19 | 20 | This example creates a file srv1.html in c:\temp. The file contains information about srv1's fixed disks. 21 | .EXAMPLE 22 | Get-DiskSpaceReport -ComputerName (Get-Content C:\temp\servers.txt) -ReportPath C:\temp -ErrorLog C:\temp\error.txt 23 | 24 | This example assumes you have a file with server names, one per line, located at C:\temp\servers.txt. This example will create a file for every Server within c:\temp. It will also log unreachable Servers to C:\temp\error.txt. 25 | .EXAMPLE 26 | Get-DiskSpaceReport -ComputerName srv1 -ReportPath C:\temp -Credential (Get-Credential) -PassThru 27 | 28 | This example prompts for credentials before connecting to srv1. It creates a report at C:\temp\ and writes the resulting objects to the pipeline, too. 29 | #> 30 | 31 | [CmdletBinding()] 32 | [OutputType([psobject])] 33 | Param 34 | ( 35 | # Specify one or more Computername(s) using a comma to separate values. The function queries those Computername(s) for information. This parameter accepts pipline input. 36 | [Parameter(Mandatory=$true, 37 | ValueFromPipeline=$true, 38 | ValueFromPipelineByPropertyName=$true, 39 | Position=0)] 40 | [ValidateNotNull()] 41 | [ValidateNotNullOrEmpty()] 42 | [string[]] 43 | $ComputerName, 44 | 45 | # Speficy a folder where HTML reports will be generated. 46 | [Parameter(Mandatory=$true, 47 | Position=1)] 48 | [ValidateNotNull()] 49 | [ValidateNotNullOrEmpty()] 50 | [System.IO.DirectoryInfo] 51 | $ReportPath, 52 | 53 | # Specify a file to log unreachable computers. 54 | [Parameter(Mandatory=$false)] 55 | [ValidateNotNull()] 56 | [ValidateNotNullOrEmpty()] 57 | [System.IO.FileInfo] 58 | $ErrorLog, 59 | 60 | # Specify credentials to connect to the servers. 61 | [Parameter(Mandatory=$false)] 62 | [ValidateNotNull()] 63 | [ValidateNotNullOrEmpty()] 64 | [System.Management.Automation.Credential()] 65 | $Credential = [System.Management.Automation.PSCredential]::Empty, 66 | 67 | # Output objects to the pipeline 68 | [Switch] 69 | $PassThru 70 | ) 71 | 72 | Begin 73 | { 74 | ### If Credential parameter is used, add it to the Get-WmiObject cmdlet 75 | if ($Credential) { 76 | $wmiParam = @{'Credential'=$Credential; 'ErrorAction'="Stop"} 77 | } else { 78 | $wmiParam = @{'ErrorAction'="Stop"} 79 | } 80 | 81 | ### check if ReportPath exists and try to create folder if it doesn't 82 | try { 83 | if (-not(Test-Path $ReportPath)) { 84 | Write-Verbose "Report Path doesn't exist, trying to create $ReportPath" 85 | New-Item -Path $ReportPath -ItemType Directory -ErrorAction Stop -ErrorVariable CreateFolderError | Out-Null 86 | } 87 | Write-Verbose "Report Path exists" 88 | } catch { 89 | Write-Warning -Message "Could not create folder: $($CreateFolderError.ErrorRecord)" 90 | break 91 | } 92 | 93 | ### if ErrorLog is used, delete existing files and try to create a new one 94 | try { 95 | if ($ErrorLog) { 96 | Write-Verbose "ErrorLog will be created at $ErrorLog. If the file already exists, delete it" 97 | Remove-Item -Path $ErrorLog -ErrorAction SilentlyContinue 98 | New-Item $ErrorLog -ItemType File -ErrorAction Stop -ErrorVariable CreateErrorLogError | Out-Null 99 | } 100 | } catch { 101 | Write-Warning -Message "Could not create file: $($CreateErrorLogError.ErrorRecord) Script continues without ErrorLogging" 102 | # Delete Variable, so Out-File does not produce errors later on 103 | Remove-Variable -Name ErrorLog 104 | } 105 | 106 | } # End Begin 107 | 108 | Process 109 | { 110 | foreach ($Computer in $ComputerName) { 111 | Write-Verbose "Trying to connect to $Computer using WMI" 112 | $Continue = $true 113 | 114 | try { 115 | Write-Verbose "Querying WMI on $Computer" 116 | $LogicalDisk = Get-WmiObject @wmiParam -ComputerName $Computer -Class Win32_LogicalDisk -Filter “DriveType=3" -Property SystemName,DeviceID,FreeSpace,Size 117 | } catch { 118 | Write-Warning "Failed to connect to $Computer" 119 | if ($ErrorLog) { 120 | Write-Warning "Writing $Computer to $ErrorLog" 121 | $Computer | Out-File -FilePath $ErrorLog -Append 122 | } 123 | $Continue = $false 124 | } 125 | 126 | if ($Continue) { 127 | 128 | # Create HTML code for heading 129 | $htmlHead = " 130 | 131 | 132 | Disk Free Space Report 133 | 134 | 135 |

136 |

Local Fixed Disk Report for $($LogicalDisk.SystemName | select -Unique)

137 |

138 | 139 | 140 | 141 | 142 | 143 | 144 | " 145 | # initialize HTML table 146 | $htmlTable = $null 147 | 148 | ### enumerate disks, create a row in the table for each disk and create an array for optional output 149 | foreach ($Disk in $LogicalDisk) { 150 | 151 | # create dictionary to work with 152 | $Data = [ordered]@{ 153 | 'ComputerName'=$Disk.SystemName; 154 | 'DeviceID'=$Disk.DeviceID; 155 | 'Size'=$Disk.Size; 156 | 'FreeSpace'=$Disk.FreeSpace; 157 | } 158 | 159 | if ($PassThru) { 160 | # Write psobject to the pipeline, I don't convert the values here 161 | Write-Output (New-Object -TypeName PSObject -Property $Data) 162 | } 163 | 164 | ### Generate HTML Table 165 | $htmlTable += foreach ($item in $Data) { 166 | " 167 | 168 | " 169 | } 170 | 171 | } # end foreach disk in logicaldisk 172 | 173 | ### create end of the HTML file, includes horizontal rule and current date 174 | $htmlTail =" 175 |
DriveSize(GB)FreeSpace(MB)
$($item.DeviceID)$(“{0:N2}” -f($item.Size/1GB))$(“{0:N2}” -f($item.FreeSpace/1MB))
176 | 177 |


178 | $(Get-Date) 179 | 180 | " 181 | 182 | ### combining parts to report 183 | $htmlReport = $htmlHead + $htmlTable + $htmlTail 184 | 185 | ### try to write html report to a file 186 | try { 187 | $htmlReport | Out-File -FilePath $ReportPath\$($LogicalDisk.SystemName | select -Unique)".html" -ErrorAction Stop -ErrorVariable OutFileError 188 | } catch { 189 | Write-Warning "Could not create logfile: $($OutFileError.ErrorRecord)" 190 | } 191 | 192 | } # End if $Continue 193 | } # End ForEach $ComputerName 194 | } # End Process 195 | 196 | End 197 | { 198 | } # End End :) 199 | } # End Get-DiskSpaceReport -------------------------------------------------------------------------------- /Get-ExVirtualDirectory.ps1: -------------------------------------------------------------------------------- 1 | function Get-ExVirtualDirectory { 2 | <# 3 | .Synopsis 4 | Get information about Exchange virtual directories 5 | .DESCRIPTION 6 | Long description 7 | .EXAMPLE 8 | Get-ExVirtualDirectory | select * 9 | 10 | This should be run in a PowerShell console with the Exchange cmdlets available. It will then gather 11 | information about virtual directories from all Client Access Servers. 12 | .INPUTS 13 | Inputs to this cmdlet (if any) 14 | .OUTPUTS 15 | Output from this cmdlet (if any) 16 | .NOTES 17 | General notes 18 | .COMPONENT 19 | The component this cmdlet belongs to 20 | .ROLE 21 | The role this cmdlet belongs to 22 | .FUNCTIONALITY 23 | The functionality that best describes this cmdlet 24 | #> 25 | [CmdletBinding(DefaultParameterSetName='Parameter Set 1', 26 | SupportsShouldProcess=$true, 27 | PositionalBinding=$false, 28 | HelpUri = 'http://www.microsoft.com/', 29 | ConfirmImpact='Medium')] 30 | [Alias()] 31 | [OutputType([String])] 32 | Param 33 | ( 34 | # Param1 help description 35 | [Parameter(Mandatory=$false, 36 | ValueFromPipeline=$true, 37 | ValueFromPipelineByPropertyName=$true, 38 | ValueFromRemainingArguments=$false, 39 | Position=0, 40 | ParameterSetName='Parameter Set 1')] 41 | [ValidateNotNull()] 42 | [ValidateNotNullOrEmpty()] 43 | $Param1, 44 | 45 | # Specify a name filter for the servers to include. 46 | [string] 47 | $filter = "*" 48 | ) 49 | 50 | Begin { 51 | try { 52 | $servers = @(Get-ExchangeServer | ?{$_.ServerRole -like "*ClientAccess*" -and (($_.AdminDisplayVersion -like "*15*") -or ($_.AdminDisplayVersion -like "*14*") -and ($_.Name -like $Filter))} | Select-Object Name) 53 | } catch { 54 | Write-Warning "Error getting Exchange Server" 55 | break 56 | } 57 | } 58 | 59 | Process { 60 | $result = @() 61 | $i=0 62 | foreach ($server in $servers) { 63 | $i++ 64 | Write-Progress -Activity "Getting Autodiscover URL information" -Status "Progress:"-PercentComplete (($i / $servers.count)*100) 65 | $casServ = Get-ClientAccessServer -Identity $server.name | Select Name,AutodiscoverServiceInternalUri 66 | $result += $casServ 67 | Clear-Variable -Name casServ 68 | } 69 | $i=0 70 | foreach ($server in $servers) { 71 | $i++ 72 | Write-Progress -Activity "Getting Autodiscover VD information" -Status "Progress:"-PercentComplete (($i / $servers.count)*100) 73 | $autoDisco = Get-AutodiscoverVirtualDirectory -Server $server.name -AdPropertiesOnly | Select Name,Server,InternalAuthenticationMethods,ExternalAuthenticationMethods 74 | $result += $autoDisco 75 | Clear-Variable -Name autoDisco 76 | } 77 | $i=0 78 | foreach ($server in $servers) { 79 | $i++ 80 | Write-Progress -Activity "Getting OWA VD information" -Status "Progress:"-PercentComplete (($i / $servers.count)*100) 81 | $owa = Get-OWAVirtualDirectory -server $server.name -AdPropertiesOnly | Select Name,Server,InternalUrl,ExternalUrl,InternalAuthenticationMethods,ExternalAuthenticationMethods 82 | $result += $owa 83 | Clear-Variable -Name owa 84 | } 85 | $i=0 86 | foreach ($server in $servers) { 87 | $i++ 88 | Write-Progress -Activity "Getting ECP VD information" -Status "Progress:"-PercentComplete (($i / $servers.count)*100) 89 | $ecp = Get-ECPVirtualDirectory -server $server.name -AdPropertiesOnly | Select Name,Server,InternalUrl,ExternalUrl,InternalAuthenticationMethods,ExternalAuthenticationMethods 90 | $result += $ecp 91 | Clear-Variable -Name ecp 92 | } 93 | $i=0 94 | foreach ($server in $servers) { 95 | $i++ 96 | Write-Progress -Activity "Getting Outlook Anywhere information" -Status "Progress:"-PercentComplete (($i / $servers.count)*100) 97 | $oa = Get-OutlookAnywhere -server $server.name -AdPropertiesOnly | Select Name,Server,InternalHostname,ExternalHostname,ExternalClientAuthenticationMethod,InternalClientAuthenticationMethod,IISAuthenticationMethods 98 | $result += $oa 99 | Clear-Variable -Name oa 100 | } 101 | $i=0 102 | foreach ($server in $servers) { 103 | $i++ 104 | Write-Progress -Activity "Getting OAB VD information" -Status "Progress:"-PercentComplete (($i / $servers.count)*100) 105 | $oab = Get-OABVirtualDirectory -server $server.name -AdPropertiesOnly | Select Server,InternalUrl,ExternalUrl,ExternalAuthenticationMethods,InternalAuthenticationMethods,OfflineAddressBooks 106 | $result += $oab 107 | Clear-Variable -Name oab 108 | } 109 | $i=0 110 | foreach ($server in $servers) { 111 | $i++ 112 | Write-Progress -Activity "Getting ActiveSync VD information" -Status "Progress:"-PercentComplete (($i / $servers.count)*100) 113 | $eas = Get-ActiveSyncVirtualDirectory -server $server.name -AdPropertiesOnly | Select Server,InternalUrl,ExternalUrl,ExternalAuthenticationMethods,InternalAuthenticationMethods 114 | $result += $eas 115 | Clear-Variable -Name eas 116 | } 117 | $i=0 118 | foreach ($server in $servers) { 119 | $i++ 120 | Write-Progress -Activity "Getting Web Services information" -Status "Progress:"-PercentComplete (($i / $servers.count)*100) 121 | $ws = Get-WebServicesVirtualDirectory -server $server.name -AdPropertiesOnly | Select Server,InternalUrl,InternalNlbBypassUrl,ExternalUrl,ExternalAuthenticationMethods,InternalAuthenticationMethods 122 | $result += $ws 123 | Clear-Variable -Name ws 124 | } 125 | 126 | $result 127 | 128 | } 129 | 130 | End { 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Get-InventoryInfo.ps1: -------------------------------------------------------------------------------- 1 | function Get-InventoryInfo { 2 | <# 3 | .Synopsis 4 | Get inventory information from remote computers. 5 | .DESCRIPTION 6 | This function was created for the Scripting Games 2013. 7 | This function uses WMI to query one or more remote computers for inventory information. Including OS version, physical memory (in MB) and CPU. 8 | The function will always output objects to the pipline, using the -Report parameter an additional HTML report can be created. Using the 9 | -Credential parameter, credentials can be supplied to connect to the remote computers. To log unreachable ComputerNames to a file, use -ErrorLog. 10 | .INPUTS 11 | [String[]] 12 | .OUTPUTS 13 | [PSObject[]] 14 | .NOTES 15 | Author: thomas torggler; @torggler 16 | Date: 2013-05-06 17 | .EXAMPLE 18 | Get-InventoryInfo -ComputerName srv1 19 | 20 | This example shows inventory information about server: srv1 21 | .EXAMPLE 22 | Get-InventoryInfo -ComputerName (Get-Content C:\temp\servers.txt) -ErrorLog C:\temp\error.txt 23 | 24 | This example assumes you have a file with server names, one per line, located at C:\temp\servers.txt. This example will show inventory information about all accessible servers and log inaccessible ones to C:\temp\error.txt. 25 | .EXAMPLE 26 | Get-InventoryInfo -ComputerName srv1 -Report C:\temp\Report.html -Credential (Get-Credential) 27 | 28 | This example prompts for credentials before connecting to srv1. It creates and displays a simple HTML report at C:\temp\Report.hmtl, too. 29 | #> 30 | [CmdletBinding()] 31 | [OutputType([psobject])] 32 | Param 33 | ( 34 | # Specify one or more Computername(s) using a comma to separate values. The function queries those Computername(s) for information. This parameter accepts pipline input. 35 | [Parameter(Mandatory=$true, 36 | ValueFromPipeline=$true, 37 | ValueFromPipelineByPropertyName=$true, 38 | Position=0)] 39 | [ValidateNotNull()] 40 | [ValidateNotNullOrEmpty()] 41 | [string[]] 42 | $ComputerName, 43 | 44 | # Specify a file to log unreachable computers. 45 | [Parameter(Mandatory=$false)] 46 | [ValidateNotNull()] 47 | [ValidateNotNullOrEmpty()] 48 | [System.IO.FileInfo] 49 | $ErrorLog, 50 | 51 | # Speficy a file for a simple HTML report that will be generated. The file will be opened once the report has been generated. 52 | [Parameter(Mandatory=$false)] 53 | [ValidateNotNull()] 54 | [ValidateNotNullOrEmpty()] 55 | [System.IO.FileInfo] 56 | $Report, 57 | 58 | # Specify credentials to connect to the servers. 59 | [Parameter(Mandatory=$false)] 60 | [ValidateNotNull()] 61 | [ValidateNotNullOrEmpty()] 62 | [pscredential] 63 | $Credential 64 | ) 65 | 66 | Begin 67 | { 68 | # If ErrorLog has been used, delete existing files 69 | 70 | if ($ErrorLog) { 71 | Write-Verbose "ErrorLog will be created at $ErrorLog. If the file already exists, delete it" 72 | Remove-Item -Path $ErrorLog -ErrorAction SilentlyContinue 73 | } 74 | 75 | # If Report has been used, delete existing files 76 | 77 | if ($Report) { 78 | Write-Verbose "Report will be created at $Report. If the file already exists, delete it" 79 | Remove-Item -Path $Report -ErrorAction SilentlyContinue 80 | } 81 | 82 | # If Credential parameter is used, add it to the Get-WmiObject cmdlet 83 | 84 | if ($Credential) { 85 | $wmiParam = @{'Credential'=$Credential} 86 | } else { 87 | $wmiParam = @{} 88 | } 89 | } # End Begin 90 | 91 | Process 92 | { 93 | foreach ($Computer in $ComputerName) { 94 | Write-Verbose "Trying to connect to $Computer using WMI" 95 | $Continue = $true 96 | 97 | try { 98 | Write-Verbose "Querying WMI on $Computer" 99 | $OperatingSystem = Get-WmiObject @wmiParam -ComputerName $Computer -Class Win32_OperatingSystem -ErrorAction Stop 100 | } catch { 101 | Write-Warning "Failed to connect to $Computer" 102 | if ($ErrorLog) { 103 | Write-Warning "Writing $Computer to $ErrorLog" 104 | $Computer | Out-File -FilePath $ErrorLog -Append 105 | } 106 | $Continue = $false 107 | } 108 | 109 | if ($Continue) { 110 | 111 | ### Query WMI for information 112 | 113 | $PhysicalMemory = Get-WmiObject @wmiParam -ComputerName $Computer -Class Win32_PhysicalMemory 114 | $Processor = Get-WmiObject @wmiParam -ComputerName $Computer -Class Win32_Processor 115 | 116 | ### Calculate Physical Memory 117 | # initialize TotalPhysicalMemory to 0 118 | 119 | $TotalPhysicalMemory = 0 120 | 121 | if ($PhysicalMemory -is [System.Array]) { 122 | Write-Verbose "$Computer has $($PhysicalMemory.Count) physical memory banks installed" 123 | foreach ($Bank in $PhysicalMemory) { 124 | $TotalPhysicalMemory += $Bank.Capacity / 1MB 125 | } 126 | } else { 127 | Write-Verbose "$Computer has 1 physical memory bank installed" 128 | $TotalPhysicalMemory = $PhysicalMemory.Capacity / 1MB 129 | } 130 | 131 | ### Calculate number of sockets 132 | # initialize Sockets to 0 133 | 134 | $Sockets = 0 135 | 136 | if ($Processor -is [System.Array]) { 137 | Write-Verbose "$Computer has $($Processor.Count) CPUs installed" 138 | $Sockets = $($Processor.Count) 139 | } else { 140 | Write-Verbose "$Computer has only 1 CPU installed" 141 | $Sockets = 1 142 | } 143 | 144 | # As the numberOfCores property is not available pre Windows Vista/2008, only use that property if the OS supports it: http://msdn.microsoft.com/en-us/library/windows/desktop/aa394373(v=vs.85).aspx 145 | # initialize Cores to 0 146 | 147 | $Cores = 0 148 | 149 | if ([int]$OperatingSystem.BuildNumber -gt 6000) { 150 | Write-Verbose "$Computer is running $($OperatingSystem.Caption) - getting number of cores" 151 | if ($Processor -is [System.Array]) { 152 | foreach ($CPU in $Processor) { 153 | $Cores += $CPU.numberOfCores 154 | } 155 | } else { 156 | $Cores = $Processor.numberOfCores 157 | } 158 | } else { 159 | Write-Verbose "$Computer is running $($OperatingSystem.Caption) - can't get number of cores" 160 | $Cores = "n/a" 161 | } 162 | 163 | ### Create output object 164 | 165 | $Data = [Ordered]@{ 166 | 'ComputerName'=$OperatingSystem.CSName; 167 | 'OSName' = $OperatingSystem.Caption; 168 | 'OSBuild' = $OperatingSystem.BuildNumber; 169 | 'ServicePack' = $OperatingSystem.ServicePackMajorVersion; 170 | 'Sockets' = $Sockets; 171 | 'Cores' = $Cores; 172 | 'ProcessorArchitecture' = $Processor.AddressWidth; 173 | 'PhysicalMemory'= $TotalPhysicalMemory 174 | } 175 | 176 | # Write psobject to the pipeline 177 | 178 | Write-Output (New-Object -TypeName PSObject -Property $Data) 179 | 180 | ### Generate simple HTML Report 181 | 182 | if ($Report) { 183 | # collect Data for reporting purposes 184 | 185 | $ReportData = @() 186 | $ReportData += $Data 187 | 188 | # Create HTML header and Table headers 189 | $htmlHead = " 190 | 191 | 192 | Server Inventory Report 193 | 194 |

195 |

Server Inventory Report

196 |

197 |

198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | " 210 | # Create a new row in the table foreach dataset 211 | $htmlTable += foreach ($ReportDataSet in $ReportData) { 212 | "" 213 | } 214 | 215 | # Close HTML 216 | $htmlTail = " 217 |
Computer NameOperating System NameOperating System Build NumberService Pack VersionCPU SocketsCPU CoresProcessor ArchitectureTotal Physical Memory (MB)
$($ReportData.ComputerName)$($ReportData.OSName)$($ReportData.OSBuild)$($ReportData.ServicePack)$($ReportData.Sockets)$($ReportData.Cores)$($ReportData.ProcessorArchitecture)$($ReportData.PhysicalMemory)
218 | 219 |

---

220 | Check out the Scripting Games at: http://scriptinggames.org 221 | 222 | " 223 | 224 | # Combine the above pieces to a single HTML container 225 | $htmlOut = $htmlHead + $htmlTable +$htmlTail 226 | 227 | # Write HTML to file 228 | $htmlOut | Out-File $Report 229 | } 230 | } # End if $Continue 231 | } # End ForEach $ComputerName 232 | } # End Process 233 | 234 | End 235 | { 236 | # Show the HTML Report in a browser 237 | if ($Report) { 238 | # Report has been specified, check if there is a file 239 | if (Test-Path $Report) { 240 | Write-Verbose "Opening $Report ..." 241 | Start-Process -FilePath $Report 242 | } 243 | } 244 | } # End End :) 245 | } # End Get-InventoryInfo -------------------------------------------------------------------------------- /Get-TeamsNetAssessmentStats.ps1: -------------------------------------------------------------------------------- 1 | function Get-Jitter { 2 | [CmdletBinding()] 3 | param ( 4 | $Filename 5 | ) 6 | process { 7 | (Select-String -Path $Filename -Pattern "jitter:\s+(\d+\.\d+)\sms").matches.groups.where{$_.name -eq 1}.value | Measure-Object -Minimum -Maximum -Average 8 | } 9 | } 10 | 11 | function Get-PLoss { 12 | [CmdletBinding()] 13 | param ( 14 | $Filename 15 | ) 16 | process { 17 | (Select-String -Path $Filename -Pattern "loss\srate:\s+(\d+\.\d+)").matches.groups.where{$_.name -eq 1}.value | ForEach-Object {[double]$_*100} | Measure-Object -Minimum -Maximum -Average 18 | } 19 | } 20 | 21 | 22 | function Get-Latency { 23 | [CmdletBinding()] 24 | param ( 25 | $Filename 26 | ) 27 | process { 28 | (Select-String -Path $Filename -Pattern "latency:\s+(\d+(\.\d+)?)").matches.groups.where{$_.name -eq 1}.value | Measure-Object -Minimum -Maximum -Average 29 | } 30 | } 31 | 32 | function Get-TeamsNetAssessmentStats { 33 | [CmdletBinding()] 34 | param ( 35 | $Filename 36 | ) 37 | process { 38 | Write-Information "Packet Loss" -InformationAction Continue 39 | Get-PLoss -Filename $Filename 40 | Write-Information "Jitter" -InformationAction Continue 41 | Get-Jitter -Filename $Filename 42 | Write-Information "Latency" -InformationAction Continue 43 | Get-Latency -Filename $Filename 44 | } 45 | 46 | } 47 | 48 | #Get-TeamsNetAssessmentStats -Filename "C:\Users\thomas.torggler\Experts Inside GmbH\DT Swiss - Netzwerk Assessment\Biel - LAN\TeamsSkypeStatsDetail.txt" 49 | 50 | Get-TeamsNetAssessmentStats -Filename "C:\Users\thomas.torggler\Experts Inside GmbH\unisg - Network Assessment\StGallen - WIFI\TeamsSkypeStatsDetail.txt" 51 | 52 | -------------------------------------------------------------------------------- /GitHub.ps1: -------------------------------------------------------------------------------- 1 | 2 | function New-GHFile { 3 | [CmdletBinding()] 4 | param ( 5 | [Parameter(Mandatory)] 6 | [string]$Path, 7 | 8 | [Parameter(Mandatory)] 9 | [string]$Owner, 10 | 11 | [Parameter(Mandatory)] 12 | [string]$Repository, 13 | 14 | [Parameter(Mandatory)] 15 | [string]$Token, 16 | 17 | [Parameter(Mandatory)] 18 | [string]$Content, 19 | 20 | [string]$Message 21 | ) 22 | $Headers = @{ 23 | Authorization = "token $Token" 24 | Accept = "application/vnd.github.v3+json" 25 | } 26 | $Body = @{ 27 | message = $Message 28 | content = $(ConvertTo-Base64 -String $Content) 29 | } 30 | $uri = "https://api.github.com/repos/{0}/{1}/contents/{2}" -f $Owner,$Repository,$Path 31 | Invoke-RestMethod -Method PUT -Uri $Uri -Headers $Headers -Body ($Body | ConvertTo-Json) 32 | } 33 | 34 | 35 | function Get-GHFile { 36 | [CmdletBinding()] 37 | param ( 38 | [Parameter(Mandatory)] 39 | [string]$Path, 40 | 41 | [Parameter(Mandatory)] 42 | [string]$Owner, 43 | 44 | [Parameter(Mandatory)] 45 | [string]$Repository, 46 | 47 | [Parameter(Mandatory)] 48 | [string]$Token 49 | ) 50 | $Headers = @{ 51 | Authorization = "token $Token" 52 | Accept = "application/vnd.github.v3+json" 53 | } 54 | $uri = "https://api.github.com/repos/{0}/{1}/contents/{2}" -f $Owner,$Repository,$Path 55 | Invoke-RestMethod -Method Get -Uri $Uri -Headers $Headers 56 | } 57 | 58 | 59 | function Remove-GHFile { 60 | [CmdletBinding(SupportsShouldProcess)] 61 | param ( 62 | [Parameter(ParameterSetName="Path",Mandatory)] 63 | [string]$Path, 64 | 65 | [Parameter(ParameterSetName="Path",Mandatory)] 66 | [string]$Owner, 67 | 68 | [Parameter(ParameterSetName="Path",Mandatory)] 69 | [string]$Repository, 70 | 71 | [Parameter(ParameterSetName="Path",Mandatory)] 72 | [string]$Sha, 73 | 74 | [Parameter(ParameterSetName="Path")] 75 | [string]$Message, 76 | 77 | [Parameter(Mandatory)] 78 | [string]$Token, 79 | 80 | [Parameter(ParameterSetName="IO",ValueFromPipeline)] 81 | $InputObject 82 | ) 83 | 84 | if($InputObject){ 85 | $uri = $InputObject.url 86 | $Sha = $InputObject.sha 87 | $Message = "Deletes item $uri" 88 | } else { 89 | $uri = "https://api.github.com/repos/{0}/{1}/contents/{2}" -f $Owner,$Repository,$Path 90 | } 91 | 92 | $Headers = @{ 93 | Authorization = "token $Token" 94 | Accept = "application/vnd.github.v3+json" 95 | } 96 | $Body = @{ 97 | message = $Message 98 | sha = $Sha 99 | } 100 | if($PSCmdlet.ShouldProcess($uri,"Remove item")){ 101 | Write-Verbose -Message ($Body | ConvertTo-Json -Compress) 102 | Invoke-RestMethod -Method Delete -Uri $Uri -Headers $Headers -Body ($Body | ConvertTo-Json) 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /Invoke-DiskSpd.ps1: -------------------------------------------------------------------------------- 1 | 2 | <#PSScriptInfo 3 | 4 | .VERSION 0.1 5 | 6 | .GUID c3e1577a-fb7f-427b-b423-8126ee17dd9b 7 | 8 | .AUTHOR @torggler 9 | 10 | .COMPANYNAME 11 | 12 | .COPYRIGHT 13 | 14 | .TAGS 15 | 16 | .LICENSEURI 17 | 18 | .PROJECTURI 19 | 20 | .ICONURI 21 | 22 | .EXTERNALMODULEDEPENDENCIES 23 | 24 | .REQUIREDSCRIPTS 25 | 26 | .EXTERNALSCRIPTDEPENDENCIES 27 | 28 | .RELEASENOTES 29 | 30 | 31 | #> 32 | 33 | <# 34 | .Synopsis 35 | A wrapper for the DiskSpd utility 36 | .DESCRIPTION 37 | Long description 38 | .EXAMPLE 39 | Example of how to use this cmdlet 40 | .EXAMPLE 41 | Another example of how to use this cmdlet 42 | .INPUTS 43 | Inputs to this cmdlet (if any) 44 | .OUTPUTS 45 | Output from this cmdlet (if any) 46 | .COMPONENT 47 | The component this cmdlet belongs to 48 | .ROLE 49 | The role this cmdlet belongs to 50 | .FUNCTIONALITY 51 | The functionality that best describes this cmdlet 52 | #> 53 | 54 | [CmdletBinding(DefaultParameterSetName='Parameter Set 1', 55 | PositionalBinding=$false, 56 | ConfirmImpact='Medium')] 57 | [Alias('diskspd')] 58 | [OutputType([psobject])] 59 | Param 60 | ( 61 | # Specify the path to the Diskspd.exe file 62 | [Parameter(Mandatory=$false, 63 | Position=0, 64 | ParameterSetName='Parameter Set 1')] 65 | [ValidateNotNull()] 66 | [ValidateNotNullOrEmpty()] 67 | [System.IO.FileInfo] 68 | $Path=(Join-Path -Path $env:USERPROFILE -ChildPath "Downloads\Diskspd-v2.0.15\amd64fre\diskspd.exe"), 69 | 70 | # Specify a filepath where the testfile will be created. Default is TEMP 71 | [System.IO.FileInfo] 72 | $TestFilePath = (Join-Path -Path $env:TEMP -ChildPath "diskspdtest.dat"), 73 | 74 | # Specify a path to the LogFile [Parameter(Mandatory=$false)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [System.IO.FileInfo] $LogFile="$env:TEMP\log-Invoke-DiskSpd.txt", 75 | 76 | # Specify the filesize for the test file. Default is 1GB 77 | [int] 78 | $TestFileSize = 1GB, 79 | 80 | # Specify the block size in bytes. Default is 4kb 81 | [int] 82 | $BlockSize = 4kb, 83 | 84 | # Specify percentage of writes. Default is 50. 0 indicates 100% read operations. 85 | [int] 86 | [ValidateRange(0,100)] 87 | $WritePercentage = 50, 88 | 89 | [int] 90 | $OutstandingIos, 91 | 92 | # Specify duration in seconds. Default is 60. 93 | [int] 94 | [ValidateRange(1,86400)] 95 | $Duration = 60, 96 | 97 | # Specify warmup duration before measurement starts in seconds. Default is 5 seconds. 98 | [int] 99 | [ValidateRange(1,86400)] 100 | $WarmUp = 5, 101 | 102 | # Specify number of threads to use for workload. Default is number of CPU cores. 103 | [int] 104 | $Threads, 105 | 106 | # Use a buffer, filled with random data, of the specified size, as source for write operations. 107 | # If not specified, a repeating pattern is used to fill write buffers. 108 | [int] 109 | $RandomWriteBufferSize, 110 | 111 | # Delete the testfile after finishing. If not specified, the testfile will not be deleted and can be used 112 | # for additional tests. 113 | [switch] 114 | $RemoveTestFile, 115 | 116 | # Do not capture latency data. Default is capture latency. 117 | [switch] 118 | $NoLatency, 119 | 120 | # Enable software and hardware caching. If TestFileSize fits into RAM, this essentially measures RAM performance. 121 | # Default is both soft/hardware caching disabled. 122 | [switch] 123 | $EnableCaching, 124 | 125 | # Random IO, if not specified, sequential-interlocked is used. 126 | [switch] 127 | $Random 128 | 129 | ) 130 | 131 | #region set environment # save the current location, then change location to the ResKit directory $CurrentLocation = Get-Location Set-Location $Path.DirectoryName Write-Verbose "Changed Location to $($Path.DirectoryName)" # delete existing logfile, and create a new one Remove-Item -Path $logfile -ErrorAction SilentlyContinue "$(Get-Date) Invoke-DiskSpd started" | Add-Content $LogFile # Write location of logfile to host Write-Host "LogFile: $LogFile" -ForegroundColor Yellow 132 | 133 | #endregion 134 | 135 | #region DiskSpd parameters 136 | 137 | $DiskSpd = ".\diskspd.exe" 138 | $DiskSpdParams = " -d$Duration -W$WarmUp -w$WritePercentage -b$BlockSize -c$TestFileSize" 139 | 140 | if(-not($NoLatency)) { 141 | $DiskSpdParams += " -L" 142 | } 143 | 144 | if(-not($EnableCaching)){ 145 | $DiskSpdParams += " -h" 146 | } 147 | 148 | if($RandomWriteBufferSize) { 149 | $DiskSpdParams += " -Z$RandomWriteBufferSize" 150 | } 151 | 152 | if($OutstandingIos) { 153 | $DiskSpdParams += " -o$OutstandingIos" 154 | } 155 | 156 | if($Random) { 157 | $DiskSpdParams += " -r" 158 | } else { 159 | $DiskSpdParams += " -si" 160 | } 161 | 162 | if($Threads) { 163 | $DiskSpdParams += " -t$Threads" 164 | } else { 165 | # borrowed from diskspdbatch (technet gallery) 166 | # get CPU cores to determine number of threads (non-hyper-threaded) 167 | $processors = Get-CimInstance -ComputerName localhost Win32_Processor 168 | $Cores = 0 169 | if ( @($processors)[0].NumberOfCores) { 170 | $Cores = @($processors).Count * @($processors)[0].NumberOfCores 171 | } else { 172 | $Cores = @($processors).Count 173 | } 174 | $DiskSpdParams += " -t$Cores" 175 | } 176 | 177 | #endregion DiskSpd params 178 | 179 | Write-Verbose "Invoking $DiskSpd $DiskSpdParams `"$TestFilePath`"" 180 | 181 | $DiskSpdOutput = Invoke-Expression -Command ($DiskSpd + $DiskSpdParams + " " + $TestFilePath) 182 | $DiskSpdOutput 183 | 184 | if ($RemoveTestFile) { 185 | Write-Verbose "Removing testfile at $TestFilePath" 186 | Remove-Item $TestFilePath 187 | } 188 | #region reset environment # set location back to where we have been originally Set-Location $CurrentLocation Write-Verbose "Changed Location back to original" "$(Get-Date) Invoke-DiskSpd finished" | Add-Content $LogFile 189 | 190 | #endregion 191 | -------------------------------------------------------------------------------- /Invoke-VocabTrainer.ps1: -------------------------------------------------------------------------------- 1 | 2 | [CmdletBinding()] 3 | param ( 4 | [System.IO.FileInfo] 5 | $Path = "C:\Users\Thomas\OneDrive\Audio\vocab\vocab_noun.csv", 6 | $Count, 7 | [ValidateSet("de","it","en","us")] 8 | $SourceLanguage = "de", 9 | [ValidateSet("de","it","en","us")] 10 | $DestinationLanguage = "es" 11 | ) 12 | 13 | 14 | try { 15 | [System.Collections.ArrayList]$csvImport = Import-Csv $Path -Encoding UTF7 -ErrorAction Stop 16 | } 17 | catch { 18 | Write-Warning "Could not import CSV file $Path" 19 | } 20 | 21 | # Randomize the list 22 | $csvImport = Get-Random -InputObject $csvImport -Count $csvImport.Count 23 | 24 | 25 | foreach ($item in $csvImport) { 26 | 27 | switch ($SourceLanguage) { 28 | "de" { 29 | $input = Read-Host -Prompt "`nWas heißt $($item.deutsch) in $DestinationLanguage" 30 | } 31 | "en" { 32 | $input = Read-Host -Prompt "`nWhat's $($item.english) in $DestinationLanguage" 33 | } 34 | "es" { 35 | $input = Read-Host -Prompt "`nQué es $($item.spanish) in $DestinationLanguage" 36 | } 37 | "it" { 38 | $input = Read-Host -Prompt "`nCom'è $($item.italiano) in $DestinationLanguage" 39 | } 40 | } 41 | 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /M01Stats.ps1: -------------------------------------------------------------------------------- 1 | function Get-M01Stats { 2 | param( 3 | $ComputerName = "m01" 4 | ) 5 | 6 | # kind of complicated 7 | #$sensors = sensors | Select-String -Pattern ":" 8 | #$sensorsTable = $sensors -replace ":","=" | ConvertFrom-StringData 9 | 10 | try { 11 | $xmrApi = Invoke-RestMethod -uri "http://$ComputerName`:16000/api.json" -ErrorAction Stop 12 | $hashrate = $xmrApi | Select-Object -ExpandProperty hashrate | Select-Object -ExpandProperty total | Select-Object -First 1 13 | } catch { 14 | Write-Warning "Could not connect to XMR Api" 15 | } 16 | 17 | $out = [ordered]@{ 18 | "TimeStamp" = $(get-date -Format "yyyy-MM-dd HH:mm:ss") 19 | "Host" = $ComputerName; 20 | "Uptime" = $xmrApi.uptime 21 | "HashRate" = $hashrate 22 | } 23 | Write-Output (New-Object -TypeName psobject -Property $out) 24 | } -------------------------------------------------------------------------------- /Microsoft.PowerShell_profile.ps1: -------------------------------------------------------------------------------- 1 | ####### 2 | ### The actual $PROFILE only contains the following line to dot-source this file 3 | ### . $env:USERPROFILE\Git\PowerShell\Microsoft.PowerShell_profile.ps1 4 | ###### 5 | $host.PrivateData.ErrorForegroundColor = 'white' 6 | ## Aliases... because of muscle memory :/ 7 | New-Alias -Name ll -Value Get-ChildItem -ErrorAction SilentlyContinue 8 | 9 | function Invoke-AsAdmin { 10 | param($command) 11 | Start-Process pwsh -Verb RunAs -ArgumentList "-noprofile $command" 12 | } 13 | 14 | function Set-DnsOpen { 15 | param( 16 | $InterfaceIndex = 4 17 | ) 18 | $command = "Set-DnsClientServerAddress -InterfaceIndex $InterfaceIndex -ServerAddresses ('208.67.222.222','208.67.220.220','2620:0:ccc::2','2620:0:ccd::2')" 19 | Invoke-AsAdmin $command 20 | } 21 | 22 | function Set-DnsCloudflare { 23 | param( 24 | $InterfaceIndex = 4 25 | ) 26 | $command = "Set-DnsClientServerAddress -InterfaceIndex $InterfaceIndex -ServerAddresses ('1.1.1.1','1.0.0.1','2606:4700:4700::1111','2606:4700:4700::1001')" 27 | Invoke-AsAdmin $command 28 | } 29 | 30 | function Set-DnsDhcp { 31 | param( 32 | $InterfaceIndex = 4 33 | ) 34 | $command = "Set-DnsClientServerAddress -InterfaceIndex $InterfaceIndex -ResetServerAddresses" 35 | Invoke-AsAdmin $command 36 | } 37 | 38 | # Chocolatey profile 39 | $ChocolateyProfile = "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" 40 | if (Test-Path($ChocolateyProfile)) { 41 | Import-Module "$ChocolateyProfile" 42 | } 43 | 44 | dir "$home\Git\IT-Pro-Trashcan\tto\tools" -Filter *.ps1 | %{ . $_.FullName } 45 | Set-SecurityProtocol -Protocol Tls12 46 | if($PSVersionTable.platform -like "Win*"){ 47 | 48 | $connectedIf = Get-NetRoute -DestinationPrefix 0.0.0.0/0 | Sort-Object -Property RouteMetric -Descending 49 | if($connectedIf){ 50 | $dnsServers = (Get-DnsClientServerAddress -InterfaceIndex $connectedIf.IfIndex | Select-Object -ExpandProperty serveraddresses -Unique) -join ", " 51 | } 52 | 53 | $write = " 54 | --- 55 | IfAlias: $(($connectedIf.InterfaceAlias | Select-Object -Unique) -join ", ") 56 | IfIndex: $(($connectedIf.IfIndex | Select-Object -Unique) -join ", ") 57 | DNS Servers: $dnsServers 58 | Security Protcols: $(Get-SecurityProtocol) 59 | --- 60 | " 61 | Write-Host $write -ForegroundColor Yellow 62 | 63 | } 64 | 65 | 66 | function prompt { 67 | $pss = Get-PSSession | Where-Object Availability 68 | $cwd = (Get-Location).Path 69 | if($pss) { 70 | $WindowTitle = "Connected to: " + ($pss.ComputerName -join ", ") 71 | $info = $pss.ComputerName -join ", " 72 | $info += ": #$($MyInvocation.HistoryId)" 73 | } else { 74 | $WindowTitle = $cwd 75 | $info = "PS: #$($MyInvocation.HistoryId)" 76 | } 77 | if($Global:WindowTitlePrefix){ 78 | $WindowTitle = $Global:WindowTitlePrefix,$WindowTitle -join ": " 79 | } 80 | $host.UI.RawUI.WindowTitle = $WindowTitle 81 | $host.UI.Write("Yellow", $host.UI.RawUI.BackGroundColor, "[$info]") 82 | " $($cwd.Replace($home,"~"))$('>' * ($nestedPromptLevel + 1)) "; 83 | } 84 | 85 | function Set-WindowTitle($String) { 86 | $Global:WindowTitlePrefix = $String 87 | } 88 | 89 | 90 | function Get-JekyllTitle { 91 | [CmdletBinding()] 92 | param ( 93 | [string] 94 | $String 95 | ) 96 | process { 97 | $date = Get-Date -Format "yyyy-MM-dd" 98 | $date,($string.ToLower() -replace "\W+","-") -join "-" 99 | } 100 | } 101 | 102 | 103 | # check if a new release of PowerShell Core is available on GitHub 104 | function Test-PSVersionGitHub { 105 | try { 106 | # get latest release from github atom feed 107 | $Release = Invoke-RestMethod https://github.com/PowerShell/PowerShell/releases.atom -ErrorAction Stop | Select-Object -First 1 108 | } catch { 109 | Write-Warning "Could not check for new version. $_ `n" 110 | break 111 | } 112 | # extract information from atom response 113 | $GitId = $Release.id -split "/" | Select-Object -Last 1 114 | $Download = -join("https://github.com",$Release.link.href) 115 | # Add information to dictionary for output 116 | $output = [ordered]@{ 117 | "PSVersion" = $PSVersionTable.PSVersion; 118 | "GitCommitId" = $PSVersionTable.GitCommitId; 119 | "GitHubReleaseVersion" = $GitId; 120 | "GitHubReleaseLink" = $Download; 121 | } 122 | if($output.PSVersion -ne $output.GitCommitId){ 123 | Write-Output (New-Object -TypeName psobject -Property $output) 124 | } 125 | } 126 | 127 | $env:Path += ";C:\Users\ThomasTorggler\OneDrive - Experts Inside AG\Tools;C:\Program Files\1Password CLI" 128 | 129 | 130 | function Test-macOSNetConnection { 131 | [CmdletBinding()] 132 | param ( 133 | [System.Net.IPAddress]$IP4Address = '1.1.1.1', 134 | [System.Net.IPAddress]$IP6Address = '2606:4700:4700::1111', 135 | [string]$TestFqdn = "onprem.wtf", 136 | [string]$WebTest = "https://outlook.office365.com/owa/favicon.ico", 137 | [int]$Count = 1 138 | ) 139 | $r = Invoke-Expression -command "ping $IP4Address -c $count" 140 | $r6 = Invoke-Expression -command "ping6 $IP6Address -c $count" 141 | $dg = netstat -nr | Select-String -Pattern default 142 | 143 | $dns = Invoke-Expression -Command "dig $TestFqdn +short" 144 | $dns6 = Invoke-Expression -Command "dig $TestFqdn AAAA +short" 145 | 146 | $web = Invoke-WebRequest -Uri $WebTest -Method head 147 | 148 | [PSCustomObject]@{ 149 | ping = $r[-1..-2] -join ', ' 150 | #ping6 = $r6[-1..-2] -join ', ' 151 | dns = $dns -join ', ' 152 | dns6 = $dns6 -join ', ' 153 | web = $web.StatusDescription 154 | } 155 | } 156 | 157 | 158 | if($IsMacOS){ 159 | Test-PSVersionGitHub 160 | New-Alias -Name tnc -Value Test-macOSNetConnection 161 | } 162 | 163 | 164 | ### version 165 | 166 | $PSReadLineVersion = (Get-Module -Name PSReadLine).Version.ToString() 167 | if($PSReadLineVersion -ge "2.2.6"){ 168 | Set-PSReadLineOption -PredictionSource HistoryAndPlugin -PredictionViewStyle ListView 169 | } -------------------------------------------------------------------------------- /Microsoft.PowerShell_profile_osx.ps1: -------------------------------------------------------------------------------- 1 | # Create a symbolic link for the file 2 | # mac 3 | # mkdir /Users/tto/.config/powershell 4 | # ln -s /Users/tto/git/PowerShell/Microsoft.PowerShell_profile_osx.ps1 /Users/tto/.config/powershell/Microsoft.PowerShell_profile.ps1 5 | # bash on win 10 6 | # mkdir /home/tto/.config/powershell 7 | # Copy-Item /mnt/c/Users/tto/OneDrive/_psscripts/git/PowerShell/Microsoft.PowerShell_profile_osx.ps1 $Profile -Force 8 | 9 | # Update PATH variable for Mac so that I can run most bash commands in PS 10 | # make sure new paths are inserted before existing 11 | $addToPath = @( 12 | "/usr/local/bin", 13 | "/opt/homebrew/opt/ruby/bin", 14 | "/opt/homebrew/bin", 15 | "/opt/homebrew/sbin", 16 | "/Users/tto/Library/Python/3.9/bin" 17 | ) 18 | $newPath = ($addToPath -join ":"),$env:PATH -join ":" 19 | $env:PATH = $newPath 20 | 21 | # import modules for powercli 22 | function Import-PowerCliModules { 23 | Import-Module PowerCLI.Vds, PowerCLI.ViCore 24 | } 25 | New-Alias -Name ipcli -Value Import-PowerCliModules 26 | 27 | # check if a new release of PowerShell Core is available on GitHub 28 | function Test-PSVersionGitHub { 29 | try { 30 | # get latest release from github atom feed 31 | $Release = Invoke-RestMethod https://github.com/PowerShell/PowerShell/releases.atom -ErrorAction Stop | Select-Object -First 1 32 | } catch { 33 | Write-Warning "Could not check for new version. $_ `n" 34 | break 35 | } 36 | # extract information from atom response 37 | $GitId = $Release.id -split "/" | Select-Object -Last 1 38 | $Download = -join("https://github.com",$Release.link.href) 39 | # Add information to dictionary for output 40 | $output = [ordered]@{ 41 | "PSVersion" = $PSVersionTable.PSVersion; 42 | "GitCommitId" = $PSVersionTable.GitCommitId; 43 | "GitHubReleaseVersion" = $GitId; 44 | "GitHubReleaseLink" = $Download; 45 | } 46 | Write-Output (New-Object -TypeName psobject -Property $output) 47 | } 48 | # powershell started checking for newer versions by itself 49 | # Test-PSVersionGitHub 50 | 51 | . ~/git/PowerShell/Microsoft.PowerShell_profile.ps1 -------------------------------------------------------------------------------- /New-FirewallRule.ps1: -------------------------------------------------------------------------------- 1 | function New-FirewallRule { 2 | Param( 3 | [string]$Name, 4 | [int]$Port, 5 | [string]$Protocol 6 | ) 7 | $params = @{ 8 | DisplayName = $Name; 9 | Action = 'Allow'; 10 | Description = "$Name on $Protocol/$Port"; 11 | Enabled = 1; 12 | Profile = 'Any'; 13 | Protocol = $Protocol; 14 | PolicyStore = 'PersistentStore'; 15 | LocalPort=$Port; 16 | ErrorAction = 'Stop'; 17 | } 18 | try { 19 | $null = New-NetFirewallRule @params 20 | } 21 | catch { 22 | Write-Warning "Could not create firewall rule: $_" 23 | } 24 | } 25 | 26 | #New-FirewallRule -Port 6060 -Name "Allow SMTP" -------------------------------------------------------------------------------- /New-SfBBackup.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Backup Skype for Business / Lync Server Data. 4 | .DESCRIPTION 5 | This script exports Skype for Business / Lync Data and Settings according to the documentation availabe on TechNet. 6 | It is intended to be run as scheduled task, the Retention parameter can be used to indicate how long 7 | to keep existing backup files in the target directory. 8 | .EXAMPLE 9 | .\New-SfBBackup.ps1 -PoolFqdn lyncpool01.example.com -Path \\SERVER\Share\CSBackup 10 | 11 | This example exports Lync config and saves it into a subfolder at \\SERVER\Share\CSBackup 12 | .EXAMPLE 13 | .\New-SfBBackup.ps1 -PoolFqdn lyncpool01.example.com -Path \\SERVER\Share\CSBackup -Retention 10 14 | 15 | This example exports Skype for Business / Lync config and saves it into a subfolder at \\SERVER\Share\CSBackup. 16 | It deletes existing backups in the destination direcotry if they are older than 10 days. 17 | .ROLE 18 | The user must be member of the RTCUniversalServerAdmins group. 19 | .NOTES 20 | Author: Thomas Torggler; @torggler 21 | Date: 2018-03-17 22 | Version: 2.0 23 | 2.0 Updated Name, verbose output 24 | 1.1 Added Retention parameter and some error handling. 25 | 1.0: Basics 26 | To-do: Multiple pools, File Store, Autodiscovery 27 | .LINK 28 | https://ntsystems.it/PowerShell/Start-LyncBackup/ 29 | .LINK 30 | http://technet.microsoft.com/en-us/library/hh202170.aspx 31 | #> 32 | 33 | #Requires -Version 3 34 | 35 | [CmdletBinding(ConfirmImpact='Medium')] 36 | Param 37 | ( 38 | # PoolFqdn, specify the fully qualified domain name of the Enterprise pool or the Servername of the Standard Server. 39 | [Parameter(Mandatory=$true, 40 | Position=0)] 41 | [ValidateScript({[bool](Get-CsPool $_ -ErrorAction SilentlyContinue)})] 42 | $PoolFqdn, 43 | 44 | # Path, specify the target root folder for the backup. A new sub folder is created every time the script is executed 45 | [Parameter(Mandatory=$true, 46 | Position=1)] 47 | [ValidateScript({Test-Path $_ -PathType Container})] 48 | $Path, 49 | 50 | # Retention, specify a number of days indicating how long to keep existing backup files. Defaults to 90. 51 | [Parameter(Mandatory=$false, 52 | Position=2)] 53 | [ValidateNotNullorEmpty()] 54 | [ValidateRange(1,65535)] 55 | [int]$Retention = 90, 56 | 57 | # LogFile, specify a path to a log file. The script will log information and erros to the file. 58 | [Parameter(Mandatory=$false, 59 | Position=0)] 60 | [System.IO.FileInfo] 61 | $LogFile=(Join-Path -Path $Path -ChildPath 'log-New-SfBBackup.txt') 62 | ) 63 | 64 | #region Initialize Logflie and import module 65 | 66 | Remove-Item $LogFile -ErrorAction SilentlyContinue 67 | "$(Get-Date) New-SfBBackup started" | Add-Content $LogFile -Force 68 | Write-Host "LogFile: $LogFile" -ForegroundColor Yellow 69 | 70 | try { 71 | Import-Module Lync -ErrorAction Stop 72 | } catch { 73 | "$(Get-Date) Error importing Module: $($_.ErrorRecord)" | Add-Content $LogFile 74 | exit 75 | } 76 | 77 | #endregion 78 | 79 | 80 | #region Create target directory to store the exported config files 81 | 82 | $TimeStamp = Get-Date -Format yyyy-MM-dd-hh-mm-ss 83 | 84 | try { 85 | $BackupDir = New-Item -Path $Path -ItemType Container -Name "SfBBackup-$TimeStamp" -ErrorAction Stop 86 | "$(Get-Date) Created Backup Target Directory at $($BackupDir.FullName)" | Add-Content $LogFile 87 | } catch { 88 | "$(Get-Date) Error creating Backup Target directory: $($_.ErrorRecord)" | Add-Content $LogFile 89 | exit 90 | } 91 | 92 | #endregion 93 | 94 | #region Export configuration 95 | 96 | try { 97 | Export-CsConfiguration -FileName (Join-Path -Path $BackupDir -ChildPath "CsConfiguration.zip") -ErrorAction Stop 98 | Write-Verbose "Export CS Configuration" 99 | "$(Get-Date) Export-CsConfiguration OK" | Add-Content $LogFile 100 | } catch { 101 | "$(Get-Date) Error running Export-CsConfiguration $($_.ErrorRecord)" | Add-Content $LogFile 102 | } 103 | 104 | try { 105 | Export-CsLisConfiguration -FileName (Join-Path -Path $BackupDir -ChildPath "CsLisConfiguration.zip") -ErrorVariable CsLisConfigurationError 106 | Write-Verbose "Export CS LIS Configuration" 107 | "$(Get-Date) Export-CsLisConfiguration OK" | Add-Content $LogFile 108 | } catch { 109 | "$(Get-Date) Error running Export-CsLisConfiguration $($_.ErrorRecord)" | Add-Content $LogFile 110 | } 111 | 112 | try { 113 | Export-CsUserData -PoolFQDN $PoolFqdn -FileName (Join-Path -Path $BackupDir -ChildPath "CsUserData.zip") -ErrorVariable CsUserDataError 114 | Write-Verbose "Export CS UserData" 115 | "$(Get-Date) Export-CsUserData OK" | Add-Content $LogFile 116 | } catch { 117 | "$(Get-Date) Error running Export-CsUserData $($_.ErrorRecord)" | Add-Content $LogFile 118 | } 119 | 120 | try { 121 | Export-CsRgsConfiguration -Source "ApplicationServer:$PoolFqdn" -FileName (Join-Path -Path $BackupDir -ChildPath "CsRgsConfiguration.zip") -ErrorVariable CsRgsConfigurationError 122 | Write-Verbose "Export CS RGS Configuration" 123 | "$(Get-Date) Export-CsRgsConfiguration OK" | Add-Content $LogFile 124 | } catch { 125 | "$(Get-Date) Error running Export-CsRgsConfiguration $($CsRgsConfigurationError.ErrorRecord)" | Add-Content $LogFile 126 | } 127 | 128 | #endregion 129 | 130 | #region Delete existing backups 131 | 132 | if ($Script:Error) { 133 | Write-Verbose "Encountered $($Script:Error.Count) errors, skip deleting existing files." 134 | "$(Get-Date) Encountered $($Script:Error.Count) errors, skip deleting existing files." | Add-Content $LogFile 135 | } else { 136 | Write-Verbose "No errors encountered, delete existing files." 137 | $exstingBackups = Get-ChildItem $Path -Directory -Filter "SfBBackup-*" 138 | $existingBackupsToDelete = $exstingBackups | Where-Object {$_.CreationTime -lt (Get-Date).AddDays(-$Retention)} 139 | 140 | foreach ($Backup in $existingBackupsToDelete) { 141 | try { 142 | Remove-Item -Path $Backup.FullName -Recurse -ErrorAction Stop 143 | Write-Verbose "Deleting $($Backup.FullName) " 144 | "$(Get-Date) Deleted existing backup: $($Backup.FullName)" | Add-Content $LogFile 145 | } catch { 146 | "$(Get-Date) Error deleting existing backup: $($_.ErrorRecord)" | Add-Content $LogFile 147 | } 148 | } 149 | } 150 | 151 | #endregion 152 | 153 | "$(Get-Date) New-SfBBackup ended with $($Script:Error.Count) errors." | Add-Content $LogFile -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShell 2 | A repository for all kinds of scripts and snippets. 3 | 4 | # Profiles 5 | The PowerShell profile files I'm using on my Mac OSX and Windows 10 Machines. 6 | * Windows: Microsoft.PowerShell_profile.ps1 7 | * OSX: Microsoft.PowerShell_profile_osx.ps1 8 | -------------------------------------------------------------------------------- /Read-GPG.ps1: -------------------------------------------------------------------------------- 1 | function Read-GPGMessage { 2 | <# 3 | .SYNOPSIS 4 | Invokes 'gpg' command-line tool to decrypt messages. 5 | .DESCRIPTION 6 | This cmdlet is a wrapper for 'gpg --decrypt' to read encrypted messages. 7 | .EXAMPLE 8 | PS C:\> Read-GPGMessage -Message "----BEGIN PGP...." 9 | 10 | The -Message parameter accepts a GPG encrypted string, GPG will prompt for a password and the output is written to the pipeline. 11 | .EXAMPLE 12 | PS C:\> Get-Content .\a.txt -Raw | Read-GPGMessage 13 | 14 | The -Message parameter accepts a GPG encrypted string, GPG will prompt for a password and the output is written to the pipeline. 15 | .EXAMPLE 16 | PS C:\> Read-GPGMessage -File .\a.txt -Output .\b.txt 17 | 18 | The file parameter specifies an input file that contains encrypted data, the output parameter specifies the destination file for the decrypted data. 19 | .INPUTS 20 | [string] 21 | As provided by Get-Content -Raw 22 | .OUTPUTS 23 | [string] 24 | .NOTES 25 | Author: @torggler 26 | #> 27 | [CmdletBinding( 28 | SupportsShouldProcess = $true, 29 | DefaultParameterSetName = "Message" 30 | )] 31 | param ( 32 | [Parameter( 33 | Position = 0, 34 | ValueFromPipeline = $true, 35 | ParameterSetName = "Message")] 36 | [string] 37 | $Message, 38 | [Parameter( 39 | Mandatory = $true, 40 | ParameterSetName = "File")] 41 | [System.IO.FileInfo] 42 | $File, 43 | [Parameter( 44 | Mandatory = $true, 45 | ParameterSetName = "File")] 46 | [System.IO.FileInfo] 47 | $Output 48 | ) 49 | process { 50 | switch ($PSBoundParameters.Keys) { 51 | "Message" { 52 | $command = " `"$message`" | gpg --decrypt" 53 | } 54 | "File" { 55 | $command = "gpg --decrypt $File" 56 | } 57 | "Output" { 58 | $command = "gpg --output $Output --decrypt $File" 59 | } 60 | } 61 | if($PSCmdlet.ShouldProcess($command, "Executing:")) { 62 | Invoke-Expression -Command $command 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Remove-LogFile.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | 3 | .VERSION 1.0.0 4 | 5 | .GUID a1ba81d3-e859-4998-b6ff-c9b8413c7316 6 | 7 | .AUTHOR @torggler 8 | 9 | .TAGS Logs 10 | 11 | .PROJECTURI https://ntsystems.it/PowerShell/Remove-LogFile/ 12 | 13 | #> 14 | 15 | <# 16 | .SYNOPSIS 17 | Deletes log files. 18 | .DESCRIPTION 19 | Deletes log files, parameters can be used to specify the root folder, whether or not to include subfolders, a file extension filter and the age. 20 | This is intended to be run as scheduled task to regularly clean-up log files. 21 | .EXAMPLE 22 | PS C:\> .\Remove-LogFile.ps1 -Path C:\inetpub\logs -Age 7 -Recurse 23 | 24 | This example removes all *.log files older than 7 days from C:\inetpub\logs and any subfolders. 25 | .INPUTS 26 | None. 27 | .OUTPUTS 28 | None. 29 | .NOTES 30 | Author: Thomas Torggler; @torggler 31 | Date: 2014-04-30 32 | Version: 1.1 33 | 1.0: Basic Script 34 | 1.1: handle if no files to delete. 35 | If PowerShell Version 2.0 is used, remove the -file Parameter from Get-Childitem 36 | .LINK 37 | https://ntsystems.it/PowerShell/Remove-LogFile/ 38 | #> 39 | [CmdletBinding(SupportsShouldProcess=$true)] 40 | Param 41 | ( 42 | # Specify folder in which logs will be deleted 43 | [Parameter(Mandatory=$true, 44 | Position=0)] 45 | [ValidateScript({Test-Path $_ -PathType Container})] 46 | $Path, 47 | 48 | # Specify a number of days. Files with a LastWriteTime older than this will be deleted. 49 | [Parameter(Mandatory=$false, 50 | Position=1)] 51 | [ValidateNotNullorEmpty()] 52 | [ValidateRange(1,65535)] 53 | [int]$Age = 90, 54 | 55 | # Specify file extension filter. Defaults to '*.log'. 56 | [Parameter(Mandatory=$false, 57 | Position=2)] 58 | [ValidatePattern('^\*\.\w{3}$')] 59 | [string] 60 | $Filter = '*.log', 61 | 62 | # Specify a path to a log file. The script will log information and erros to the file. 63 | [Parameter(Mandatory=$false)] 64 | [System.IO.FileInfo] 65 | $LogFile="$env:temp\log-RemoveLogFile.ps1", 66 | 67 | # Include subfolders. 68 | [switch] 69 | $Recurse 70 | ) 71 | # Clean and initialize Log File 72 | Remove-Item $LogFile -ErrorAction SilentlyContinue -WhatIf:$false 73 | "$(Get-Date) Remove-LogFile started!" | Add-Content $LogFile -WhatIf:$false 74 | Write-Host "LogFile: $LogFile" -ForegroundColor Yellow 75 | 76 | $filesToDelete = Get-ChildItem -Path:$Path -File -Recurse:$Recurse -Filter:$Filter | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-$Age)} 77 | 78 | if ($filesToDelete) { 79 | 80 | "$(Get-Date) Found $($filesToDelete.Count) files to delete." | Add-Content $LogFile 81 | 82 | foreach ($file in $filesToDelete) { 83 | if ($pscmdlet.ShouldProcess("$($File.FullName)", "Delete")) 84 | { 85 | Remove-Item -Path $file.FullName 86 | "$(Get-Date) Removed $($file.FullName)" | Add-Content $LogFile 87 | } 88 | } 89 | } else { 90 | "$(Get-Date) Nothing to delete." | Add-Content $LogFile -WhatIf:$false 91 | } 92 | 93 | "$(Get-Date) Remove-LogFile ended!" | Add-Content $LogFile -WhatIf:$false -------------------------------------------------------------------------------- /Restore-VMPermission.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | 3 | .VERSION 1.0.1 4 | 5 | .GUID c5fdb6c5-a3d3-4d1b-90e6-9c878dbae95b 6 | 7 | .AUTHOR @torggler 8 | 9 | .PROJECTURI http://www.ntsystems.it/page/PS-Restore-VMPermissionps1.aspx 10 | 11 | .EXTERNALMODULEDEPENDENCIES Hyper-V 12 | 13 | #> 14 | 15 | <# 16 | .Synopsis 17 | Adds permissions for the VMId to all assigned disks. 18 | .DESCRIPTION 19 | This script uses the Hyper-V Module to update permissions for all assigned disks on one ore more VMs. 20 | This is useful if you move/replace VHDs and the read permission for VMId is missing. 21 | .INPUTS 22 | You can pipe objcets with a VMName property, such as returned by Get-VM, to this script. 23 | .OUTPUTS 24 | None. This script does not write any objects to the pipeline. 25 | .EXAMPLE 26 | Restore-VMPermission.ps1 -VM dc01 27 | 28 | This example adds permission for dc01 VMId to the ACL of all assigned disks for dc01. 29 | .EXAMPLE 30 | Get-VM | Restore-VMPermission.ps1 31 | 32 | This example uses Get-VM to get all VMs on the local machine. It gets all disks for all VMs and adds the required premissions for VMId to the ACL. 33 | .ROLE 34 | Get-VM requires administrative rights. 35 | .LINK 36 | https://ntsystems.it/PowerShell/Restore-VMPermission 37 | #> 38 | 39 | [CmdletBinding(ConfirmImpact='Medium',SupportsShouldProcess=$true)] 40 | Param 41 | ( 42 | # VM, specify the VM that needs permissions fixed. 43 | [Parameter(Mandatory=$true, 44 | ValueFromPipelineByPropertyName=$true, 45 | Position=0, 46 | ParameterSetName='Parameter Set 1')] 47 | [ValidateNotNull()] 48 | [ValidateNotNullOrEmpty()] 49 | [Alias("VMName")] 50 | [Alias("Name")] 51 | [string[]] 52 | $VM 53 | ) 54 | 55 | begin { 56 | 57 | try { 58 | Import-Module Hyper-V -ErrorAction Stop -Verbose:$false 59 | } catch { 60 | Write-Warning -Message "$(Get-Date) Error importing Lync Module: $($_.ErrorRecord)" 61 | exit 62 | } 63 | 64 | } # end Begin 65 | 66 | process { 67 | 68 | try { 69 | Write-Verbose 'Trying to get VM' 70 | $VirtualMachines = Get-Vm $VM -ErrorAction Stop -ErrorVariable GetVmError 71 | } 72 | catch { 73 | Write-Warning -Message "Could get VM: $($GetVmError.ErrorRecord)" 74 | } 75 | 76 | foreach ($VirtualMachine in $VirtualMachines) { 77 | 78 | Write-Verbose "Processing VM: $($VirtualMachine.Name)" 79 | 80 | $colRights = [System.Security.AccessControl.FileSystemRights]"Read, Write, Synchronize" 81 | $InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::None 82 | $PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None 83 | $objType =[System.Security.AccessControl.AccessControlType]::Allow 84 | $objUser = New-Object System.Security.Principal.NTAccount("NT VIRTUAL MACHINE\$($VirtualMachine.VMId)") 85 | $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule ($objUser, $colRights, $InheritanceFlag, $PropagationFlag, $objType) 86 | 87 | foreach ($Disk in $VirtualMachine.HardDrives) { 88 | Write-Verbose "Processing VM $($VirtualMachines.Name) controller: $($Disk.ControllerNumber) disk: $($Disk.ControllerLocation)" 89 | $objACL = Get-ACL -Path $Disk.Path 90 | $objACL.AddAccessRule($objACE) 91 | 92 | if ($pscmdlet.ShouldProcess("$($Disk.Path)", "Adding permission for $objUser")) { 93 | Set-ACL -Path $Disk.path -AclObject $objACL 94 | } 95 | } # end foreach 96 | } # end foreach 97 | } # end Process -------------------------------------------------------------------------------- /Restore-VmPermission.Tests.ps1: -------------------------------------------------------------------------------- 1 | 2 | # Define variables for Mock runs 3 | $global:mockedVM = [pscustomobject] @{ 4 | Name = "My VM 01" 5 | VmId = (New-Guid).ToString() 6 | HardDrives = @{ 7 | ControllerNumber = 0 8 | ControllerLocation = 0 9 | Path = "myvhd.vhd" 10 | } 11 | } 12 | 13 | # Testing with mocked commands 14 | Describe "Testing Restore-VmPermission" { 15 | 16 | Mock Get-VM -MockWith {return $global:mockedVM} -verifiable 17 | 18 | $VhdPath = Join-Path -Path $TestDrive -ChildPath "myvhd.vhd" 19 | 20 | Context "Testing Prerequisites" { 21 | It "Test Hyper-V PowerShell module" { 22 | $module = Get-Module -Name Hyper-V -ListAvailable -ErrorAction SilentlyContinue 23 | $module -is [PSModuleInfo] | Should be $true 24 | } 25 | It "Test Get-VM Connectivity" { 26 | $vm = Get-VM 27 | $vm | Should Not Be $null 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Send-MailJetMail.ps1: -------------------------------------------------------------------------------- 1 | function Send-MailJetMail { 2 | [cmdletbinding()] 3 | param( 4 | [string]$Sender, 5 | [string]$Recipient, 6 | [string]$Subject, 7 | [string]$Text, 8 | [string]$User, 9 | [string]$Key 10 | ) 11 | 12 | $body = [ordered]@{ 13 | Messages= @(@{ 14 | to = @(@{email = $recipient}) 15 | from = @{email = $Sender ; name = "notification"} 16 | subject = $Subject 17 | TextPart = $Text 18 | }) 19 | } 20 | 21 | $userkey = $user,$key -join ":" 22 | $auth = $userkey | ConvertTo-Base64 23 | $param = @{ 24 | Uri = "https://api.mailjet.com/v3.1/send" 25 | ContentType = "application/json" 26 | body = ($body | ConvertTo-Json -Depth 4 -Compress) 27 | Method = "POST" 28 | Headers = @{Authorization=("Basic $auth")} 29 | UseBasicParsing = [switch]::Present 30 | } 31 | $Result = Invoke-RestMethod @param 32 | if($Result) { 33 | $Result.Messages 34 | } 35 | } -------------------------------------------------------------------------------- /Send-SplunkEvent.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | 3 | .VERSION 1.0.1 4 | 5 | .GUID a7d9b0b5-0f81-4ec7-be89-7c6a0390ef50 6 | 7 | .AUTHOR @torggler 8 | 9 | .TAGS Splunk 10 | 11 | .PROJECTURI https://ntsystems.it/post/sending-events-to-splunks-http-event-collector-with-powershell 12 | 13 | #> 14 | 15 | <# 16 | .SYNOPSIS 17 | Send events to Splunk's HTTP Event Collector. 18 | .DESCRIPTION 19 | This function uses Invoke-RestMethod to send structured data to Splunk HTTP Event Collector. Use the 20 | HostName and DateTime parameters to control Splunk's 'host' and 'time' properties for the generated event. 21 | .EXAMPLE 22 | PS C:\> .\Send-SplunkEvent.ps1 -InputObject @{message="Hello Splunk!"} -Key 23 | 24 | This example sends a simple event containing "message": "Hello Splunk!" to the event collector running on the local system. 25 | .EXAMPLE 26 | PS C:\> Import-Csv logs.csv | .\Send-SplunkEvent -Key -HostName SBC1 -Uri "https://splunk01.example.com:8088/services/collector" 27 | 28 | This example imports logs from a CSV file and sends each one of them to event collector running on splunk01.example.com. 29 | The HostName parameter specifies which host created the logs. 30 | .INPUTS 31 | [psobject] 32 | .OUTPUTS 33 | None. 34 | .NOTES 35 | Author: @torggler 36 | .LINK 37 | https://ntsystems.it/PowerShell/Send-SplunkEvent/ 38 | #> 39 | [CmdletBinding(SupportsShouldProcess)] 40 | param ( 41 | # Data object that will be sent to Splunk's HTTP Event Collector. 42 | [Parameter(Mandatory,ValueFromPipeline)] 43 | $InputObject, 44 | 45 | # HostName to be used for Splunk's 'host' property. Default's to name of the local system. 46 | [Parameter()] 47 | [string] 48 | $HostName = (hostname), 49 | 50 | # Date and Time of the event. Defaults to now() on the local system. 51 | [Parameter()] 52 | [System.DateTime] 53 | $DateTime = (Get-Date), 54 | 55 | # URI of the Splunk HTTP Event Collector instance. 56 | [Parameter()] 57 | [string] 58 | $Uri = "http://localhost:8088/services/collector", 59 | 60 | # Key for the Splunk HTTP Event Collector instance. 61 | [Parameter()] 62 | [string] 63 | $Key 64 | ) 65 | process { 66 | # Splunk events can have a 'time' property in epoch time. If it's not set, use current system time. 67 | $unixEpochStart = New-Object -TypeName DateTime -ArgumentList 1970,1,1,0,0,0,([DateTimeKind]::Utc) 68 | $unixEpochTime = [int]($DateTime.ToUniversalTime() - $unixEpochStart).TotalSeconds 69 | # Create json object to send 70 | $Body = ConvertTo-Json -InputObject @{event=$InputObject; host=$HostName; time=$unixEpochTime} -Compress 71 | Write-Verbose "Sending $Body to $Uri" 72 | if($PSCmdlet.ShouldProcess($Body,"Send")) { 73 | # Only return if something went wrong, i.e. http response is not "success" 74 | $r = Invoke-RestMethod -Uri $uri -Method Post -Headers @{Authorization="Splunk $Key"} -Body $Body 75 | if($r.text -ne "Success") {$r} 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Start-ArchiveLog.ps1: -------------------------------------------------------------------------------- 1 | function Start-ArchiveLog { 2 | <# 3 | .Synopsis 4 | Archive log files older than 90 days. 5 | .DESCRIPTION 6 | This function was created for the Scripting Games 2013. 7 | This function can be used to move ".log" files from one directory to another directory, local or on a remote server. 8 | The -OlderThan parameter can be used to specify which files should be moved. 9 | .EXAMPLE 10 | Start-ArchiveLog -LogPath C:\Application\Log -ArchivePath \\Server\Archive -Verbose 11 | 12 | This example moves all files with a .log extension from C:\Application\Log and subfolders to \\Server\Archive. It moves files with a CreationTime older than 90 days. 13 | .EXAMPLE 14 | Start-ArchiveLog -LogPath C:\Application\Log -ArchivePath D:\Archive -OlderThan 20 15 | 16 | This example moves all files with a .log extension from C:\Application\Log and subfolders to D:\Archive, the OlderThan parameter is used to move files with a CreationTime of 20 days ago. 17 | .EXAMPLE 18 | Start-ArchiveLog -LogPath C:\Application\Log -ArchivePath D:\Archive -Force 19 | 20 | This example moves all files with a .log extension from C:\Application\Log and subfolders to D:\Archive, the -Force switch overwrites existing files in the destination directory. 21 | #> 22 | [CmdletBinding(SupportsShouldProcess=$true)] 23 | 24 | Param 25 | ( 26 | # Specify the path containing logs that should be archived. Files will be moved from LogPath to ArchivePath. 27 | [Parameter(Mandatory=$true, 28 | Position=0)] 29 | [ValidateNotNull()] 30 | [ValidateNotNullOrEmpty()] 31 | [ValidateScript({Test-Path -Path $_})] 32 | [System.IO.DirectoryInfo] 33 | $LogPath, 34 | 35 | # Specify the path where the archive is located. Files will be moved from LogPath to ArchivePath. 36 | [Parameter(Mandatory=$true, 37 | Position=1)] 38 | [ValidateNotNull()] 39 | [ValidateNotNullOrEmpty()] 40 | [ValidateScript({Test-Path -Path $_})] 41 | [System.IO.DirectoryInfo] 42 | $ArchivePath, 43 | 44 | # Set the Creationtime in days. 45 | [Parameter(Mandatory=$false, 46 | Position=3)] 47 | [ValidateNotNull()] 48 | [ValidateNotNullOrEmpty()] 49 | [int] 50 | $OlderThan = '90', 51 | 52 | # Use the -Force switch parameter to overwrite existing files in the destination directory. 53 | [switch] 54 | $Force 55 | ) 56 | 57 | Begin 58 | { 59 | } 60 | Process 61 | { 62 | # Get the files in the folders, only select files with a .Log extension and the files which were created $OlderThan days ago 63 | $LogFiles = Get-ChildItem $LogPath.FullName -File -Recurse | Where-Object { 64 | ($_.Extension -eq ".log") -and ($_.CreationTime -lt (Get-Date).AddDays(-$OlderThan)) 65 | } 66 | 67 | # Check if $LogFiles actually contains something 68 | if (-not($LogFiles -eq $null)) { 69 | # Get the parent directories of the log files selected before 70 | $Parents = $LogFiles.Directory | Select-Object -Unique -ExpandProperty Name 71 | 72 | # Enumerate parent directories and check if the respective directory exists in the destination. 73 | # If parent dir doesn't exist, try to create it. If creation fails, output the error and set $Continue to $false so the script stops. 74 | foreach ($Parent in $Parents) { 75 | try { 76 | if (-not(Test-Path $ArchivePath\$Parent -ErrorAction Stop)) { 77 | Write-Verbose "Destination Folder doesn't exist, trying to create: $ArchivePath\$Parent" 78 | New-Item -Path $ArchivePath -Name $Parent -ItemType Directory -ErrorAction Stop | Out-Null 79 | $Continue = $true 80 | } else { 81 | Write-Verbose "Destination Folder exists: $ArchivePath\$Parent" 82 | $Continue = $true 83 | } 84 | } catch { 85 | $ErrorMsg = "Could not create folder $ArchivePath\$Parent : $($_.Exception.Message)" 86 | Write-Warning $ErrorMsg 87 | $Continue = $false 88 | } 89 | } # end foreach $Parents 90 | 91 | } else { 92 | # If no files have been found, provide a Verbos message and exit. 93 | Write-Verbose "No files to move, bye" 94 | $Continue = $false 95 | } 96 | 97 | # If everything went fine ($Continue is true) go ahead and try to move the files 98 | if ($Continue) { 99 | 100 | # Enumerate LogFiles so we can use the Directory property to get the parent directory 101 | foreach ($LogFile in $LogFiles) { 102 | 103 | # support for -whatIf 104 | if ($pscmdlet.ShouldProcess("$($LogFile.FullName)", "Move to $ArchivePath\$($LogFile.Directory.Name)")) { 105 | try { 106 | # HashTable for -force parameter 107 | $parms = @{'Force'=$Force} 108 | Move-Item @parms -Path $($LogFile.FullName) -Destination $ArchivePath\$($LogFile.Directory.Name) -ErrorAction Stop 109 | } catch { 110 | $ErrorMsg = "Could not move file $($LogFile.FullName) to $ArchivePath\$($LogFile.Directory.Name) : $($_.Exception.Message)" 111 | Write-Warning $ErrorMsg 112 | } 113 | } # end if -whatif 114 | } # end foreach $LogFiles 115 | } # end if $continue 116 | } 117 | End 118 | { 119 | } 120 | } -------------------------------------------------------------------------------- /Start-OutlookBackup.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Start-OutlookBackup { 3 | <# 4 | .SYNOPSIS 5 | Backup Outlook Contacts and Calendar folders to PST. 6 | .DESCRIPTION 7 | This function uses the Outlook COM object to copy the Contacts and Calendar Folders to a PST file. 8 | .EXAMPLE 9 | PS C:\> Start-OutlookBackup -EmailAddress user@example.com -Path c:\backup\file.pst 10 | This example makes a PST file at c:\backup\file.pst and copys the Calendar and Contacts folders of the user@example.com Mailbox 11 | to the PST file. 12 | .INPUTS 13 | None. 14 | .OUTPUTS 15 | None. 16 | .NOTES 17 | @torggler 18 | 2018-08-30 19 | Missing: Error handling, folder selection 20 | #> 21 | [cmdletbinding()] 22 | param( 23 | [Parameter()] 24 | [system.io.fileinfo]$Path = (Join-Path -Path $env:USERPROFILE -ChildPath Documents\olBackup.pst), 25 | [Parameter()] 26 | [string]$EmailAddress = ("{0}@{1}" -f $env:USERNAME,$env:USERDNSDOMAIN)$EmailAddress = ("{0}@{1}" -f $env:USERNAME,$env:USERDNSDOMAIN) 27 | ) 28 | $Outlook = New-Object -ComObject Outlook.Application 29 | Write-Verbose "Connected to Outlook Profile $($outlook.DefaultProfileName)" 30 | $NS = $Outlook.GetNamespace('MAPI') 31 | Write-Verbose "Opened Namespace $($NS.Type)" 32 | $Store = $NS.Stores | Where-Object {$_.displayname -eq $EmailAddress} 33 | Write-Verbose "Connected to Store $($Store.DisplayName)" 34 | $Calendar = $Store.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]"olFolderCalendar") 35 | $Contacts = $Store.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]"olFolderContacts") 36 | Write-Verbose "Default Calendar Folder is $($Calendar.Name)" 37 | Write-Verbose "Default Contacts Folder is $($Contacts.Name)" 38 | $Outlook.Session.AddStore($Path) 39 | $PST = $ns.Stores | Where-Object {$_.filepath -eq $Path.Fullname} 40 | $ca = $Calendar.CopyTo($PST) 41 | $co = $Contacts.CopyTo($PST) 42 | Write-Verbose "Backed up $($ca.Items.Count) Calendar Items" 43 | Write-Verbose "Backed up $($co.Items.Count) Contact Items" 44 | $PSTRoot = $PST.GetRootFolder() 45 | $PSTFolder = $NS.Folders.Item($PSTRoot.Name) 46 | $NS.GetType().InvokeMember('RemoveStore',[System.Reflection.BindingFlags]::InvokeMethod,$null,$NS,($PSTFolder)) 47 | } 48 | -------------------------------------------------------------------------------- /Start-TestWebServer.Tests.ps1: -------------------------------------------------------------------------------- 1 | # find current path to use when starting process 2 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 3 | Describe "Testing Web Server" { 4 | # Start another instance of powershell as the script blocks the console 5 | # use Verb RunAs to start the new instance as admin 6 | Start-Process powershell -Argument "$here\Start-TestWebServer.ps1 -Port 80 -CreateFirewallRule" -Verb RunAs 7 | # Wait a sec for the instance to come up 8 | Start-Sleep -Seconds 5 9 | Context "Starting" { 10 | It "Creates a firewall rule upon starting" { 11 | (Get-NetFirewallRule -PolicyStore ActiveStore -DisplayName "Allow PS TestWS Port*") -is [Microsoft.Management.Infrastructure.CimInstance] | Should Be True 12 | } 13 | It "creates a listening tcp connection" { 14 | (Get-NetTCPConnection -LocalPort 80 -State Listen) -is [Microsoft.Management.Infrastructure.CimInstance] | Should Be True 15 | } 16 | It "returns the requests as json object" { 17 | $response = Invoke-RestMethod http://localhost/ 18 | $response | Select-Object -ExpandProperty UserAgent | Should Match 'WindowsPowerShell' 19 | } 20 | It "returns 'Bye' when sending /end" { 21 | Invoke-RestMethod -Uri http://localhost/end | Should Match 'Bye' 22 | } 23 | } 24 | Context "Stopping" { 25 | # Wait a sec for the process to stopping 26 | Start-Sleep -Seconds 5 27 | It "Remove firewall rule when stopping" { 28 | $null = Get-NetFirewallRule -PolicyStore ActiveStore -DisplayName "Allow PS TestWS Port*" | Should throw 29 | } 30 | It "removes the listening tcp connection" { 31 | Get-NetTCPConnection -State Listen | Where-Object LocalPort -eq 80 | Should Be $null 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Start-TestWebServer.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | .VERSION 1.0.1 3 | .GUID f92f5ec3-48a9-45ee-aee7-372fdb0e6e35 4 | .AUTHOR @torggler 5 | .PROJECTURI https://ntsystems.it/PowerShell/start-testwebserver/ 6 | #> 7 | 8 | <# 9 | .SYNOPSIS 10 | A basic web server that returns any request as JSON object. 11 | .DESCRIPTION 12 | Start a web listener that listens on a specified port and simply answers to any request, returning JSON object containing the request. 13 | Requires administrative rights to create the listener. 14 | .EXAMPLE 15 | .\Start-TestWebServer.ps1 -Port 8001 16 | 17 | Start the test WebServer on port 8001. 18 | .EXAMPLE 19 | .\Start-TestWebServer.ps1 -Port 80 -CreateFirewallRule 20 | Invoke-RestMethod -Uri http://localhost | Select-Object UserAgent 21 | 22 | Start the test WebServer on port 80 and create a Firewall Rule to allow traffic to the specified port. 23 | The Invoke-RestMethod cmdlet is used to send a request to the listener and parse the output. 24 | .INPUTS 25 | None. 26 | .OUTPUTS 27 | None. 28 | .LINK 29 | https://ntsystems.it/PowerShell/start-testwebserver/ 30 | #> 31 | 32 | #Requires -RunAsAdministrator 33 | 34 | [CmdletBinding(HelpUri = 'https://ntsystems.it/PowerShell/start-testwebserver/')] 35 | Param( 36 | # Specify a TCP port number for the HTTP listener to use. Defaults to 8000. 37 | [Parameter(Position=0)] 38 | [ValidateRange(1,65535)] 39 | [int] 40 | $Port = 8000, 41 | 42 | # Use this switch to automatically create a Windows Firewall rule to allow incoming connections on the specified port. 43 | [switch] 44 | $CreateFirewallRule 45 | ) 46 | 47 | #region Supporting Functions 48 | 49 | function Write-Response { 50 | Param( 51 | $ResponseObject, 52 | $ContentType = 'application/json', 53 | $StatusCode = 200, 54 | $Message 55 | ) 56 | $ResponseObject.ContentType = $ContentType 57 | $ResponseObject.StatusCode = $StatusCode 58 | [byte[]] $buffer = [System.Text.Encoding]::UTF8.GetBytes($Message) 59 | $ResponseObject.ContentLength64 = $buffer.length 60 | $output = $ResponseObject.OutputStream 61 | $output.Write($buffer, 0, $buffer.length) 62 | $output.Close() 63 | } 64 | 65 | function New-FirewallRule { 66 | Param($Port) 67 | $params = @{ 68 | DisplayName = "Allow PS TestWS Port $Port"; 69 | Action = 'Allow'; 70 | Description ="Allow PowerShell Test Web Server on Port $Port"; 71 | Enabled = 1; 72 | Profile = 'Any'; 73 | Protocol = 'TCP'; 74 | PolicyStore = 'ActiveStore'; 75 | LocalPort=$Port; 76 | ErrorAction = 'Stop'; 77 | } 78 | try { 79 | $null = New-NetFirewallRule @params 80 | } 81 | catch { 82 | Write-Warning "Could not create firewall rule: $_" 83 | } 84 | } 85 | 86 | function Remove-FirewallRule { 87 | Param($Port) 88 | try { 89 | Remove-NetFirewallRule -DisplayName "Allow PS TestWS Port $Port" -PolicyStore ActiveStore -ErrorAction Stop 90 | } 91 | catch { 92 | Write-Warning "Could not remove firewall rule: $_" 93 | } 94 | } 95 | 96 | #endregion 97 | 98 | #region WebServer 99 | 100 | if ($CreateFirewallRule) { 101 | Write-Verbose "Creating Firewall Rule" 102 | New-FirewallRule($Port) 103 | } 104 | 105 | # Define listener 106 | $listener = New-Object System.Net.HttpListener 107 | $listener.Prefixes.Add("http://+:$port/") 108 | 109 | # Start listener 110 | try { 111 | $listener.Start() 112 | Write-Verbose "Listening on port: $port - End with /end" 113 | while ($true) { 114 | # blocks until request is received 115 | $context = $listener.GetContext() 116 | $request = $context.Request 117 | $response = $context.Response 118 | 119 | if ($request.Url -match '/end$') { 120 | Write-Verbose "Received END request: $($request.Url) from UA $($request.UserAgent)" 121 | Write-Response -ResponseObject $response -ContentType 'text/plain' -Message 'Bye' 122 | Remove-Variable request, response 123 | break 124 | } 125 | 126 | # The default behaviour is to simply return the request as JSON object 127 | else { 128 | Write-Verbose "Received URL: $($request.Url) from UA: $($request.UserAgent)" 129 | Write-Response -ResponseObject $response -Message ($request | ConvertTo-Json) 130 | } 131 | } 132 | } 133 | catch { 134 | Write-Warning -Message $_ 135 | } 136 | finally { 137 | $listener.Stop() 138 | 139 | if($CreateFirewallRule) { 140 | Write-Verbose "Remove Firewall Rule" 141 | Remove-FirewallRule($Port) 142 | } 143 | } 144 | 145 | #endregion 146 | -------------------------------------------------------------------------------- /Telegram.ps1: -------------------------------------------------------------------------------- 1 | function New-TelegramMessage { 2 | [cmdletbinding()] 3 | param( 4 | $ChatId, 5 | $Text, 6 | $Mode = "Markdown", 7 | $ReplyId, 8 | $ReplyMarkup, 9 | $ApiKey = $env:TG_Token 10 | ) 11 | # build body, only add ReplyId and Markup if necessary 12 | $body = @{ 13 | "parse_mode" = $mode; 14 | "chat_id"= $ChatId; 15 | "text" = $Text; 16 | } 17 | if($ReplyId) { 18 | $body.Add("reply_to_message_id",$ReplyId) 19 | } 20 | if($ReplyMarkup) { 21 | $body.Add("reply_markup",(ConvertTo-Json $ReplyMarkup -Depth 5)) 22 | } 23 | Invoke-RestMethod -Uri https://api.telegram.org/bot$ApiKey/sendMessage -Body $body -Method Post 24 | } 25 | # Send a message using the input values received from the StorageQueue 26 | # Splatting doesn't work with objects created by ConvertFrom-Json 27 | # New-TelegramMessage -ChatId $requestBody.ChatId -Text $requestBody.Text -ReplyId $requestBody.ReplyId -ReplyMarkup $requestBody.ReplyMarkup 28 | 29 | function Get-TelegramFilePath { 30 | [CmdletBinding()] 31 | param ( 32 | $FileId, 33 | $ApiKey 34 | ) 35 | process { 36 | Write-Verbose "Get File Path for file_id [$FileId]" 37 | $Result = Invoke-RestMethod -Uri "https://api.telegram.org/bot$ApiKey/getFile?file_id=$FileId" | Select-Object -ExpandProperty Result 38 | if($Result){ 39 | $Result.file_path 40 | } 41 | } 42 | } 43 | 44 | function Get-TelegramFile { 45 | [CmdletBinding()] 46 | param ( 47 | $FileId, 48 | $ApiKey, 49 | $Path 50 | ) 51 | process { 52 | $TelegramFilePath = Get-TelegramFilePath -FileId $FileId -ApiKey $ApiKey 53 | if($TelegramFilePath) { 54 | $TelegramFileName = Split-Path -Path $TelegramFilePath -Leaf 55 | $OutputFilePath = Join-Path -Path $Path -ChildPath $TelegramFileName 56 | Write-Verbose "Download File from [$TelegramFilePath] to [$OutputFilePath]" 57 | Invoke-WebRequest -Uri "https://api.telegram.org/file/bot$ApiKey/$TelegramFilePath" -UseBasicParsing -OutFile $OutputFilePath 58 | } 59 | } 60 | } 61 | 62 | # Get-TelegramFile -ApiKey "key" -FileId AgADAgADUasxGxIpWEh0sk6-k2noT0j-tw8ABJ5RD7ZyHEnAoxIAAgI -Path ~\Desktop -Verbose 63 | -------------------------------------------------------------------------------- /Test-GroupMembership.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | 3 | .VERSION 1.0.1 4 | 5 | .GUID 87079941-27c8-44b1-af29-2fb447ccb883 6 | 7 | .AUTHOR @torggler 8 | 9 | .PROJECTURI https://ntsystems.it/PowerShell/Test-GroupMembership 10 | 11 | .TAGS ActiveDirectory 12 | 13 | .EXTERNALMODULEDEPENDENCIES ActiveDirectory 14 | 15 | #> 16 | 17 | <# 18 | .Synopsis 19 | Test AD Group Membership for an account. 20 | .DESCRIPTION 21 | This function uses [ADSI] to test group membership based on the security token of the account. 22 | You can pipe objects of the type [Microsoft.ActiveDirectory.Management.ADAccount[]] to this function. 23 | The function writes $true or $false fore each tested object. 24 | This function makes use of Richard Muellers "PowerShell script to check group membership". Check the related Links. 25 | .EXAMPLE 26 | Get-AdUser -Filter * | .\Test-GroupMemership.ps1 -GroupName "Domain Users" 27 | 28 | This example gets users from Active Directory and tests wether or not they are member of the "Domain Users" security group. 29 | .EXAMPLE 30 | Get-AdComputer -Filter * | .\Test-GroupMemership.ps1 -GroupName "Domain Computers" 31 | 32 | This example gets computers from Active Directory and tests wether or not they are member of the "Domain Computers" security group. 33 | .INPUTS 34 | [Microsoft.ActiveDirectory.Management.ADAccount] 35 | You can pipe an ADAccount object, such as returned by Get-AdUser or Get-AdComputer, to Test-GroupMembership. 36 | .OUTPUTS 37 | [bool] 38 | Test-GroupMembership returns $true or $false for each tested account. 39 | .LINK 40 | http://www.ntsystems.it/page/PS-Test-GroupMembership.aspx 41 | .LINK 42 | http://gallery.technet.microsoft.com/scriptcenter/5adf9ad0-1abf-4557-85cd-657da1cc7df4 43 | #> 44 | 45 | [CmdletBinding(PositionalBinding=$true)] 46 | [OutputType([bool])] 47 | 48 | Param( 49 | # InputObject, an Object of the Type [Microsoft.ActiveDirectory.Management.ADAccount] 50 | [Parameter(Mandatory=$true, 51 | ValueFromPipeline=$true, 52 | Position=1)] 53 | [ValidateNotNull()] 54 | [ValidateNotNullOrEmpty()] 55 | [Microsoft.ActiveDirectory.Management.ADAccount[]] 56 | $InputObject, 57 | 58 | # GroupName, the name of the Group to test 59 | [Parameter(Mandatory=$true, 60 | ValueFromPipelineByPropertyName=$true, 61 | Position=0)] 62 | [ValidateScript({Get-ADGroup -Identity $_ -ErrorAction Stop})] 63 | $GroupName 64 | ) 65 | 66 | process { 67 | foreach ($Object in $InputObject) { 68 | $GroupList = @{} 69 | 70 | # get ADSI object for user 71 | Write-Verbose "Creating ADSI Object for $($Object.SamAccountName)" 72 | $AdObject = [ADSI]"LDAP://$($Object.DistinguishedName)" 73 | 74 | # Check if security group memberships for this principal have been determined. 75 | If ($GroupList.ContainsKey($ADObject.sAMAccountName.ToString() + "\") -eq $False) 76 | { 77 | # Memberships need to be determined for this principal. Add "pre-Windows 2000" 78 | # name to the hash table. 79 | $GroupList.Add($ADObject.sAMAccountName.ToString() + "\", $True) 80 | 81 | # Retrieve tokenGroups attribute of principal, which is operational. 82 | $ADObject.psbase.RefreshCache("tokenGroups") 83 | $SIDs = $ADObject.psbase.Properties.Item("tokenGroups") 84 | 85 | # Populate hash table with security group memberships. 86 | ForEach ($Value In $SIDs) 87 | { 88 | $SID = New-Object System.Security.Principal.SecurityIdentifier $Value, 0 89 | 90 | if ($sid.BinaryLength -gt 16) { 91 | # the length is used to skip well-known SIDs that cannot be translated to NTAccount 92 | # Translate into "pre-Windows 2000" name. 93 | 94 | $Group = $SID.Translate([System.Security.Principal.NTAccount]) 95 | $GroupList.Add($ADObject.sAMAccountName.ToString() + "\" + $Group.Value.Split("\")[1], $True) 96 | } 97 | } 98 | } 99 | 100 | # Check if $ADObject is a member of $GroupName. 101 | If ($GroupList.ContainsKey($ADObject.sAMAccountName.ToString() + "\" + $GroupName)) { 102 | Write-Verbose "$($Object.SamAccountName) is member of $GroupName" 103 | Return $True 104 | } else { 105 | Write-Verbose "$($Object.SamAccountName) is not member of $GroupName" 106 | Return $False 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /Untitled-2.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Get-FindPeopleResponse { 3 | param( 4 | [System.IO.FileInfo] 5 | $Path 6 | ) 7 | $lines = Select-String -Pattern "FindPeopleResponse" -Path $path | Select-Object -ExpandProperty Line 8 | foreach ($l in $lines) { 9 | ([regex]::Match($l,"") | Select-Object -ExpandProperty Value) -as [xml] 10 | } 11 | } 12 | 13 | 14 | $n = $resp.Body.FindPeopleResponse.People.Persona.MobilePhones.PhoneNumberAttributedValue.Value.Number 15 | $n += $resp.Body.FindPeopleResponse.People.Persona.HomePhones.PhoneNumberAttributedValue.Value.Number 16 | $n += $resp.Body.FindPeopleResponse.People.Persona.BusinessPhoneNumbers.PhoneNumberAttributedValue.Value.Number 17 | 18 | $n | select -Unique 19 | 20 | 21 | $j = start-job -ScriptBlock { 1..9999 } 22 | 23 | do { 24 | if(-not(Get-UDDashboard)) { 25 | Start-UDDashboard -Dashboard $tempdb 26 | } 27 | } while ($j.State -eq "Running") 28 | 29 | -------------------------------------------------------------------------------- /Untitled-4.ps1: -------------------------------------------------------------------------------- 1 | 2 | # import cdr from: .\a.b.c.d\CDR\*.log 3 | # create temp files in: .\a.b.c.d\temp\*.txt 4 | # move imported files to: .\a.b.c.d\spluked\*.log 5 | 6 | 7 | 8 | function Import-CdrSplunk { 9 | [CmdletBinding(SupportsShouldProcess)] 10 | param( 11 | # Folder containing cdr syslogs 12 | [Parameter(Mandatory)] 13 | [System.IO.FileInfo] 14 | $Path, 15 | 16 | # Filter 17 | [Parameter()] 18 | [string] 19 | $Filter = "*.log" 20 | ) 21 | # load functions 22 | . C:\users\thomas.torggler\Git\PowerShell\AudioCodesCdr.ps1 23 | . C:\Users\thomas.torggler\Git\PowerShell\Send-SplunkEvent.ps1 24 | 25 | # get files 26 | $SyslogFiles = Get-ChildItem -Path $Path\*\CDR\$Filter 27 | $UniqueHosts = $SyslogFiles | Get-HostFromPath | Select-Object -Unique 28 | foreach($ip in $UniqueHosts){ 29 | Write-Verbose "Current host is $IP" 30 | $perHostPath = Join-Path -Path $Path -ChildPath $ip 31 | $FilesToImport = Get-ChildItem -Path $perHostPath\CDR\$Filter | Sort-Object -Property LastWriteTime | Select-Object -Last 1 32 | 33 | foreach($f in $FilesToImport) { 34 | if ($pscmdlet.ShouldProcess($f, "Import")) { 35 | $null = New-Item -Path $perHostPath -Name temp -Type Directory -ErrorAction SilentlyContinue 36 | 37 | $f | Split-Cdr -OutputFolder $perHostPath\temp 38 | $SbcTitle = Get-CdrTitle -Type SBC -Path $SyslogFiles[0].FullName 39 | $MediaTitle = Get-CdrTitle -Type MEDIA -Path $SyslogFiles[0].FullName 40 | 41 | Write-Verbose "SBC Title: $($SbcTitle -join ', ')" 42 | Write-Verbose "Media Title: $($MediaTitle -join ', ')" 43 | 44 | $SbcObjects = Get-ChildItem -Path $Path\*\temp\sbc.txt | Import-Cdr -Header $SbcTitle 45 | $MediaObjects = Get-ChildItem -Path $Path\*\temp\media.txt | Import-Cdr -Header $MediaTitle 46 | 47 | $SbcObjects | % {Send-SplunkEvent -HostName $ip -DateTime $_.TimeStamp -InputObject $_ } 48 | $MediaObjects | % {Send-SplunkEvent -HostName $ip -DateTime $_.TimeStamp -InputObject $_ } 49 | 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Update-DashboardInfo.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | function Update-DashboardInfo { 4 | [CmdletBinding(SupportsShouldProcess)] 5 | param ( 6 | [Parameter(Mandatory)] 7 | [System.IO.FileInfo] 8 | $Path, 9 | [Parameter(Mandatory)] 10 | $Subscription, 11 | [Parameter(Mandatory)] 12 | $ResourceGroup, 13 | [Parameter(Mandatory)] 14 | $Name 15 | ) 16 | 17 | $content = Get-Content -Path $Path -Raw 18 | $contentJson = $content | ConvertFrom-Json 19 | $outPath = $path -replace ".json","-new.json" 20 | $oldSubscription = $contentJson.properties.lenses.0.0.parts.0.0.metadata.inputs.where{$_.Name -eq "ComponentId"}.Value.SubscriptionId 21 | $oldName = $contentJson.properties.lenses.0.0.parts.0.0.metadata.inputs.where{$_.Name -eq "ComponentId"}.Value.Name 22 | $oldResourceGroup = $contentJson.properties.lenses.0.0.parts.0.0.metadata.inputs.where{$_.Name -eq "ComponentId"}.Value.ResourceGroup 23 | 24 | 25 | Write-Verbose "Replacing Subscription [$oldSubscription] with [$Subscription]" 26 | Write-Verbose "Replacing Name [$oldName] with [$Name]" 27 | Write-Verbose "Replacing ResourceGroup [$oldResourceGroup] with [$ResourceGroup]" 28 | 29 | 30 | if($PSCmdlet.ShouldProcess($outPath, "Update info and write")){ 31 | $content = $content -replace $oldSubscription,$Subscription 32 | $content = $content -replace "`"ResourceGroup`": `"$oldResourceGroup`"","`"ResourceGroup`": `"$ResourceGroup`"" 33 | $content = $content -replace "resourcegroups/$oldResourceGroup","resourcegroups/$ResourceGroup" 34 | $content = $content -replace "`"Name`": `"$oldName`"","`"Name`": `"$Name`"" 35 | $content = $content -replace "`"PartSubTitle`": `"$oldName`"","`"PartSubTitle`": `"$Name`"" 36 | $content = $content -replace "`"value`": `"$oldName`"","`"value`": `"$Name`"" 37 | $content = $content -replace "workspaces/$oldName","workspaces/$Name" 38 | 39 | Write-Verbose "Writing file $outPath" 40 | 41 | $content | Set-Content $outPath 42 | } 43 | } 44 | 45 | 46 | #Update-DashboardInfo -Path 'C:\Users\thomas.torggler\Downloads\Exchange Online.json' -Name "na-unistgallen" -ResourceGroup "rg_unistgallen" -Subscription "001e6d27-4a75-4812-8a38-a83d71942523" -Verbose 47 | 48 | -------------------------------------------------------------------------------- /WordPressApiDemo.ps1: -------------------------------------------------------------------------------- 1 | # Playing around with the WordPress JSON API 2 | function Get-WPPosts { 3 | [CmdletBinding()] 4 | param ( 5 | [ValidateNotNullOrEmpty()] 6 | [string]$Url 7 | ) 8 | process { 9 | $RequestUrl = $url + "/wp-json/wp/v2/posts" 10 | try { 11 | Write-Verbose "Intenando coneccion a $RequestUrl" 12 | Invoke-RestMethod -Uri $RequestUrl -ErrorAction Stop 13 | } catch { 14 | Write-Warning "No se pudo connectar: $_" 15 | } 16 | } 17 | } 18 | 19 | function Search-WPPosts { 20 | [CmdletBinding()] 21 | param ( 22 | [ValidateNotNullOrEmpty()] 23 | [string]$Url, 24 | [ValidateNotNullOrEmpty()] 25 | [string]$PalabraClave 26 | ) 27 | process { 28 | $RequestUrl = $url + "/wp-json/wp/v2/posts?search=$PalabraClave" 29 | try { 30 | Write-Verbose "Intenando coneccion a $RequestUrl" 31 | $Resultado = Invoke-RestMethod -Uri $RequestUrl -ErrorAction Stop 32 | } catch { 33 | Write-Warning "No se pudo connectar: $_" 34 | break 35 | } 36 | if(-not($Resultado)) { 37 | Write-Warning "No se econtró ninguna entrada con $PalabraClave" 38 | } 39 | Write-Output $Resultado 40 | } 41 | } 42 | 43 | function Get-WPComments { 44 | [CmdletBinding()] 45 | param ( 46 | [ValidateNotNullOrEmpty()] 47 | [string]$Url 48 | ) 49 | process { 50 | $RequestUrl = $url + "/wp-json/wp/v2/comments" 51 | try { 52 | Write-Verbose "Intenando coneccion a $RequestUrl" 53 | Invoke-RestMethod -Uri $RequestUrl -ErrorAction Stop 54 | } catch { 55 | Write-Warning "No se pudo connectar: $_" 56 | } 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /bv-calc.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Get-InvoiceDetail { 3 | param( 4 | $year = [datetime]::UtcNow.Year, 5 | $quarter = "*" 6 | ) 7 | process { 8 | $path = Join-Path $HOME -ChildPath "OneDrive/_BV/Experts Inside BV/$year/Q$quarter/Sales Invoice" 9 | Get-ChildItem $path -Filter *.xlsx -Recurse | ForEach-Object { 10 | $n = $_.basename.Split('-')[0] 11 | $e = Import-Excel -Path $_.fullname -NoHeader 12 | $c = $e | Where-Object p8 -eq 'Due Upon Receipt' | Select-Object -expand p1 13 | $t = $e | Where-Object p6 -eq total | Select-Object -expand p8 14 | [pscustomobject]@{ 15 | Invoice = $n 16 | Customer = $c 17 | Value = $t 18 | } 19 | } 20 | } 21 | } 22 | 23 | Get-InvoiceDetail 24 | 25 | 26 | Get-InvoiceDetail | Group-Object Customer | Select-Object @{ 27 | n="Invoice" 28 | e={"SUBTOTAL"} 29 | },@{ 30 | n='Customer' 31 | e={$_.Name} 32 | },@{ 33 | n='Value' 34 | e={($_.group.value | Measure-Object -sum).Sum} 35 | } 36 | 37 | -------------------------------------------------------------------------------- /chromecast.ps1: -------------------------------------------------------------------------------- 1 | function Get-ChromeCastDevice { 2 | [cmdletbinding()] 3 | param($ip) 4 | $p = @{ 5 | Method = "get" 6 | ContentType = "application/json" 7 | #Uri = "http://{0}:8008/setup/eureka_info?options=detail" -f $ip 8 | Uri = "http://{0}:8008/setup/eureka_info?options=detail¶ms=version,name,build_info,device_info,net,wifi,setup,settings,opt_in,opencast,multizone,audio,detail,night_mode_params,user_eq,room_equalizer" -f $ip 9 | UserAgent = "curl" 10 | } 11 | irm @p -TimeoutSec 1 12 | } 13 | 14 | 15 | $range = 1..20 16 | 17 | foreach ($i in $range) { 18 | $ip = Get-NetIPAddress -PrefixOrigin Dhcp | select -ExpandProperty ipaddress 19 | $ip = $ip -replace (".\d$",$i) 20 | Get-ChromeCastDevice -ip $ip -ErrorAction SilentlyContinue 21 | } 22 | #https://github.com/balloob/pychromecast/blob/master/pychromecast/__init__.py 23 | 24 | Sent from Mail for Windows 10 25 | 26 | 27 | function Get-ChromeCastDeviceStatus { 28 | [CmdletBinding()] 29 | param($ip) 30 | $p = @{ 31 | Method = "post" 32 | ContentType = "application/json" 33 | Uri = "http://{0}:8008/setup/assistant/check_ready_status" -f $ip 34 | UserAgent = "curl" 35 | Body = ConvertTo-Json @{"play_ready_message" = $false;"user_id"="118300704319639860919"} 36 | } 37 | irm @p -TimeoutSec 1 38 | } 39 | 40 | function Get-ChromeCastDeviceOffer { 41 | [CmdletBinding()] 42 | param($ip) 43 | $p = @{ 44 | Method = "get" 45 | ContentType = "application/json" 46 | Uri = "http://{0}:8008/setup/offer" -f $ip 47 | UserAgent = "curl" 48 | } 49 | irm @p -TimeoutSec 1 50 | } -------------------------------------------------------------------------------- /compare-directory.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomtorggler/PowerShell/be616fc7185c04f9342f4614661980b9564baca6/compare-directory.ps1 -------------------------------------------------------------------------------- /ews.ps1: -------------------------------------------------------------------------------- 1 | Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" 2 | 3 | $EmailAccount = "tom@uclab.eu" 4 | $Password = "*" 5 | 6 | $EWS = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1) 7 | $EWS.Credentials = New-Object Net.NetworkCredential($EmailAccount, $Password) 8 | $EWS.AutodiscoverUrl($EmailAccount,{$true}) 9 | 10 | $userid = [Microsoft.Exchange.WebServices.Data.ImpersonatedUserId]::new() 11 | $userid.id = "user1@uclab.eu" 12 | $ews.ImpersonatedUserId = $userid 13 | 14 | $Calendar = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($ews,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar) 15 | 16 | $appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $EWS 17 | $appointment.Subject = "EWS Test" 18 | $appointment.Body = "EWS Test" 19 | $appointment.Start = Get-Date (Get-DAte).AddHours(1) 20 | $appointment.End = Get-Date (Get-DAte).AddHours(2) 21 | $appointment.IsReminderSet = $true 22 | $appointment.LegacyFreeBusyStatus = [Microsoft.Exchange.WebServices.Data.LegacyFreeBusyStatus]::Free 23 | $appointment.Location = "Location" 24 | $appointment.Save([Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToNone) 25 | $appointment.id.ChangeKey 26 | $Calendar.FindItems(2).id.changekey 27 | -------------------------------------------------------------------------------- /exchange.ps1: -------------------------------------------------------------------------------- 1 | # a quick & drity backup check that can be run as scheduled task 2 | . 'C:\Program Files\Microsoft\Exchange Server\V15\bin\RemoteExchange.ps1'; Connect-ExchangeServer -auto -ClientApplication:ManagementShell 3 | (Get-MailboxDatabase -Status).ForEach({ $data += "Database: " + $_.Name + " Last Backup: " + $_.lastFullBackup + "`n" }) 4 | Send-MailMessage -From check@ntsystems.it -To tom@ntsystems.it -Subject "Exchange Check" -Body $data -SmtpServer localhost 5 | 6 | # check protocol logs for unique remote-endpoints in SmtpReceive 7 | $logPath = 'C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive' 8 | $files = (Get-ChildItem $logPath).Where{$_.LastWriteTime -GT (get-date).AddDays(-30)} 9 | $data = $null 10 | foreach ($f in $files) { 11 | $data = (Import-Csv -Path $f.FullName -Delimiter ',' -Header date-time,connector-id,session-id,sequence-number,local-endpoint,remote-endpoint,event,data,context).Where{$_.'remote-endpoint'} 12 | } 13 | $data.'remote-endpoint' -replace "\:\d+","" | Select-Object -Unique 14 | # check protocol logs for unique remote-endpoints in SmtpSend 15 | $logPath = 'C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpSend' 16 | $files = (Get-ChildItem $logPath).Where{$_.LastWriteTime -GT (get-date).AddDays(-30)} 17 | $data = $null 18 | foreach ($f in $files) { 19 | $data = (Import-Csv -Path $f.FullName -Delimiter ',' -Header date-time,connector-id,session-id,sequence-number,local-endpoint,remote-endpoint,event,data,context).Where{$_.'remote-endpoint'} 20 | } 21 | $data.'remote-endpoint' -replace "\:\d+","" | Select-Object -Unique -------------------------------------------------------------------------------- /get-sfbconnections.ps1: -------------------------------------------------------------------------------- 1 | 2 | <# 3 | This is a stripped down version of https://www.ucunleashed.com/269 Get-CsConnections.ps1 4 | #> 5 | function Get-SFBConnections { 6 | param([string]$PoolFqdn) 7 | begin { 8 | function Get-Data { 9 | [CmdletBinding()] 10 | param ([string]$Server) 11 | 12 | #Define SQL Connection String and command 13 | [string] $connstring = "server=$server\rtclocal;database=rtcdyn;trusted_connection=true;" 14 | [object] $command = New-Object System.Data.SqlClient.SqlCommand 15 | 16 | # SQL query for Lync Server 2013 17 | $command.CommandText = "Select (cast (RE.ClientApp as varchar (100))) as ClientVersion, R.UserAtHost as UserName, RA.Fqdn ` 18 | From rtcdyn.dbo.RegistrarEndpoint RE ` 19 | Inner Join rtcdyn.dbo.Endpoint EP on RE.EndpointId = EP.EndpointId ` 20 | Inner Join rtc.dbo.Resource R on R.ResourceId = RE.OwnerId ` 21 | Inner Join rtcdyn.dbo.Registrar RA on EP.RegistrarId = RA.RegistrarId ` 22 | Order By ClientVersion, UserName" 23 | 24 | [object] $connection = New-Object System.Data.SqlClient.SqlConnection 25 | $connection.ConnectionString = $connstring 26 | try { 27 | $connection.Open() 28 | } 29 | catch { 30 | Write-Warning "Could not connect to $server" 31 | return 32 | } 33 | 34 | $command.Connection = $connection 35 | 36 | [object] $sqladapter = New-Object System.Data.SqlClient.SqlDataAdapter 37 | $sqladapter.SelectCommand = $command 38 | 39 | [object] $results = New-Object System.Data.Dataset 40 | $recordcount = $sqladapter.Fill($results) 41 | $connection.Close() 42 | return $Results.Tables[0] 43 | } 44 | } 45 | process { 46 | $feServers = Get-CsComputer -Pool $PoolFqdn -ErrorAction Stop | Sort-Object identity 47 | $global:Records = @() 48 | foreach ($fe in $feServers) { 49 | Write-Verbose "Getting info for $($fe.identity)" 50 | $data = Get-Data -Server $fe.identity 51 | if ($data) { 52 | $global:Records += $data 53 | } 54 | } 55 | $global:Records 56 | Write-Host -ForegroundColor Yellow "Result available in `$Records" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ifttt.ps1: -------------------------------------------------------------------------------- 1 | # https://maker.ifttt.com/use/fZoRxGSv0O75fEkFzIAxMnWywojs61puHn1eGlrj7dy 2 | 3 | 4 | <# 5 | To trigger an Event 6 | Make a POST or GET web request to: 7 | 8 | https://maker.ifttt.com/trigger/{event}/with/key/fZoRxGSv0O75fEkFzIAxMnWywojs61puHn1eGlrj7dy 9 | With an optional JSON body of: 10 | 11 | { "value1" : "", "value2" : "", "value3" : "" } 12 | 13 | The data is completely optional, and you can also pass value1, value2, and value3 as query parameters or form variables. This content will be passed on to the Action in your Recipe. 14 | 15 | You can also try it with curl from a command line. 16 | 17 | curl -X POST https://maker.ifttt.com/trigger/{event}/with/key/fZoRxGSv0O75fEkFzIAxMnWywojs61puHn1eGlrj7dy 18 | 19 | #> -------------------------------------------------------------------------------- /kraken-stats.ps1: -------------------------------------------------------------------------------- 1 | 2 | function round2($i){ 3 | [math]::round($i,2) 4 | } 5 | 6 | function Get-KrakenStats { 7 | param( 8 | $Path, 9 | $Pair 10 | ) 11 | process { 12 | $items = import-csv -Path $path 13 | if($Pair){ 14 | $allPairs = $pair 15 | } else { 16 | $allPairs = ($items | Group-Object -Property pair).Name 17 | } 18 | foreach($pair in $allPairs){ 19 | $out = [ordered]@{ pair='';pos='';vol='';cost='';fee='';maxprice='';minprice='';pnl='' } 20 | 21 | $pairItems = $items.where{$_.pair -eq $pair} 22 | $minmax = $pairItems | Measure-Object -Property price -Maximum -Minimum 23 | $buys = $pairItems.where{$_.type -eq "buy"} 24 | $sells = $pairItems.where{$_.type -eq "sell"} 25 | $sumBuys = round2(($buys.vol | Measure-Object -Sum).Sum) 26 | $sumSells = round2(($sells.vol | Measure-Object -Sum).sum) 27 | $costBuys = round2(($buys.cost | Measure-Object -Sum).Sum) 28 | $costSells = round2(($sells.cost | Measure-Object -Sum).sum) 29 | $fee = round2(($pairItems | Measure-Object -Property fee -Sum).Sum) 30 | 31 | $out.pair = $pair 32 | $out.pos = $sumBuys - $sumSells 33 | $out.vol = round2(($pairItems | Measure-Object -Property vol -sum).Sum) 34 | $out.cost = round2(($pairItems | Measure-Object -Property cost -sum).Sum) 35 | $out.fee = $fee 36 | $out.maxprice = round2($minmax.Maximum) 37 | $out.minprice = round2($minmax.Minimum) 38 | if($out.pos -eq 0){ 39 | $out.pnl = round2($costSells - $costBuys - $fee) 40 | } 41 | 42 | [PSCustomObject]$out 43 | } 44 | } 45 | } 46 | 47 | Get-KrakenStats -Path ~/Downloads/trades.csv | ft 48 | 49 | -------------------------------------------------------------------------------- /logmytime.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | function Get-LogMyTimeEntries { 4 | [CmdletBinding()] 5 | param ( 6 | $Key = 'b4x4xrkjpy' 7 | ) 8 | 9 | $apiResult = Invoke-LMTApiCall -Key $Key -Uri https://api.logmytime.de/V1/Api.svc/TimeEntries | Select-Object -ExpandProperty results 10 | 11 | foreach ($te in $apiResult) { 12 | $out = [ordered]@{ 13 | 'LastChange' = (Get-Date $te.LastChangeTime) 14 | 'Client' = (Get-LogMyTimeProject -InputObject $te).Client 15 | 'Project' = (Get-LogMyTimeProject -InputObject $te).Project 16 | 'Comment' = $te.Comment 17 | 'DurationString' = $te.DurationString 18 | 'Billable' = $te.Billable 19 | } 20 | Write-Output (New-Object -TypeName psobject -Property $out) | Sort-Object -Property LastChange -Descending 21 | } 22 | } 23 | 24 | 25 | function Get-LogMyTimeProject { 26 | [CmdletBinding()] 27 | param ( 28 | $InputObject 29 | ) 30 | process { 31 | foreach ($project in $($InputObject.project.__deferred.uri)) { 32 | $p = Invoke-LMTApiCall -Uri $project 33 | $result = @{ 34 | "Project" = $($p.Name) 35 | "Client" = (Get-LogMyTimeClient -InputObject $p) 36 | } 37 | Write-Output (New-Object -TypeName psobject -Property $result) 38 | } 39 | } 40 | } 41 | 42 | function Get-LogMyTimeClient { 43 | [CmdletBinding()] 44 | param ( 45 | $InputObject 46 | ) 47 | process { 48 | $cl = Invoke-LMTApiCall -Uri $($InputObject.client.__deferred.uri) 49 | $cl.Name 50 | } 51 | } 52 | 53 | function Invoke-LMTApiCall { 54 | [CmdletBinding()] 55 | param ( 56 | $Uri, 57 | $Key = 'b4x4xrkjpy' 58 | ) 59 | 60 | begin { 61 | $Headers = @{ 62 | 'X-LogMyTimeApiKey' = $key 63 | 'User-Agent' = 'PowerShell' 64 | 'accept' = 'application/json' 65 | } 66 | } 67 | 68 | process { 69 | Invoke-RestMethod -Method Get -Headers $Headers -Uri $Uri | Select-Object -ExpandProperty d 70 | } 71 | 72 | end { 73 | } 74 | } -------------------------------------------------------------------------------- /new-cap.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Get-AdObject { 3 | param($identity) 4 | if($identity -match "ou="){ 5 | New-Object -TypeName psobject -Property (@{DistinguishedName = "OU=b,DC=example,DC=com"}) 6 | } 7 | 8 | } 9 | 10 | function new-AdObject { 11 | New-Object -TypeName psobject -Property (@{DistinguishedName = "CN=a,OU=b,DC=example,DC=com"}) 12 | } 13 | 14 | function New-CAP { 15 | [CmdletBinding(SupportsShouldProcess)] 16 | param ( 17 | [Parameter(Mandatory)] 18 | [ValidateSet("EMEA-ES-ITV-ARB","EMEA-ES-ITV-URN")] 19 | [string] 20 | $Location, 21 | 22 | [Parameter(Mandatory)] 23 | [string] 24 | $DisplayName, 25 | 26 | [Parameter(Mandatory)] 27 | [ValidatePattern("^\+\d+")] 28 | [string] 29 | $DisplayNumber, 30 | 31 | [Parameter(Mandatory)] 32 | [string] 33 | $OU, 34 | 35 | [Parameter(Mandatory)] 36 | [int] 37 | $PIN, 38 | 39 | [Parameter()] 40 | [int] 41 | $ExtensionLength = 4 42 | ) 43 | 44 | process { 45 | $e164 = $DisplayNumber -replace "\+","" -replace " ","" 46 | $Extension = $e164.Substring($e164.length -$ExtensionLength) 47 | $ContactName = ($Location -replace "^\w4","PHONE"),$e164 -join "-" 48 | $LineUri = "tel:+{0};ext={1}" -f $e164,$Extension 49 | $SipUri = "sip:{0}@{1}.example.com" -f $ContactName,($Location.ToLower() -split "-" | Select-Object -First 1) 50 | $ClientPolicy = $Location + "-CAP-STD" 51 | $DialPlan = "tag:{0}" -f $Location 52 | $VoicePolicy = $Location -replace "\w+$","International" 53 | 54 | $Pool = switch -Regex ($Location) { 55 | "EMEA-ES" { "es-bar-sbs1.example.com" } 56 | "EMEA-DE" { "de-ber-sbs1.example.com" } 57 | } 58 | 59 | # learn how test-path works with ou 60 | if($ContactObject = Get-AdObject $ContactName -ErrorAction SilentlyContinue){ 61 | Write-Verbose "Contact [$ContactName] found. Using existing object." 62 | } elseif($OU = Get-Adobject -Identity $ou -ErrorAction SilentlyContinue) { 63 | Write-Verbose "OU [$OU] exists" 64 | Write-Verbose "Contact [$ContactName] does not exist." 65 | 66 | if($PSCmdlet.ShouldProcess("Name [$ContactName]","Create Contact")){ 67 | $ContactObject = New-AdObject -Type Contact -Path $OU -Name $ContactName -PassThru 68 | Write-Information "Created Contact: $($ContactObject.DistinguishedName)" 69 | } 70 | 71 | } else { 72 | Write-Warning "Contact [$ContactName] not found, OU [$OU] not found. End of story." 73 | continue 74 | } 75 | 76 | 77 | if($ContactObject){ 78 | Write-Verbose "Created Contact, starting jobs" 79 | 80 | $InfoDict = @{ 81 | DN = $ContactObject.DistinguishedName 82 | SipUri = $SipUri 83 | LineUri = $LineUri 84 | RegistrarPool = $Pool 85 | DisplayName = $DisplayName 86 | Pin = $Pin 87 | DialPlan = $DialPlan 88 | ClientPolicy = $ClientPolicy 89 | VoicePolicy = $VoicePolicy 90 | 91 | } 92 | 93 | $InfoDict | ConvertTo-Json 94 | 95 | if($PSCmdlet.ShouldProcess("dn [$($ContactObject.DistinguishedName)] pool [$pool] pin [$pin] cp [$ClientPolicy]","Create CAP")){ 96 | 97 | Start-Job -ArgumentList $InfoDict -ScriptBlock { 98 | param($Input) 99 | New-CsCommonAreaPhone @Input 100 | Grant-CsDialPlan -Identity $Input.SipAddress -PolicyName $Input.DialPlan 101 | Grant-CsVoicePolicy -Identity $Input.SipAddress -PolicyName $Input.VoicePolicy 102 | Grant-CsClientPolicy -Identity $Input.SipAddress -PolicyName $Input.ClienPolicy 103 | Set-CsClientPin -Identity $Input.SipAddress -PolicyName $Input.Pin 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | function Get-CAP { 111 | [CmdletBinding()] 112 | param ( 113 | [Parameter(Mandatory)] 114 | [ValidateSet("EMEA-ES-ARB","EMEA-ES-URN")] 115 | [string] 116 | $Location 117 | ) 118 | Get-CsCommonAreaPhone -Filter "DialPlan -eq $Location" | Select-Object -Property DisplayName,DisplayNumber,LineUri,SipAddress,DialPlan,VoicePolicy,ClienPolicy 119 | } 120 | 121 | #new-cap -Location EMEA-ES-ITV-ARB -DisplayName "itv arbizu test" -DisplayNumber "+34 123 456 789" -OU "ou=weur,dc=example,dc=com" -pin 123 -InformationAction Continue -------------------------------------------------------------------------------- /nostr.ps1: -------------------------------------------------------------------------------- 1 | 2 | function ConvertTo-UnixTime { 3 | param( 4 | [Parameter(ValueFromPipeline)] 5 | [datetime]$Date = [datetime]::Now 6 | ) 7 | process{ 8 | [int]((New-TimeSpan -Start (Get-Date -Date '1970-01-01') -End (($Date).ToUniversalTime())).TotalSeconds) 9 | } 10 | } 11 | 12 | function New-NostrFilterString { 13 | [CmdletBinding()] 14 | param( 15 | [string[]]$ids, 16 | [string[]]$authors, 17 | [int[]]$kinds, 18 | [string]$etag, 19 | [string]$ptag, 20 | [int]$limit, 21 | [datetime]$since, 22 | [datetime]$until, 23 | $relay 24 | ) 25 | $filters = [ordered]@{} 26 | switch($PSBoundParameters.Keys){ 27 | 'ids' {$filters.add('ids',$ids)} 28 | 'authors' {$filters.add('authors',$authors)} 29 | 'kinds' {$filters.add('kinds',$kinds)} 30 | 'etag' {$filters.add('#e',$etag)} 31 | 'ptag' {$filters.add('#p',$ptag)} 32 | 'limit' {$filters.add('limit',$limit)} 33 | 'since' {$filters.add('since',($since | ConvertTo-UnixTime))} 34 | 'until' {$filters.add('until',($until | ConvertTo-UnixTime))} 35 | } 36 | $nosQ = @( 37 | "REQ", 38 | "nostrps", 39 | $filters 40 | ) 41 | 42 | ConvertTo-Json -InputObject $nosQ -Compress 43 | } 44 | 45 | function New-NostrRelayConnection { 46 | [CmdletBinding()] 47 | param( 48 | $URL 49 | 50 | ) 51 | $WS = New-Object System.Net.WebSockets.ClientWebSocket 52 | $CT = New-Object System.Threading.CancellationToken 53 | $WS.Options.UseDefaultCredentials = $true 54 | 55 | #Get connected 56 | $Conn = $WS.ConnectAsync($URL, $CT) 57 | While (!$Conn.IsCompleted) { 58 | Start-Sleep -Milliseconds 100 59 | } 60 | Write-Verbose "Connected to $($URL)" 61 | 62 | return $ws 63 | 64 | } 65 | function Send-NostrRequest { 66 | [CmdletBinding()] 67 | param( 68 | $WebSocket, 69 | $QueryString 70 | ) 71 | 72 | $Command = [System.Text.Encoding]::UTF8.GetBytes($QueryString) 73 | $Send = New-Object System.ArraySegment[byte] -ArgumentList @(,$Command) 74 | $CT = New-Object System.Threading.CancellationToken 75 | $Conn = $WebSocket.SendAsync($Send, [System.Net.WebSockets.WebSocketMessageType]::Text, $true, $CT) 76 | 77 | While (!$Conn.IsCompleted) { 78 | Start-Sleep -Milliseconds 100 79 | } 80 | Write-Verbose "Sent Request $QueryString" 81 | 82 | return $conn 83 | } 84 | 85 | function Get-NostrEvent { 86 | [CmdletBinding()] 87 | param ( 88 | [Parameter()] 89 | #[ValidateSet('wss://eden.nostr.land','wss://relay.snort.social','wss://relay.damus.io','wss://relay.nostr.info')] 90 | [string[]]$Relay = 'wss://eden.nostr.land', 91 | $Ids, 92 | $Kinds, 93 | $Authors, 94 | $Limit, 95 | $Since, 96 | $Until 97 | ) 98 | begin { 99 | $filter = New-NostrFilterString @PSBoundParameters 100 | } 101 | process { 102 | foreach($rUri in $Relay){ 103 | $ws = New-NostrRelayConnection -URL $rUri 104 | $send = Send-NostrRequest -QueryString $filter -WebSocket $ws 105 | While ($WS.State -eq 'Open') { 106 | $Array = [byte[]] @(,0) * 1024 107 | $Recv = New-Object System.ArraySegment[byte] -ArgumentList @(,$Array) 108 | $CT = New-Object System.Threading.CancellationToken 109 | $Conn = $WS.ReceiveAsync($Recv, $CT) 110 | While (!$Conn.IsCompleted) { 111 | Start-Sleep -Milliseconds 100 112 | } 113 | $out = [System.Text.Encoding]::utf8.GetString($Recv.array) 114 | if($out -match '^\["EOSE"' ){ 115 | Write-Verbose "Received EOSE, closing connection." 116 | $send = Send-NostrRequest -QueryString '["CLOSE", "nostrps"]' -WebSocket $ws 117 | $ws.dispose() 118 | $send.dispose() 119 | $conn.dispose() 120 | continue 121 | } 122 | try { 123 | $outobj = $out | ConvertFrom-Json -ErrorAction stop -AsHashtable | Where-Object {$_ -is [hashtable]} 124 | $outobj.created_at = Get-Date -UnixTimeSeconds $outobj.created_at 125 | $outobj.add('Relay',$rUri) 126 | new-object -TypeName psobject -Property $outobj 127 | } catch { 128 | # "could not convert from json" 129 | } 130 | } 131 | } 132 | } 133 | } 134 | 135 | 136 | function Test-NostrWebSocket { 137 | param( 138 | [Parameter(ValueFromPipeline)] 139 | $Relay 140 | ) 141 | process{ 142 | $WS = New-Object System.Net.WebSockets.ClientWebSocket 143 | $CT = New-Object System.Threading.CancellationToken 144 | $WS.Options.UseDefaultCredentials = $true 145 | Write-Host "Testing $Relay " -NoNewline 146 | $sw = [System.Diagnostics.Stopwatch]::StartNew() 147 | $Conn = $WS.ConnectAsync($Relay, $CT) 148 | While (!$Conn.IsCompleted) { 149 | Write-Host "." -NoNewline 150 | Start-Sleep -Milliseconds 10 151 | } 152 | $sw.Stop(); $Conn.Dispose(); $ws.Dispose() 153 | Write-Host " connected after $($sw.ElapsedMilliseconds) ms" 154 | return $sw.ElapsedMilliseconds 155 | } 156 | } 157 | 158 | $relays = @( 159 | 'wss://nostr.milou.lol', 160 | 'wss://bitcoiner.social', 161 | 'wss://relay.nostr.com.au', 162 | 'wss://relay.nostrati.com', 163 | 'wss://nostr.inosta.cc', 164 | 'wss://nostr.plebchain.org', 165 | 'wss://atlas.nostr.land', 166 | 'wss://relay.nostrich.land', 167 | 'wss://relay.nostriches.org', 168 | 'wss://private.red.gb.net', 169 | 'wss://nostr.decentony.com', 170 | 'wss://relay.orangepill.dev', 171 | 'wss://puravida.nostr.land', 172 | 'wss://nostr.wine', 173 | 'wss://eden.nostr.land', 174 | 'wss://nostr.gives.africa', 175 | 'wss://relay.snort.social', 176 | 'wss://relay.damus.io' 177 | ) 178 | 179 | function Get-NostrRelayInfo { 180 | [CmdletBinding()] 181 | param( 182 | [Parameter(ValueFromPipeline)] 183 | $Uri 184 | ) 185 | process { 186 | write-verbose "testing $uri" 187 | $httpsUri = $uri -replace '^ws','http' 188 | $out = [ordered]@{ 189 | relay = $uri 190 | } 191 | $out.timeHttps = (Measure-Command {$r=Invoke-RestMethod -TimeoutSec 2 $httpsUri -Headers @{accept='application/nostr+json'} -ErrorAction SilentlyContinue }).TotalMilliseconds 192 | $out.timeWss = Test-NostrWebSocket -Relay $uri 193 | $out.nips = $r.supported_nips -join ', ' 194 | $out.software = $r.software 195 | $out.version = $r.version 196 | $out.pubkey = $r.pubkey 197 | [pscustomobject]$out 198 | } 199 | } 200 | 201 | #$relays | Get-NostrRelayInfo -------------------------------------------------------------------------------- /npslog.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param($filename) 3 | 4 | $PACKET_TYPES = @{ 5 | 1 = "Access-Request"; 6 | 2 = "Access-Accept"; 7 | 3 = "Access-Reject"; 8 | 4 = "Accounting-Request"; 9 | 5 = "Accounting-Response"; 10 | 6 = "Accounting-Status"; 11 | 7 = "Password-Request"; 12 | 8 = "Password-Ack"; 13 | 9 = "Password-Reject"; 14 | 10 = "Accounting-Message"; 15 | 11 = "Access-Challenge"; 16 | 21 = "Resource-Free-Request"; 17 | 22 = "Resource-Free-Response"; 18 | 23 = "Resource-Query-Request"; 19 | 24 = "Resource-Query-Response"; 20 | 25 = "Alternate-Resource-Reclaim-Request"; 21 | 26 = "NAS-Reboot-Request"; 22 | 27 = "NAS-Reboot-Response"; 23 | 29 = "Next-Passcode"; 24 | 30 = "New-Pin"; 25 | 31 = "Terminate-Session"; 26 | 32 = "Password-Expired"; 27 | 33 = "Event-Request"; 28 | 34 = "Event-Response"; 29 | 40 = "Disconnect-Request"; 30 | 41 = "Disconnect-ACK"; 31 | 42 = "Disconnect-NAK"; 32 | 43 = "CoA-Request"; 33 | 44 = "CoA-ACK"; 34 | 45 = "CoA-NAK"; 35 | 50 = "IP-Address-Allocate"; 36 | 51 = "IP-Address-Release"; 37 | } 38 | 39 | $SERVICE_TYPES = @{ 40 | 1 = "Login"; 41 | 2 = "Framed"; 42 | 3 = "Callback Login"; 43 | 4 = "Callback Framed"; 44 | 5 = "Outbound"; 45 | 6 = "Administrative"; 46 | 7 = "NAS Prompt"; 47 | 8 = "Authenticate Only"; 48 | 9 = "Callback NAS Prompt"; 49 | 10 = "Call Check"; 50 | 11 = "Callback Administrative"; 51 | 12 = "Voice"; 52 | 13 = "Fax"; 53 | 14 = "Modem Relay"; 54 | 15 = "IAPP-Register"; 55 | 16 = "IAPP-AP-Check"; 56 | 17 = "Authorize Only"; 57 | 18 = "Framed-Management" 58 | 19 = "Additional-Authorization" 59 | } 60 | 61 | $AUTHENTICATION_TYPES = @{ 62 | 1 = "PAP"; 63 | 2 = "CHAP"; 64 | 3 = "MS-CHAP"; 65 | 4 = "MS-CHAP v2"; 66 | 5 = "EAP"; 67 | 7 = "None"; 68 | 8 = "Custom"; 69 | 11 = "PEAP" 70 | } 71 | 72 | $REASON_CODES = @{ 73 | 0 = "IAS_SUCCESS"; 74 | 1 = "IAS_INTERNAL_ERROR"; 75 | 2 = "IAS_ACCESS_DENIED"; 76 | 3 = "IAS_MALFORMED_REQUEST"; 77 | 4 = "IAS_GLOBAL_CATALOG_UNAVAILABLE"; 78 | 5 = "IAS_DOMAIN_UNAVAILABLE"; 79 | 6 = "IAS_SERVER_UNAVAILABLE"; 80 | 7 = "IAS_NO_SUCH_DOMAIN"; 81 | 8 = "IAS_NO_SUCH_USER"; 82 | 16 = "IAS_AUTH_FAILURE"; 83 | 17 = "IAS_CHANGE_PASSWORD_FAILURE"; 84 | 18 = "IAS_UNSUPPORTED_AUTH_TYPE"; 85 | 32 = "IAS_LOCAL_USERS_ONLY"; 86 | 33 = "IAS_PASSWORD_MUST_CHANGE"; 87 | 34 = "IAS_ACCOUNT_DISABLED"; 88 | 35 = "IAS_ACCOUNT_EXPIRED"; 89 | 36 = "IAS_ACCOUNT_LOCKED_OUT"; 90 | 37 = "IAS_INVALID_LOGON_HOURS"; 91 | 38 = "IAS_ACCOUNT_RESTRICTION"; 92 | 48 = "IAS_NO_POLICY_MATCH"; 93 | 64 = "IAS_DIALIN_LOCKED_OUT"; 94 | 65 = "IAS_DIALIN_DISABLED"; 95 | 66 = "IAS_INVALID_AUTH_TYPE"; 96 | 67 = "IAS_INVALID_CALLING_STATION"; 97 | 68 = "IAS_INVALID_DIALIN_HOURS"; 98 | 69 = "IAS_INVALID_CALLED_STATION"; 99 | 70 = "IAS_INVALID_PORT_TYPE"; 100 | 71 = "IAS_INVALID_RESTRICTION"; 101 | 80 = "IAS_NO_RECORD"; 102 | 96 = "IAS_SESSION_TIMEOUT"; 103 | 97 = "IAS_UNEXPECTED_REQUEST"; 104 | } 105 | 106 | $ACCT_TERMINATE_CAUSES = @{ 107 | 1 = "User Request"; 108 | 2 = "Lost Carrier"; 109 | 3 = "Lost Service"; 110 | 4 = "Idle Timeout"; 111 | 5 = "Session Timeout"; 112 | 6 = "Admin Reset"; 113 | 7 = "Admin Reboot"; 114 | 8 = "Port Error"; 115 | 9 = "NAS Error"; 116 | 10 = "NAS Request"; 117 | 11 = "NAS Reboot"; 118 | 12 = "Port Unneeded"; 119 | 13 = "Port Preempted"; 120 | 14 = "Port Suspended"; 121 | 15 = "Service Unavailable"; 122 | 16 = "Callback"; 123 | 17 = "User Error"; 124 | 18 = "Host Request"; 125 | 19 = "Supplicant Restart"; 126 | 20 = "Reauthentication Failure"; 127 | 21 = "Port Reinitialized"; 128 | 22 = "Port Administratively Disabled"; 129 | 23 = "Lost Power"; 130 | } 131 | 132 | $ACCT_STATUS_TYPES = @{ 133 | 1 = "Start"; 134 | 2 = "Stop"; 135 | 3 = "Interim-Update"; 136 | 7 = "Accounting-On"; 137 | 8 = "Accounting-Off"; 138 | 9 = "Tunnel-Start"; 139 | 10 = "Tunnel-Stop"; 140 | 11 = "Tunnel-Reject"; 141 | 12 = "Tunnel-Link-Start"; 142 | 13 = "Tunnel-Link-Stop"; 143 | 14 = "Tunnel-Link-Reject"; 144 | 15 = "Failed"; 145 | } 146 | 147 | $ACCT_AUTHENTICS = @{ 148 | 1 = "RADIUS"; 149 | 2 = "Local"; 150 | 3 = "Remote"; 151 | 4 = "Diameter"; 152 | } 153 | 154 | 155 | $header = "ComputerName","ServiceName","Record-Date","Record-Time","Packet-Type","User-Name","Fully-Qualified-Distinguished-Name","Called-Station-ID","Calling-Station-ID","Callback-Number","Framed-IP-Address","NAS-Identifier","NAS-IP-Address","NAS-Port","Client-Vendor","Client-IP-Address","Client-Friendly-Name","Event-Timestamp","Port-Limit","NAS-Port-Type","Connect-Info","Framed-Protocol","Service-Type","Authentication-Type","Policy-Name","Reason-Code","Class","Session-Timeout","Idle-Timeout","Termination-Action","EAP-Friendly-Name","Acct-Status-Type","Acct-Delay-Time","Acct-Input-Octets","Acct-Output-Octets","Acct-Session-Id","Acct-Authentic","Acct-Session-Time","Acct-Input-Packets","Acct-Output-Packets","Acct-Terminate-Cause","Acct-Multi-Ssn-ID","Acct-Link-Count","Acct-Interim-Interval","Tunnel-Type","Tunnel-Medium-Type","Tunnel-Client-Endpt","Tunnel-Server-Endpt","Acct-Tunnel-Conn","Tunnel-Pvt-Group-ID","Tunnel-Assignment-ID","Tunnel-Preference","MS-Acct-Auth-Type","MS-Acct-EAP-Type","MS-RAS-Version","MS-RAS-Vendor","MS-CHAP-Error","MS-CHAP-Domain","MS-MPPE-Encryption-Types","MS-MPPE-Encryption-Policy","Proxy-Policy-Name","Provider-Type","Provider-Name","Remote-Server-Address","MS-RAS-Client-Name","MS-RAS-Client-Version" 156 | $data = Import-Csv $filename -Header $header 157 | 158 | foreach ($obj in $data) { 159 | 160 | if ($obj.'Packet-Type') { 161 | $obj.'Packet-Type' = $PACKET_TYPES[[int]$obj.'Packet-Type'] 162 | } 163 | if ($obj.'Authentication-Type') { 164 | $obj.'Authentication-Type' = $AUTHENTICATION_TYPES[[int]$obj.'Authentication-Type'] 165 | } 166 | if ($obj.'Reason-Code') { 167 | $obj.'Reason-Code' = $REASON_CODES[[int]$obj.'Reason-Code'] 168 | } 169 | if ($obj.'Acct-Terminate-Cause') { 170 | $obj.'Acct-Terminate-Cause' = $ACCT_TERMINATE_CAUSES[[int]$obj.'Acct-Terminate-Cause'] 171 | } 172 | if ($obj.'Service-Type') { 173 | $obj.'Service-Type' = $SERVICE_TYPES[[int]$obj.'Service-Type'] 174 | } 175 | if ($obj.'Acct-Status-Type') { 176 | $obj.'Acct-Status-Type' = $ACCT_STATUS_TYPES[[int]$obj.'Acct-Status-Type'] 177 | } 178 | if ($obj.'Acct-Authentic') { 179 | $obj.'Acct-Authentic' = $ACCT_AUTHENTICS[[int]$obj.'Acct-Authentic'] 180 | } 181 | } 182 | 183 | $data -------------------------------------------------------------------------------- /prtg.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | function Stop-PrtgProbe { 5 | <# 6 | .SYNOPSIS 7 | Stop a PRTG probe. 8 | .DESCRIPTION 9 | This function uses the API to stop a PRTG probe (eg. before patching a system) 10 | .EXAMPLE 11 | PS C:\> Stop-PrtgProbe -UserName -Password -ProbeId 10659 -Message "Pause Test" -Duration 60 12 | This example stops probe id 10659 for 60 seconds 13 | .INPUTS 14 | None. 15 | .OUTPUTS 16 | None. 17 | .NOTES 18 | The api sucks. 19 | #> 20 | [CmdletBinding()] 21 | param ( 22 | $ComputerName = "prtg01.uclab.eu", 23 | $UserName, 24 | $Password, 25 | $ProbeId, 26 | $Message = "Paused by script", 27 | $Duration = 300 28 | ) 29 | $Message = [uri]::EscapeUriString($Message) 30 | $uri = "https://$ComputerName/api/pauseobjectfor.htm?id=$ProbeId&pausemsg=$Message&duration=$Duration&username=$Username&password=$password" 31 | $null = Invoke-RestMethod -Uri $uri 32 | } 33 | 34 | function Resume-PrtgProbe { 35 | <# 36 | .SYNOPSIS 37 | Resume a PRTG probe. 38 | .DESCRIPTION 39 | This function uses the API to resume a previously stopped PRTG probe (eg. after patching a system) 40 | .EXAMPLE 41 | PS C:\> Resume-PrtgProbe -UserName -Password -ProbeId 10659 42 | This example resumes probe id 10659 43 | .INPUTS 44 | None. 45 | .OUTPUTS 46 | None. 47 | .NOTES 48 | The api sucks. 49 | #> 50 | [CmdletBinding()] 51 | param ( 52 | $ComputerName = "prtg01.uclab.eu", 53 | $UserName, 54 | $Password, 55 | $ProbeId 56 | ) 57 | $uri = "https://$ComputerName/api/pause.htm?id=$ProbeId&action=1&username=$Username&password=$password" 58 | $null = Invoke-RestMethod -Uri $uri 59 | } 60 | -------------------------------------------------------------------------------- /pskeybase.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Read-KeybasePgpMessage { 3 | <# 4 | .SYNOPSIS 5 | A wrapper for the keybase tool. 6 | .DESCRIPTION 7 | This function provides a wrapper for "keybase pgp decrypt" to make it easier to use. 8 | .EXAMPLE 9 | PS > Read-KeybasePgpMessage -File ./Downloads/mail.txt 10 | 11 | This example tries to decrypt the mail.txt file using "keybase pgp decrypt -i mail.txt" 12 | .EXAMPLE 13 | PS > Read-KeybasePgpMessage .\Downloads | Invoke-Keybase 14 | 15 | This example first gets all items in the ./Downloads folder and then tries to decrypt each one using "keybase pgp decrypt -i " 16 | .INPUTS 17 | [System.IO.FileInfo] 18 | .OUTPUTS 19 | [PSObject] 20 | .NOTES 21 | Autor: @torggler 22 | Tested on Desktop and Core. 23 | #> 24 | [CmdletBinding()] 25 | param ( 26 | [ValidateNotNull()] 27 | [ValidateNotNullOrEmpty()] 28 | [System.IO.FileInfo] 29 | $Path, 30 | 31 | [Parameter(ValueFromPipeline=$True)] 32 | [ValidateNotNull()] 33 | [ValidateNotNullOrEmpty()] 34 | [System.IO.FileInfo] 35 | $File, 36 | 37 | [ValidateNotNull()] 38 | [ValidateNotNullOrEmpty()] 39 | [string] 40 | $Message, 41 | 42 | [ValidateNotNull()] 43 | [ValidateNotNullOrEmpty()] 44 | [System.IO.FileInfo] 45 | $OutFile 46 | 47 | ) 48 | begin { 49 | # Set default value for path parameter only if -Path is not used. 50 | if(-not($PSBoundParameters.Path) -and $PSEdition -eq "Core") { 51 | $Path = "/usr/bin/keybase" 52 | $env:HOMEPATH = $env:HOME 53 | Write-Verbose "Running on PS $PSEdition and using $Path" 54 | } elseif(-not($PSBoundParameters.Path)) { 55 | $Path = "C:\Users\Thomas\AppData\Local\Keybase\keybase.exe" 56 | Write-Verbose "Running on PS $PSEdition and using $Path" 57 | } else { 58 | Write-Verbose "Running on PS $PSEdition and using $Path" 59 | } 60 | } 61 | process { 62 | Write-Verbose "Processing $File" 63 | 64 | if ($Message) { 65 | $KBParams = " pgp decrypt -m `"$Message`" " 66 | } elseif ($File){ 67 | $KBParams = " pgp decrypt -i $($file.FullName)" 68 | } 69 | 70 | #$KBResult = Invoke-Expression -Command ("$Path $KBParams") 71 | $tmpout = Join-Path $env:HOMEPATH -ChildPath keybaseout.txt 72 | $tmperr = Join-Path $env:HOMEPATH -ChildPath keybaseerror.txt 73 | 74 | Start-Process -FilePath $Path -ArgumentList $KBParams -NoNewWindow -RedirectStandardOutput $tmpout -RedirectStandardError $tmperr -Wait 75 | 76 | $KBResult = Get-Content -Path $tmpout -Encoding UTF8 77 | if((Get-Item $tmperr).Length -ne 0) { 78 | $KBResult = Get-Content -Path $tmperr -Encoding UTF8 79 | } 80 | Remove-Item $tmpout,$tmperr -ErrorAction SilentlyContinue 81 | 82 | $output = @{ 83 | CommandLine = "$($Path.Name) $KBParams"; 84 | InFile = $File.FullName; 85 | Output = $KBResult; 86 | } 87 | if($OutFile) { 88 | $KBResult | Set-Content -Path $OutFile -Force 89 | } else { 90 | Write-Output (New-Object -TypeName psobject -Property $output) 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /qos.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | function Get-QoSPolicy { 4 | [CmdletBinding()] 5 | param( 6 | [Parameter()] 7 | [ValidateSet("User","Computer","All")] 8 | $Type = "Computer" 9 | ) 10 | 11 | switch ($Type) { 12 | 'User' { 13 | $QoSPath = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\QoS" 14 | } 15 | 'Computer' { 16 | $QoSPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\QoS" 17 | } 18 | 'All' { 19 | $QoSPath = @("HKLM:\SOFTWARE\Policies\Microsoft\Windows\QoS","HKCU:\SOFTWARE\Policies\Microsoft\Windows\QoS") 20 | } 21 | } 22 | 23 | foreach($path in $QoSPath) { 24 | $QoS = Get-Item -Path $path 25 | Write-Verbose "Found $($Qos.SubkeyCount) Policies in $($QoS.PSDrive)." 26 | if($QoS.SubKeyCount -ne 0){ 27 | Get-ChildItem $path | ForEach-Object { 28 | 29 | New-Object -TypeName psobject -Property ([ordered]@{ 30 | Drive = $_.PSDrive 31 | Name = $_.PsChildName 32 | Properties = Get-ItemProperty -Path $_.PsPath | Select-Object -Property * -ExcludeProperty ps* 33 | }) 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /rgsreport-xml.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | $queue = Import-Clixml "C:\Users\Skype for Business\rgsq.xml" 4 | $agent = Import-Clixml "C:\Users\Skype for Business\rgsagent.xml" 5 | $rgsw = Import-Clixml "C:\Users\Skype for Business\RGSWorkflow.xml" 6 | 7 | 8 | function New-RgsReport { 9 | <# 10 | .SYNOPSIS 11 | Gather information about Skype for Business Response Groups, Queues, Agent Groups. 12 | .DESCRIPTION 13 | This function uses varios cmdlets of the Lync module (or an appropriate remote session) to 14 | gather information about Response Groups. 15 | .EXAMPLE 16 | PS C:\> Get-RgsReport -Filter Office -Path .\Desktop\report.csv 17 | 18 | This example creates a CSV report for all RGS workflows matching Office. 19 | .EXAMPLE 20 | PS C:\> Get-RgsReport -Filter Office -Path .\Desktop\report.html -Html 21 | 22 | This example creates a HTML report for all RGS workflows matching Office. 23 | .EXAMPLE 24 | PS C:\> Get-RgsReport -Filter Office -Path .\Desktop\report.html -Html -PassThru | Out-GridView 25 | 26 | This example creates a HTML report for all RGS workflows matching Office, because the PassThru switch is present, 27 | the collected data will also be written to the pipeline. From there we can use it and pipe it to Out-GridView or do whatever. 28 | .INPUTS 29 | None. 30 | .OUTPUTS 31 | [psobject] 32 | .NOTES 33 | Author: @torggler 34 | #> 35 | [CmdletBinding()] 36 | param( 37 | [Parameter()] 38 | [string] 39 | $Filter, 40 | [Parameter()] 41 | [System.IO.FileInfo] 42 | $Path, 43 | [Parameter()] 44 | [switch] 45 | $Html, 46 | [Parameter()] 47 | [switch] 48 | $PassThru 49 | ) 50 | foreach ($wf in $rgsw) { 51 | 52 | $wf | Select-Object -Property Name, LineUri, PrimaryUri, @{ 53 | Name = "Queue"; 54 | Expression = { 55 | $queue | ? {$_.Identity.InstanceID -like $wf.DefaultAction.QueueId.InstanceID } | 56 | Select-Object -ExpandProperty Name } 57 | }, @{ 58 | Name = "Group"; 59 | Expression = { 60 | $aglist = $queue | ? {$_.Identity.InstanceId -like $wf.DefaultAction.QueueId.InstanceId} | Select-Object -ExpandProperty AgentGroupIDList 61 | $temp = foreach($ag in $aglist) { 62 | $agent.where{$_.Identity.InstanceId -like $ag.InstanceId}.Name 63 | }; $temp -join ", " 64 | } 65 | }, @{ 66 | Name = "RoutingMethod"; 67 | Expression = { 68 | $aglist = $queue | ? {$_.Identity.InstanceId -like $wf.DefaultAction.QueueId.InstanceId} | Select-Object -ExpandProperty AgentGroupIDList 69 | $temp=foreach($ag in $aglist) { 70 | $agent.where{$_.Identity.InstanceId -like $ag.InstanceId}.RoutingMethod 71 | }; $temp -join ", " 72 | } 73 | }, @{ 74 | Name = "Participation"; 75 | Expression = { 76 | $aglist = $queue | ? {$_.Identity.InstanceId -like $wf.DefaultAction.QueueId.InstanceId} | Select-Object -ExpandProperty AgentGroupIDList 77 | $temp=foreach($ag in $aglist) { 78 | $agent.where{$_.Identity.InstanceId -like $ag.InstanceId}.ParticipationPolicy 79 | }; $temp -join ", " 80 | } 81 | }, @{ 82 | Name = "Agents"; 83 | Expression = { 84 | $aglist = $queue | ? {$_.Identity.InstanceId -like $wf.DefaultAction.QueueId.InstanceId} | Select-Object -ExpandProperty AgentGroupIDList 85 | $temp=foreach($ag in $aglist) { 86 | $agent.where{$_.Identity.InstanceId -like $ag.InstanceId}.AgentsByUri 87 | }; $temp -join ", " 88 | } 89 | 90 | }, Active, Anonymous, EnabledForFederation 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /sqltools.ps1: -------------------------------------------------------------------------------- 1 | function Get-SMSCommandLog { 2 | <# 3 | .SYNOPSIS 4 | Read SQL Server Maintenance Solution Logs. 5 | .DESCRIPTION 6 | Read SQL Server Maintenance Solution Logs from the CommandLog table in the database set by the Database parameter. 7 | https://ola.hallengren.com/sql-server-backup.html 8 | .EXAMPLE 9 | PS C:\> 10 | Explanation of what the example does 11 | .INPUTS 12 | Inputs (if any) 13 | .OUTPUTS 14 | Output (if any) 15 | .NOTES 16 | General notes 17 | #> 18 | [CmdletBinding()] 19 | param ( 20 | [string]$ComputerName = "localhost", 21 | [string]$Instance, 22 | [string]$Database = "Master" 23 | ) 24 | 25 | $SqlCommand = New-Object System.Data.SqlClient.SqlCommand 26 | $SqlCommand.CommandText = "select * from dbo.CommandLog" 27 | 28 | $SqlConnection = New-Object System.Data.SqlClient.SqlConnection 29 | $SqlConnection.ConnectionString = "server=$ComputerName\$instance;database=$database;trusted_connection=true;" 30 | try { 31 | Write-Verbose "Trying to connect to $database on $ComputerName\$(if($Instance){$instance}else{"Default"})" 32 | $SqlConnection.Open() 33 | } 34 | catch { 35 | Write-Verbose "Could not connect to $database on $ComputerName\$(if($Instance){$instance}else{"Default"})" 36 | return 37 | } 38 | 39 | $SqlCommand.Connection = $SqlConnection 40 | $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter 41 | $SqlAdapter.SelectCommand = $SqlCommand 42 | 43 | $results = New-Object System.Data.Dataset 44 | 45 | $SqlAdapter.Fill($results) | Out-Null 46 | $SqlConnection.Close() 47 | 48 | return $Results.Tables[0] 49 | } 50 | 51 | function Get-SqlDBCC { 52 | [CmdletBinding()] 53 | param ( 54 | [Parameter()] 55 | [string]$ComputerName = "localhost", 56 | [Parameter()] 57 | [string]$Instance, 58 | [Parameter()] 59 | [ValidateSet("Logspace","UserOptions","Stats")] 60 | [string]$Name = "Logspace" 61 | ) 62 | $SqlCommand = New-Object System.Data.SqlClient.SqlCommand 63 | # set the command according to name parameter 64 | switch ($Name) 65 | { 66 | 'Logspace' {$SqlCommand.CommandText = "DBCC SQLPERF (LOGSPACE)"} 67 | 'UserOptions' {$SqlCommand.CommandText = "DBCC USEROPTIONS"} 68 | 'Stats' {$SqlCommand.CommandText = "DBCC SHOW_STATISTICS"} 69 | } 70 | $SqlConnection = New-Object System.Data.SqlClient.SqlConnection 71 | $SqlConnection.ConnectionString = "server=$ComputerName\$instance;trusted_connection=true;" 72 | try { 73 | Write-Verbose "Trying to connect to $ComputerName\$(if($Instance){$instance}else{"Default"})" 74 | $SqlConnection.Open() 75 | } 76 | catch { 77 | Write-Verbose "Could not connect to $ComputerName\$(if($Instance){$instance}else{"Default"})" 78 | return 79 | } 80 | 81 | $SqlCommand.Connection = $SqlConnection 82 | $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter 83 | $SqlAdapter.SelectCommand = $SqlCommand 84 | 85 | $results = New-Object System.Data.Dataset 86 | 87 | $SqlAdapter.Fill($results) | Out-Null 88 | $SqlConnection.Close() 89 | 90 | return $Results.Tables[0] | Sort-Object -Property "Log Space Used (%)" -Descending 91 | } 92 | 93 | function Get-SqlLastBackup { 94 | [CmdletBinding()] 95 | param ( 96 | [Parameter()] 97 | [string]$ComputerName = "localhost", 98 | [string]$Instance 99 | ) 100 | $SqlCommand = New-Object System.Data.SqlClient.SqlCommand 101 | $SqlCommand.CommandText = "-- D = Full, I = Differential and L = Log. 102 | -- There are other types of backups too but those are the primary ones. 103 | SELECT backupset.database_name, 104 | MAX(CASE WHEN backupset.type = 'D' THEN backupset.backup_finish_date ELSE NULL END) AS LastFullBackup, 105 | MAX(CASE WHEN backupset.type = 'I' THEN backupset.backup_finish_date ELSE NULL END) AS LastDiffBackup, 106 | MAX(CASE WHEN backupset.type = 'L' THEN backupset.backup_finish_date ELSE NULL END) AS LastLogBackup 107 | FROM backupset 108 | GROUP BY backupset.database_name 109 | ORDER BY backupset.database_name DESC" 110 | 111 | $SqlConnection = New-Object System.Data.SqlClient.SqlConnection 112 | $SqlConnection.ConnectionString = "server=$ComputerName\$instance;trusted_connection=true;" 113 | try { 114 | Write-Verbose "Trying to connect to $ComputerName\$(if($Instance){$instance}else{"Default"})" 115 | $SqlConnection.Open() 116 | $SqlConnection.ChangeDatabase("msdb") 117 | } 118 | catch { 119 | Write-Verbose "Could not connect to $ComputerName\$(if($Instance){$instance}else{"Default"})" 120 | return 121 | } 122 | 123 | $SqlCommand.Connection = $SqlConnection 124 | $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter 125 | $SqlAdapter.SelectCommand = $SqlCommand 126 | 127 | $results = New-Object System.Data.Dataset 128 | 129 | $SqlAdapter.Fill($results) | Out-Null 130 | $SqlConnection.Close() 131 | 132 | return $Results.Tables[0] | Sort-Object -Property "Log Space Used (%)" -Descending 133 | 134 | } 135 | 136 | -------------------------------------------------------------------------------- /tak.Import-NPSLog.ps1: -------------------------------------------------------------------------------- 1 | function Import-NPSLog { 2 | <# 3 | .SYNOPSIS 4 | Import Network Policy Server log files. 5 | 6 | .DESCRIPTION 7 | This function uses Import-Csv to import Network Policy Server logfiles. It adds the header information in order 8 | to create usable objects. 9 | 10 | .PARAMETER Path 11 | Path to the log files. 12 | 13 | .PARAMETER Delimiter 14 | Delimiter as used by Impor-Csv. 15 | 16 | .PARAMETER Filter 17 | Specify a filter to use when retreiving log files. 18 | 19 | .PARAMETER Age 20 | Specify the number of days to be included. 21 | 22 | .EXAMPLE 23 | An example 24 | 25 | .NOTES 26 | General notes 27 | #> 28 | [CmdletBinding()] 29 | param ( 30 | [System.IO.FileInfo] 31 | $Path = "C:\Windows\System32\LogFiles", 32 | [string] 33 | $Delimiter = ",", 34 | [string] 35 | $Filter = "*.log", 36 | [int] 37 | $Age = 1 38 | ) 39 | begin { 40 | # must be an array, so split at each "," 41 | $header = "ComputerName,ServiceName,Record-Date,Record-Time,Packet-Type,User-Name,Fully-Qualified-Distinguished-Name,Called-Station-ID,Calling-Station-ID,Callback-Number,Framed-IP-Address,NAS-Identifier,NAS-IP-Address,NAS-Port,Client-Vendor,Client-IP-Address,Client-Friendly-Name,Event-Timestamp,Port-Limit,NAS-Port-Type,Connect-Info,Framed-Protocol,Service-Type,Authentication-Type,Policy-Name,Reason-Code,Class,Session-Timeout,Idle-Timeout,Termination-Action,EAP-Friendly-Name,Acct-Status-Type,Acct-Delay-Time,Acct-Input-Octets,Acct-Output-Octets,Acct-Session-Id,Acct-Authentic,Acct-Session-Time,Acct-Input-Packets,Acct-Output-Packets,Acct-Terminate-Cause,Acct-Multi-Ssn-ID,Acct-Link-Count,Acct-Interim-Interval,Tunnel-Type,Tunnel-Medium-Type,Tunnel-Client-Endpt,Tunnel-Server-Endpt,Acct-Tunnel-Conn,Tunnel-Pvt-Group-ID,Tunnel-Assignment-ID,Tunnel-Preference,MS-Acct-Auth-Type,MS-Acct-EAP-Type,MS-RAS-Version,MS-RAS-Vendor,MS-CHAP-Error,MS-CHAP-Domain,MS-MPPE-Encryption-Types,MS-MPPE-Encryption-Policy,Proxy-Policy-Name,Provider-Type,Provider-Name,Remote-Server-Address,MS-RAS-Client-Name,MS-RAS-Client-Version" -split "," 42 | } 43 | process { 44 | $Files = Get-ChildItem -Path $Path -Filter $Filter | Where-Object LastWriteTime -gt (Get-Date).AddDays(-$age) 45 | foreach ($File in $Files) { 46 | Import-Csv -Path $file.FullName -Header $header -Delimiter $Delimiter | % {[NPSIASLog]::New($_)} 47 | } 48 | } 49 | end { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test-exchangeautodisco.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | function Test-ExchangeAutodiscover { 5 | [CmdletBinding()] 6 | param ( 7 | [string] 8 | $EmailAddress, 9 | [string] 10 | $ComputerName, 11 | $Credential, 12 | [switch] 13 | $ExcludeExplicitO365Endpoint 14 | ) 15 | 16 | begin { 17 | 18 | $domainName = $EmailAddress -split "@" | Select-Object -Last 1 19 | 20 | $adURIs = @{ 21 | "root" = "https://$domainName/autodiscover/autodiscover.xml"; 22 | "autodiscover" = "https://autodiscover.$domainName/autodiscover/autodiscover.xml"; 23 | } 24 | 25 | if($ComputerName) { 26 | $adURIs.Add("uri","https://$ComputerName/autodiscover/autodiscover.xml") 27 | } elseif (-not($ExcludeExplicitO365Endpoint)) { 28 | $adURIs.Add("o365","https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml") 29 | } 30 | 31 | 32 | [xml]$body = @" 33 | 34 | 35 | $EmailAddress 36 | http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a 37 | 38 | 39 | "@ 40 | 41 | } 42 | 43 | process { 44 | 45 | $out = @{} 46 | 47 | foreach($key in $adURIs.keys){ 48 | Write-Verbose "Testing $key domain for $EmailAddress" 49 | 50 | try { 51 | $r = Invoke-RestMethod -uri $adURIs[$key] -Credential $Credential -Method post -Body $body -Headers @{"content-type"="text/xml"} -DisableKeepAlive -TimeoutSec 3 52 | $out.add($key,$r.Autodiscover.Response.Account) 53 | } catch { 54 | Write-Verbose "Could not connect to $key domain" 55 | } 56 | } 57 | Write-Output (New-Object -TypeName psobject -Property $out) 58 | } 59 | 60 | end { 61 | } 62 | } 63 | 64 | 65 | function Get-ExchangeAutodisoverRecords { 66 | [CmdletBinding()] 67 | param ( 68 | $Domain, 69 | $NameServer 70 | ) 71 | 72 | begin { 73 | $aRecord = "autodiscover",$domain -join "." 74 | $srvRecord = "_autodiscover","_tcp",$domain -join "." 75 | } 76 | 77 | process { 78 | Resolve-DnsName -Name $aRecord -Type CNAME -ErrorAction SilentlyContinue | Where-Object {$_.QueryType -notlike "SOA"} 79 | Resolve-DnsName -Name $srvRecord -Type SRV -ErrorAction SilentlyContinue | Where-Object {$_.QueryType -notlike "SOA"} 80 | } 81 | 82 | end { 83 | } 84 | } 85 | 86 | # Autodiscover behaviour 87 | #https://support.microsoft.com/en-us/help/3211279/outlook-2016-implementation-of-autodiscover 88 | 89 | # 1. https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml can be disabled with ExcludeExplicitO365Endpoint 90 | # 2. SCP 91 | # 3. Root Domain https: https:///autodiscover/autodiscover.xml 92 | # 4. Autodiscover Domain https: https://autodiscover./autodiscover/autodiscover.xml 93 | # 5. Local Data PreferLocalXML 94 | # 6. HTTP Redirect from Autodiscover Domain. Ignore Acutual Autodiscover XML because retrieved incseucre. can be disabled by ExcludeHttpRedirect 95 | # 7. SRV Record: loops through entries and tries first https: _autodiscover._tcp. # ExcludeSrvRecord 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /uccapilog.ps1: -------------------------------------------------------------------------------- 1 | 2 | # extracing some useful bits from the uccapilog 3 | function Get-DialPlan { 4 | [CmdletBinding()] 5 | param( 6 | $Path = "$env:USERPROFILE\AppData\Local\Microsoft\Office\16.0\Lync\Tracing\", 7 | [switch]$all 8 | ) 9 | $logs = Get-ChildItem -Path $Path -Filter "*.uccapilog" 10 | $DPs = Select-String -Pattern "LocationProfileDescription" -Path $logs 11 | 12 | if($all) { 13 | [system.io.fileinfo[]]$paths = $DPs | Select-Object -Property path -Unique 14 | } 15 | 16 | foreach($dp in $DPs) { 17 | new-object -type psobject -Property @{ 18 | FileName = $dp.FileName 19 | DialPlan = ([xml]$dp.Line).LocationProfileDescription.Name 20 | Rules = ([xml]$dp.Line).LocationProfileDescription.Rule 21 | } 22 | } 23 | } 24 | 25 | function Get-IceWarn { 26 | [CmdletBinding()] 27 | param( 28 | $Path = "$env:USERPROFILE\AppData\Local\Microsoft\Office\16.0\Lync\Tracing\", 29 | $Count = 1 30 | ) 31 | $logs = Get-ChildItem -Path $Path -Filter "*.uccapilog" 32 | $l = Select-String -Pattern "ICEwarn=0x\d*" -Path $logs | Select-Object -Last $Count | Select-Object -ExpandProperty Line 33 | $l | ForEach-Object {New-Object -TypeName psobject -Property @{ 34 | ICEWarn = [regex]::match($_,"ICEWarn=(0x\d*),").Groups[1].Value 35 | ICEWarnEx = [regex]::match($_,"ICEWarnEx=(0x\d*),").Groups[1].Value 36 | LocalMR = [regex]::match($_,"LocalMR=([0-9]+(?:\.[0-9]+){3}(:[0-9]+)?)").Groups[1].Value 37 | RemoteMR = [regex]::match($_,"RemoteMR=([0-9]+(?:\.[0-9]+){3}(:[0-9]+)?)").Groups[1].Value 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /vmware.ps1: -------------------------------------------------------------------------------- 1 | function Get-Fnic { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory=$true, 5 | ValueFromPipelineByPropertyName=$true)] 6 | $Name 7 | ) 8 | process { 9 | try { 10 | $xcli = Get-EsxCli -VMHost $Name -ErrorAction Stop 11 | } catch { 12 | Write-Warning "Could not connect EsxCli to $Name" 13 | break 14 | } 15 | $output = $xcli.software.vib.list() 16 | $outData = @{ 17 | 'Host' = $Name; 18 | 'Version' = $output.Where{$_.Name -like "scsi-fnic"}.Version; 19 | 'ID' = $output.Where{$_.Name -like "scsi-fnic"}.ID; 20 | } 21 | Write-Output (New-Object -TypeName psobject -Property $OutData) 22 | } 23 | } 24 | function Get-Enic { 25 | [CmdletBinding()] 26 | param ( 27 | [Parameter(Mandatory=$true, 28 | ValueFromPipelineByPropertyName=$true)] 29 | $Name 30 | ) 31 | process { 32 | try { 33 | $xcli = Get-EsxCli -VMHost $Name -ErrorAction Stop 34 | } catch { 35 | Write-Warning "Could not connect EsxCli to $Name" 36 | break 37 | } 38 | foreach ($nic in ($xcli.network.nic.list()).Name) { 39 | $outData = @{ 40 | 'Host' = $Name; 41 | 'Driver' = ($xcli.network.nic.get($nic).DriverInfo).Driver; 42 | 'FirmwareVersion' = ($xcli.network.nic.get($nic).DriverInfo).FirmwareVersion; 43 | 'Version' = ($xcli.network.nic.get($nic).DriverInfo).Version; 44 | } 45 | Write-Output (New-Object -TypeName psobject -Property $OutData) 46 | } 47 | } 48 | } --------------------------------------------------------------------------------