RyanTools.psm1
# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # # Copyright 2019 Ryan James Decker function Compare-FileHash { <# .SYNOPSIS Compare two files' hashes. .DESCRIPTION Pick two files and an encryption algorithm to check their hashes to confirm integrity. #> Param( [ValidateSet( 'SHA1', 'SHA256', 'SHA384', 'SHA512', 'MACTripleDES', 'MD5', 'RIPEMD160' )][string]$Algorithm, [string]$FirstFile, [string]$SecondFile ) Begin{} Process { $_nl = [System.Environment]::NewLine $_firstHash = Get-FileHash -Path $FirstFile -Algorithm $Algorithm $_secondHash = Get-FileHash -Path $SecondFile -Algorithm $Algorithm $_output = @( @{ $FirstFile=$_firstHash.hash }, @{ $SecondFile=$_secondHash.hash } ) if ( $_firstHash.hash -eq $null -or $_secondHash.hash -eq $null ) { Write-Host "Something went wrong." return } $_output if ( $_firstHash.hash -eq $_secondHash.hash ) { return $true } else { return $false } End{} } } function Get-DesktopMonitorInfo { <# .SYNOPSIS Get desktop monitor information like resolution, etc... .DESCRIPTION This is a wrapper for querying WMI for the Win32_DesktopMonitor class. .PARAMETER ComputerName Enter an array of hosts to query the desktop monitor statistics. .PARAMETER Credential This will invoke Get-Credential and use your credentials with Get-WmiObject Win32_DesktopMonitor. .INPUTS None .OUTPUTS None #> Param( $ComputerName, $Credential ) $_gwmiArgs = @{Class = 'Win32_DesktopMonitor'} if ($ComputerName) {$_gwmiArgs.Add('ComputerName',$ComputerName)} if ($Credential) {$_gwmiArgs.Add('Credential',(Get-Credential $Credential))} Get-WmiObject @_gwmiArgs | Select-Object ` SystemName, DeviceId, Name, PixelsPerXLogicalInch, PixelsPerYLogicalInch, ScreenHeight, ScreenWidth, Status } function Get-IpV4NetworkId { <# .SYNOPSIS Calculates the network address from ipv4 and netmask. .DESCRIPTION Enter an IP address and a subnet mask to find out the ip address of the network. If you don't enter a subnet mask, the 24 bit mask is used by default. .PARAMETER IpV4Address Alias: ip,ipaddress,ipv4 Enter an IP address to find out what network it is on. .PARAMETER SubnetMask Alias: mask,subnet,snm Enter a subnet mask to calculate the network address from the ip address. .EXAMPLE Get-IpV4NetworkId -IpV4Address 192.168.190.253 -SubnetMask 255.255.240.0 192.168.176.0 In the example above, if you enter 192.168.190.253 with a mask of 255.255.240.0, the network ID is 192.168.176.0. .INPUTS Piping not supported. .OUTPUTS String #> Param ( [Parameter(Mandatory=$true)] [Alias("ip","ipaddress","ipv4")] $IpV4Address = "10.20.30.190", [Alias("mask","subnet","snm")] $SubnetMask = "255.255.255.192" ) Begin { if ( $IpV4Address -notmatch "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" -or $SubnetMask -notmatch "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" ) { Write-Error "Ip address or subnet mask not formatted correctly." } [uint16[]]$_ipAddressOctets = $IpV4Address -split '\.' [uint16[]]$_subnetMaskOctets = $SubnetMask -split '\.' [uint16[]]$_ipAddressOctets | % { if ($_ -lt 0 -or $_ -gt 255) { Write-Output $_ Write-Error "Ip address octets must be from 0 to 255, inclusive." } } [uint16[]]$_subnetMaskOctets | % { if ($_ -lt 0 -or $_ -gt 255) { Write-Output $_ Write-Error "Subnet mask octets must be from 0 to 255, inclusive." } } } Process { $_networkIdOctets = New-Object -TypeName uint16[] 4 for ($i = 0; $i -lt 4; $i++) { $_networkIdOctets[$i] = $_ipAddressOctets[$i] -band $_subnetMaskOctets[$i] } $_networkId = $_networkIdOctets -join "." } End {return $_networkId} } function Get-LogonHistory { <# .SYNOPSIS Shows user logon and logoff times. .DESCRIPTION This command searches Windows System logs for events 7001 and 7002 and returns .INPUTS None .OUTPUTS psobject[] #> Param( [int64]$MaxEvents, $ComputerName, $Credential ) Begin { $_icmArgs = @{} if ($MaxEvents) { $_icmArgs += @{ ArgumentList=@( $MaxEvents ) } } if ($ComputerName) {$_icmArgs += @{ComputerName=$ComputerName}} if ($Credential) {$_icmArgs += @{Credential=$Credential}} } Process { $_scriptblock = { Param ( $MaxEvents ) $_getWinEventArgs = @{} if ($MaxEvents) { $_getWinEventArgs += @{MaxEvents=$MaxEvents} } $_xpath = @" *[System[EventID=7001 or EventID=7002]] "@ $_getWinEventArgs = @{} if ($MaxEvents) {$_getWinEventArgs = @{MaxEvents=$MaxEvents}} $_events = Get-WinEvent ` -LogName system ` -FilterXPath $_xpath ` @_getWinEventArgs $_results = @() $_events | % { [xml[]]$_eventXml = $_.ToXml() $_sidRaw = $_eventXml.Event.EventData.Data | ? { $_.Name -eq 'UserSid' } $_sid = $_sidRaw.'#text' $_user = Get-WmiObject ` -Class win32_useraccount ` -Filter "SID='$_sid'" $_results += New-Object ` -TypeName psobject ` -Property @{ Event = switch ($_.id) { 7001 { "Logon" break } 7002 { "Logoff" break } default { break } } DateTime = Get-Date $_.TimeCreated User = $_user.Caption } } return $_results } $_icmArgs += @{ScriptBlock=$_scriptblock} Invoke-Command @_icmArgs } End { } } function Get-NetworkInterfaceConfiguration { Param( $ComputerName, $Credential ) Begin { $_gwmiArgs = @{ Class = "Win32_NetworkAdapterConfiguration" Filter = "IpEnabled=TRUE AND MacAddress IS NOT NULL" } if ($ComputerName) {$_gwmiArgs.Add("ComputerName",$ComputerName)} if ($Credential) {$_gwmiArgs.Add("Credential",$Credential)} } Process{ Get-WmiObject @_gwmiArgs | Select-Object ` DnsHostName, DnsDomain, @{ Name="Adapter Name"; Expression={$_.Caption} }, @{ Name="IP Address"; Expression={$_.IpAddress} }, @{ Name="Subnet Mask"; Expression={$_.IpSubnet} }, @{ Name="MAC Address"; Expression={$_.MacAddress} }, @{ Name="Default Gateway"; Expression={$_.DefaultIpGateway} }, @{ Name="DNS Servers"; Expression={$_.DnsServerSearchOrder} }, @{ Name="DHCP Server"; Expression={$_.DhcpServer} }, @{ Name="DHCP Lease Timestamp"; Expression={ $_year = $_.DhcpLeaseObtained.Substring(0,4) $_month = $_.DhcpLeaseObtained.Substring(4,2) $_day = $_.DhcpLeaseObtained.Substring(6,2) $_hour = $_.DhcpLeaseObtained.Substring(8,2) $_minute = $_.DhcpLeaseObtained.Substring(10,2) $_second = $_.DhcpLeaseObtained.Substring(12,2) Get-Date "$_year-$_month-$_day $_hour`:$_minute`:$_second" } }, @{ Name="DHCP Lease Expiration"; Expression={ $_year = $_.DhcpLeaseExpires.Substring(0,4) $_month = $_.DhcpLeaseExpires.Substring(4,2) $_day = $_.DhcpLeaseExpires.Substring(6,2) $_hour = $_.DhcpLeaseExpires.Substring(8,2) $_minute = $_.DhcpLeaseExpires.Substring(10,2) $_second = $_.DhcpLeaseExpires.Substring(12,2) Get-Date "$_year-$_month-$_day $_hour`:$_minute`:$_second" } } } End{} } function Get-TrustedHosts { <# .SYNOPSIS Simple command that gets the trusted hosts configuration in WsMan .DESCRIPTION .INPUTS None .OUTPUTS [Microsoft.WSMan.Management.WSManConfigElement] or [System.Array] #> [CmdletBinding( DefaultParameterSetName = 'Object' )] Param( [Parameter( ParameterSetName = 'Object' )] [switch]$AsObject = $true, [Parameter( ParameterSetName = 'Array' )] [switch]$AsArray = $false ) Begin{ $_trustedHosts = Get-Item WSMan:\localhost\Client\TrustedHosts $_trustedHostsAsArray = $_trustedHosts.Value -split ',' } Process{ if ($AsArray.IsPresent) { $AsObject = $AsObject:false } } End{ if ($AsObject.IsPresent) {$_trustedHosts} if ($AsArray.IsPresent) {$_trustedHostsAsArray} } } function Get-WlanInfo { $_netshOutputArray = NETSH WLAN SHOW INTERFACES $_adapterOutputStrings = $_netshOutputArray.trim() | ? { $_ -match " : " } $_adapterProperties = @{} foreach ( $_adapterOutputString in $_adapterOutputStrings ) { $_adapterPropertyArray = $_adapterOutputString -split " : " $_adapterPropertyName = $_adapterPropertyArray[0].Trim() $_adapterPropertyValue = $_adapterPropertyArray[1].Trim() $_adapterProperties["$_adapterPropertyName"] = "$_adapterPropertyValue" } $_adapterProperties.GetEnumerator() | Sort-Object Name } function Invoke-AsynchronousPing { <# .SYNOPSIS Ping many hosts asynchronously. .DESCRIPTION The parallel pinging of many hosts is asychronous but the script's entire execution is not. This script is blocking only until the longest ping finishes. Each host is sent four ICMP echo requests. The output comes out in three sections: 1) Hosts that responded 2) Hosts that did not respond 3) A full summary of all the ping activity. I recommend running this with the -Verbose switch if you'd like to get all of the information during execution. .PARAMETER ComputerName Aliases: cn hostname host ComputerName accepts an array of the computers you're wanting to ping in parallel. #> Param( [Alias('cn','hostname','host')] [Parameter( Mandatory=$true, ValueFromPipeline=$true )] [ValidateNotNullOrEmpty()] $ComputerName ) Begin { Get-Variable | Out-String | Write-Verbose $_nl = [System.Environment]::NewLine } Process { Write-Host "Starting computer connections...$_nl" foreach($_computer in $ComputerName) { Write-Verbose "Starting ping for $_computer..." Test-Connection -ComputerName $_computer -AsJob | Out-Null } while ( (gjb | ? {$_.JobStateInfo -notmatch 'complete'}).length -ne 0 ) { $_unprocessed = (gjb | ? {$_.JobStateInfo -notmatch 'complete'}).length $_total = (gjb).length $_processed = $_total - $_unprocessed $_progress = $_processed / $_total * 100 Write-Progress ` -Activity "Waiting for computers to finihs pinging..." ` -Status ( "Waiting for " + (gjb | ? { $_.JobStateInfo -notmatch 'complete' }).length + " computers to fininsh..." ) ` -PercentComplete $_progress sleep -Seconds 1 } Write-Verbose "All pings finished." Write-Host "These computers responded to at least one ping:" gjb | rcjb -k | ? { $_.ReplySize -ne $null } | group Address | % { $_.Group | select Address,ReplySize,ResponseTime ` -First 1 } | ft -au Write-Host "These computers failed at least one ping response:" gjb | rcjb -k | ? { $_.ReplySize -eq $null } | group Address | % { $_.Group | select Address,ReplySize,ResponseTime ` -First 1 } | ft -au Write-Host "All results:" gjb | rcjb | select Address,ReplySize,ResponseTime | ft -au gjb | rjb } End { } } function New-Shortcut { <# .SYNOPSIS Easily create a shortcut with PowerShell. .DESCRIPTION This is a wrapper for the Windows Script Host WshShortcut Object .PARAMETER ShortcutPath Alias: Path This is the filename for your shortcut file. .PARAMETER TargetPath Alias: Target This is what your shortcut will reference. .PARAMETER Arguments Specify which arguments will be sent to the target. .PARAMETER WorkingDirectory Set a directory for the context of the target's execution. .PARAMETER Description You can include a description with this parameter. .PARAMETER FullName Alias: FullPath This is the full path to your shortcut file. .PARAMETER HotKey Alias: KeyboardShortcut Enter a string as a keyboard shortcut to fire off your shortcut. .PARAMETER IconLocationPath Alias: IconPath Enter a path to the file that has your icon graphic. .PARAMETER IconLocationIndex Alias: IconIndex If your icon file path has multiple icons, you can select the index starting at 0. Usually exe or dll files will have multiple icon indicies. .PARAMETER RelativePath This is the relative path to your shortcut file. .PARAMETER WindowStyle This sets the way the window of your target will be opened. The three choices are Default, Minimized, and Maximized. .LINK https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/windows-scripting/9bbdkx3k(v%3dvs.84) .LINK https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/windows-scripting/xk6kst2k%28v%3dvs.84%29 #> Param( [Parameter( Mandatory=$true )] [Alias('Path')] $ShortcutPath, [Parameter( Mandatory=$true )] [Alias('Target')] $TargetPath, $Arguments, $WorkingDirectory = '', $Description, [Alias('FullPath')] $FullName, [Alias('KeyboardShortcut')] $HotKey, [Alias('IconPath')] $IconLocationPath, [Alias('IconIndex')] [uint16] $IconLocationIndex = 0, $RelativePath, [ValidateSet( 'Default', 'Maximized', 'Minimized' )] $WindowStyle ) Begin { } Process { $WshShell = New-Object -ComObject WScript.Shell switch ($WindowStyle) { 'Default' { $_windowStyle = 1 break } 'Maximized' { $_windowStyle = 3 break } 'Minimized' { $_windowStyle = 7 break } Default { $_windowStyle = 1 break } } $WshShortcut = $WshShell.CreateShortcut($ShortcutPath) if ($Arguments) {$WshShortcut.Arguments = $Arguments} if ($Description) {$WshShortcut.Description = $Description} if ($FullName) {$WshShortcut.FullName = $FullName} if ($HotKey) {$WshShortcut.Hotkey = $HotKey} if ($IconLocationPath) {$WshShortcut.IconLocation = "$IconLocationPath`, $IconLocationIndex"} if ($RelativePath) {$WshShortcut.RelativePath = $RelativePath} $WshShortcut.TargetPath = $TargetPath if ($WindowStyle) {$WshShortcut.WindowStyle = $_windowStyle} if ($WorkingDirectory) {$WshShortcut.WorkingDirectory = $WorkingDirectory} $WshShortcut.Save() } End {} } function Out-Prepend { <# .SYNOPSIS Prepends content to file. .DESCRIPTION This basically works like Out-File -Append but prepends content to the top of a file. .PARAMETER InputObject The input object is designed to be piped output from other commands. .PARAMETER Path This is the file which will have the content prepended. .EXAMPLE Out-Prepend -InputObject Get-Date -Path .\dates.txt PS C:\>Out-Prepend -InputObject (ls) -Path .\dates.txt .INPUTS [System.Object] .OUTPUTS None #> Param( [Parameter( ValueFromPipeline = $false, Position = 0 )] [system.object]$InputObject, [Parameter( Mandatory=$true, Position=1 )] $Path ) Begin { if (!(Test-Path $Path)) { Write-Warning "File not found: `"$Path`"" Write-Verbose "Creating new file: `"$Path`"..." New-Item -Path $Path -Force } } Process { $_newContent = $InputObject $_existingContent = Get-Content $Path $_newContent | Out-File $Path $_newContentWritten = $? $_existingContent | Out-File $Path -Append $_existingContentWritten = $? if ($_existingContentWritten -and $_newContentWritten) { Write-Verbose 'Content prepended successfully.' } else { if (!$_existingContentWritten) {Write-Warning "Failed to write existing content."} if (!$_newContentWritten) {Write-Warning "Failed to prepend new content."} } } End {} } function Restart-Module { <# .SYNOPSIS Reload a PowerShell module .DESCRIPTION Removes and imports a PowerShell module. Just supply the module name. .PARAMETER Name Enter the name of a loaded module. .INPUST None .OUTPUTS None #> [CmdletBinding()] Param( [Parameter(Mandatory=$true)] $Name ) Begin{ $_verbose = @{} if ( $PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent ) { $_verbose = @{Verbose=$true} } } Process{ Remove-Module -Name $Name @_verbose Import-Module -Name $Name @_verbose } End{} } function Test-Layer4Port { <# .SYNOPSIS Tests port on computer. .DESCRIPTION Tests port on computer. .PARAMETER ComputerName Name of server to test the port connection on. .PARAMETER Port Port to test .PARAMETER TCP Use tcp port .PARAMETER UDP Use udp port .PARAMETER UdpTimeOut Sets a timeout for UDP port query. (In milliseconds, Default is 1000) .PARAMETER TcpTimeOut Sets a timeout for TCP port query. (In milliseconds, Default is 1000) .NOTES Name: Test-Port.ps1 Author: Boe Prox DateCreated: 18Aug2010 List of Ports: http://www.iana.org/assignments/port-numbers To Do: Add capability to run background jobs for each host to shorten the time to scan. .LINK https://boeprox.wordpress.org .EXAMPLE Test-Port -ComputerName 'server' -Port 80 Checks port 80 on server 'server' to see if it is listening .EXAMPLE 'server' | Test-Port -Port 80 Checks port 80 on server 'server' to see if it is listening .EXAMPLE Test-Port -ComputerName @("server1","server2") -Port 80 Checks port 80 on server1 and server2 to see if it is listening .EXAMPLE Test-Port -ComputerName dc1 -Port 17 -UDP -UdpTimeOut 10000 Server : dc1 Port : 17 TypePort : UDP Open : True Notes : "My spelling is Wobbly. It's good spelling but it Wobbles, and the letters get in the wrong places." A. A. Milne (1882-1958) Description ----------- Queries port 17 (qotd) on the UDP port and returns whether port is open or not .EXAMPLE @("server1","server2") | Test-Port -port 80 Checks port 80 on server1 and server2 to see if it is listening .EXAMPLE (Get-Content hosts.txt) | Test-Port -port 80 Checks port 80 on servers in host file to see if it is listening .EXAMPLE Test-Port -ComputerName (Get-Content hosts.txt) -port 80 Checks port 80 on servers in host file to see if it is listening .EXAMPLE Test-Port -ComputerName (Get-Content hosts.txt) -port @(1..59) Checks a range of ports from 1-59 on all servers in the hosts.txt file #> [cmdletbinding( DefaultParameterSetName = 'tcp', ConfirmImpact = 'low' )] Param( [Parameter( Mandatory = $True, Position = 0, ParameterSetName = '', ValueFromPipeline = $True )] [array] $ComputerName, [Parameter( Position = 1, Mandatory = $True, ParameterSetName = '' )] [array] $Port, [Parameter( Mandatory = $False, ParameterSetName = 'tcp' )] [switch] $TCP, [Parameter( Mandatory = $False, ParameterSetName = 'tcp' )] [int] $TcpTimeOut=1000, [Parameter( Mandatory = $False, ParameterSetName = 'udp' )] [switch] $UDP, [Parameter( Mandatory = $False, ParameterSetName = 'udp' )] [int] $UdpTimeout=1000 ) Begin { If (!$TCP -AND !$UDP) {$TCP = $True} #Typically you never do this, but in this case I felt it was for the benefit of the function #as any errors will be noted in the output of the report $ErrorActionPreference = "SilentlyContinue" $_report = @() } Process { ForEach ($_computer in $ComputerName) { ForEach ($_port in $Port) { If ($TCP) { #Create temporary holder $temp = "" | Select Server, Port, TypePort, Open, Notes #Create object for connecting to port on computer $tcpobject = new-Object system.Net.Sockets.TcpClient #Connect to remote machine's port $connect = $tcpobject.BeginConnect($_computer,$_port,$null,$null) #Configure a timeout before quitting $wait = $connect.AsyncWaitHandle.WaitOne($TcpTimeOut,$false) #If timeout If(!$wait) { #Close connection $tcpobject.Close() Write-Verbose "Connection Timeout" #Build report $temp.Server = $_computer $temp.Port = $_port $temp.TypePort = "TCP" $temp.Open = "False" $temp.Notes = "Connection to Port Timed Out" } Else { $error.Clear() $tcpobject.EndConnect($connect) | out-Null #If error If($error[0]){ #Begin making error more readable in report [string]$string = ($error[0].exception).message $message = (($string.split(":")[1]).replace('"',"")).TrimStart() $failed = $true } #Close connection $tcpobject.Close() #If unable to query port to due failure If($failed){ #Build report $temp.Server = $_computer $temp.Port = $_port $temp.TypePort = "TCP" $temp.Open = "False" $temp.Notes = "$message" } Else{ #Build report $temp.Server = $_computer $temp.Port = $_port $temp.TypePort = "TCP" $temp.Open = "True" $temp.Notes = "" } } #Reset failed value $failed = $Null #Merge temp array with report $_report += $temp } If ($UDP) { #Create temporary holder $temp = "" | Select Server, Port, TypePort, Open, Notes #Create object for connecting to port on computer $udpobject = new-Object system.Net.Sockets.Udpclient #Set a timeout on receiving message $udpobject.client.ReceiveTimeout = $UdpTimeout #Connect to remote machine's port Write-Verbose "Making UDP connection to remote server" $udpobject.Connect("$_computer",$_port) #Sends a message to the host to which you have connected. Write-Verbose "Sending message to remote host" $a = new-object system.text.asciiencoding $byte = $a.GetBytes("$(Get-Date)") [void]$udpobject.Send($byte,$byte.length) #IPEndPoint object will allow us to read datagrams sent from any source. Write-Verbose "Creating remote endpoint" $remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any,0) Try { #Blocks until a message returns on this socket from a remote host. Write-Verbose "Waiting for message return" $receivebytes = $udpobject.Receive([ref]$remoteendpoint) [string]$returndata = $a.GetString($receivebytes) If ($returndata) { Write-Verbose "Connection Successful" #Build report $temp.Server = $_computer $temp.Port = $_port $temp.TypePort = "UDP" $temp.Open = "True" $temp.Notes = $returndata $udpobject.close() } } Catch { If ($Error[0].ToString() -match "\bRespond after a period of time\b") { #Close connection $udpobject.Close() #Make sure that the host is online and not a false positive that it is open If (Test-Connection -comp $_computer -count 1 -quiet) { Write-Verbose "Connection Open" #Build report $temp.Server = $_computer $temp.Port = $_port $temp.TypePort = "UDP" $temp.Open = "True" $temp.Notes = "" } Else { <# It is possible that the host is not online or that the host is online, but ICMP is blocked by a firewall and this port is actually open. #> Write-Verbose "Host maybe unavailable" #Build report $temp.Server = $_computer $temp.Port = $_port $temp.TypePort = "UDP" $temp.Open = "False" $temp.Notes = "Unable to verify if port is open or if host is unavailable." } } ElseIf ( $Error[0].ToString() -match "forcibly closed by the remote host" ) { #Close connection $udpobject.Close() Write-Verbose "Connection Timeout" #Build report $temp.Server = $_computer $temp.Port = $_port $temp.TypePort = "UDP" $temp.Open = "False" $temp.Notes = "Connection to Port Timed Out" } Else { $udpobject.close() } } #Merge temp array with report $_report += $temp } } } } End { #Generate Report $_report } } function Update-RyanToolsModule { <# .SYNOPSIS Updates the modules Ryan Tools and Ryan Tools Admin. .DESCRIPTION This command will update the modules Ryan Tools and Ryan Tools Admin from a public Bitbucket repository at: https://bitbucket.org/Rhyknowscerious/ryan_tools Basically it will download, extract, and move these modules to an appropriate location for PowerShell to automatically load the module upon use. If you want to install the modules yourself, just download the modules and put them in the appropriate directories .PARAMETER Force This switch will enable over-writing files and automatically creating necessary folders if missing. .PARAMETER NoCleanup Normally, this command will delete the downloaded archive and extracted temporary files. Use this switch to keep these files instead. .PARAMETER ModuleArchiveDownloadUrl This is where the module zip file is on BitBucket. .PARAMETER ModuleArchiveDownloadFilePath This is where the downloaded zip file will be saved before extraction. .PARAMETER ModuleExtractionDirectory This is where the modules will be stored temporarily before extraction. .PARAMETER ModuleInstallDirectory This tells the command where to install the updated module. .INPUTS None .OUTPUTS None #> [CmdletBinding()] Param( [switch]$Force, [switch]$NoCleanup, $ModuleArchiveDownloadUrl, $ModuleArchiveDownloadFilePath = "$HOME\Downloads\RyanToolsModuleArchive.zip", $ModuleExtractionDirectory = "$HOME\Downloads\RyanToolsModuleArchiveExtracted\" ) DynamicParam { $_nl = [System.Environment]::NewLine # Create the dynamic parameter name, dictionary, attribute collection $_dynamicParameterName = 'ModuleInstallDirectory' $_runtimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $_attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] # Create and set the parameters' attributes $_parameterAttribute = New-Object System.Management.Automation.ParameterAttribute $_parameterAttribute.Mandatory = $true # Create, generate, and set the ValidateSet $_validateSet = $env:PSModulePath -split ';' #$_validateSet += "$HOME\Downloads\PowerShellModules" #$_validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($_validateSet) # Create parameter attribute: help message $_helpMessage = "$_nl`Here are configured powershell module installation " + "locations on for this computer but you can choose whatever you " + "want:$_nl$_nl" + ($_validateSet -join $_nl) + $_nl+$_nl + "You should usually put modules for specific users to access in " + "`"`$HOME\Documents\WindowsPowerShell`". Modules for all users " + "typically go into `"`$env:ProgramFiles\WindowsPowerShell\Modules`" " + "or `"`${env:ProgramFiles(x86)}\WindowsPowerShell\Modules`" " + "And system modules typically go in `"`$PSHOME\Modules`"."+$_nl+$_nl $_parameterAttribute.HelpMessage = $_helpMessage # Add the Parameter and ValidateSet attributes to the attributes collection $_attributeCollection.Add($_parameterAttribute) $_attributeCollection.Add($_validateSetAttribute) # Create the dynamic parameter $_runtimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter( $_dynamicParameterName, [string], $_attributeCollection ) # Add the dynamic parameter to a collection and return them $_runtimeParameterDictionary.Add( $_dynamicParameterName, $_runtimeParameter ) return $_runtimeParameterDictionary } begin { # Bind the parameter to a friendly variable $ModuleInstallDirectory = $PsBoundParameters[$_dynamicParameterName] $_verbose = @{} $_force = @{} if ( $PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent ) { $_verbose = @{Verbose=$true} } if ($Force.IsPresent) { $_force = @{Force=$true} } if (!$ModuleArchiveDownloadUrl) { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $_html = Invoke-WebRequest ` -Uri "https://bitbucket.org/Rhyknowscerious/ryan_tools/downloads/" ` @_verbose $_htmlAsArray = $_html.RawContent.Split([System.Environment]::NewLine) $_htmlTargetLine = $_htmlAsArray | sls '\.zip' $_url = "https://www.bitbucket.org" $_url += $_htmlTargetLine.ToString().trim() -replace '^.*href="','' -replace '">Download reposit.*$','' $ModuleArchiveDownloadUrl = $_url } } process{ $_updateStartTime = Get-Date [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $_webClient = New-Object System.Net.WebClient @_verbose Write-Verbose "Attempting to download module `"$ModuleArchiveDownloadUrl`" to `"$ModuleArchiveDownloadFilePath`". Please wait...$_nl$_nl" $_downloadStartTime = Get-Date $_webClient.DownloadFile( $ModuleArchiveDownloadUrl, $ModuleArchiveDownloadFilePath ) $_downloadSucceeded = $? $_downloadEndTime = Get-Date if ($_downloadSucceeded) { Write-Verbose "Module downloaded from `"$ModuleArchiveDownloadUrl`" saved: `"$ModuleArchiveDownloadFilePath`"$_nl$_nl" } else { Write-Error "Module update failed. Download failed: `"$ModuleArchiveDownloadUrl`"" return } $_downloadTimeSpan = New-TimeSpan $_downloadStartTime $_downloadEndTime Write-Verbose "Download duration: $_downloadTimeSpan$_nl$_nl" Write-Verbose "Extracting $ModuleArchiveDownloadFilePath to `"$ModuleExtractionDirectory`"...$_nl$_nl" Expand-Archive ` -Path $ModuleArchiveDownloadFilePath ` -DestinationPath "$ModuleExtractionDirectory" ` @_force ` @_verbose $_extractedSuccessfully = $? if ($_extractedSuccessfully) { Write-Verbose "Extraction successful.$_nl$_nl" } else { Write-Error "Module update failed. Extraction failed: `"$ModuleArchiveDownloadFilePath`"" return } $_updatedModulesFolder = ( Get-ChildItem $ModuleExtractionDirectory ).FullName $_updatedModules = ( Get-ChildItem $_updatedModulesFolder -Directory ).FullName Write-Verbose "Copying modules from `"$_updatedModulesFolder`" to `"$ModuleInstallDirectory...$_nl$_nl" if (!(Test-Path $ModuleInstallDirectory)) { Write-Verbose "Folder doesn't exist `"$ModuleInstallDirectory`"$_nl$_nl" New-Item ` -ItemType Directory ` -Path $ModuleInstallDirectory ` @_verbose if (!$?) { Write-Error "Could not create directory $" return } } Copy-Item ` -Path $_updatedModules ` -Destination $ModuleInstallDirectory ` -Recurse ` -Container ` @_verbose ` @_force $_filesCopiedSuccesfully = $? if ($_filesCopiedSuccesfully) { $_updateEndTime = Get-Date $_updateTimeSpan = New-TimeSpan $_updateStartTime $_updateEndTime } else { Write-Error "Module update failed: Error copying files from `"$_updatedModulesFolder`" to `"$ModuleInstallDirectory`"." return } Write-Verbose "Module updated successfully. Update duration: $_updateTimeSpan$_nl$_nl" if ($NoCleanup.IsPresent) { Write-Verbose "Temporary files NOT removed: $_nl`t$ModuleArchiveDownloadUrl$_nl`t$ModuleExtractionDirectory$_nl$_nl" } else { Write-Verbose "Cleaning up downloaded files.$_nl$_nl" Remove-Item ` -Path $ModuleArchiveDownloadFilePath,$ModuleExtractionDirectory ` -Recurse ` @_force ` @_verbose $_tempFile = ls $ModuleInstallDirectory -Filter "*Rhyknowscerious*" if ($_tempFile.FullName -match "Rhyknowscerious") { Remove-Item ` -Path $_tempFile.FullName ` -Recurse ` @_force ` @_verbose } } } End { } } function Invoke-WpfXamlForm { [CmdletBinding( DefaultParameterSetName="Xaml As String, Logic As Scriptblock" )] <##> Param( [Parameter( ParameterSetName="Xaml As String, Logic As File", Mandatory=$true )] [Parameter( ParameterSetName="Xaml As String, Logic As Scriptblock", Mandatory=$true )] [string] $XamlString, [Parameter( ParameterSetName="Xaml As File, Logic As File", Mandatory=$true )] [Parameter( ParameterSetName="Xaml As File, Logic As Scriptblock", Mandatory=$true )] $XamlPath, [Parameter( ParameterSetName="Xaml As String, Logic As File", Mandatory=$true )] [Parameter( ParameterSetName="Xaml As File, Logic As File", Mandatory=$true )] [string] $LogicPath, [Parameter( ParameterSetName="Xaml As String, Logic As Scriptblock", Mandatory=$true )] [Parameter( ParameterSetName="Xaml As File, Logic As Scriptblock", Mandatory=$true )] [scriptblock] $LogicScriptblock ) DynamicParam{} Begin{ Add-Type -AssemblyName PresentationFramework if ($XamlString) { [xml]$_xaml = $XamlString } if ($XamlPath) { [xml]$_xaml = Get-Content $XamlPath } $_reader = (New-Object System.Xml.XmlNodeReader $_xaml) $_window = [Windows.Markup.XamlReader]::Load($_reader) } Process{ if ($LogicPath) {. $LogicPath} if ($LogicScriptblock) {$LogicScriptblock} } End{ $_window.ShowDialog() } } function Invoke-AdoSqlDataReader { <# .SYNOPSIS Invokes an ADO.NET SqlDataReader instance. .DESCRIPTION Uses an ADO.NET SqlDataReader instance to capture query output and formats it as a psobject. .PARAMETER ComputerName The name or ip address of your SQL Server. .PARAMETER DatabaseName (Alias: InitialCatalog) The name of the database aka initial catalog .PARAMETER Query Enter an SQL query as a string which is sent to the SqlDataAdapter(String,SqlConnection) constructor overload. .PARAMETER IntegratedSecurity Sets the SQL connection string keyword "Integrated Security" to SSPI and uses Windows Authentication. .PARAMETER Credential Enter SQL Server credentials instead of Integrated Security aka Windows Authentication. Careful, this may not actually be secure. .INPUTS None .OUTPUTS System.Management.Automation.PsObject #> [CmdletBinding( DefaultParameterSetName="IntegratedSecurity" )] Param( [Parameter( ParameterSetName="IntegratedSecurity" )] [Parameter( ParameterSetName="UseSqlCredentials" )] [Alias("Server","cn")] $ComputerName, [Parameter( ParameterSetName="IntegratedSecurity", Mandatory=$true )] [Parameter( ParameterSetName="UseSqlCredentials", Mandatory=$true )] [Alias("InitialCatalog","Database")] $DatabaseName, [Parameter( ParameterSetName="IntegratedSecurity" )] [Parameter( ParameterSetName="UseSqlCredentials" )] [Alias("q")] $Query, [Parameter( ParameterSetName="IntegratedSecurity" )] [switch] [Alias("i")] $IntegratedSecurity, [Parameter( ParameterSetName="UseSqlCredentials", Mandatory=$true )] [Alias("User","u")] $Credential ) $_connection = $null $_dataReader = $null $_connectionArray = @() if ($ComputerName) { $_connectionArray += "Data Source='$ComputerName';" } else { $_connectionArray += "Data Source=(local);" } $_connectionArray += @( "Initial Catalog='$DatabaseName';" ) if ($Credential) { $Credential = Get-Credential $Credential if (!$?) { return } $_connectionArray += @( "User ID=$($Credential.UserName);", "Password=$($Credential.GetNetworkCredential().Password);" ) } else { $_connectionArray += "Integrated Security=SSPI;" } $_connectionString = -join $_connectionArray Write-Verbose "Using connection string: `"$_connectionString`"" $_connection = [System.Data.SqlClient.SqlConnection]::new( $_connectionString ) try { Write-Verbose "Opening connection to database `"$DatabaseName`" on SQL Server `"$ComputerName`"..." $_connection.Open() $_command = [System.Data.SqlClient.SqlCommand]::new( [string]$Query, $_connection ) Write-Verbose "Initializing DataReader..." $_dataReader = $_command.ExecuteReader() $_table = @() while ( $_dataReader.Read() ) { $_row = New-Object psobject for ($i=0; $i -lt $_dataReader.FieldCount; $i++){ $_row | Add-Member ` -MemberType NoteProperty ` -Name $_dataReader.GetName($i)` -Value $_dataReader.GetValue($i) } $_table += $_row } } finally { Write-Verbose "Terminating DataReader" if ($_dataReader -ne $null) { $_dataReader.Close() } Write-Verbose "Closing connection to database `"$DatabaseName`" on SQL Server `"$ComputerName`"..." if ($_connection -ne $null) { $_connection.Close() } } return $_table } function Get-AdoDataSet { <# .SYNOPSIS Gets an ADO.NET DataSet instance. .DESCRIPTION Uses ADO Classes SqlConnection,SqlDataAdapter,SqlCommandBuilder to return an ADO.NET DataSet instance. .PARAMETER ComputerName The name or ip address of your SQL Server. .PARAMETER DatabaseName (Alias: InitialCatalog) The name of the database aka initial catalog .PARAMETER Query Enter an SQL query as a string which is sent to the SqlDataAdapter(String,SqlConnection) constructor overload. .PARAMETER IntegratedSecurity Sets the SQL connection string keyword "Integrated Security" to SSPI and uses Windows Authentication. .PARAMETER Credential Enter SQL Server credentials instead of Integrated Security aka Windows Authentication. Careful, this may not actually be secure. .INPUTS None .OUTPUTS System.Data.Dataset .LINK https://docs.microsoft.com/en-us/dotnet/api/system.data.dataset #> [CmdletBinding( DefaultParameterSetName="IntegratedSecurity" )] Param( [Parameter( ParameterSetName="IntegratedSecurity" )] [Parameter( ParameterSetName="UseSqlCredentials" )] [Alias("Server","cn")] $ComputerName, [Parameter( ParameterSetName="IntegratedSecurity", Mandatory=$true )] [Parameter( ParameterSetName="UseSqlCredentials", Mandatory=$true )] [Alias("InitialCatalog","Database")] $DatabaseName, [Parameter( ParameterSetName="IntegratedSecurity" )] [Parameter( ParameterSetName="UseSqlCredentials" )] [Alias("q")] $Query, [Parameter( ParameterSetName="IntegratedSecurity" )] [switch] [Alias("i")] $IntegratedSecurity, [Parameter( ParameterSetName="UseSqlCredentials", Mandatory=$true )] [Alias("User","u")] $Credential ) $_connectionArray = @() if ($ComputerName) { $_connectionArray += "Data Source='$ComputerName';" } else { $_connectionArray += "Data Source=(local);" } $_connectionArray += @( "Initial Catalog='$DatabaseName';" ) if ($Credential) { $Credential = Get-Credential $Credential if (!$?) { return } $_connectionArray += @( "User ID=$($Credential.UserName);", "Password=$($Credential.GetNetworkCredential().Password);" ) } else { $_connectionArray += "Integrated Security=SSPI;" } $_connectionString = -join $_connectionArray Write-Verbose "Using connection string: `"$_connectionString`"" $_connection = $null $_connection = [System.Data.SqlClient.SqlConnection]::new( $_connectionString ) $_dataSet = [System.Data.DataSet]::new() Write-Verbose "Initializing DataAdapter..." $_dataAdapter = [System.Data.SqlClient.SqlDataAdapter]::new( $Query, $_connection ) $_commandBuilder = [System.Data.SqlClient.SqlCommandBuilder]::new( $_sqlDataAdapter ) Write-Verbose "Filling DataSet..." $_dataAdapter.Fill( $_dataSet ) if ($?) { return $_dataSet } } |