From 869d9c9c8e9cfb378c3678123f7f61eb5d0cea1c Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Fri, 26 Apr 2024 08:25:16 -0500 Subject: [PATCH 1/2] Added some initial general scripts --- .../ConvertFrom-UserPrincipalName.ps1 | 42 ++++ .../ConvertTo-DistinguishedName.ps1 | 53 ++++ .../Get-SqlServerInstanceListeningPort.ps1 | 78 ++++++ .../Set-SqlServerInstanceListeningPort.ps1 | 76 ++++++ File System/Compare-Folder.ps1 | 91 +++++++ File System/Get-SmbSharePermission.ps1 | 58 +++++ .../Enable-DynamicDnsRegistration.ps1 | 58 +++++ Name Resolution/HostsFileEntry.psm1 | 233 ++++++++++++++++++ ...t-DnsClientPrimaryAdapterServerAddress.ps1 | 33 +++ Networking/Send-FtpFile.ps1 | 65 +++++ Security/New-RandomPassword.ps1 | 56 +++++ 11 files changed, 843 insertions(+) create mode 100644 Active Directory/ConvertFrom-UserPrincipalName.ps1 create mode 100644 Active Directory/ConvertTo-DistinguishedName.ps1 create mode 100644 Database/Get-SqlServerInstanceListeningPort.ps1 create mode 100644 Database/Set-SqlServerInstanceListeningPort.ps1 create mode 100644 File System/Compare-Folder.ps1 create mode 100644 File System/Get-SmbSharePermission.ps1 create mode 100644 Name Resolution/Enable-DynamicDnsRegistration.ps1 create mode 100644 Name Resolution/HostsFileEntry.psm1 create mode 100644 Name Resolution/Set-DnsClientPrimaryAdapterServerAddress.ps1 create mode 100644 Networking/Send-FtpFile.ps1 create mode 100644 Security/New-RandomPassword.ps1 diff --git a/Active Directory/ConvertFrom-UserPrincipalName.ps1 b/Active Directory/ConvertFrom-UserPrincipalName.ps1 new file mode 100644 index 0000000..e2734d8 --- /dev/null +++ b/Active Directory/ConvertFrom-UserPrincipalName.ps1 @@ -0,0 +1,42 @@ + +<# +.SYNOPSIS +Converts a User Principal Name (UPN) to a DOMAIN\Username format. + +.DESCRIPTION +Takes a User Principal Name (UPN) as input and converts it to a DOMAIN\Username format. The UPN must be in the correct +format (username@domain.com). + +.PARAMETER UserPrincipalName +The User Principal Name that needs to be converted. Must contain an "@" symbol and be in the format username@domain.com. + +.EXAMPLE +PS> ConvertFrom-UserPrincipalName -UserPrincipalName "jdoe@example.com" +Outputs: EXAMPLE\jdoe +This example converts the UPN "jdoe@example.com" into "EXAMPLE\jdoe". + +.INPUTS +String +You can pipe a string that represents the user principal name to ConvertFrom-UserPrincipalName. + +.OUTPUTS +String +Returns the DOMAIN\Username format. +#> +[CmdletBinding()] +param +( + [Parameter(Mandatory, ValueFromPipeline)] + [ValidateScript( { + if ($_.Contains('@') -eq $false) { + throw 'The parameter is not in the correct format.' + } else { $true } + })] + [string]$UserPrincipalName +) + + +$upnParts = $UserPrincipalName.Split('@') +$username = $upnParts[0] +$domain = $upnParts[1].Split('.')[0] +'{0}\{1}' -f $domain.ToUpper(), $username \ No newline at end of file diff --git a/Active Directory/ConvertTo-DistinguishedName.ps1 b/Active Directory/ConvertTo-DistinguishedName.ps1 new file mode 100644 index 0000000..9caab8c --- /dev/null +++ b/Active Directory/ConvertTo-DistinguishedName.ps1 @@ -0,0 +1,53 @@ +<# +.SYNOPSIS +Converts a domain name and optionally an OU path into a distinguished name (DN) format. + +.DESCRIPTION +The ConvertTo-DistinguishedName function takes a domain name and an optional organizational unit (OU) path and converts +them into a distinguished name format used in LDAP environments. This is useful for scripting against Active Directory +and other directory services. + +.PARAMETER DomainName +Specifies the domain name to convert into the distinguished name format. The domain name must be in 'domain.com' format. + +.PARAMETER OUPath +Specifies the organizational unit path which will be included in the distinguished name. This parameter is optional. +If provided, it should be in the LDAP OU path format but can use either '\' or '/' as separators. + +.EXAMPLE +PS> .\ConvertTo-DistinguishedName.ps1 -DomainName "example.com" +Outputs: "DC=example,DC=com" +This example converts a simple domain name into its distinguished name format. + +.EXAMPLE +PS> .\ConvertTo-DistinguishedName.ps1 -DomainName "example.com" -OUPath "OU=Sales\OU=West" +Outputs: "OU=West,OU=Sales,DC=example,DC=com" +This example includes an organizational unit path in the distinguished name conversion. + +.OUTPUTS +String +Returns the distinguished name as a string. +#> +[CmdletBinding()] +[OutputType([string])] +param +( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidatePattern('^\w+\.\w+$')] + [string]$DomainName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$OUPath +) + +$domainSplit = $DomainName.Split('.') + +if ($PSBoundParameters.ContainsKey('OUPath')) { + $OUPath = $OUPath.Replace('\', '/') + $ouSplit = $OUPath.Split('/') + "OU=$($ouSplit -join ',OU='),DC=$($domainSplit -join ',DC=')" +} else { + "DC=$($domainSplit -join ',DC=')" +} \ No newline at end of file diff --git a/Database/Get-SqlServerInstanceListeningPort.ps1 b/Database/Get-SqlServerInstanceListeningPort.ps1 new file mode 100644 index 0000000..f19c387 --- /dev/null +++ b/Database/Get-SqlServerInstanceListeningPort.ps1 @@ -0,0 +1,78 @@ +<# +.SYNOPSIS +Retrieves SQL Server instances and their listening ports on a Windows server. + +.DESCRIPTION +This script retrieves information about SQL Server instances installed on a Windows server and their corresponding listening ports. It can retrieve information for all instances or a specific instance specified by the -InstanceName parameter. + +.PARAMETER InstanceName +The name of a specific SQL Server instance to retrieve information for. If not provided, the script retrieves information for all instances. + +.EXAMPLE +.\Get-SqlServerInstanceListeningPort.ps1 + +Retrieves information for all SQL Server instances on the server. + +.EXAMPLE +.\Get-SqlServerInstanceListeningPort.ps1 -InstanceName "SQLEXPRESS" + +Retrieves information for the SQL Server instance named "SQLEXPRESS". + +.OUTPUTS +Returns an array of PSCustomObject with the following properties: +- InstanceName: The name of the SQL Server instance. +- Port: The listening port number of the instance. If the port is dynamically assigned, it will display "Dynamic". + +.NOTES +- This script requires administrative privileges to access the Windows registry. +- The script assumes that the SQL Server instances are installed on the same server where the script is running. +- The script has only been tested with SQL Server Express 2019 and SQL Server 2019 +#> + +[CmdletBinding()] +param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$InstanceName +) + +$instanceNames = $null +$instanceNames = Get-Service -Name "MSSQL*" | Select-Object @{n = 'InstanceName'; e = { $_ -replace "^MSSQL\$" } } | Select-Object -ExpandProperty 'InstanceName' +if (!$instanceNames) { + throw 'No SQL instances found.' +} +$instanceNames = $instanceNames.where({ !$InstanceName -or $_ -eq $InstanceName }) + +foreach ($instanceName in $instanceNames) { + + # Get the SQL Server configuration + $sqlConfig = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" -Name $instanceName -ErrorAction SilentlyContinue + + if ($sqlConfig) { + # Get the instance ID + $instanceId = $sqlConfig.$instanceName + + # Get the SQL Server network configuration + $networkConfig = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceId\MSSQLServer\SuperSocketNetLib\Tcp\IPAll" -ErrorAction SilentlyContinue + + # Get the TCP port number + $tcpPort = $networkConfig.TcpPort + + # If the TCP port is not explicitly set, use the default port + if ([string]::IsNullOrEmpty($tcpPort)) { + if ($instanceName -eq "MSSQLSERVER") { + $tcpPort = "1433" # Default port for the default instance + } else { + $tcpDynamicPorts = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceId\MSSQLServer\SuperSocketNetLib\Tcp\IPAll" -Name TcpDynamicPorts -ErrorAction SilentlyContinue + if ($tcpDynamicPorts.TcpDynamicPorts -eq 0) { + $tcpPort = 'Dynamic' + } + } + } + + [pscustomobject]@{ + InstanceName = $instanceName + Port = $tcpPort + } + } +} \ No newline at end of file diff --git a/Database/Set-SqlServerInstanceListeningPort.ps1 b/Database/Set-SqlServerInstanceListeningPort.ps1 new file mode 100644 index 0000000..9b33b82 --- /dev/null +++ b/Database/Set-SqlServerInstanceListeningPort.ps1 @@ -0,0 +1,76 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS +Configures the TCP port for a specified SQL Server instance. + +.DESCRIPTION +This script updates the TCP port for a specified SQL Server instance by modifying the Windows Registry. It ensures that TCP/IP is enabled for the instance and sets the specified port. If the changes are made successfully, the SQL Server instance service is restarted to apply the changes. + +.PARAMETER InstanceName +Specifies the name of the SQL Server instance to configure. This parameter is mandatory. + +.PARAMETER Port +Specifies the TCP port number to set for the SQL Server instance. This parameter is mandatory. + +.EXAMPLE +PS> .\Set-SqlServerPort.ps1 -InstanceName "MSSQLSERVER" -Port 1433 + +This command sets the TCP port of the SQL Server instance named MSSQLSERVER to 1433. + +.INPUTS +None. You cannot pipe input to this script. + +.OUTPUTS +None. This script does not produce any output. + +.NOTES + + + +.LINK +https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/configure-a-server-to-listen-on-a-specific-tcp-port + +#> + +[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] +param ( + [Parameter(Mandatory)] + [string]$InstanceName, + + [Parameter(Mandatory)] + [int]$Port +) + +# Find SQL Server instances and their versions +$instances = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' + +# Determine the version and registry path for each instance +foreach ($instance in $instances.PSObject.Properties) { + if ($instance.Name -eq $InstanceName) { + $instanceID = $instance.Value + $rootKeyPath = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceID\MSSQLServer\SuperSocketNetLib\Tcp" + $ipKeys = Get-ChildItem $rootKeyPath -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'IP[0-9]+$' } + + $anyIPEnabled = $false + foreach ($ipKey in $ipKeys) { + $ipEnabled = (Get-ItemProperty -Path $ipKey.PSPath -Name "Enabled").Enabled + if ($ipEnabled -eq '1') { + $anyIPEnabled = $true + $tcpKeyPath = $ipKey.PSPath + if ($PSCmdlet.ShouldProcess("$tcpKeyPath (Enabled IP)", "Set TCP Port")) { + Set-ItemProperty -Path $tcpKeyPath -Name "TcpPort" -Value "$Port" + Set-ItemProperty -Path $tcpKeyPath -Name "TcpDynamicPorts" -Value "" + } + } + } + + if (-not $anyIPEnabled) { + throw "No enabled IP addresses found for instance $InstanceName. Enable TCP/IP in SQL Server Configuration Manager." + } + + $serviceName = "MSSQL`$$InstanceName" + if ($PSCmdlet.ShouldProcess("$serviceName service", 'Restart')) { + Restart-Service -Name $serviceName -Force + } + } +} diff --git a/File System/Compare-Folder.ps1 b/File System/Compare-Folder.ps1 new file mode 100644 index 0000000..000050c --- /dev/null +++ b/File System/Compare-Folder.ps1 @@ -0,0 +1,91 @@ +<# +.SYNOPSIS +Compares two folders and identifies files that are missing or have mismatched hashes. + +.DESCRIPTION +This script analyzes two specified folders and provides a detailed report on differences. It identifies: + +* Files present in the Reference Folder but absent in the Difference Folder. +* Files present in the Difference Folder but absent in the Reference Folder. +* Files with the same name in both folders but different content (hash mismatch). + +The script allows for an optional ExcludeFilePath parameter to disregard a specific file during comparison. + +.PARAMETER ReferenceFolder +The path to the reference folder used as the baseline for comparison. This parameter is mandatory. + +.PARAMETER DifferenceFolder +The path to the folder that will be compared against the reference folder. This parameter is mandatory. + +.PARAMETER ExcludeFilePath +An optional path (relative to the root of the folders) to a file that should be excluded from the comparison. The path must start with a backslash (\). + +.EXAMPLE +Compare folders C:\Source and C:\Target, excluding the file "temp.log" + +.\Compare-Folder.ps1 -ReferenceFolder "C:\Source" -DifferenceFolder "C:\Target" -ExcludeFilePath "\temp.log" + +.EXAMPLE +Compare folders C:\Documents and D:\Backup + +.\Compare-Folder.ps1 -ReferenceFolder "C:\Documents" -DifferenceFolder "D:\Backup" + +.NOTES + - The script uses the Get-FileHash cmdlet to calculate file hashes. + - Ensure you have the necessary permissions to access both the reference and difference folders. +#> + +[CmdletBinding()] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path -Path $_ -PathType Container })] + [string]$ReferenceFolder, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path -Path $_ -PathType Container })] + [string]$DifferenceFolder, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [ValidatePattern('^\\')] + [string]$ExcludeFilePath +) + +function Get-FileHashesInFolder { + param ( + [string]$Folder + ) + $files = Get-ChildItem -Path $Folder -Recurse -File + foreach ($s in $files) { + $selectObjects = @('Hash', @{ n = 'Path'; e = { $_.Path.SubString($Folder.Length) } }) + Get-FileHash $s.Fullname | Select-Object $selectObjects -ExcludeProperty Path + } +} + +$refHashes = Get-FileHashesInFolder -Folder $ReferenceFolder +$destHashes = Get-FileHashesInFolder -Folder $DifferenceFolder +if ($PSBoundParameters.ContainsKey('ExcludeFilePath')) { + $refHashes = $refHashes.Where({ $_.Path -ne $ExcludeFilePath }) + $destHashes = $destHashes.Where({ $_.Path -ne $ExcludeFilePath }) +} + +$refHashes.Where({ $_.Path -notin $destHashes.Path }).ForEach({ + [pscustomobject]@{ + 'Path' = $_.Path + 'Reason' = 'NotInDifferenceFolder' + } + }) +$destHashes.Where({ $_.Path -notin $refHashes.Path }).ForEach({ + [pscustomobject]@{ + 'Path' = $_.Path + 'Reason' = 'NotInReferenceFolder' + } + }) +$refHashes.Where({ $_.Hash -notin $destHashes.Hash -and $_.Path -in $destHashes.Path }).ForEach({ + [pscustomobject]@{ + 'Path' = $_.Path + 'Reason' = 'HashDifferent' + } + }) diff --git a/File System/Get-SmbSharePermission.ps1 b/File System/Get-SmbSharePermission.ps1 new file mode 100644 index 0000000..6615a3f --- /dev/null +++ b/File System/Get-SmbSharePermission.ps1 @@ -0,0 +1,58 @@ +<# +.SYNOPSIS +Retrieves and formats the access control list (ACL) of a Windows file share. + +.DESCRIPTION +This script checks if the specified file share exists. If it does, it retrieves the share's +security descriptor and enumerates the access control entries (ACEs). For each ACE, it displays +the principal (user or group), domain, and the assigned permission level. + +.PARAMETER Name +The name of the file share to query. + +.EXAMPLE +Get the ACL for the share named "Documents": +.\Get-SmbSharePermission.ps1 -Name "Documents" +#> +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name +) + +# Verify that the share exists +$existingShare = Get-SmbShare -Name $Name -ErrorAction 'SilentlyContinue' +if (-not $existingShare) { + throw "The share [$($Name)] does not exist." +} + +# Define a lookup table for translating access mask values into permission names +$accessRights = @{ + 2032127 = 'Full Control' + 1245631 = 'Change' + 1179817 = 'Read' +} + +# Retrieve the share's security descriptor +$shareParams = @{ + ClassName = 'Win32_LogicalShareSecuritySetting' + Filter = "Name = '$Name'" +} +$shareSecurityDescriptor = (Get-CimInstance @shareParams).GetSecurityDescriptor() + +# Access the DACL (Discretionary Access Control List) +$dacl = $shareSecurityDescriptor.Descriptor.DACL + +# Process each ACE (Access Control Entry) +foreach ($ace in $dacl) { + $trustee = $ace.Trustee + + # Construct a custom output object with Principal, Domain, and Permission + [pscustomobject]@{ + Principal = $trustee.Name -or $trustee.SIDString # Use SID if the user/group name is unavailable + Domain = $trustee.Domain + Permission = $accessRights[$ace.AccessMask] + } +} \ No newline at end of file diff --git a/Name Resolution/Enable-DynamicDnsRegistration.ps1 b/Name Resolution/Enable-DynamicDnsRegistration.ps1 new file mode 100644 index 0000000..ae3c092 --- /dev/null +++ b/Name Resolution/Enable-DynamicDnsRegistration.ps1 @@ -0,0 +1,58 @@ +<# + .SYNOPSIS + Enables dynamic DNS registration on network adapters that are IP enabled and have a default gateway configured. + + .DESCRIPTION + Configures network adapters on a specified computer to enable dynamic + DNS registration. This setting is particularly useful in environments where IP addresses are dynamically assigned + and network changes are frequent. + + .PARAMETER ComputerName + Specifies the name of the computer on which the network adapters will be configured. This parameter is mandatory. + + .PARAMETER Credential + Specifies the credentials to use when connecting to the remote computer. This parameter is optional. If not provided, + the current user's credentials are used. + + .EXAMPLE + PS> .\Enable-DynamicDnsRegistration.ps1 -ComputerName "Server01" + Enables dynamic DNS registration on all IP-enabled network adapters with a default gateway on the computer named + Server01 using the current user's credentials. + + .EXAMPLE + PS> .\Enable-DynamicDnsRegistration.ps1 -ComputerName "Server01" -Credential (Get-Credential) + Enables dynamic DNS registration on the computer named "Server01", using specified credentials. The command prompts + for credentials. + + .OUTPUTS + None. Outputs are directed to the Verbose stream or error messages are thrown as exceptions. + + #> +[CmdletBinding()] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ComputerName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [pscredential]$Credential +) + +$icmParams = @{ + ComputerName = $ComputerName +} +if ($PSBoundParameters.ContainsKey('Credential')) { + $icmParams.Credential = $Credential +} + +$icmParams.ScriptBlock = { + Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled='true'" | Where-Object { $_.DefaultIPGateway.Count -gt 0 } | ForEach-Object { + Write-Verbose -Message "Setting DDNS registration..." + $result = $_.SetDynamicDNSRegistration($true) + if ($result.ReturnValue -ne 0) { + throw "Failed to set DDNS registration on with return code [$($result.ReturnValue)]" + } + } +} +Invoke-Command @icmParams \ No newline at end of file diff --git a/Name Resolution/HostsFileEntry.psm1 b/Name Resolution/HostsFileEntry.psm1 new file mode 100644 index 0000000..00ce01e --- /dev/null +++ b/Name Resolution/HostsFileEntry.psm1 @@ -0,0 +1,233 @@ +$script:hostFilePath = "$Env:SystemRoot\System32\drivers\etc\hosts" + +function Get-HostsFileEntry { + <# + .SYNOPSIS + Gets the entries from the hosts file. + + .DESCRIPTION + The Get-HostsFileEntry cmdlet gets the entries from the hosts file. The hosts file is a text file that maps hostnames to + IP addresses. It is typically used to resolve hostnames that cannot be resolved using DNS. + + .PARAMETER HostFilePath + The path to the hosts file. The default value is "$Env:SystemRoot\System32\drivers\etc\hosts". + + .OUTPUTS + The cmdlet outputs an array of custom objects. Each custom object represents a single entry in the hosts file. The + custom object has the following properties: + + * IPAddress: The IP address of the host. + * HostName: The hostname of the host. + * Comment: The comment associated with the host. + + .EXAMPLE + Get the entries from the hosts file: + .\Get-HostsFileEntry.ps1 + + #> + param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [ValidateScript( + { Test-Path -Path $_.FullName }, + ErrorMessage = "The hosts file at [{0}] could not be found." + )] + [string]$HostFilePath = $script:hostFilePath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$HostName + ) + + $ErrorActionPreference = 'Stop' + + $regex = '^(?[0-9.]+)\s+(?[^\s#]+)(\s*#\s*(?.*))?$' + + $whereFilter = '$_' + if ($PSBoundParameters.ContainsKey('HostName')) { + $whereFilter = "`$_.HostName -eq '$Hostname'" + } + $whereFilter = [scriptblock]::Create($whereFilter) + + Get-Content -Path $HostFilePath | ForEach-Object { + if ($_ -match $regex) { + [pscustomobject]@{ + IPAddress = $matches['ipAddress'] + HostName = $matches['hostname'] + Comment = $matches['comment'] + } + } + } | Where-Object -FilterScript $whereFilter +} + +function Remove-HostsFileEntry { + <# + .SYNOPSIS + Removes an entry from the hosts file. + + .DESCRIPTION + The Remove-HostsFileEntry cmdlet removes an entry from the hosts file based on the specified hostname. It is designed + to safely modify the hosts file by utilizing a temporary file during the deletion process to prevent data loss. + + .PARAMETER HostName + Specifies the hostname of the entry to remove from the hosts file. This parameter is mandatory. + + .PARAMETER HostFilePath + Specifies the path to the hosts file. The default is set to a script-scoped variable ($script:hostFilePath) which + is predefined in the module. + + .EXAMPLE + Remove-HostsFileEntry -HostName "examplehost" + + This example removes the entry for 'examplehost' from the default hosts file. + + .EXAMPLE + Remove-HostsFileEntry -HostName "testhost" -HostFilePath "C:\Windows\System32\drivers\etc\hosts" + + This example demonstrates how to specify both the hostname and the path of the hosts file from which to remove an entry. + + .NOTES + Ensure that the PowerShell session running this cmdlet has adequate permissions to modify the hosts file, typically + requiring administrative rights. + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [ValidateNotNullOrEmpty()] + [string]$HostName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$HostFilePath = $script:hostFilePath + ) + + $regex = "^(?[0-9.]+)[^\w]*($HostName)(`$|[\W]{0,}#\s+(?.*))" + $toRemove = (Get-Content -Path $HostFilePath | Select-String -Pattern $regex).Line + + # Safer to create a temp file. + $tempFile = "$Env:SystemRoot\System32\drivers\etc\hosts.temp" + (Get-Content -Path $HostFilePath | Where-Object { $_ -ne $toRemove }) | Add-Content -Path $tempFile + if (Test-Path -Path $tempFile -PathType Leaf) { + Remove-Item -Path $HostFilePath + Move-Item -Path $tempFile -Destination $HostFilePath + } else { + throw 'Failed to create temp hosts file to make the change' + } +} + +function Set-HostsFileEntry { + <# + .SYNOPSIS + Sets or updates an entry in the hosts file. + + .DESCRIPTION + The Set-HostsFileEntry cmdlet adds or updates an entry in the hosts file for a specified hostname and IP address. + If the hostname exists but with a different IP address, the existing entry is updated. This cmdlet also handles + fully qualified domain names (FQDNs) by resolving them to IP addresses if no IP address is provided. + + .PARAMETER HostName + Specifies the hostname for the entry in the hosts file. This parameter is mandatory. + + .PARAMETER IPAddress + Specifies the IP address for the hostname. If not provided and the hostname is a FQDN, the cmdlet attempts to resolve + the IP address. + + .PARAMETER Comment + Allows adding a comment for the hosts file entry. This is optional and can be used for documentation or clarification + purposes. + + .PARAMETER HostFilePath + Specifies the path to the hosts file. The default is set to a script-scoped variable ($script:hostFilePath), which + is provided in the module. + + .EXAMPLE + Set-HostsFileEntry -HostName "example.com" -IPAddress "192.168.1.1" + + This example adds an entry to the hosts file linking "example.com" with the IP address "192.168.1.1". + + .EXAMPLE + Set-HostsFileEntry -HostName "example.com" -IPAddress "192.168.1.1" -Comment "Test server" + + This example does the same as the previous one but includes a comment "Test server" for the entry. + + .EXAMPLE + Set-HostsFileEntry -HostName "example.com" + + If "example.com" is a FQDN and no IP address is provided, this example tries to resolve "example.com" and adds the + resulting IP address to the hosts file. + + .NOTES + Ensure that you have the necessary administrative rights to modify the hosts file, as this operation typically + requires elevated privileges. + + #> + + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$HostName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [ipaddress]$IPAddress, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Comment, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$HostFilePath = $script:hostFilePath + ) + + $ErrorActionPreference = 'Stop' + + $isFqdn = $false + if ($PSBoundParameters.HostName -match '\.+') { + $isFqdn = $true + $shortHostName = $HostName.Split('.')[0].ToUpper() + } else { + $HostName = $HostName.ToUpper() + } + + $vals = @() + ## If a FQDN was passed, grab the hostname and IP address that resolves and use that + if ($isFqdn) { + if (-not $PSBoundParameters.ContainsKey('IPAddress')) { + $ip = (Resolve-DnsName -Name $HostName -DnsOnly -ErrorAction SilentlyContinue).IPAddress + if (-not $ip) { + throw 'No IP address provided and hostname could not be resolved' + } + $HostName = $shortHostName + $IPAddress = $ip + } + } elseif (-not $PSBoundParameters.ContainsKey('IPAddress')) { + throw 'Hostname is not a FQDN and no IP address provided.' + } + + $vals += $IPAddress + $vals += $HostName + + if ($PSBoundParameters.ContainsKey('Comment')) { + $vals += "# $Comment" + } + + $existingEntry = Get-HostsFileEntry -HostName $HostName + if ($existingEntry) { + if ($existingEntry.IPAddress -eq $IPAddress) { + Write-Verbose "The hostname '$HostName' already exists on computer $ComputerName" + return + } else { + Write-Verbose "Removing hostname '$HostName' on computer $ComputerName' because IP address does not match" + $null = Remove-HostsFileEntry -HostName $HostName + } + } + + ## If the hosts file doesn't end with a blank line, make it so + if ((Get-Content -Path $HostFilePath -Raw) -notmatch '\n$') { + Add-Content -Path $HostFilePath -Value '' + } + + Add-Content -Path $HostFilePath -Value ($vals -join "`t") +} \ No newline at end of file diff --git a/Name Resolution/Set-DnsClientPrimaryAdapterServerAddress.ps1 b/Name Resolution/Set-DnsClientPrimaryAdapterServerAddress.ps1 new file mode 100644 index 0000000..88e9152 --- /dev/null +++ b/Name Resolution/Set-DnsClientPrimaryAdapterServerAddress.ps1 @@ -0,0 +1,33 @@ +<# +.SYNOPSIS +Updates the DNS server search order on network adapters. + +.DESCRIPTION +This script checks network adapters that have a default gateway and are IP enabled. +It updates the DNS server search order if the current order differs from the specified addresses. + +.PARAMETER ServerAddress +A mandatory array of DNS server IP addresses (in string format) to be set as the new search order. + +.EXAMPLE +Update DNS settings using two DNS servers: +.\Set-DnsClientPrimaryAdapterServerAddress.ps1 -ServerAddress "10.0.0.10", "10.0.0.20" +#> + +[CmdletBinding()] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string[]]$ServerAddress +) + +Get-CimInstance -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled='true' AND DefaultIPGateway[0]" | + Where-Object { $_.DNSServerSearchOrder -notmatch ($ServerAddress -join ',') } | # Filter for adapters needing updates + ForEach-Object { + Write-Verbose "Changing DNS server search order to '$($ServerAddress -join ',')'" + + $setDnsResult = $_.SetDNSServerSearchOrder($ServerAddress) # Update the DNS search order + if ($setDnsResult.ReturnValue -notin @(0, 1)) { # Basic error checking + throw "Error setting DNS server search order: [$setDnsResult]" + } + } diff --git a/Networking/Send-FtpFile.ps1 b/Networking/Send-FtpFile.ps1 new file mode 100644 index 0000000..63fb830 --- /dev/null +++ b/Networking/Send-FtpFile.ps1 @@ -0,0 +1,65 @@ +<# +.SYNOPSIS +Uploads a file to an FTP server. + +.DESCRIPTION +This cmdlet uploads a specified file to a given FTP server. It requires FTP server address, the local file path, and user credentials for authentication. + +.PARAMETER Uri +The FTP server address (e.g., ftp://myftpserver.com) to upload the file to. + +.PARAMETER FilePath +The full path to the local file to be uploaded. + +.PARAMETER Credential +A PSCredential object containing the username and password for FTP authentication. +You can create this using the Get-Credential cmdlet. + +.EXAMPLE +Upload a file named report.txt to an FTP server, using stored credentials: + +$myCredentials = Get-Credential +.\Send-FtpFile.ps1 -Uri "ftp://myftpserver.com/uploads/" -FilePath "C:\reports\report.txt" -Credential $myCredentials + +.NOTES +- This cmdlet uses passive FTP mode, which is often required for compatibility with firewalls. +- The file is uploaded in binary mode to prevent any unwanted text conversions. +#> + +[OutputType('void')] +[CmdletBinding()] +param +( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Uri, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$FilePath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [pscredential]$Credential +) + +try { + $request = [System.Net.FtpWebRequest]::Create($Uri) # Initiate communication with the FTP server + + $request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile # Specify that we're uploading a file + $request.Credentials = New-Object System.Net.NetworkCredential($Credential.UserName, $Credential.GetNetworkCredential().Password) # Provide authentication for access + $request.UseBinary = $true # Ensures accurate file transfer without text conversion + $request.UsePassive = $true # Improves compatibility through firewalls + + $fileContent = Get-Content -Encoding Byte -Path $FilePath # Fetch file data in a suitable format for transfer + + $request.ContentLength = $fileContent.Length # Communicate the expected file size to the server + + $run = $request.GetRequestStream() # Open a channel to send the file content + $run.Write($fileContent, 0, $fileContent.Length) # Transmit the file data +} catch { + throw $_ # Propagate critical errors for proper handling +} finally { + $run.Close() # Properly terminate the data channel + $run.Dispose() # Release resources for efficiency +} \ No newline at end of file diff --git a/Security/New-RandomPassword.ps1 b/Security/New-RandomPassword.ps1 new file mode 100644 index 0000000..bb855b1 --- /dev/null +++ b/Security/New-RandomPassword.ps1 @@ -0,0 +1,56 @@ + +<# + .SYNOPSIS + Generates a secure random password containing a mix of uppercase, lowercase, numbers, and symbols. + + .DESCRIPTION + Generates a random password of a specified length. The password includes a mix of + uppercase letters, lowercase letters, numbers, and special characters. This function uses cryptographic random number + generation to ensure that the password is generated with a high degree of randomness suitable for secure applications. + + .PARAMETER Length + Specifies the length of the password to generate. The default length is 12 characters. + + .EXAMPLE + PS> New-RandomPassword -Length 16 + This example generates a 16-character long random password. + + .EXAMPLE + PS> New-RandomPassword + This example generates a random password using the default length of 12 characters. + + .OUTPUTS + System.Security.SecureString + The function returns the password as a SecureString to ensure that it is not displayed in plain text or stored in + memory as a regular string. + #> +[CmdletBinding()] +param( + [int]$Length = 12 +) + +# Define character sets +$lowercase = 'abcdefghijklmnopqrstuvwxyz' +$uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +$numbers = '0123456789' +$symbols = '!@#$%^&*()_-+=[]{}|;:,.<>?' + +# Combine all character sets +$charSet = $lowercase + $uppercase + $numbers + $symbols + +# Create an array to hold the password characters +$passwordChars = New-Object char[] $Length + +# Random number generator +$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create() + +# Generate password +for ($i = 0; $i -lt $Length; $i++) { + $byte = New-Object byte[] 1 + $rng.GetBytes($byte) + $randomIndex = $byte[0] % $charSet.Length + $passwordChars[$i] = $charSet[$randomIndex] +} + +# Convert the password to a secure string so we don't put plain text passwords on the pipeline. +ConvertTo-SecureString -String (-join $passwordChars) -AsPlainText -Force \ No newline at end of file From fb7e4628846f0df01e885a7e3111965eda47f69e Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Thu, 2 May 2024 07:58:59 -0500 Subject: [PATCH 2/2] removed unwanted initial scripts --- .../ConvertFrom-UserPrincipalName.ps1 | 42 ---- .../ConvertTo-DistinguishedName.ps1 | 53 ---- .../Get-SqlServerInstanceListeningPort.ps1 | 78 ------ .../Set-SqlServerInstanceListeningPort.ps1 | 76 ------ File System/Compare-Folder.ps1 | 91 ------- File System/Get-SmbSharePermission.ps1 | 58 ----- .../Enable-DynamicDnsRegistration.ps1 | 58 ----- Name Resolution/HostsFileEntry.psm1 | 233 ------------------ ...t-DnsClientPrimaryAdapterServerAddress.ps1 | 33 --- Networking/Send-FtpFile.ps1 | 65 ----- Security/New-RandomPassword.ps1 | 56 ----- 11 files changed, 843 deletions(-) delete mode 100644 Active Directory/ConvertFrom-UserPrincipalName.ps1 delete mode 100644 Active Directory/ConvertTo-DistinguishedName.ps1 delete mode 100644 Database/Get-SqlServerInstanceListeningPort.ps1 delete mode 100644 Database/Set-SqlServerInstanceListeningPort.ps1 delete mode 100644 File System/Compare-Folder.ps1 delete mode 100644 File System/Get-SmbSharePermission.ps1 delete mode 100644 Name Resolution/Enable-DynamicDnsRegistration.ps1 delete mode 100644 Name Resolution/HostsFileEntry.psm1 delete mode 100644 Name Resolution/Set-DnsClientPrimaryAdapterServerAddress.ps1 delete mode 100644 Networking/Send-FtpFile.ps1 delete mode 100644 Security/New-RandomPassword.ps1 diff --git a/Active Directory/ConvertFrom-UserPrincipalName.ps1 b/Active Directory/ConvertFrom-UserPrincipalName.ps1 deleted file mode 100644 index e2734d8..0000000 --- a/Active Directory/ConvertFrom-UserPrincipalName.ps1 +++ /dev/null @@ -1,42 +0,0 @@ - -<# -.SYNOPSIS -Converts a User Principal Name (UPN) to a DOMAIN\Username format. - -.DESCRIPTION -Takes a User Principal Name (UPN) as input and converts it to a DOMAIN\Username format. The UPN must be in the correct -format (username@domain.com). - -.PARAMETER UserPrincipalName -The User Principal Name that needs to be converted. Must contain an "@" symbol and be in the format username@domain.com. - -.EXAMPLE -PS> ConvertFrom-UserPrincipalName -UserPrincipalName "jdoe@example.com" -Outputs: EXAMPLE\jdoe -This example converts the UPN "jdoe@example.com" into "EXAMPLE\jdoe". - -.INPUTS -String -You can pipe a string that represents the user principal name to ConvertFrom-UserPrincipalName. - -.OUTPUTS -String -Returns the DOMAIN\Username format. -#> -[CmdletBinding()] -param -( - [Parameter(Mandatory, ValueFromPipeline)] - [ValidateScript( { - if ($_.Contains('@') -eq $false) { - throw 'The parameter is not in the correct format.' - } else { $true } - })] - [string]$UserPrincipalName -) - - -$upnParts = $UserPrincipalName.Split('@') -$username = $upnParts[0] -$domain = $upnParts[1].Split('.')[0] -'{0}\{1}' -f $domain.ToUpper(), $username \ No newline at end of file diff --git a/Active Directory/ConvertTo-DistinguishedName.ps1 b/Active Directory/ConvertTo-DistinguishedName.ps1 deleted file mode 100644 index 9caab8c..0000000 --- a/Active Directory/ConvertTo-DistinguishedName.ps1 +++ /dev/null @@ -1,53 +0,0 @@ -<# -.SYNOPSIS -Converts a domain name and optionally an OU path into a distinguished name (DN) format. - -.DESCRIPTION -The ConvertTo-DistinguishedName function takes a domain name and an optional organizational unit (OU) path and converts -them into a distinguished name format used in LDAP environments. This is useful for scripting against Active Directory -and other directory services. - -.PARAMETER DomainName -Specifies the domain name to convert into the distinguished name format. The domain name must be in 'domain.com' format. - -.PARAMETER OUPath -Specifies the organizational unit path which will be included in the distinguished name. This parameter is optional. -If provided, it should be in the LDAP OU path format but can use either '\' or '/' as separators. - -.EXAMPLE -PS> .\ConvertTo-DistinguishedName.ps1 -DomainName "example.com" -Outputs: "DC=example,DC=com" -This example converts a simple domain name into its distinguished name format. - -.EXAMPLE -PS> .\ConvertTo-DistinguishedName.ps1 -DomainName "example.com" -OUPath "OU=Sales\OU=West" -Outputs: "OU=West,OU=Sales,DC=example,DC=com" -This example includes an organizational unit path in the distinguished name conversion. - -.OUTPUTS -String -Returns the distinguished name as a string. -#> -[CmdletBinding()] -[OutputType([string])] -param -( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [ValidatePattern('^\w+\.\w+$')] - [string]$DomainName, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$OUPath -) - -$domainSplit = $DomainName.Split('.') - -if ($PSBoundParameters.ContainsKey('OUPath')) { - $OUPath = $OUPath.Replace('\', '/') - $ouSplit = $OUPath.Split('/') - "OU=$($ouSplit -join ',OU='),DC=$($domainSplit -join ',DC=')" -} else { - "DC=$($domainSplit -join ',DC=')" -} \ No newline at end of file diff --git a/Database/Get-SqlServerInstanceListeningPort.ps1 b/Database/Get-SqlServerInstanceListeningPort.ps1 deleted file mode 100644 index f19c387..0000000 --- a/Database/Get-SqlServerInstanceListeningPort.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -<# -.SYNOPSIS -Retrieves SQL Server instances and their listening ports on a Windows server. - -.DESCRIPTION -This script retrieves information about SQL Server instances installed on a Windows server and their corresponding listening ports. It can retrieve information for all instances or a specific instance specified by the -InstanceName parameter. - -.PARAMETER InstanceName -The name of a specific SQL Server instance to retrieve information for. If not provided, the script retrieves information for all instances. - -.EXAMPLE -.\Get-SqlServerInstanceListeningPort.ps1 - -Retrieves information for all SQL Server instances on the server. - -.EXAMPLE -.\Get-SqlServerInstanceListeningPort.ps1 -InstanceName "SQLEXPRESS" - -Retrieves information for the SQL Server instance named "SQLEXPRESS". - -.OUTPUTS -Returns an array of PSCustomObject with the following properties: -- InstanceName: The name of the SQL Server instance. -- Port: The listening port number of the instance. If the port is dynamically assigned, it will display "Dynamic". - -.NOTES -- This script requires administrative privileges to access the Windows registry. -- The script assumes that the SQL Server instances are installed on the same server where the script is running. -- The script has only been tested with SQL Server Express 2019 and SQL Server 2019 -#> - -[CmdletBinding()] -param( - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$InstanceName -) - -$instanceNames = $null -$instanceNames = Get-Service -Name "MSSQL*" | Select-Object @{n = 'InstanceName'; e = { $_ -replace "^MSSQL\$" } } | Select-Object -ExpandProperty 'InstanceName' -if (!$instanceNames) { - throw 'No SQL instances found.' -} -$instanceNames = $instanceNames.where({ !$InstanceName -or $_ -eq $InstanceName }) - -foreach ($instanceName in $instanceNames) { - - # Get the SQL Server configuration - $sqlConfig = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" -Name $instanceName -ErrorAction SilentlyContinue - - if ($sqlConfig) { - # Get the instance ID - $instanceId = $sqlConfig.$instanceName - - # Get the SQL Server network configuration - $networkConfig = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceId\MSSQLServer\SuperSocketNetLib\Tcp\IPAll" -ErrorAction SilentlyContinue - - # Get the TCP port number - $tcpPort = $networkConfig.TcpPort - - # If the TCP port is not explicitly set, use the default port - if ([string]::IsNullOrEmpty($tcpPort)) { - if ($instanceName -eq "MSSQLSERVER") { - $tcpPort = "1433" # Default port for the default instance - } else { - $tcpDynamicPorts = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceId\MSSQLServer\SuperSocketNetLib\Tcp\IPAll" -Name TcpDynamicPorts -ErrorAction SilentlyContinue - if ($tcpDynamicPorts.TcpDynamicPorts -eq 0) { - $tcpPort = 'Dynamic' - } - } - } - - [pscustomobject]@{ - InstanceName = $instanceName - Port = $tcpPort - } - } -} \ No newline at end of file diff --git a/Database/Set-SqlServerInstanceListeningPort.ps1 b/Database/Set-SqlServerInstanceListeningPort.ps1 deleted file mode 100644 index 9b33b82..0000000 --- a/Database/Set-SqlServerInstanceListeningPort.ps1 +++ /dev/null @@ -1,76 +0,0 @@ -#Requires -RunAsAdministrator -<# -.SYNOPSIS -Configures the TCP port for a specified SQL Server instance. - -.DESCRIPTION -This script updates the TCP port for a specified SQL Server instance by modifying the Windows Registry. It ensures that TCP/IP is enabled for the instance and sets the specified port. If the changes are made successfully, the SQL Server instance service is restarted to apply the changes. - -.PARAMETER InstanceName -Specifies the name of the SQL Server instance to configure. This parameter is mandatory. - -.PARAMETER Port -Specifies the TCP port number to set for the SQL Server instance. This parameter is mandatory. - -.EXAMPLE -PS> .\Set-SqlServerPort.ps1 -InstanceName "MSSQLSERVER" -Port 1433 - -This command sets the TCP port of the SQL Server instance named MSSQLSERVER to 1433. - -.INPUTS -None. You cannot pipe input to this script. - -.OUTPUTS -None. This script does not produce any output. - -.NOTES - - - -.LINK -https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/configure-a-server-to-listen-on-a-specific-tcp-port - -#> - -[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] -param ( - [Parameter(Mandatory)] - [string]$InstanceName, - - [Parameter(Mandatory)] - [int]$Port -) - -# Find SQL Server instances and their versions -$instances = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' - -# Determine the version and registry path for each instance -foreach ($instance in $instances.PSObject.Properties) { - if ($instance.Name -eq $InstanceName) { - $instanceID = $instance.Value - $rootKeyPath = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceID\MSSQLServer\SuperSocketNetLib\Tcp" - $ipKeys = Get-ChildItem $rootKeyPath -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'IP[0-9]+$' } - - $anyIPEnabled = $false - foreach ($ipKey in $ipKeys) { - $ipEnabled = (Get-ItemProperty -Path $ipKey.PSPath -Name "Enabled").Enabled - if ($ipEnabled -eq '1') { - $anyIPEnabled = $true - $tcpKeyPath = $ipKey.PSPath - if ($PSCmdlet.ShouldProcess("$tcpKeyPath (Enabled IP)", "Set TCP Port")) { - Set-ItemProperty -Path $tcpKeyPath -Name "TcpPort" -Value "$Port" - Set-ItemProperty -Path $tcpKeyPath -Name "TcpDynamicPorts" -Value "" - } - } - } - - if (-not $anyIPEnabled) { - throw "No enabled IP addresses found for instance $InstanceName. Enable TCP/IP in SQL Server Configuration Manager." - } - - $serviceName = "MSSQL`$$InstanceName" - if ($PSCmdlet.ShouldProcess("$serviceName service", 'Restart')) { - Restart-Service -Name $serviceName -Force - } - } -} diff --git a/File System/Compare-Folder.ps1 b/File System/Compare-Folder.ps1 deleted file mode 100644 index 000050c..0000000 --- a/File System/Compare-Folder.ps1 +++ /dev/null @@ -1,91 +0,0 @@ -<# -.SYNOPSIS -Compares two folders and identifies files that are missing or have mismatched hashes. - -.DESCRIPTION -This script analyzes two specified folders and provides a detailed report on differences. It identifies: - -* Files present in the Reference Folder but absent in the Difference Folder. -* Files present in the Difference Folder but absent in the Reference Folder. -* Files with the same name in both folders but different content (hash mismatch). - -The script allows for an optional ExcludeFilePath parameter to disregard a specific file during comparison. - -.PARAMETER ReferenceFolder -The path to the reference folder used as the baseline for comparison. This parameter is mandatory. - -.PARAMETER DifferenceFolder -The path to the folder that will be compared against the reference folder. This parameter is mandatory. - -.PARAMETER ExcludeFilePath -An optional path (relative to the root of the folders) to a file that should be excluded from the comparison. The path must start with a backslash (\). - -.EXAMPLE -Compare folders C:\Source and C:\Target, excluding the file "temp.log" - -.\Compare-Folder.ps1 -ReferenceFolder "C:\Source" -DifferenceFolder "C:\Target" -ExcludeFilePath "\temp.log" - -.EXAMPLE -Compare folders C:\Documents and D:\Backup - -.\Compare-Folder.ps1 -ReferenceFolder "C:\Documents" -DifferenceFolder "D:\Backup" - -.NOTES - - The script uses the Get-FileHash cmdlet to calculate file hashes. - - Ensure you have the necessary permissions to access both the reference and difference folders. -#> - -[CmdletBinding()] -param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [ValidateScript({ Test-Path -Path $_ -PathType Container })] - [string]$ReferenceFolder, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [ValidateScript({ Test-Path -Path $_ -PathType Container })] - [string]$DifferenceFolder, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [ValidatePattern('^\\')] - [string]$ExcludeFilePath -) - -function Get-FileHashesInFolder { - param ( - [string]$Folder - ) - $files = Get-ChildItem -Path $Folder -Recurse -File - foreach ($s in $files) { - $selectObjects = @('Hash', @{ n = 'Path'; e = { $_.Path.SubString($Folder.Length) } }) - Get-FileHash $s.Fullname | Select-Object $selectObjects -ExcludeProperty Path - } -} - -$refHashes = Get-FileHashesInFolder -Folder $ReferenceFolder -$destHashes = Get-FileHashesInFolder -Folder $DifferenceFolder -if ($PSBoundParameters.ContainsKey('ExcludeFilePath')) { - $refHashes = $refHashes.Where({ $_.Path -ne $ExcludeFilePath }) - $destHashes = $destHashes.Where({ $_.Path -ne $ExcludeFilePath }) -} - -$refHashes.Where({ $_.Path -notin $destHashes.Path }).ForEach({ - [pscustomobject]@{ - 'Path' = $_.Path - 'Reason' = 'NotInDifferenceFolder' - } - }) -$destHashes.Where({ $_.Path -notin $refHashes.Path }).ForEach({ - [pscustomobject]@{ - 'Path' = $_.Path - 'Reason' = 'NotInReferenceFolder' - } - }) -$refHashes.Where({ $_.Hash -notin $destHashes.Hash -and $_.Path -in $destHashes.Path }).ForEach({ - [pscustomobject]@{ - 'Path' = $_.Path - 'Reason' = 'HashDifferent' - } - }) diff --git a/File System/Get-SmbSharePermission.ps1 b/File System/Get-SmbSharePermission.ps1 deleted file mode 100644 index 6615a3f..0000000 --- a/File System/Get-SmbSharePermission.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -<# -.SYNOPSIS -Retrieves and formats the access control list (ACL) of a Windows file share. - -.DESCRIPTION -This script checks if the specified file share exists. If it does, it retrieves the share's -security descriptor and enumerates the access control entries (ACEs). For each ACE, it displays -the principal (user or group), domain, and the assigned permission level. - -.PARAMETER Name -The name of the file share to query. - -.EXAMPLE -Get the ACL for the share named "Documents": -.\Get-SmbSharePermission.ps1 -Name "Documents" -#> -[CmdletBinding()] -[OutputType([pscustomobject])] -param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Name -) - -# Verify that the share exists -$existingShare = Get-SmbShare -Name $Name -ErrorAction 'SilentlyContinue' -if (-not $existingShare) { - throw "The share [$($Name)] does not exist." -} - -# Define a lookup table for translating access mask values into permission names -$accessRights = @{ - 2032127 = 'Full Control' - 1245631 = 'Change' - 1179817 = 'Read' -} - -# Retrieve the share's security descriptor -$shareParams = @{ - ClassName = 'Win32_LogicalShareSecuritySetting' - Filter = "Name = '$Name'" -} -$shareSecurityDescriptor = (Get-CimInstance @shareParams).GetSecurityDescriptor() - -# Access the DACL (Discretionary Access Control List) -$dacl = $shareSecurityDescriptor.Descriptor.DACL - -# Process each ACE (Access Control Entry) -foreach ($ace in $dacl) { - $trustee = $ace.Trustee - - # Construct a custom output object with Principal, Domain, and Permission - [pscustomobject]@{ - Principal = $trustee.Name -or $trustee.SIDString # Use SID if the user/group name is unavailable - Domain = $trustee.Domain - Permission = $accessRights[$ace.AccessMask] - } -} \ No newline at end of file diff --git a/Name Resolution/Enable-DynamicDnsRegistration.ps1 b/Name Resolution/Enable-DynamicDnsRegistration.ps1 deleted file mode 100644 index ae3c092..0000000 --- a/Name Resolution/Enable-DynamicDnsRegistration.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -<# - .SYNOPSIS - Enables dynamic DNS registration on network adapters that are IP enabled and have a default gateway configured. - - .DESCRIPTION - Configures network adapters on a specified computer to enable dynamic - DNS registration. This setting is particularly useful in environments where IP addresses are dynamically assigned - and network changes are frequent. - - .PARAMETER ComputerName - Specifies the name of the computer on which the network adapters will be configured. This parameter is mandatory. - - .PARAMETER Credential - Specifies the credentials to use when connecting to the remote computer. This parameter is optional. If not provided, - the current user's credentials are used. - - .EXAMPLE - PS> .\Enable-DynamicDnsRegistration.ps1 -ComputerName "Server01" - Enables dynamic DNS registration on all IP-enabled network adapters with a default gateway on the computer named - Server01 using the current user's credentials. - - .EXAMPLE - PS> .\Enable-DynamicDnsRegistration.ps1 -ComputerName "Server01" -Credential (Get-Credential) - Enables dynamic DNS registration on the computer named "Server01", using specified credentials. The command prompts - for credentials. - - .OUTPUTS - None. Outputs are directed to the Verbose stream or error messages are thrown as exceptions. - - #> -[CmdletBinding()] -param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$ComputerName, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [pscredential]$Credential -) - -$icmParams = @{ - ComputerName = $ComputerName -} -if ($PSBoundParameters.ContainsKey('Credential')) { - $icmParams.Credential = $Credential -} - -$icmParams.ScriptBlock = { - Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled='true'" | Where-Object { $_.DefaultIPGateway.Count -gt 0 } | ForEach-Object { - Write-Verbose -Message "Setting DDNS registration..." - $result = $_.SetDynamicDNSRegistration($true) - if ($result.ReturnValue -ne 0) { - throw "Failed to set DDNS registration on with return code [$($result.ReturnValue)]" - } - } -} -Invoke-Command @icmParams \ No newline at end of file diff --git a/Name Resolution/HostsFileEntry.psm1 b/Name Resolution/HostsFileEntry.psm1 deleted file mode 100644 index 00ce01e..0000000 --- a/Name Resolution/HostsFileEntry.psm1 +++ /dev/null @@ -1,233 +0,0 @@ -$script:hostFilePath = "$Env:SystemRoot\System32\drivers\etc\hosts" - -function Get-HostsFileEntry { - <# - .SYNOPSIS - Gets the entries from the hosts file. - - .DESCRIPTION - The Get-HostsFileEntry cmdlet gets the entries from the hosts file. The hosts file is a text file that maps hostnames to - IP addresses. It is typically used to resolve hostnames that cannot be resolved using DNS. - - .PARAMETER HostFilePath - The path to the hosts file. The default value is "$Env:SystemRoot\System32\drivers\etc\hosts". - - .OUTPUTS - The cmdlet outputs an array of custom objects. Each custom object represents a single entry in the hosts file. The - custom object has the following properties: - - * IPAddress: The IP address of the host. - * HostName: The hostname of the host. - * Comment: The comment associated with the host. - - .EXAMPLE - Get the entries from the hosts file: - .\Get-HostsFileEntry.ps1 - - #> - param ( - [Parameter()] - [ValidateNotNullOrEmpty()] - [ValidateScript( - { Test-Path -Path $_.FullName }, - ErrorMessage = "The hosts file at [{0}] could not be found." - )] - [string]$HostFilePath = $script:hostFilePath, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$HostName - ) - - $ErrorActionPreference = 'Stop' - - $regex = '^(?[0-9.]+)\s+(?[^\s#]+)(\s*#\s*(?.*))?$' - - $whereFilter = '$_' - if ($PSBoundParameters.ContainsKey('HostName')) { - $whereFilter = "`$_.HostName -eq '$Hostname'" - } - $whereFilter = [scriptblock]::Create($whereFilter) - - Get-Content -Path $HostFilePath | ForEach-Object { - if ($_ -match $regex) { - [pscustomobject]@{ - IPAddress = $matches['ipAddress'] - HostName = $matches['hostname'] - Comment = $matches['comment'] - } - } - } | Where-Object -FilterScript $whereFilter -} - -function Remove-HostsFileEntry { - <# - .SYNOPSIS - Removes an entry from the hosts file. - - .DESCRIPTION - The Remove-HostsFileEntry cmdlet removes an entry from the hosts file based on the specified hostname. It is designed - to safely modify the hosts file by utilizing a temporary file during the deletion process to prevent data loss. - - .PARAMETER HostName - Specifies the hostname of the entry to remove from the hosts file. This parameter is mandatory. - - .PARAMETER HostFilePath - Specifies the path to the hosts file. The default is set to a script-scoped variable ($script:hostFilePath) which - is predefined in the module. - - .EXAMPLE - Remove-HostsFileEntry -HostName "examplehost" - - This example removes the entry for 'examplehost' from the default hosts file. - - .EXAMPLE - Remove-HostsFileEntry -HostName "testhost" -HostFilePath "C:\Windows\System32\drivers\etc\hosts" - - This example demonstrates how to specify both the hostname and the path of the hosts file from which to remove an entry. - - .NOTES - Ensure that the PowerShell session running this cmdlet has adequate permissions to modify the hosts file, typically - requiring administrative rights. - #> - [CmdletBinding()] - param ( - [Parameter(Mandatory, ValueFromPipelineByPropertyName)] - [ValidateNotNullOrEmpty()] - [string]$HostName, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$HostFilePath = $script:hostFilePath - ) - - $regex = "^(?[0-9.]+)[^\w]*($HostName)(`$|[\W]{0,}#\s+(?.*))" - $toRemove = (Get-Content -Path $HostFilePath | Select-String -Pattern $regex).Line - - # Safer to create a temp file. - $tempFile = "$Env:SystemRoot\System32\drivers\etc\hosts.temp" - (Get-Content -Path $HostFilePath | Where-Object { $_ -ne $toRemove }) | Add-Content -Path $tempFile - if (Test-Path -Path $tempFile -PathType Leaf) { - Remove-Item -Path $HostFilePath - Move-Item -Path $tempFile -Destination $HostFilePath - } else { - throw 'Failed to create temp hosts file to make the change' - } -} - -function Set-HostsFileEntry { - <# - .SYNOPSIS - Sets or updates an entry in the hosts file. - - .DESCRIPTION - The Set-HostsFileEntry cmdlet adds or updates an entry in the hosts file for a specified hostname and IP address. - If the hostname exists but with a different IP address, the existing entry is updated. This cmdlet also handles - fully qualified domain names (FQDNs) by resolving them to IP addresses if no IP address is provided. - - .PARAMETER HostName - Specifies the hostname for the entry in the hosts file. This parameter is mandatory. - - .PARAMETER IPAddress - Specifies the IP address for the hostname. If not provided and the hostname is a FQDN, the cmdlet attempts to resolve - the IP address. - - .PARAMETER Comment - Allows adding a comment for the hosts file entry. This is optional and can be used for documentation or clarification - purposes. - - .PARAMETER HostFilePath - Specifies the path to the hosts file. The default is set to a script-scoped variable ($script:hostFilePath), which - is provided in the module. - - .EXAMPLE - Set-HostsFileEntry -HostName "example.com" -IPAddress "192.168.1.1" - - This example adds an entry to the hosts file linking "example.com" with the IP address "192.168.1.1". - - .EXAMPLE - Set-HostsFileEntry -HostName "example.com" -IPAddress "192.168.1.1" -Comment "Test server" - - This example does the same as the previous one but includes a comment "Test server" for the entry. - - .EXAMPLE - Set-HostsFileEntry -HostName "example.com" - - If "example.com" is a FQDN and no IP address is provided, this example tries to resolve "example.com" and adds the - resulting IP address to the hosts file. - - .NOTES - Ensure that you have the necessary administrative rights to modify the hosts file, as this operation typically - requires elevated privileges. - - #> - - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$HostName, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [ipaddress]$IPAddress, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$Comment, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [string]$HostFilePath = $script:hostFilePath - ) - - $ErrorActionPreference = 'Stop' - - $isFqdn = $false - if ($PSBoundParameters.HostName -match '\.+') { - $isFqdn = $true - $shortHostName = $HostName.Split('.')[0].ToUpper() - } else { - $HostName = $HostName.ToUpper() - } - - $vals = @() - ## If a FQDN was passed, grab the hostname and IP address that resolves and use that - if ($isFqdn) { - if (-not $PSBoundParameters.ContainsKey('IPAddress')) { - $ip = (Resolve-DnsName -Name $HostName -DnsOnly -ErrorAction SilentlyContinue).IPAddress - if (-not $ip) { - throw 'No IP address provided and hostname could not be resolved' - } - $HostName = $shortHostName - $IPAddress = $ip - } - } elseif (-not $PSBoundParameters.ContainsKey('IPAddress')) { - throw 'Hostname is not a FQDN and no IP address provided.' - } - - $vals += $IPAddress - $vals += $HostName - - if ($PSBoundParameters.ContainsKey('Comment')) { - $vals += "# $Comment" - } - - $existingEntry = Get-HostsFileEntry -HostName $HostName - if ($existingEntry) { - if ($existingEntry.IPAddress -eq $IPAddress) { - Write-Verbose "The hostname '$HostName' already exists on computer $ComputerName" - return - } else { - Write-Verbose "Removing hostname '$HostName' on computer $ComputerName' because IP address does not match" - $null = Remove-HostsFileEntry -HostName $HostName - } - } - - ## If the hosts file doesn't end with a blank line, make it so - if ((Get-Content -Path $HostFilePath -Raw) -notmatch '\n$') { - Add-Content -Path $HostFilePath -Value '' - } - - Add-Content -Path $HostFilePath -Value ($vals -join "`t") -} \ No newline at end of file diff --git a/Name Resolution/Set-DnsClientPrimaryAdapterServerAddress.ps1 b/Name Resolution/Set-DnsClientPrimaryAdapterServerAddress.ps1 deleted file mode 100644 index 88e9152..0000000 --- a/Name Resolution/Set-DnsClientPrimaryAdapterServerAddress.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -<# -.SYNOPSIS -Updates the DNS server search order on network adapters. - -.DESCRIPTION -This script checks network adapters that have a default gateway and are IP enabled. -It updates the DNS server search order if the current order differs from the specified addresses. - -.PARAMETER ServerAddress -A mandatory array of DNS server IP addresses (in string format) to be set as the new search order. - -.EXAMPLE -Update DNS settings using two DNS servers: -.\Set-DnsClientPrimaryAdapterServerAddress.ps1 -ServerAddress "10.0.0.10", "10.0.0.20" -#> - -[CmdletBinding()] -param ( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string[]]$ServerAddress -) - -Get-CimInstance -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled='true' AND DefaultIPGateway[0]" | - Where-Object { $_.DNSServerSearchOrder -notmatch ($ServerAddress -join ',') } | # Filter for adapters needing updates - ForEach-Object { - Write-Verbose "Changing DNS server search order to '$($ServerAddress -join ',')'" - - $setDnsResult = $_.SetDNSServerSearchOrder($ServerAddress) # Update the DNS search order - if ($setDnsResult.ReturnValue -notin @(0, 1)) { # Basic error checking - throw "Error setting DNS server search order: [$setDnsResult]" - } - } diff --git a/Networking/Send-FtpFile.ps1 b/Networking/Send-FtpFile.ps1 deleted file mode 100644 index 63fb830..0000000 --- a/Networking/Send-FtpFile.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -<# -.SYNOPSIS -Uploads a file to an FTP server. - -.DESCRIPTION -This cmdlet uploads a specified file to a given FTP server. It requires FTP server address, the local file path, and user credentials for authentication. - -.PARAMETER Uri -The FTP server address (e.g., ftp://myftpserver.com) to upload the file to. - -.PARAMETER FilePath -The full path to the local file to be uploaded. - -.PARAMETER Credential -A PSCredential object containing the username and password for FTP authentication. -You can create this using the Get-Credential cmdlet. - -.EXAMPLE -Upload a file named report.txt to an FTP server, using stored credentials: - -$myCredentials = Get-Credential -.\Send-FtpFile.ps1 -Uri "ftp://myftpserver.com/uploads/" -FilePath "C:\reports\report.txt" -Credential $myCredentials - -.NOTES -- This cmdlet uses passive FTP mode, which is often required for compatibility with firewalls. -- The file is uploaded in binary mode to prevent any unwanted text conversions. -#> - -[OutputType('void')] -[CmdletBinding()] -param -( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$Uri, - - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$FilePath, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [pscredential]$Credential -) - -try { - $request = [System.Net.FtpWebRequest]::Create($Uri) # Initiate communication with the FTP server - - $request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile # Specify that we're uploading a file - $request.Credentials = New-Object System.Net.NetworkCredential($Credential.UserName, $Credential.GetNetworkCredential().Password) # Provide authentication for access - $request.UseBinary = $true # Ensures accurate file transfer without text conversion - $request.UsePassive = $true # Improves compatibility through firewalls - - $fileContent = Get-Content -Encoding Byte -Path $FilePath # Fetch file data in a suitable format for transfer - - $request.ContentLength = $fileContent.Length # Communicate the expected file size to the server - - $run = $request.GetRequestStream() # Open a channel to send the file content - $run.Write($fileContent, 0, $fileContent.Length) # Transmit the file data -} catch { - throw $_ # Propagate critical errors for proper handling -} finally { - $run.Close() # Properly terminate the data channel - $run.Dispose() # Release resources for efficiency -} \ No newline at end of file diff --git a/Security/New-RandomPassword.ps1 b/Security/New-RandomPassword.ps1 deleted file mode 100644 index bb855b1..0000000 --- a/Security/New-RandomPassword.ps1 +++ /dev/null @@ -1,56 +0,0 @@ - -<# - .SYNOPSIS - Generates a secure random password containing a mix of uppercase, lowercase, numbers, and symbols. - - .DESCRIPTION - Generates a random password of a specified length. The password includes a mix of - uppercase letters, lowercase letters, numbers, and special characters. This function uses cryptographic random number - generation to ensure that the password is generated with a high degree of randomness suitable for secure applications. - - .PARAMETER Length - Specifies the length of the password to generate. The default length is 12 characters. - - .EXAMPLE - PS> New-RandomPassword -Length 16 - This example generates a 16-character long random password. - - .EXAMPLE - PS> New-RandomPassword - This example generates a random password using the default length of 12 characters. - - .OUTPUTS - System.Security.SecureString - The function returns the password as a SecureString to ensure that it is not displayed in plain text or stored in - memory as a regular string. - #> -[CmdletBinding()] -param( - [int]$Length = 12 -) - -# Define character sets -$lowercase = 'abcdefghijklmnopqrstuvwxyz' -$uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -$numbers = '0123456789' -$symbols = '!@#$%^&*()_-+=[]{}|;:,.<>?' - -# Combine all character sets -$charSet = $lowercase + $uppercase + $numbers + $symbols - -# Create an array to hold the password characters -$passwordChars = New-Object char[] $Length - -# Random number generator -$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create() - -# Generate password -for ($i = 0; $i -lt $Length; $i++) { - $byte = New-Object byte[] 1 - $rng.GetBytes($byte) - $randomIndex = $byte[0] % $charSet.Length - $passwordChars[$i] = $charSet[$randomIndex] -} - -# Convert the password to a secure string so we don't put plain text passwords on the pipeline. -ConvertTo-SecureString -String (-join $passwordChars) -AsPlainText -Force \ No newline at end of file