From 9f4d65536f8c779dee1cc94034593f6896d0c166 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Sun, 30 Aug 2015 13:15:29 -0500 Subject: [PATCH] organized scripts in folders --- .../PostMessageToCampfire.ps1 | 0 .../Get-ActiveDirectoryUserActivity.ps1 | 142 +++++++++++++ .../Get-AdDnsRecordAcl.ps1 | 0 .../Get-AdGroupMembershipChange.ps1 | 164 ++++++++++++++ .../Get-AdUserMatches.ps1 | 0 .../Get-UserLogonSessionHistory.ps1 | 0 .../Create-AzureVMSnapshot.ps1 | 0 .../Restore-AzureVMSnapshot.ps1 | 0 .../Import-Certificate.ps1 | 0 .../Set-ServiceAccount.ps1 | 0 .../Get-DhcpLeasesInDomain.ps1 | 0 .../Convert-DynamicDnsRecordToStatic.ps1 | 0 .../Get-DynamicDNSRecordsToBeScavenged.ps1 | 0 .../Get-RecordsToBeScavenged.ps1 | 0 .../Test-ClientDynamicDns.ps1 | 0 .../Sync-CsvToSql.ps1 | 0 File-Folder Management/Send-File.ps1 | 141 ++++++++++++ Cowbell.wav => Fun Stuff/Cowbell.wav | Bin .../Get-MoreCowbell.ps1 | 0 .../Remove-AutoConfigProxy.ps1 | 0 .../Get-FreeHardDriveSpace.ps1 | 0 .../Get-ServerUptimeReport.ps1 | 0 .../Get-UpTime.ps1 | 0 .../Get-LocalGroupMembership.ps1 | 0 ...Set-MsRandomPasswordToLocalUserAccount.ps1 | 0 .../Set-RandomPasswordToLocalUserAccount.ps1 | 0 Modules/GnuPg.psm1 | 190 +++++++++++++++++ .../Get-LocalPort.ps1 | 0 .../Get-ServiceListeningPort.ps1 | 0 .../PacketCapture.ps1 | 0 .../Send-WolProxyRequest.ps1 | 0 .../Test-ServerRolePortGroup.ps1 | 0 .../Enable-RemotePSRemoting.ps1 | 0 .../Get-FunctionDefaultParameters.ps1 | 0 .../Invoke-RemoteScript.ps1 | 0 .../New-ValidationDynamicParam.ps1 | 0 .../Show-SimpleMenu.ps1 | 0 .../SetPrinterComments.ps1 | 0 README.md | 2 + .../Connect-CmRemoteTools.ps1 | 0 .../Convert-CMApplicationToPackage.ps1 | 0 .../Create-CMRandomMemberCollection.ps1 | 0 .../Get-CMDpContent.ps1 | 0 .../Get-CmAppDeploymentStatus.ps1 | 0 .../New-CMMyApplication.ps1 | 0 .../Remove-CMDirectMembershipRule.ps1 | 0 .../Start-CMApplicationDeploymentTest.ps1 | 0 .../Start-PostConfigmgrBackupTasks.ps1 | 0 .../Sync-CmToWsusSoftwareUpdates.ps1 | 0 .../Test-CmDeploymentClientStatus.ps1 | 0 .../New-ScheduledScript.ps1 | 0 .../DownloadQualysPatches.ps1 | 0 Send-TwitterDirectMessage.ps1 | 61 ------ Send-TwitterDirectMessagev2.ps1 | 201 ------------------ Set-Exchange2013AdSchema.ps1 | 119 ----------- Test-PotentialScomAgent.ps1 | 120 ----------- TimeframeInvestigator.ps1 | 142 ------------- .../Get-EventsFromTimeframe.ps1 | 0 .../Copy-LocalPathToVmCdRom.ps1 | 0 Find-WmiClass.ps1 => WMI/Find-WmiClass.ps1 | 0 60 files changed, 639 insertions(+), 643 deletions(-) rename PostMessageToCampfire.ps1 => APIs/PostMessageToCampfire.ps1 (100%) create mode 100644 ActiveDirectory/Get-ActiveDirectoryUserActivity.ps1 rename Get-AdDnsRecordAcl.ps1 => ActiveDirectory/Get-AdDnsRecordAcl.ps1 (100%) create mode 100644 ActiveDirectory/Get-AdGroupMembershipChange.ps1 rename Get-AdUserMatches.ps1 => ActiveDirectory/Get-AdUserMatches.ps1 (100%) rename Get-UserLogonSessionHistory.ps1 => ActiveDirectory/Get-UserLogonSessionHistory.ps1 (100%) rename Create-AzureVMSnapshot.ps1 => Azure/Create-AzureVMSnapshot.ps1 (100%) rename Restore-AzureVMSnapshot.ps1 => Azure/Restore-AzureVMSnapshot.ps1 (100%) rename Import-Certificate.ps1 => Certificates/Import-Certificate.ps1 (100%) rename Set-ServiceAccount.ps1 => Configuration Mangaement/Set-ServiceAccount.ps1 (100%) rename Get-DhcpLeasesInDomain.ps1 => DHCP/Get-DhcpLeasesInDomain.ps1 (100%) rename Convert-DynamicDnsRecordToStatic.ps1 => DNS/Convert-DynamicDnsRecordToStatic.ps1 (100%) rename Get-DynamicDNSRecordsToBeScavenged.ps1 => DNS/Get-DynamicDNSRecordsToBeScavenged.ps1 (100%) rename Get-RecordsToBeScavenged.ps1 => DNS/Get-RecordsToBeScavenged.ps1 (100%) rename Test-ClientDynamicDns.ps1 => DNS/Test-ClientDynamicDns.ps1 (100%) rename Sync-CsvToSql.ps1 => Database-Datasets/Sync-CsvToSql.ps1 (100%) create mode 100644 File-Folder Management/Send-File.ps1 rename Cowbell.wav => Fun Stuff/Cowbell.wav (100%) rename Get-MoreCowbell.ps1 => Fun Stuff/Get-MoreCowbell.ps1 (100%) rename Remove-AutoConfigProxy.ps1 => Internet Explorer/Remove-AutoConfigProxy.ps1 (100%) rename Get-FreeHardDriveSpace.ps1 => Inventorying-Reporting Tools/Get-FreeHardDriveSpace.ps1 (100%) rename Get-ServerUptimeReport.ps1 => Inventorying-Reporting Tools/Get-ServerUptimeReport.ps1 (100%) rename Get-UpTime.ps1 => Inventorying-Reporting Tools/Get-UpTime.ps1 (100%) rename Get-LocalGroupMembership.ps1 => Local Account Management/Get-LocalGroupMembership.ps1 (100%) rename Set-MsRandomPasswordToLocalUserAccount.ps1 => Local Account Management/Set-MsRandomPasswordToLocalUserAccount.ps1 (100%) rename Set-RandomPasswordToLocalUserAccount.ps1 => Local Account Management/Set-RandomPasswordToLocalUserAccount.ps1 (100%) create mode 100644 Modules/GnuPg.psm1 rename Get-LocalPort.ps1 => Networking/Get-LocalPort.ps1 (100%) rename Get-ServiceListeningPort.ps1 => Networking/Get-ServiceListeningPort.ps1 (100%) rename PacketCapture.ps1 => Networking/PacketCapture.ps1 (100%) rename Send-WolProxyRequest.ps1 => Networking/Send-WolProxyRequest.ps1 (100%) rename Test-ServerRolePortGroup.ps1 => Networking/Test-ServerRolePortGroup.ps1 (100%) rename Enable-RemotePSRemoting.ps1 => PowerShell Internals/Enable-RemotePSRemoting.ps1 (100%) rename Get-FunctionDefaultParameters.ps1 => PowerShell Internals/Get-FunctionDefaultParameters.ps1 (100%) rename Invoke-RemoteScript.ps1 => PowerShell Internals/Invoke-RemoteScript.ps1 (100%) rename New-ValidationDynamicParam.ps1 => PowerShell Internals/New-ValidationDynamicParam.ps1 (100%) rename Show-SimpleMenu.ps1 => PowerShell Internals/Show-SimpleMenu.ps1 (100%) rename SetPrinterComments.ps1 => Print Management/SetPrinterComments.ps1 (100%) rename Connect-CmRemoteTools.ps1 => SCCM/Connect-CmRemoteTools.ps1 (100%) rename Convert-CMApplicationToPackage.ps1 => SCCM/Convert-CMApplicationToPackage.ps1 (100%) rename Create-CMRandomMemberCollection.ps1 => SCCM/Create-CMRandomMemberCollection.ps1 (100%) rename Get-CMDpContent.ps1 => SCCM/Get-CMDpContent.ps1 (100%) rename Get-CmAppDeploymentStatus.ps1 => SCCM/Get-CmAppDeploymentStatus.ps1 (100%) rename New-CMMyApplication.ps1 => SCCM/New-CMMyApplication.ps1 (100%) rename Remove-CMDirectMembershipRule.ps1 => SCCM/Remove-CMDirectMembershipRule.ps1 (100%) rename Start-CMApplicationDeploymentTest.ps1 => SCCM/Start-CMApplicationDeploymentTest.ps1 (100%) rename Start-PostConfigmgrBackupTasks.ps1 => SCCM/Start-PostConfigmgrBackupTasks.ps1 (100%) rename Sync-CmToWsusSoftwareUpdates.ps1 => SCCM/Sync-CmToWsusSoftwareUpdates.ps1 (100%) rename Test-CmDeploymentClientStatus.ps1 => SCCM/Test-CmDeploymentClientStatus.ps1 (100%) rename New-ScheduledScript.ps1 => Scheduled Tasks/New-ScheduledScript.ps1 (100%) rename DownloadQualysPatches.ps1 => Security/DownloadQualysPatches.ps1 (100%) delete mode 100644 Send-TwitterDirectMessage.ps1 delete mode 100644 Send-TwitterDirectMessagev2.ps1 delete mode 100644 Set-Exchange2013AdSchema.ps1 delete mode 100644 Test-PotentialScomAgent.ps1 delete mode 100644 TimeframeInvestigator.ps1 rename Get-EventsFromTimeframe.ps1 => Troubleshooting Tools/Get-EventsFromTimeframe.ps1 (100%) rename Copy-LocalPathToVmCdRom.ps1 => VMware/Copy-LocalPathToVmCdRom.ps1 (100%) rename Find-WmiClass.ps1 => WMI/Find-WmiClass.ps1 (100%) diff --git a/PostMessageToCampfire.ps1 b/APIs/PostMessageToCampfire.ps1 similarity index 100% rename from PostMessageToCampfire.ps1 rename to APIs/PostMessageToCampfire.ps1 diff --git a/ActiveDirectory/Get-ActiveDirectoryUserActivity.ps1 b/ActiveDirectory/Get-ActiveDirectoryUserActivity.ps1 new file mode 100644 index 0000000..64ed85b --- /dev/null +++ b/ActiveDirectory/Get-ActiveDirectoryUserActivity.ps1 @@ -0,0 +1,142 @@ +#Requires -Module ActiveDirectory + +<# +.SYNOPSIS + This script finds all logon and logoff times of all users on all computers in an Active Directory organizational unit. + + The appropriate audit policies must be enabled first because the appropriate event IDs will show up. +.EXAMPLE + PS> Get-ActiveDirectoryUserActivity.ps1 -OrganizationalUnit 'OU=My Desktops,DC=lab,DC=local' -EmailToAddress administrator@lab.local + + This example will query the security event logs of all computers in the AD OU 'My Desktops' and find + all instances of the events IDs 4647 and 4648 in the security event logs. It will then generate a friendly + report showing the user name, time generated, if the event was a logon or logoff and the computer + the event came from. Once the file is generated, it will then send an email to administrator@lab.local + with the user report attached. + +.PARAMETER OrganizationalUnit + The distinguisned name of the AD organizational unit that you'd like to query the security event log of computers. + +.PARAMETER EventID + Two event IDs representing logon and logoff events. + +.PARAMETER EmailToAddress + The email address that you'd like to send the final report to. + +.PARAMETER EmailFromAddress + The email address you'd like the report sent to show it's from. + +.PARAMETER EmailSubject + The subject of the email that will contain the user activity report. + +.INPUTS + None. You cannot pipe objects to Get-ActiveDirectoryUserActivity.ps1. + +.OUTPUTS + None. If successful, this script does not output anything. +#> +[CmdletBinding()] +[OutputType()] +param +( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidatePattern('^OU\=')] + [string]$OrganizationalUnit, + + [Parameter()] + [string[]]$EventId = @(4647,4648), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$EmailToAddress, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$EmailFromAddress = 'IT Administrator', + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$EmailSubject = 'User Activity Report' + +) +process { + try + { + #region Gather all applicable computers + $Computers = Get-ADComputer -SearchBase $OrganizationalUnit -Filter * | Select-Object Name + if (-not $Computers) + { + throw "No computers found in OU [$($OrganizationalUnit)]" + } + #endregion + + #region Build XPath filter + $XPathElements = @() + foreach ($id in $EventId) + { + $XPathElements += "Event[System[EventID='$Id']]" + } + $EventFilterXPath = $XPathElements -join ' or ' + #endregion + + #region Build the array that will display the information we want + $LogonId = $EventId[1] + $LogoffId = $EventId[0] + + $SelectOuput = @( + @{ n = 'ComputerName'; e = { $_.MachineName } }, + @{ + n = 'Event'; e = { + if ($_.Id -eq $LogonId) + { + 'Logon' + } + else + { + 'LogOff' + } + } + }, + @{ n = 'Time'; e = { $_.TimeCreated } }, + @{ + n = 'Account'; e = { + if ($_.Id -eq $LogonId) + { + $i = 1 + } + else + { + $i = 3 + } + [regex]::Matches($_.Message, 'Account Name:\s+(.*)\n').Groups[$i].Value.Trim() + } + } + ) + #endregion + + #region Query the computers' event logs and send output to a file to email + $TempFile = 'C:\useractivity.txt' + foreach ($Computer in $Computers) { + Get-WinEvent -ComputerName $Computer -LogName Security -FilterXPath $EventFilterXPath | Select-Object $SelectOuput | Out-File $TempFile + } + #endregion + + $emailParams = @{ + 'To' = $EmailToAddress + 'From' = $EmailFromAddress + 'Subject' = $EmailSubject + 'Attachments' = $TempFile + } + + Send-MailMessage @emailParams + + } catch { + Write-Error $_.Exception.Message + } + finally + { + ## Cleanup the temporary file generated + Remove-Item -Path $TempFile -Force -ErrorAction SilentlyContinue + } +} \ No newline at end of file diff --git a/Get-AdDnsRecordAcl.ps1 b/ActiveDirectory/Get-AdDnsRecordAcl.ps1 similarity index 100% rename from Get-AdDnsRecordAcl.ps1 rename to ActiveDirectory/Get-AdDnsRecordAcl.ps1 diff --git a/ActiveDirectory/Get-AdGroupMembershipChange.ps1 b/ActiveDirectory/Get-AdGroupMembershipChange.ps1 new file mode 100644 index 0000000..af1ee49 --- /dev/null +++ b/ActiveDirectory/Get-AdGroupMembershipChange.ps1 @@ -0,0 +1,164 @@ +#requires -Module ActiveDirectory + +<# + .SYNOPSIS + This script queries multiple Active Directory groups for new members in a domain. It records group membership + in a CSV file in the same location as the script is located. On the script's initial run it will simply record + all members of all groups into this CSV file. On subsequent runs it will query each group's member list and compare + that list to what's in the CSV file. If any differences are found (added or removed) the script will update the + CSV file to reflect current memberships and notify an administrator of which members were either added or removed. + .NOTES + Filename: Get-AdGroupMembershipChange.ps1 + .EXAMPLE + PS> .\Get-AdGroupMembershipChange.ps1 -Group 'Enterprise Admins','Domain Admins','Schema Admins' -Email abertram@lab.local + + This example will query group memberships of the Enterprise Admins, Domain Admins and Schema Admins groups and email + abertram@lab.local when a member is either added or removed from any of these groups. + + .PARAMETER Group + One or more group names to monitor for membership changes + .PARAMETER DomainController + By default the Active Directory module will automatically find a domain controller to query. If this parameter is set + the script will directly query this domain controller. + .PARAMETER Email + The email address of the administrator that would like to get notified of group changes. + .PARAMETER LogFilePath + The path to where the group membership CSV file will be placed. This is the file that will record the most recent + group membership and will be used to compare current to most recent. +#> +[CmdletBinding(SupportsShouldProcess)] +[OutputType('System.Management.Automation.PSCustomObject')] +param ( + [Parameter(Mandatory)] + [string[]]$Group, + [Parameter()] + [ValidatePattern('\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b')] + [string]$Email = 'admin@lab.local', + [Parameter()] + [string]$LogFilePath = "$PsScriptRoot\$($MyInvocation.MyCommand.Name).csv" +) + +begin { + $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop + Set-StrictMode -Version Latest + + function Write-Log { + <# + .SYNOPSIS + This function creates or appends a line to a log file + + .DESCRIPTION + This function writes a log line to a log file + .PARAMETER Message + The message parameter is the log message you'd like to record to the log file + .PARAMETER LogLevel + The logging level is the severity rating for the message you're recording. + You have 3 severity levels available; 1, 2 and 3 from informational messages + for FYI to critical messages. This defaults to 1. + + .EXAMPLE + PS C:\> Write-Log -Message 'Value1' -LogLevel 'Value2' + + This example shows how to call the Write-Log function with named parameters. + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$Message, + [Parameter()] + [ValidateSet(1, 2, 3)] + [int]$LogLevel = 1 + ) + + try { + $TimeGenerated = "$(Get-Date -Format HH:mm:ss).$((Get-Date).Millisecond)+000" + ## Build the line which will be recorded to the log file + $Line = '{2} {1}: {0}' + $LineFormat = $Message, $TimeGenerated, (Get-Date -Format MM-dd-yyyy) + $Line = $Line -f $LineFormat + + Add-Content -Value $Line -Path $LogFilePath + } catch { + Write-Error $_.Exception.Message + } + } + + function Add-GroupMemberToLogFile ($GroupName,[string[]]$Member) { + foreach ($m in $Member) { + [pscustomobject]@{'Group' = $GroupName; 'Member' = $m} | Export-Csv -Path $LogFilePath -Append -NoTypeInformation + } + } + + function Get-GroupMemberFromLogFile ([string]$GroupName) { + (Import-Csv -Path $LogFilePath | Where-Object { $_.Group -eq $GroupName }).Member + } + + function Send-ChangeNotification ($GroupName,$ChangeType,$Members) { + $EmailBody = " + The following group has changed: $GroupName`n`r + The following members were $ChangeType`n`r + $($Members -join ',') + " + + $Params = @{ + 'From' = 'Active Directory Administrator ' + 'To' = $Email + 'Subject' = 'AD Group Change' + 'SmtpServer' = 'my.smptpserver.local' + 'Body' = $EmailBody + } + Send-MailMessage @Params + } +} + +process { + try { + Write-Log -Message 'Querying Active directory domain for group memberships...' + foreach ($g in $Group) { + Write-Log -Message "Querying the [$g] group for members..." + $CurrentMembers = (Get-ADGroupMember -Identity $g).Name + if (-not $CurrentMembers) { + Write-Log -Message "No members found in the [$g] group." + } else { + Write-Log -Message "Found [$($CurrentMembers.Count)] members in the [$g] group" + if (-not (Test-Path -Path $LogFilePath -PathType Leaf)) { + Write-Log -Message "The log file [$LogFilePath] does not exist yet. This must be the first run. Dumping all members into it..." + Add-GroupMemberToLogFile -GroupName $g -Member $CurrentMembers + } else { + Write-Log -Message 'Existing log file found. Reading previous group members...' + $PreviousMembers = Get-GroupMemberFromLogFile -GroupName $g + $ComparedMembers = Compare-Object -ReferenceObject $PreviousMembers -DifferenceObject $CurrentMembers + if (-not $ComparedMembers) { + Write-Log "No differences found in group $g" + } else { + $RemovedMembers = ($ComparedMembers | Where-Object { $_.SideIndicator -eq '<=' }).InputObject + if (-not $RemovedMembers) { + Write-Log -Message 'No members have been removed since last check' + } else { + Write-Log -Message "Found [$($RemovedMembers.Count)] members that have been removed since last check" + Send-ChangeNotification -GroupName $g -ChangeType 'Removed' -Members $RemovedMembers + Write-Log -Message "Emailed change notification to $Email" + ## Remove the members from the CSV file to keep the file current + (Import-Csv -Path $LogFilePath | Where-Object {$RemovedMembers -notcontains $_.Member}) | Export-Csv -Path $LogFilePath -NoTypeInformation + } + $AddedMembers = ($ComparedMembers | Where-Object { $_.SideIndicator -eq '=>' }).InputObject + if (-not $AddedMembers) { + Write-Log -Message 'No members have been removed since last check' + } else { + Write-Log -Message "Found [$($AddedMembers.Count)] members that have been added since last check" + Send-ChangeNotification -GroupName $g -ChangeType 'Added' -Members $AddedMembers + Write-Log -Message "Emailed change notification to $Email" + ## Add the members from the CSV file to keep the file current + $AddedMembers | foreach {[pscustomobject]@{'Group' = $g; 'Member' = $_}} | Export-Csv -Path $LogFilePath -Append -NoTypeInformation + } + + } + } + } + + } + + } catch { + Write-Error "$($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" + } +} \ No newline at end of file diff --git a/Get-AdUserMatches.ps1 b/ActiveDirectory/Get-AdUserMatches.ps1 similarity index 100% rename from Get-AdUserMatches.ps1 rename to ActiveDirectory/Get-AdUserMatches.ps1 diff --git a/Get-UserLogonSessionHistory.ps1 b/ActiveDirectory/Get-UserLogonSessionHistory.ps1 similarity index 100% rename from Get-UserLogonSessionHistory.ps1 rename to ActiveDirectory/Get-UserLogonSessionHistory.ps1 diff --git a/Create-AzureVMSnapshot.ps1 b/Azure/Create-AzureVMSnapshot.ps1 similarity index 100% rename from Create-AzureVMSnapshot.ps1 rename to Azure/Create-AzureVMSnapshot.ps1 diff --git a/Restore-AzureVMSnapshot.ps1 b/Azure/Restore-AzureVMSnapshot.ps1 similarity index 100% rename from Restore-AzureVMSnapshot.ps1 rename to Azure/Restore-AzureVMSnapshot.ps1 diff --git a/Import-Certificate.ps1 b/Certificates/Import-Certificate.ps1 similarity index 100% rename from Import-Certificate.ps1 rename to Certificates/Import-Certificate.ps1 diff --git a/Set-ServiceAccount.ps1 b/Configuration Mangaement/Set-ServiceAccount.ps1 similarity index 100% rename from Set-ServiceAccount.ps1 rename to Configuration Mangaement/Set-ServiceAccount.ps1 diff --git a/Get-DhcpLeasesInDomain.ps1 b/DHCP/Get-DhcpLeasesInDomain.ps1 similarity index 100% rename from Get-DhcpLeasesInDomain.ps1 rename to DHCP/Get-DhcpLeasesInDomain.ps1 diff --git a/Convert-DynamicDnsRecordToStatic.ps1 b/DNS/Convert-DynamicDnsRecordToStatic.ps1 similarity index 100% rename from Convert-DynamicDnsRecordToStatic.ps1 rename to DNS/Convert-DynamicDnsRecordToStatic.ps1 diff --git a/Get-DynamicDNSRecordsToBeScavenged.ps1 b/DNS/Get-DynamicDNSRecordsToBeScavenged.ps1 similarity index 100% rename from Get-DynamicDNSRecordsToBeScavenged.ps1 rename to DNS/Get-DynamicDNSRecordsToBeScavenged.ps1 diff --git a/Get-RecordsToBeScavenged.ps1 b/DNS/Get-RecordsToBeScavenged.ps1 similarity index 100% rename from Get-RecordsToBeScavenged.ps1 rename to DNS/Get-RecordsToBeScavenged.ps1 diff --git a/Test-ClientDynamicDns.ps1 b/DNS/Test-ClientDynamicDns.ps1 similarity index 100% rename from Test-ClientDynamicDns.ps1 rename to DNS/Test-ClientDynamicDns.ps1 diff --git a/Sync-CsvToSql.ps1 b/Database-Datasets/Sync-CsvToSql.ps1 similarity index 100% rename from Sync-CsvToSql.ps1 rename to Database-Datasets/Sync-CsvToSql.ps1 diff --git a/File-Folder Management/Send-File.ps1 b/File-Folder Management/Send-File.ps1 new file mode 100644 index 0000000..fc8d6db --- /dev/null +++ b/File-Folder Management/Send-File.ps1 @@ -0,0 +1,141 @@ +function Send-File +{ + <# + .SYNOPSIS + This function sends a file (or folder of files recursively) to a destination WinRm session. This function was originally + built by Lee Holmes (http://poshcode.org/2216) but has been modified to recursively send folders of files as well + as to support UNC paths. + + .PARAMETER Path + The local or UNC folder path that you'd like to copy to the session. This also support multiple paths in a comma-delimited format. + If this is a UNC path, it will be copied locally to accomodate copying. If it's a folder, it will recursively copy + all files and folders to the destination. + + .PARAMETER Destination + The local path on the remote computer where you'd like to copy the folder or file. If the folder does not exist on the remote + computer it will be created. + + .PARAMETER Session + The remote session. Create with New-PSSession. + + .EXAMPLE + $session = New-PSSession -ComputerName MYSERVER + Send-File -Path C:\test.txt -Destination C:\ -Session $session + + This example will copy the file C:\test.txt to be C:\test.txt on the computer MYSERVER + + .INPUTS + None. This function does not accept pipeline input. + + .OUTPUTS + System.IO.FileInfo + #> + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string[]]$Path, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Destination, + + [Parameter(Mandatory)] + [System.Management.Automation.Runspaces.PSSession]$Session + ) + process + { + foreach ($p in $Path) + { + try + { + if ($p.StartsWith('\\')) + { + Write-Verbose -Message "[$($p)] is a UNC path. Copying locally first" + Copy-Item -Path $p -Destination ([environment]::GetEnvironmentVariable('TEMP', 'Machine')) + $p = "$([environment]::GetEnvironmentVariable('TEMP', 'Machine'))\$($p | Split-Path -Leaf)" + } + if (Test-Path -Path $p -PathType Container) + { + Write-Log -Source $MyInvocation.MyCommand -Message "[$($p)] is a folder. Sending all files" + $files = Get-ChildItem -Path $p -File -Recurse + $sendFileParamColl = @() + foreach ($file in $Files) + { + $sendParams = @{ + 'Session' = $Session + 'Path' = $file.FullName + } + if ($file.DirectoryName -ne $p) ## It's a subdirectory + { + $subdirpath = $file.DirectoryName.Replace("$p\", '') + $sendParams.Destination = "$Destination\$subDirPath" + } + else + { + $sendParams.Destination = $Destination + } + $sendFileParamColl += $sendParams + } + foreach ($paramBlock in $sendFileParamColl) + { + Send-File @paramBlock + } + } + else + { + Write-Verbose -Message "Starting WinRM copy of [$($p)] to [$($Destination)]" + # Get the source file, and then get its contents + $sourceBytes = [System.IO.File]::ReadAllBytes($p); + $streamChunks = @(); + + # Now break it into chunks to stream. + $streamSize = 1MB; + for ($position = 0; $position -lt $sourceBytes.Length; $position += $streamSize) + { + $remaining = $sourceBytes.Length - $position + $remaining = [Math]::Min($remaining, $streamSize) + + $nextChunk = New-Object byte[] $remaining + [Array]::Copy($sourcebytes, $position, $nextChunk, 0, $remaining) + $streamChunks +=, $nextChunk + } + $remoteScript = { + if (-not (Test-Path -Path $using:Destination -PathType Container)) + { + $null = New-Item -Path $using:Destination -Type Directory -Force + } + $fileDest = "$using:Destination\$($using:p | Split-Path -Leaf)" + ## Create a new array to hold the file content + $destBytes = New-Object byte[] $using:length + $position = 0 + + ## Go through the input, and fill in the new array of file content + foreach ($chunk in $input) + { + [GC]::Collect() + [Array]::Copy($chunk, 0, $destBytes, $position, $chunk.Length) + $position += $chunk.Length + } + + [IO.File]::WriteAllBytes($fileDest, $destBytes) + + Get-Item $fileDest + [GC]::Collect() + } + + # Stream the chunks into the remote script. + $Length = $sourceBytes.Length + $streamChunks | Invoke-Command -Session $Session -ScriptBlock $remoteScript + Write-Verbose -Message "WinRM copy of [$($p)] to [$($Destination)] complete" + } + } + catch + { + Write-Error $_.Exception.Message + } + } + } + +} \ No newline at end of file diff --git a/Cowbell.wav b/Fun Stuff/Cowbell.wav similarity index 100% rename from Cowbell.wav rename to Fun Stuff/Cowbell.wav diff --git a/Get-MoreCowbell.ps1 b/Fun Stuff/Get-MoreCowbell.ps1 similarity index 100% rename from Get-MoreCowbell.ps1 rename to Fun Stuff/Get-MoreCowbell.ps1 diff --git a/Remove-AutoConfigProxy.ps1 b/Internet Explorer/Remove-AutoConfigProxy.ps1 similarity index 100% rename from Remove-AutoConfigProxy.ps1 rename to Internet Explorer/Remove-AutoConfigProxy.ps1 diff --git a/Get-FreeHardDriveSpace.ps1 b/Inventorying-Reporting Tools/Get-FreeHardDriveSpace.ps1 similarity index 100% rename from Get-FreeHardDriveSpace.ps1 rename to Inventorying-Reporting Tools/Get-FreeHardDriveSpace.ps1 diff --git a/Get-ServerUptimeReport.ps1 b/Inventorying-Reporting Tools/Get-ServerUptimeReport.ps1 similarity index 100% rename from Get-ServerUptimeReport.ps1 rename to Inventorying-Reporting Tools/Get-ServerUptimeReport.ps1 diff --git a/Get-UpTime.ps1 b/Inventorying-Reporting Tools/Get-UpTime.ps1 similarity index 100% rename from Get-UpTime.ps1 rename to Inventorying-Reporting Tools/Get-UpTime.ps1 diff --git a/Get-LocalGroupMembership.ps1 b/Local Account Management/Get-LocalGroupMembership.ps1 similarity index 100% rename from Get-LocalGroupMembership.ps1 rename to Local Account Management/Get-LocalGroupMembership.ps1 diff --git a/Set-MsRandomPasswordToLocalUserAccount.ps1 b/Local Account Management/Set-MsRandomPasswordToLocalUserAccount.ps1 similarity index 100% rename from Set-MsRandomPasswordToLocalUserAccount.ps1 rename to Local Account Management/Set-MsRandomPasswordToLocalUserAccount.ps1 diff --git a/Set-RandomPasswordToLocalUserAccount.ps1 b/Local Account Management/Set-RandomPasswordToLocalUserAccount.ps1 similarity index 100% rename from Set-RandomPasswordToLocalUserAccount.ps1 rename to Local Account Management/Set-RandomPasswordToLocalUserAccount.ps1 diff --git a/Modules/GnuPg.psm1 b/Modules/GnuPg.psm1 new file mode 100644 index 0000000..cdd1883 --- /dev/null +++ b/Modules/GnuPg.psm1 @@ -0,0 +1,190 @@ +function Install-GnuPg +{ + <# + .SYNOPSIS + This function installed the GnuPg for Windows application. It the installer file is not in + the DownloadFolderPath, the function will download the file from the Internet and then execute a silent installation. + .PARAMETER DownloadFolderPath + The folder path where you'd like to download the GnuPg for Windows installer into. + + .PARAMETER DownloadUrl + The URL that will be used to download the EXE setup installer. + + .EXAMPLE + PS> Install-GnuPg -DownloadFolderPath C:\Downloads + + This will first check to ensure the GnuPg for Windows installer is in the C:\Downloads folder. If not, it will then + download the file from the default URL set at DownloadUrl. Once downloaded, it will then silently execute + the installation and get the application installed with default parameters. + + .INPUTS + None. This function does not accept pipeline input. + + .OUTPUTS + None. If successful, this function will not return any output. + #> + + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$DownloadFolderPath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$DownloadUrl = 'http://files.gpg4win.org/gpg4win-2.2.5.exe' + + ) + process + { + try + { + $DownloadFilePath = "$DownloadFolderPath\$($DownloadUrl | Split-Path -Leaf)" + if (-not (Test-Path -Path $DownloadFilePath -PathType Leaf)) + { + Write-Verbose -Message "Downloading [$($DownloadUrl)] to [$($DownloadFilePath)]" + Invoke-WebRequest -Uri $DownloadUrl -OutFile $DownloadFilePath + } + else + { + Write-Verbose -Message "The download file [$($DownloadFilePath)] already exists" + } + Write-Verbose -Message 'Attempting to install GPG4Win...' + Start-Process -FilePath $DownloadFilePath -ArgumentList '/S' -NoNewWindow -Wait -PassThru + Write-Verbose -Message 'GPG4Win installed' + } + catch + { + Write-Error $_.Exception.Message + } + } +} + +function Add-Encryption +{ + <# + .SYNOPSIS + This function uses the GnuPG for Windows application to symmetrically encrypt a set of files in a folder. + + .DESCRIPTION + A detailed description of the function. + + .PARAMETER FolderPath + This is the folder path that contains all of the files you'd like to encrypt. + + .PARAMETER Password + This is the password that will be used to encrypt the files. + + .EXAMPLE + PS> Add-Encryption -FolderPath C:\TestFolder -Password secret + + This example would encrypt all of the files in the C:\TestFolder folder with the password of 'secret'. The encrypted + files would be created with the same name as the original files only with a GPG file extension. + + .INPUTS + None. This function does not accept pipeline input. + + .OUTPUTS + System.IO.FileInfo + #> + + [CmdletBinding()] + [OutputType([System.IO.FileInfo])] + param + ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidateScript({Test-Path -Path $_ -PathType Container})] + [string]$FolderPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Password, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$GpgPath = 'C:\Program Files (x86)\GNU\GnuPG\gpg2.exe' + + ) + process { + try + { + Get-ChildItem -Path $FolderPath | foreach { + Write-Verbose -Message "Encrypting [$($_.FullName)]" + Start-Process -FilePath $GpgPath -ArgumentList "--batch --passphrase $Password -c $($_.FullName)" -Wait -NoNewWindow + } + Get-ChildItem -Path $FolderPath -Filter '*.gpg' + } + catch + { + Write-Error $_.Exception.Message + } + } +} + +function Remove-Encryption +{ + <# + .SYNOPSIS + This function decrypts all files encrypted with the Add-Encryption function. Once decrypted, it will add the files + to the same directory that contains the encrypted files and will remove the GPG file extension. + + .PARAMETER FolderPath + The folder path that contains all of the encrypted *.gpg files. + + .PARAMETER Password + The password that was used to encrypt the files. + + .EXAMPLE + PS> Remove-Encryption -FolderPath C:\MyFolder -Password secret + + This example will attempt to decrypt all files inside of the C:\MyFolder folder using the password of 'secret' + + .INPUTS + None. This function does not accept pipeline input. + + .OUTPUTS + System.IO.FileInfo + + #> + + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path -Path $_ -PathType Container })] + [string]$FolderPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Password, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$GpgPath = 'C:\Program Files (x86)\GNU\GnuPG\gpg2.exe' + ) + process + { + try + { + Get-ChildItem -Path $FolderPath -Filter '*.gpg' | foreach { + $decryptFilePath = $_.FullName.TrimEnd('.gpg') + Write-Verbose -Message "Decrypting [$($_.FullName)] to [$($decryptFilePath)]" + $startProcParams = @{ + 'FilePath' = $GpgPath + 'ArgumentList' = "--batch --yes --passphrase $Password -o $decryptFilePath -d $($_.FullName)" + 'Wait' = $true + 'NoNewWindow' = $true + } + $null = Start-Process @startProcParams + } + Get-ChildItem -Path $FolderPath | where {$_.Extension -ne 'gpg'} + } + catch + { + Write-Error $_.Exception.Message + } + } +} \ No newline at end of file diff --git a/Get-LocalPort.ps1 b/Networking/Get-LocalPort.ps1 similarity index 100% rename from Get-LocalPort.ps1 rename to Networking/Get-LocalPort.ps1 diff --git a/Get-ServiceListeningPort.ps1 b/Networking/Get-ServiceListeningPort.ps1 similarity index 100% rename from Get-ServiceListeningPort.ps1 rename to Networking/Get-ServiceListeningPort.ps1 diff --git a/PacketCapture.ps1 b/Networking/PacketCapture.ps1 similarity index 100% rename from PacketCapture.ps1 rename to Networking/PacketCapture.ps1 diff --git a/Send-WolProxyRequest.ps1 b/Networking/Send-WolProxyRequest.ps1 similarity index 100% rename from Send-WolProxyRequest.ps1 rename to Networking/Send-WolProxyRequest.ps1 diff --git a/Test-ServerRolePortGroup.ps1 b/Networking/Test-ServerRolePortGroup.ps1 similarity index 100% rename from Test-ServerRolePortGroup.ps1 rename to Networking/Test-ServerRolePortGroup.ps1 diff --git a/Enable-RemotePSRemoting.ps1 b/PowerShell Internals/Enable-RemotePSRemoting.ps1 similarity index 100% rename from Enable-RemotePSRemoting.ps1 rename to PowerShell Internals/Enable-RemotePSRemoting.ps1 diff --git a/Get-FunctionDefaultParameters.ps1 b/PowerShell Internals/Get-FunctionDefaultParameters.ps1 similarity index 100% rename from Get-FunctionDefaultParameters.ps1 rename to PowerShell Internals/Get-FunctionDefaultParameters.ps1 diff --git a/Invoke-RemoteScript.ps1 b/PowerShell Internals/Invoke-RemoteScript.ps1 similarity index 100% rename from Invoke-RemoteScript.ps1 rename to PowerShell Internals/Invoke-RemoteScript.ps1 diff --git a/New-ValidationDynamicParam.ps1 b/PowerShell Internals/New-ValidationDynamicParam.ps1 similarity index 100% rename from New-ValidationDynamicParam.ps1 rename to PowerShell Internals/New-ValidationDynamicParam.ps1 diff --git a/Show-SimpleMenu.ps1 b/PowerShell Internals/Show-SimpleMenu.ps1 similarity index 100% rename from Show-SimpleMenu.ps1 rename to PowerShell Internals/Show-SimpleMenu.ps1 diff --git a/SetPrinterComments.ps1 b/Print Management/SetPrinterComments.ps1 similarity index 100% rename from SetPrinterComments.ps1 rename to Print Management/SetPrinterComments.ps1 diff --git a/README.md b/README.md index 59194d4..30935f9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # Random-PowerShell-Work This is a conglomeration of PowerShell scripts that I've written over the years. If you'd like more PowerShell awesomeness check out my blog at [Adam, the Automator](http://www.adamtheautomator.com). This is where I write about automation, lots of PowerShell and post regularly about time-saving tricks with PowerShell. + +##WARNING: I cannot guarantee all scripts in this repo have been thoroughly tested or even work at all! These are working scripts that are in all different stages of development. Use at your own risk! \ No newline at end of file diff --git a/Connect-CmRemoteTools.ps1 b/SCCM/Connect-CmRemoteTools.ps1 similarity index 100% rename from Connect-CmRemoteTools.ps1 rename to SCCM/Connect-CmRemoteTools.ps1 diff --git a/Convert-CMApplicationToPackage.ps1 b/SCCM/Convert-CMApplicationToPackage.ps1 similarity index 100% rename from Convert-CMApplicationToPackage.ps1 rename to SCCM/Convert-CMApplicationToPackage.ps1 diff --git a/Create-CMRandomMemberCollection.ps1 b/SCCM/Create-CMRandomMemberCollection.ps1 similarity index 100% rename from Create-CMRandomMemberCollection.ps1 rename to SCCM/Create-CMRandomMemberCollection.ps1 diff --git a/Get-CMDpContent.ps1 b/SCCM/Get-CMDpContent.ps1 similarity index 100% rename from Get-CMDpContent.ps1 rename to SCCM/Get-CMDpContent.ps1 diff --git a/Get-CmAppDeploymentStatus.ps1 b/SCCM/Get-CmAppDeploymentStatus.ps1 similarity index 100% rename from Get-CmAppDeploymentStatus.ps1 rename to SCCM/Get-CmAppDeploymentStatus.ps1 diff --git a/New-CMMyApplication.ps1 b/SCCM/New-CMMyApplication.ps1 similarity index 100% rename from New-CMMyApplication.ps1 rename to SCCM/New-CMMyApplication.ps1 diff --git a/Remove-CMDirectMembershipRule.ps1 b/SCCM/Remove-CMDirectMembershipRule.ps1 similarity index 100% rename from Remove-CMDirectMembershipRule.ps1 rename to SCCM/Remove-CMDirectMembershipRule.ps1 diff --git a/Start-CMApplicationDeploymentTest.ps1 b/SCCM/Start-CMApplicationDeploymentTest.ps1 similarity index 100% rename from Start-CMApplicationDeploymentTest.ps1 rename to SCCM/Start-CMApplicationDeploymentTest.ps1 diff --git a/Start-PostConfigmgrBackupTasks.ps1 b/SCCM/Start-PostConfigmgrBackupTasks.ps1 similarity index 100% rename from Start-PostConfigmgrBackupTasks.ps1 rename to SCCM/Start-PostConfigmgrBackupTasks.ps1 diff --git a/Sync-CmToWsusSoftwareUpdates.ps1 b/SCCM/Sync-CmToWsusSoftwareUpdates.ps1 similarity index 100% rename from Sync-CmToWsusSoftwareUpdates.ps1 rename to SCCM/Sync-CmToWsusSoftwareUpdates.ps1 diff --git a/Test-CmDeploymentClientStatus.ps1 b/SCCM/Test-CmDeploymentClientStatus.ps1 similarity index 100% rename from Test-CmDeploymentClientStatus.ps1 rename to SCCM/Test-CmDeploymentClientStatus.ps1 diff --git a/New-ScheduledScript.ps1 b/Scheduled Tasks/New-ScheduledScript.ps1 similarity index 100% rename from New-ScheduledScript.ps1 rename to Scheduled Tasks/New-ScheduledScript.ps1 diff --git a/DownloadQualysPatches.ps1 b/Security/DownloadQualysPatches.ps1 similarity index 100% rename from DownloadQualysPatches.ps1 rename to Security/DownloadQualysPatches.ps1 diff --git a/Send-TwitterDirectMessage.ps1 b/Send-TwitterDirectMessage.ps1 deleted file mode 100644 index 2a850cc..0000000 --- a/Send-TwitterDirectMessage.ps1 +++ /dev/null @@ -1,61 +0,0 @@ -$VerbosePreference = 'continue' - -## Works with Twitter API v1.1 -[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null -[Reflection.Assembly]::LoadWithPartialName("System.Net") | Out-Null - -$status = [System.Uri]::EscapeDataString("test") -$oauth_consumer_key = "qE0WtEleJzp8ErWI82B3Jw8gl" -$oauth_consumer_secret = "r06813yKNEjaErJc4mkacWmFSNwnT1H05Yyjd8XQ5bOhKxVBVc" -$oauth_token = "19891458-D4dvUVxHpHMCiTppg7OF4kPKgQQXM2O8rhXMFiNVc" -$oauth_token_secret = "ZPLaltGH39jdYagotyQL5C9IYAKty6xovhBUcvPc5IYbr" -$oauth_nonce = [System.Convert]::ToBase64String(([System.Text.Encoding]::ASCII.GetBytes("$([System.DateTime]::Now.Ticks.ToString())12345"))).Replace('=','g') -Write-Verbose "Using oauth_nonce of $oauth_nonce" -$ts = [System.DateTime]::UtcNow - [System.DateTime]::ParseExact("01/01/1970", "dd/MM/yyyy", $null) -$oauth_timestamp = [System.Convert]::ToInt64($ts.TotalSeconds).ToString(); -Write-Verbose "Oauth timestamp is $oauth_timestamp" - -## Keys must be in alphabetical order -$signature = "POST&"; -$signature += [System.Uri]::EscapeDataString("https://api.twitter.com/1.1/statuses/update.json") + "&" -$signature += [System.Uri]::EscapeDataString("oauth_consumer_key=" + $oauth_consumer_key + "&"); -$signature += [System.Uri]::EscapeDataString("oauth_nonce=" + $oauth_nonce + "&"); -$signature += [System.Uri]::EscapeDataString("oauth_signature_method=HMAC-SHA1&"); -$signature += [System.Uri]::EscapeDataString("oauth_timestamp=" + $oauth_timestamp + "&"); -$signature += [System.Uri]::EscapeDataString("oauth_token=" + $oauth_token + "&"); -$signature += [System.Uri]::EscapeDataString("oauth_version=1.0&"); -$signature += [System.Uri]::EscapeDataString("status=" + $status); -Write-Verbose "Unencrypted signature = $signature" - -$signature_key = [System.Uri]::EscapeDataString($oauth_consumer_secret) + "&" + [System.Uri]::EscapeDataString($oauth_token_secret); - -$hmacsha1 = new-object System.Security.Cryptography.HMACSHA1; -$hmacsha1.Key = [System.Text.Encoding]::ASCII.GetBytes($signature_key); -$oauth_signature = [System.Convert]::ToBase64String($hmacsha1.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($signature))); -Write-Verbose "Using the signature '$oauth_signature'" - -$oauth_authorization = 'OAuth '; -$oauth_authorization += 'oauth_consumer_key="' + [System.Uri]::EscapeDataString($oauth_consumer_key) + '", '; -$oauth_authorization += 'oauth_nonce="' + [System.Uri]::EscapeDataString($oauth_nonce) + '", '; -$oauth_authorization += 'oauth_signature="' + [System.Uri]::EscapeDataString($oauth_signature) + '", '; -$oauth_authorization += 'oauth_signature_method="HMAC-SHA1",' -$oauth_authorization += 'oauth_timestamp="' + [System.Uri]::EscapeDataString($oauth_timestamp) + '", ' -$oauth_authorization += 'oauth_token="' + [System.Uri]::EscapeDataString($oauth_token) + '", '; -$oauth_authorization += 'oauth_version="1.0"'; -Write-Verbose "Authorization string is $oauth_authorization" - -$post_body = [System.Text.Encoding]::ASCII.GetBytes("status=" + $status); - -Invoke-RestMethod -URI 'https://api.twitter.com/1.1/statuses/update.json' -Method Post -Body $post_body -Headers @{ 'Authorization' = $oauth_authorization } -ContentType "application/x-www-form-urlencoded" - -<# -[System.Net.HttpWebRequest] $request = [System.Net.WebRequest]::Create("https://api.twitter.com/1.1/statuses/update.json"); -$request.Method = "POST"; -$request.Headers.Add("Authorization", $oauth_authorization); -$request.ContentType = "application/x-www-form-urlencoded"; -$body = $request.GetRequestStream(); -$body.write($post_body, 0, $post_body.length); -$body.flush(); -$body.close(); -$response = $request.GetResponse(); -#> \ No newline at end of file diff --git a/Send-TwitterDirectMessagev2.ps1 b/Send-TwitterDirectMessagev2.ps1 deleted file mode 100644 index 6688fac..0000000 --- a/Send-TwitterDirectMessagev2.ps1 +++ /dev/null @@ -1,201 +0,0 @@ -function Get-OAuthAuthorization { - <# - .SYNOPSIS - This function is used to setup all the appropriate security stuff needed to issue - API calls against Twitter's API. It has been tested with v1.1 of the API. It currently - includes support only for sending tweets from a single user account and to send DMs from - a single user account. - .EXAMPLE - Get-OAuthAuthorization -DmMessage 'hello' -HttpEndPoint 'https://api.twitter.com/1.1/direct_messages/new.json' -Username adam - - This example gets the authorization string needed in the HTTP POST method to send a direct - message with the text 'hello' to the user 'adam'. - .EXAMPLE - Get-OAuthAuthorization -TweetMessage 'hello' -HttpEndPoint 'https://api.twitter.com/1.1/statuses/update.json' - - This example gets the authorization string needed in the HTTP POST method to send out a tweet. - .PARAMETER HttpEndPoint - This is the URI that you must use to issue calls to the API. - .PARAMETER TweetMessage - Use this parameter if you're sending a tweet. This is the tweet's text. - .PARAMETER DmMessage - If you're sending a DM to someone, this is the DM's text. - .PARAMETER Username - If you're sending a DM to someone, this is the username you'll be sending to. - .PARAMETER ApiKey - The API key for the Twitter application you previously setup. - .PARAMETER ApiSecret - The API secret key for the Twitter application you previously setup. - .PARAMETER AccessToken - The access token that you generated within your Twitter application. - .PARAMETER - The access token secret that you generated within your Twitter application. - #> - [CmdletBinding(DefaultParameterSetName = 'None')] - [OutputType('System.Management.Automation.PSCustomObject')] - param ( - [Parameter(Mandatory)] - [string]$HttpEndPoint, - [Parameter(Mandatory,ParameterSetName='NewTweet')] - [string]$TweetMessage, - [Parameter(Mandatory, ParameterSetName = 'DM')] - [string]$DmMessage, - [Parameter(Mandatory,ParameterSetName='DM')] - [string]$Username, - [Parameter()] - [string]$ApiKey = 'qE0WtEleJzp8ErWI82B3Jw8gl', - [Parameter()] - [string]$ApiSecret = 'r06813yKNEjaErJc4mkacWmFSNwnT1H05Yyjd8XQ5bOhKxVBVc', - [Parameter()] - [string]$AccessToken = '19891458-FL3362GmrreMWUfoPM9Y1gpluAigRbsS36Fv2t2d0', - [Parameter()] - [string]$AccessTokenSecret = 'muMHKAbmdIu1P462gZel4HhhnySGx6VwoaeN2cBVt8EEf' - ) - - begin { - $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop - Set-StrictMode -Version Latest - try { - [Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null - [Reflection.Assembly]::LoadWithPartialName("System.Net") | Out-Null - } catch { - Write-Error $_.Exception.Message - } - } - - process { - try { - ## Generate a random 32-byte string. I'm using the current time (in seconds) and appending 5 chars to the end to get to 32 bytes - ## Base64 allows for an '=' but Twitter does not. If this is found, replace it with some alphanumeric character - $OauthNonce = [System.Convert]::ToBase64String(([System.Text.Encoding]::ASCII.GetBytes("$([System.DateTime]::Now.Ticks.ToString())12345"))).Replace('=', 'g') - Write-Verbose "Generated Oauth none string '$OauthNonce'" - - ## Find the total seconds since 1/1/1970 (epoch time) - $EpochTimeNow = [System.DateTime]::UtcNow - [System.DateTime]::ParseExact("01/01/1970", "dd/MM/yyyy", $null) - Write-Verbose "Generated epoch time '$EpochTimeNow'" - $OauthTimestamp = [System.Convert]::ToInt64($EpochTimeNow.TotalSeconds).ToString(); - Write-Verbose "Generated Oauth timestamp '$OauthTimestamp'" - - ## Build the signature - $SignatureBase = "$([System.Uri]::EscapeDataString($HttpEndPoint))&" - $SignatureParams = @{ - 'oauth_consumer_key' = $ApiKey; - 'oauth_nonce' = $OauthNonce; - 'oauth_signature_method' = 'HMAC-SHA1'; - 'oauth_timestamp' = $OauthTimestamp; - 'oauth_token' = $AccessToken; - 'oauth_version' = '1.0'; - } - if ($TweetMessage) { - $SignatureParams.status = $TweetMessage - } elseif ($DmMessage) { - $SignatureParams.screen_name = $Username - $SignatureParams.text = $DmMessage - } - - ## Create a string called $SignatureBase that joins all URL encoded 'Key=Value' elements with a & - ## Remove the URL encoded & at the end and prepend the necessary 'POST&' verb to the front - $SignatureParams.GetEnumerator() | sort name | foreach { $SignatureBase += [System.Uri]::EscapeDataString("$($_.Key)=$($_.Value)&") } - $SignatureBase = $SignatureBase.TrimEnd('%26') - $SignatureBase = 'POST&' + $SignatureBase - Write-Verbose "Base signature generated '$SignatureBase'" - - ## Create the hashed string from the base signature - $SignatureKey = [System.Uri]::EscapeDataString($ApiSecret) + "&" + [System.Uri]::EscapeDataString($AccessTokenSecret); - - $hmacsha1 = new-object System.Security.Cryptography.HMACSHA1; - $hmacsha1.Key = [System.Text.Encoding]::ASCII.GetBytes($SignatureKey); - $OauthSignature = [System.Convert]::ToBase64String($hmacsha1.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($SignatureBase))); - Write-Verbose "Using signature '$OauthSignature'" - - ## Build the authorization headers using most of the signature headers elements. This is joining all of the 'Key=Value' elements again - ## and only URL encoding the Values this time while including non-URL encoded double quotes around each value - $AuthorizationParams = $SignatureParams - $AuthorizationParams.Add('oauth_signature', $OauthSignature) - - ## Remove any API call-specific params from the authorization params - $AuthorizationParams.Remove('status') - $AuthorizationParams.Remove('text') - $AuthorizationParams.Remove('screen_name') - - $AuthorizationString = 'OAuth ' - $AuthorizationParams.GetEnumerator() | sort name | foreach { $AuthorizationString += $_.Key + '="' + [System.Uri]::EscapeDataString($_.Value) + '", ' } - $AuthorizationString = $AuthorizationString.TrimEnd(', ') - Write-Verbose "Using authorization string '$AuthorizationString'" - - $AuthorizationString - - } catch { - Write-Error $_.Exception.Message - } - } -} - -function Send-Tweet { - <# - .SYNOPSIS - This sends a tweet under a username. - .EXAMPLE - Send-Tweet -Message 'hello, world' - - This example will send a tweet with the text 'hello, world'. - .PARAMETER Message - The text of the tweet. - #> - [CmdletBinding()] - [OutputType('System.Management.Automation.PSCustomObject')] - param ( - [Parameter(Mandatory)] - [ValidateLength(1, 140)] - [string]$Message - ) - - process { - $HttpEndPoint = 'https://api.twitter.com/1.1/statuses/update.json' - - $AuthorizationString = Get-OAuthAuthorization -TweetMessage $Message -HttpEndPoint $HttpEndPoint - - ## Convert the message to a Byte array - $Body = [System.Text.Encoding]::ASCII.GetBytes("status=$Message"); - Write-Verbose "Using POST body '$Body'" - Invoke-RestMethod -URI $HttpEndPoint -Method Post -Body $Body -Headers @{ 'Authorization' = $AuthorizationString } -ContentType "application/x-www-form-urlencoded" - } -} - -function Send-TwitterDm { - <# - .SYNOPSIS - This sends a DM to another Twitter user. - .EXAMPLE - Send-TwitterDm -Message 'hello, Adam' -Username 'adam' - - This sends a DM with the text 'hello, Adam' to the username 'adam' - .PARAMETER Message - The text of the DM. - .PARAMETER Username - The username you'd like to send the DM to. - #> - [CmdletBinding(DefaultParameterSetName='None')] - [OutputType('System.Management.Automation.PSCustomObject')] - param ( - [Parameter(Mandatory)] - [ValidateLength(1, 140)] - [string]$Message, - [Parameter(Mandatory,ParameterSetName='Username')] - [string]$Username - ) - - process { - $HttpEndPoint = 'https://api.twitter.com/1.1/direct_messages/new.json' - - $AuthorizationString = Get-OAuthAuthorization -DmMessage $Message -HttpEndPoint $HttpEndPoint -Username $Username -Verbose - - ## Convert the message to a Byte array - $Message = [System.Uri]::EscapeDataString($Message) - $Username = [System.Uri]::EscapeDataString($Username) - $Body = [System.Text.Encoding]::ASCII.GetBytes("text=$Message&screen_name=$Username"); - Write-Verbose "Using POST body '$Body'" - Invoke-RestMethod -URI $HttpEndPoint -Method Post -Body $Body -Headers @{ 'Authorization' = $AuthorizationString } -ContentType "application/x-www-form-urlencoded" - - } -} \ No newline at end of file diff --git a/Set-Exchange2013AdSchema.ps1 b/Set-Exchange2013AdSchema.ps1 deleted file mode 100644 index 7790177..0000000 --- a/Set-Exchange2013AdSchema.ps1 +++ /dev/null @@ -1,119 +0,0 @@ -#Requires -Module ActiveDirectory - -<# -.SYNOPSIS - This script is designed to expedite the AD schema extension for Exchange 2013. It is an automated way to not only - perform the schema change itself but also to confirm success as well. This script is intended to be run on a domain-joined - workstation under an account that is both in the Schema Admins group and the Enterprise Admins group. - - It will require the Exhcange 2013 media (eval or licensed) -.NOTES - Created on: 8/22/2014 - Created by: Adam Bertram - Filename: Set-Exchange2013AdSchema.ps1 -.EXAMPLE - -.EXAMPLE - -.PARAMETER ExchangeMediaFolderPath - The path to where the contents of the Exchange 2013 media is located. This is typically the ISO extracted. - -#> -[CmdletBinding()] -[OutputType([bool])] -param ( - [Parameter(Mandatory)] - [ValidateScript({Test-Path $_ -PathType 'Container'})] - [string]$ExchangeMediaFolderPath -) - -begin { - $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop - Set-StrictMode -Version Latest - - function Test-GroupMembership { - $RequiredGroups = 'Schema Admins', 'Enterprise Admins' - $Username = whoami - $Username = $Username.Split('\')[1] - $Groups = (Get-ADUser -Identity $Username -Properties Memberof).MemberOf | foreach { $_.Split(',')[0].TrimStart('CN=') } - if (($Groups | where { $RequiredGroups -contains $_ }).Count -ne 2) { - $false - } else { - $true - } - } - - function Get-InstalledSoftwareInRegistry { - <# - .SYNOPSIS - Retrieves a list of all software installed - .DESCRIPTION - Retrieves a list of all software installed via the specified method - .EXAMPLE - Get-InstalledSoftware - This example retrieves all software installed on the local computer - .PARAMETER Computername - Use this parameter if you'd like to query installed software on a remote computer - #> - [CmdletBinding()] - param ( - [string]$Computername = 'localhost' - ) - process { - try { - $ScriptBlock = { - $UninstallKeys = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" - New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null - $UninstallKeys += Get-ChildItem HKU: | where { $_.Name -match 'S-\d-\d+-(\d+-){1,14}\d+$' } | foreach { "HKU:\$($_.PSChildName)\Software\Microsoft\Windows\CurrentVersion\Uninstall" } - foreach ($UninstallKey in $UninstallKeys) { - $Keys = Get-ItemProperty -Path "$UninstallKey\*" -ErrorAction SilentlyContinue - foreach ($Key in ($Keys | where { $_.SystemComponent -ne '1' })) { - $Key - } - } - } - if ($Computername -ne 'localhost') { - Invoke-Command -ComputerName $Computername -ScriptBlock $ScriptBlock - } else { - & $ScriptBlock - } - } catch { - $_.Exception.Message - } - } - } -} - -process { - try { - ## Ensure the account this is being run under is in the appropriate groups - if (-not (Test-GroupMembership)) { - throw "The user is not in the proper groups" - } - - ## Ensure .NET 4.5 and at least PSv3 is installed on the schema master - $SchemaMaster = (Get-ADForest).SchemaMaster - - ## Disable replication on the schema master to prevent any potential problems from replicating out - - ## Perform the schema extension - - ## Verify the extension was successful by checking the log file - - ## Verify the extension was successful by checking for the msExch attribute on the user - - ## Reenable replication - - ## Ensure replication is successful - } catch { - Write-Error "$($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" - } -} - -end { - try { - - } catch { - Write-Error "$($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" - } -} \ No newline at end of file diff --git a/Test-PotentialScomAgent.ps1 b/Test-PotentialScomAgent.ps1 deleted file mode 100644 index 8c42904..0000000 --- a/Test-PotentialScomAgent.ps1 +++ /dev/null @@ -1,120 +0,0 @@ -function Test-Port { - <# - .SYNOPSIS - This function tests for open TCP/UDP ports. - .DESCRIPTION - This function tests any TCP/UDP port to see if it's open or closed. - .NOTES - Known Issue: If this function is called within 10-20 consecutively on the same port - and computer, the UDP port check will output $false when it can be - $true. I haven't figured out why it does this. - .PARAMETER Computername - One or more remote, comma-separated computer names - .PARAMETER Port - One or more comma-separated port numbers you'd like to test. - .PARAMETER Protocol - The protocol (UDP or TCP) that you'll be testing - .PARAMETER TcpTimeout - The number of milliseconds that the function will wait until declaring - the TCP port closed. - .PARAMETER - The number of millieconds that the function will wait until declaring - the UDP port closed. - .EXAMPLE - PS> Test-Port -Computername 'LABDC','LABDC2' -Protocol TCP 80,443 - - This example tests the TCP network ports 80 and 443 on both the LABDC - and LABDC2 servers. - #> - [CmdletBinding(DefaultParameterSetName = 'TCP')] - [OutputType([System.Management.Automation.PSCustomObject])] - param ( - [Parameter(Mandatory)] - [string[]]$ComputerName, - [Parameter(Mandatory)] - [int[]]$Port, - [Parameter(Mandatory)] - [ValidateSet('TCP', 'UDP')] - [string]$Protocol, - [Parameter(ParameterSetName = 'TCP')] - [int]$TcpTimeout = 1000, - [Parameter(ParameterSetName = 'UDP')] - [int]$UdpTimeout = 1000 - ) - process { - foreach ($Computer in $ComputerName) { - foreach ($Portx in $Port) { - $Output = @{ 'Computername' = $Computer; 'Port' = $Portx; 'Protocol' = $Protocol; 'Result' = '' } - Write-Verbose "$($MyInvocation.MyCommand.Name) - Beginning port test on '$Computer' on port '$Protocol`:$Portx'" - if ($Protocol -eq 'TCP') { - $TcpClient = New-Object System.Net.Sockets.TcpClient - $Connect = $TcpClient.BeginConnect($Computer, $Portx, $null, $null) - $Wait = $Connect.AsyncWaitHandle.WaitOne($TcpTimeout, $false) - if (!$Wait) { - $TcpClient.Close() - Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' failed port test on port '$Protocol`:$Portx'" - $Output.Result = $false - } else { - $TcpClient.EndConnect($Connect) - $TcpClient.Close() - Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' passed port test on port '$Protocol`:$Portx'" - $Output.Result = $true - } - $TcpClient.Close() - $TcpClient.Dispose() - } elseif ($Protocol -eq 'UDP') { - $UdpClient = New-Object System.Net.Sockets.UdpClient - $UdpClient.Client.ReceiveTimeout = $UdpTimeout - $UdpClient.Connect($Computer, $Portx) - Write-Verbose "$($MyInvocation.MyCommand.Name) - Sending UDP message to computer '$Computer' on port '$Portx'" - $a = new-object system.text.asciiencoding - $byte = $a.GetBytes("$(Get-Date)") - [void]$UdpClient.Send($byte, $byte.length) - #IPEndPoint object will allow us to read datagrams sent from any source. - Write-Verbose "$($MyInvocation.MyCommand.Name) - 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 "$($MyInvocation.MyCommand.Name) - Waiting for message return" - $receivebytes = $UdpClient.Receive([ref]$remoteendpoint) - [string]$returndata = $a.GetString($receivebytes) - If ($returndata) { - Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' passed port test on port '$Protocol`:$Portx'" - $Output.Result = $true - } - } catch { - Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' failed port test on port '$Protocol`:$Portx' with error '$($_.Exception.Message)'" - $Output.Result = $false - } - $UdpClient.Close() - $UdpClient.Dispose() - } - [pscustomobject]$Output - } - } - } -} - -$Computername = '' -$RequiredSerices = 'Netlogon', 'RemoteRegistry' -$RequiredPorts = @( - @{ 'Protocol' = 'TCP'; 'Port' = 5723 }, - @{ 'Protocol' = 'UDP'; 'Port' = 5723 }, - @{ 'Protocol' = 'TCP'; 'Port' = 135 }, - @{ 'Protocol' = 'UDP'; 'Port' = 135 }, - @{ 'Protocol' = 'TCP'; 'Port' = 137 }, - @{ 'Protocol' = 'UDP'; 'Port' = 137 }, - @{ 'Protocol' = 'TCP'; 'Port' = 139 }, - @{ 'Protocol' = 'UDP'; 'Port' = 139 }, - @{ 'Protocol' = 'TCP'; 'Port' = 445 } -) - -foreach ($Port in $RequiredPorts) { - Test-Port -ComputerName $Computername -Protocol $Port.Protocol -Port $Port.Port -} - -## DNS hostname works forward and back - -## TCP/UDP port 5723,135,137,139 is open and the remote administration (RPC) rule is added, TCP/445 is open - -## the SCOM action account is a local admin somehow \ No newline at end of file diff --git a/TimeframeInvestigator.ps1 b/TimeframeInvestigator.ps1 deleted file mode 100644 index dc4060d..0000000 --- a/TimeframeInvestigator.ps1 +++ /dev/null @@ -1,142 +0,0 @@ -#requires -version 3 - -<# -.SYNOPSIS - This script searches a Windows computer for all event log or text log entries - between a specified start and end time. -.DESCRIPTION - This script enumerates all event logs and all text logs on a local or remote - Windows computer. It looks for any entries between a specified start and end - time. It then copies this activity to an output location for further analysis. -.EXAMPLE - SCRIPTNAME -StartTimestamp '01-29-2014 13:25:00' -EndTimeStamp '01-29-2014 13:28:00' -ComputerName COMPUTERNAME -OutputDirectory $desktop\trouble_logs -SkipEventLog Security -.PARAMETER computername - The computer name to query. Just one. -.PARAMETER logname - The name of a file to write failed computer names to. Defaults to errors.txt. -#> -[CmdletBinding()] -param ( - [Parameter(Mandatory)] - [datetime]$StartTimestamp, - [Parameter(Mandatory)] - [datetime]$EndTimestamp, - [Parameter(ValueFromPipeline, - ValueFromPipelineByPropertyName)] - [string]$ComputerName = 'localhost', - [Parameter()] - [string]$OutputDirectory = ".\$Computername", - [Parameter()] - [string]$LogAuditFilePath = "$OutputDirectory\LogActivity.csv", - [Parameter()] - [switch]$EventLogsOnly, - [Parameter()] - [switch]$LogFilesOnly, - [Parameter()] - [string[]]$ExcludeDirectory, - [Parameter()] - [string[]]$FileExtension = @('log', 'txt', 'wer') -) - -begin { - $LogsFolderPath = "$OutputDirectory\logs" - if (!(Test-Path $LogsFolderPath)) { - mkdir $LogsFolderPath | Out-Null - } - - function Add-ToLog($FilePath,$LineText,$LineNumber,$MatchType) { - $Audit = @{ - 'FilePath' = $FilePath; - 'LineText' = $LineText - 'LineNumber' = $LineNumber - 'MatchType' = $MatchType - } - [pscustomobject]$Audit | Export-Csv -Path $LogAuditFilePath -Append -NoTypeInformation - } -} - -process { - - if (!($LogFilesOnly.IsPresent)) { - $Logs = (Get-WinEvent -ListLog * -ComputerName $ComputerName | where { $_.RecordCount }).LogName - $FilterTable = @{ - 'StartTime' = $StartTimestamp - 'EndTime' = $EndTimestamp - 'LogName' = $Logs - } - - $Events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $FilterTable -ea 'SilentlyContinue' - Write-Verbose "Found $($Events.Count) total events" - - ## Convert the properties to something friendlier - $LogProps = @{ } - [System.Collections.ArrayList]$MyEvents = @() - foreach ($Event in $Events) { - $LogProps.Time = $Event.TimeCreated - $LogProps.Source = $Event.ProviderName - $LogProps.EventId = $Event.Id - $LogProps.Message = $Event.Message.Replace("`n", '|').Replace("`r", '|') - $LogProps.EventLog = $Event.LogName - $MyEvents.Add([pscustomobject]$LogProps) | Out-Null - } - $MyEvents | sort Time | Export-Csv -Path "$OutputDirectory\eventlogs.txt" -Append -NoTypeInformation - } - - if (!($EventLogsOnly.IsPresent)) { - ## Enumerate all shares - $Shares = Get-WmiObject -ComputerName $ComputerName -Class Win32_Share | where { $_.Path -match '^\w{1}:\\$' } - [System.Collections.ArrayList]$AccessibleShares = @() - foreach ($Share in $Shares) { - $Share = "\\$ComputerName\$($Share.Name)" - if (!(Test-Path $Share)) { - Write-Warning "Unable to access the '$Share' share on '$Computername'" - } else { - $AccessibleShares.Add($Share) | Out-Null - } - } - - $AllFilesQueryParams = @{ - Path = $AccessibleShares - Recurse = $true - Force = $true - ErrorAction = 'SilentlyContinue' - File = $true - } - if ($ExcludeDirectory) { - $AllFilesQueryParams.ExcludeDirectory = $ExcludeDirectory - } - ##TODO: Add capability to match on Jan,Feb,Mar,etc - $DateTimeRegex = "($($StartTimestamp.Month)[\\.\-/]?$($StartTimestamp.Day)[\\.\-/]?[\\.\-/]$($StartTimestamp.Year))|($($StartTimestamp.Year)[\\.\-/]?$($StartTimestamp.Month)[\\.\-/]?[\\.\-/]?$($StartTimestamp.Day))" - Get-ChildItem @AllFilesQueryParams | where { $_.Length -ne 0 } | foreach { - try { - Write-Verbose "Processing file '$($_.Name)'" - if (($_.LastWriteTime -ge $StartTimestamp) -and ($_.LastWriteTime -le $EndTimestamp)) { - Write-Verbose "Last write time within timeframe for file '$($_.Name)'" - Add-ToLog -FilePath $_.FullName -MatchType 'LastWriteTime' - } - if ($FileExtension -contains $_.Extension.Replace('.','') -and !((Get-Content $_.FullName -Encoding Byte -TotalCount 1024) -contains 0)) { - ## Check the contents of text file to references to dates in the timeframe - Write-Verbose "Checking log file '$($_.Name)' for date/time match in contents" - $LineMatches = Select-String -Path $_.FullName -Pattern $DateTimeRegex - if ($LineMatches) { - Write-Verbose "Date/time match found in file '$($_.FullName)'" - foreach ($Match in $LineMatches) { - Add-ToLog -FilePath $_.FullName -LineNumber $Match.LineNumber -LineText $Match.Line -MatchType 'Contents' - } - ## I must create the directory ahead of time if it doesn't exist because - ## Copy-Item doesn't have the ability to automatically create the directory - $Trim = $_.FullName.Replace("\\$Computername\", '') - $Destination = "$OutputDirectory\$Trim" - if (!(Test-Path $Destination)) { - ##TODO: Remove the error action when long path support is implemented - mkdir $Destination -ErrorAction SilentlyContinue | Out-Null - } - Copy-Item -Path $_.FullName -Destination $Destination -ErrorAction SilentlyContinue -Recurse - } - } - } catch { - Write-Warning $_.Exception.Message - } - } - } -} diff --git a/Get-EventsFromTimeframe.ps1 b/Troubleshooting Tools/Get-EventsFromTimeframe.ps1 similarity index 100% rename from Get-EventsFromTimeframe.ps1 rename to Troubleshooting Tools/Get-EventsFromTimeframe.ps1 diff --git a/Copy-LocalPathToVmCdRom.ps1 b/VMware/Copy-LocalPathToVmCdRom.ps1 similarity index 100% rename from Copy-LocalPathToVmCdRom.ps1 rename to VMware/Copy-LocalPathToVmCdRom.ps1 diff --git a/Find-WmiClass.ps1 b/WMI/Find-WmiClass.ps1 similarity index 100% rename from Find-WmiClass.ps1 rename to WMI/Find-WmiClass.ps1