Powershell scripting check and change location help needed - powershell

I have written the following script:
clear-host
## Step 1 (Creating 2 command line variables)
##########################################################################################################################
Clear-Host
$WorkingDirectory = $ARGS[0]
$DirectoryName = $ARGS[1]
##Step 2 (Check if variables are empty and get user input if necessary)
##########################################################################################################################
if ("$WorkingDirectory" -eq "")
{
Write-Warning "Parameter Required"
$WorkingDirectory = Read-Host "Enter the absolute path to working directory "
}
if ("$DirectoryName" -eq "")
{
Write-Warning "Paramater Required"
$DirectoryName = Read-Host "Enter a directory name to search for in $WorkingDirectory "
}
for ($i=0; $i -le 2; $i++) {write-host ""} # to print 2 blank lines using for loop
##Step 3 (Test to see if PWD is equal to $WorkingDirectory and if not move the location )
##########################################################################################################################
if ("$PWD" -ne "$WorkinDirectory")
{
write-host "You are not in the < $WorkingDirectory > . Do you wish to move? Press CTRL+C to exit or wait"
write-host ""
pause
Set-Location -Path $WorkingDirectory
}
##Step 4 (Test to see the directory exists in the working directory or not )
##########################################################################################################################
if((Test-Path $DirectoryName) -ne "True")
{
write-host "Directory $DirectoryName does not exist"
}
The problem I am facing at step 3. It should check my present location with the $WorkingDirectory variable, if they are not same then the location will be changed. But the problem is even if I am at the same location as the $WorkingDirectory it is still giving me the warning message. how to solve that

Related

PowerShell error "The argument is null or empty."

I am trying to create a PowerShell script that uses the below code. I believe that the projects folder having spaces is causing my error but I am unable to come up with a solution. I have tried numerous things from different stackoverflow solutions with no success. I am not a PowerShell guy so I know it is something simple (maybe). Anyone help me out there...
Script code:
Write-Host ""
$projectsFolder = Read-Host **'E:\Work - VS.NET 2022'**
Write-Host ""
Write-Host "Adding Path Exclusion: " $projectsFolder
Add-MpPreference -ExclusionPath $projectsFolder
foreach ($exclusion in $pathExclusions)
{
Write-Host "Adding Path Exclusion: " $exclusion
Add-MpPreference -ExclusionPath $exclusion
}
Error:
Adding Path Exclusion: Add-MpPreference : Cannot validate argument
on parameter 'ExclusionPath'. The argument is null or empty. Provide
an argument that is not null or empty, and then try the command
again. At E:\Work - VS.NET 2022\Tools\Visual
Studio\Windows_Defender_Exclusions_VS_2022.ps1:54 char:33
+ Add-MpPreference -ExclusionPath $projectsFolder
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Add-MpPreference], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Add-MpPreference
I do not think you want to read host. I think you want to get the content of a file. Your code will be like the bottom but I also added a simple read-host for you to review.
==========read-host================
$YesNoResponse = $null
while ($YesNoResponse -ne "y" -and $YesNoResponse -ne "n") {
Clear-Host
write-host “Yes or No?”
Write-Host "Please confirm."
$YesNoResponse = Read-Host " [Y/N] "
If ($YesNoResponse -eq "") {
Write-Host "Enter was inputted"
Return $null
}
}
I think you are trying to do something like this. If you need something else please expand.
Write-Host ""
$projectsFolder = Import-Csv 'E:\Work - VS.NET 2022'
Write-Host ""
Write-Host "Adding Path Exclusion: "
$projectsFolder = Add-MpPreference -ExclusionPath $projectsFolder
foreach ($exclusion in $pathExclusions) {
Write-Host "Adding Path Exclusion: "
$exclusion = Add-MpPreference -ExclusionPath $exclusion }
The problem in your script is that the array you use for your loop ($pathExclusions) is not created and thus is empty, resulting in $exclusion variable being always empty/null... which explain your error message.
If the goal is to prompt the user to enter manually a list of directories to be processed, then you can do something like that:
# Create the exclusions array (of strings)
$pathExclusions = #()
Write-Host ""
$projectsFolder = Read-Host **'E:\Work - VS.NET 2022'**
# Press Enter at read-host prompt to exit the loop
while ($projectsFolder -ne "") {
$pathExclusions += $projectsFolder
$projectsFolder = Read-Host **'E:\Work - VS.NET 2022'**
}
Write-Host ""
# Process each item in the exclusions array
# Loop not executed if $pathExclusions is empty so no error message
foreach ($exclusion in $pathExclusions) {
Write-Host "Adding Path Exclusion: " $exclusion
Add-MpPreference -ExclusionPath $exclusion
}
If the goal is to get the list of directories from a file, then just populate the exclusions array by getting the file content instead of the Read-Host loop:
$pathExclusions = Get-Content -Path "c:\temp\exclusionsfile.txt"

selecting multiple options from the menu list

Set-ExecutionPolicy Bypass -Scope Process -Force;
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
}
# Step 2) define the array of packages you are offering
$Packages = 'googlechrome',
'firefox',
'codeblocks',
'windbg',
'nasm',
'explorersuite',
'pestudio',
'vscode',
'sysinternals',
'python',
'ccleaner',
'anaconda3',
'wireshark',
'sublimetext3',
'notepadplusplus',
'Exit'
# Step 3) define the Show-Menu function
function Show-Menu
{
Clear-Host
Write-Host "**********************************************"
Write-Host "LIST OF SOFTWARES"
# write the options using the array of packages
for ($i = 0; $i -lt $Packages.Count; $i++)
{
# {0,10} means right align with spaces to max 2 characters
Write-Host ('{0,10}. {1}' -f ($i + 1), $Packages[$i])
}
Write-Host " q. Exit the script"
Write-Host "*************************************************"
Write-Host
}
# Step 4) enter an endless loop you only exit if the user enters 'q'
while ($true)
{
Show-Menu
# $UserInput = Read-Host "Enter the software number to be installed"
$UserInput = Read-Host "Select the softwares number(s) to be installed"
$ok = $UserInput -match '[123456789101112131415]+$'
if( -not $ok)
{
write-host "Invalid selection"
sleep 2
write-host ""
}
until ($ok)
switch -Regex ($UserInput)
{
"1" {googlechrome}
"2" {firefox}
"3" {codeblocks}
"4" {windbg}
"5" {nasm}
"6" {explorersuite}
"7" {pestudio}
"8" {vscode}
"9" {sysinternals}
"10" {python}
"11" {ccleaner}
"12" {anaconda3}
"13" {wireshark}
"14" {sublimetext3}
"15" {notepadplusplus}
} until ($ok)
# test if the user wants to quit and if so, break the loop
if ($UserInput -eq 'q') { break }
# test if the user entered a number between 1 and the total number of packages (inclusive)
if ([int]::TryParse($UserInput,[ref]$null) -and 1..$Packages.Count -contains [int]$UserInput)
{
# here you install the chosen package using the array index number (= user input number minus 1)
$packageIndex = [int]$UserInput - 1
Write-Host "Installing $($Packages[$packageIndex])"
# Choco install $Packages[$packageIndex] -y
Choco install $Packages[$packageIndex] -y --ignore-checksums
}
else
{
$availableOptions = 1..$Packages.Count -join ','
Write-Host "Error in selection, choose $availableOptions or q" -Foreground Color Red
}
$null = Read-Host "Press Enter to continue"
}
I have written the script which is working when the user select the number then that corresponding software will be downloaded and installed. Now instead of selecting one option the user selects multiple options from the menu list then that software's will be downloaded and installed parallely so, I have modified my script for selecting multiple options but the script is not working so, please tell me how to achieve this functionality. Thanks in Advance
I've removed the parts which (for me) made no sense or produced errors in my environment - removed the option 'Exit' from $Packages and added the foreach part. So now the user can enter several numbers space separated so that $UserInput contains e.g. 1 2. this is then split an iterated in the foreach part.
Set-ExecutionPolicy Bypass -Scope Process -Force;
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Step 2) define the array of packages you are offering
$Packages = 'googlechrome',
'firefox',
'codeblocks',
'windbg',
'nasm',
'explorersuite',
'pestudio',
'vscode',
'sysinternals',
'python',
'ccleaner',
'anaconda3',
'wireshark',
'sublimetext3',
'notepadplusplus'
# Step 3) define the Show-Menu function
function Show-Menu
{
Clear-Host
Write-Host "**********************************************"
Write-Host "LIST OF SOFTWARES"
# write the options using the array of packages
for ($i = 0; $i -lt $Packages.Count; $i++)
{
# {0,10} means right align with spaces to max 2 characters
Write-Host ('{0,10}. {1}' -f ($i + 1), $Packages[$i])
}
Write-Host " q. Exit the script"
Write-Host "*************************************************"
Write-Host
}
# Step 4) enter an endless loop you only exit if the user enters 'q'
while ($true)
{
Show-Menu
$UserInput = Read-Host "Select the softwares number(s) to be installed (space separated)"
# test if the user wants to quit and if so, break the loop
if ($UserInput -eq 'q') { break }
foreach($input in $UserInput.Split(' ')) {
# test if the user entered a number between 1 and the total number of packages (inclusive)
if ([int]::TryParse($input,[ref]$null) -and 1..$Packages.Count -contains [int]$input)
{
# here you install the chosen package using the array index number (= user input number minus 1)
$packageIndex = [int]$input - 1
Write-Host "Installing $($Packages[$packageIndex])"
Choco install $Packages[$packageIndex] -y --ignore-checksums
} else {
$availableOptions = 1..$Packages.Count -join ','
Write-Host "Error in selection, choose $availableOptions or q" -Foreground Color Red
}
}
$null = Read-Host "Press Enter to continue"
}

Enviroment Paths without overwriting String

I would like to ask question about how I should proceed or how I should fix the code.
My problem is that I need my code to write into the Path three different paths for Logstash, Kibana and ElasticSearch, but I have no idea how to do it. It returns always the same error about missing ")" error
Here's the whole code ¨
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[string]$NewLocation.GetType($ElasticSearch)
[string]$ElasticSearch = "C:\Elastic_Test_Server\elasticsearch\bin"
[string]$Kibana = "C:\Elastic_Test_Server\kibana\bin"
[string]$Logstash = "C:\Elastic_Test_Server\logstash\bin"
)
Begin
{
#Je potřeba spustit jako Administrátor
$regPath = "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
$hklm = [Microsoft.Win32.Registry]::LocalMachine
Function GetOldPath()
{
$regKey = $hklm.OpenSubKey($regPath, $FALSE)
$envpath = $regKey.GetValue("Path", "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
return $envPath
}
}
Process
{
# Win32API errory
$ERROR_SUCCESS = 0
$ERROR_DUP_NAME = 34
$ERROR_INVALID_DATA = 13
$NewLocation = $NewLocation.Trim();
If ($NewLocation -eq "" -or $NewLocation -eq $null)
{
Exit $ERROR_INVALID_DATA
}
[string]$oldPath = GetOldPath
Write-Verbose "Old Path: $oldPath"
# Zkontroluje zda cesta již existuje
$parts = $oldPath.split(";")
If ($parts -contains $NewLocation)
{
Write-Warning "The new location is already in the path"
Exit $ERROR_DUP_NAME
}
# Nová cesta
$newPath = $oldPath + ";" + $NewLocation
$newPath = $newPath -replace ";;",""
if ($pscmdlet.ShouldProcess("%Path%", "Add $NewLocation")){
# Přidá to přítomné session
$env:path += ";$NewLocation"
# Uloží do registru
$regKey = $hklm.OpenSubKey($regPath, $True)
$regKey.SetValue("Path", $newPath, [Microsoft.Win32.RegistryValueKind]::ExpandString)
Write-Output "The operation completed successfully."
}
Exit $ERROR_SUCCESS
}
Thank you for your help.
I really think you could simplify this a lot, unless I have misunderstood. Apologies, I am not currently on a Windows machine so can't test this.
function Add-ESPath {
# Create an array of the paths we wish to add.
$ElasticSearch = #(
"C:\Elastic_Test_Server\elasticsearch\bin",
"C:\Elastic_Test_Server\kibana\bin",
"C:\Elastic_Test_Server\logstash\bin"
)
# Collect the current PATH string and split it out in to an array
$CurrentPath = [System.Environment]::GetEnvironmentVariable("PATH")
$PathArray = $CurrentPath -split ";"
# Loop though the paths we wish to add.
foreach ($Item in $ElasticSearch) {
if ($PathArray -notcontains $Item) {
$PathArray += $Item
}
else {
Write-Output -Message "$Item is already a member of the path." # Use Write-Warning if you wish. I see it more as a notification here.
}
}
# Set the path.
$PathString = $PathArray -join ";"
Try {
[System.Environment]::SetEnvironmentVariable("PATH", $PathString)
exit 0
}
Catch {
Write-Warning -Message "There was an issue setting PATH on this machine. The path was:" # Use $env:COMPUTERNAME here perhaps instead of 'this machine'.
Write-Warning -Message $PathString
Write-Warning -Message $_.Exception.Message
exit 1
}
}
Add-ESPath
Perhaps you want to add some kind of log file rather than writing messages/warnings to the console. You can use Add-Content for this.
I long time ago i wrote some functions to add a path to system path + their is an check if the path is already inside the system path. And i also did an elevation check so when i use this function and i forgot to elevate my powershell that i get a warning. Its a different approach, I hope it will help you.
I only use the begin {} proccess{} statements for when i want to write a function that excepts pipeline inputs. So its if you want to write a function that will work as the following:
$paths = #("C:\Elastic_Test_Server\elasticsearch\bin", "C:\Elastic_Test_Server\kibana\bin")
$paths | my-append-these-to-system-path-function
Elevation check:
function G-AmIelevated($warningMessage){
if([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544")){
return $true
}else{
write-host "not elevated $warningMessage" -ForegroundColor Red
return $false
}
}
append something to system path with check if its already inside system path:
function G-appendSystemEnvironmentPath($str){
if(test-path $str){
if(!((Get-Itemproperty -path 'hklm:\system\currentcontrolset\control\session manager\environment' -Name Path) -like "*$str*")){
write-host "`t $str exists...`n adding $str to environmentPath" -ForegroundColor Yellow
if(G-AmIelevated){
write-host `t old: (Get-Itemproperty -path 'hklm:\system\currentcontrolset\control\session manager\environment' -Name Path).Path
Set-ItemProperty -path 'hklm:\system\currentcontrolset\control\session manager\environment' `
-Name Path `
-Value "$((Get-Itemproperty -path 'hklm:\system\currentcontrolset\control\session manager\environment' -Name Path).Path);$str"
write-host `t new: (Get-Itemproperty -path 'hklm:\system\currentcontrolset\control\session manager\environment' -Name Path).Path
write-host `t restart the computer for the changes to take effect -ForegroundColor Red
write-host `t `$Env:Path is the merge of System Path and User Path This function set the system path
write-host `t $str appended to environmet variables. -ForegroundColor Green
}else{
write-host `t rerun ise in elevated mode -ForegroundColor Red
}
}else{
write-host "`t $str is in system environmenth path"
}
}else{
write-host `t $str does not exist
}
}
G-appendSystemEnvironmentPath -str "C:\Elastic_Test_Server\elasticsearch\bin"
G-appendSystemEnvironmentPath -str "C:\Elastic_Test_Server\kibana\bin"
G-appendSystemEnvironmentPath -str "C:\Elastic_Test_Server\logstash\bin"

How to count columns for every file after unzipping a folder using powershell

I am trying to unzip a folder and take the count of the files that exist in the folder and check the file count =32 else send an email with present file names and execute the infacmds for present files and wait for 32 conditions to satisfy.
In this script I want to check the file count and also the column count for the existing files and if the column count -neq 280 it has to send email error.
where the column code is like this, which i have to introduce in main code :
$columnCount = ( ( Get-Content "C:\Users\xs15169\Desktop\temp\OEC2_CFLOW.txt" | Select-Object -First 1 ) -Split ',' ).Count
echo "Column count is: $columnCount"
The below code which I have tried(#looks for a file with below type format .zip and it will unzip the files and will take count):
#unzip commands will be here
#LogWrite "Starting of process"
Function LogWrite
{
Param ([string]$logstring)
Add-content $log -value "$(Get-Date) $logstring"
}
try {
LogWrite "Start unzipping $zipfilename"
Remove-Item $unzipfolderdelete | Where { ! $_.PSIsContainer }
#Unzip $zipfilename $unzipfolder
Set-Alias sz "C:\Program Files\7-Zip\7z.exe"
sz x $zipfilename -o"$unzipfolder" -y
LogWrite "$zipfilename unzipped to $unzipfolder successfully"
$filecount= (dir $unzipfolder | measure).Count
if ($filecount -eq 32)
{
#LogWrite "File Count 32
#LogWrite "Executed informatica scripts for condition filecount equal to 32"
#LogWrite "End of if"
}
elseif ($filecount -ne 0)
{
LogWrite "File Count " + $filecount
$list = Get-ChildItem $unzipfolder
$incompletefilenames =""
ForEach($n in $list){
$incompletefilenames = $incompletefilenames + $n.Name + "<br>`n"
}
LogWrite $incompletefilenames
LogWrite "Sending email for filecount not equal to 32"
$Params = #{
email setup
}
Send-MailMessage #Params
LogWrite "Successfully emailed available file names"
#Execute Informatica scripts
LogWrite "Executed informatica scripts for condition filecount not equal to 32"
LogWrite "End of elseif"
}
else
{
LogWrite "File Count " + $filecount
$Params = #{
email setup
}
Send-MailMessage #Params
LogWrite "End of else"
}
}
To do this you must have some paths figured out.
Try this:
Set-Alias sz "C:\Program Files\7-Zip\7z.exe"
$zipFile = "C:\temp\10 yard Fight.zip"
# Get Name of file without extension or path
$zipFolderToCreate = (gci $zipFile).BaseName
# Get path of zip
$path = (gci $zipFile).Directory.FullName
sz x -o"$path\*" "$zipFile" -r
$filecount= (dir "$path\$zipFolderToCreate" | measure).Count

Using Read-host with Input validation and TRAP

I'm just a typical admin trying to make a simple script for some IT assistants in remote offices, to make domain joins easier while minimizing potential errors. The script's end game is to run the one-liner command Add-Computer -DomainName $DomainToJoin -OUPath $LocDN -NewName $WS_NewName -Restart.
But the whole script is supposed to include input validation for the computer's new name as well as for the target OU for the two-letter code for the remote office.
Googling for code snippets for days, esp. from sites like yours, was very helpful. But the problem I have now is I couldn't find the right codes to combine Read-Host , input length validation, and TRAP to work together without losing the value for my variables.
Pardon my coding as obviously I'm no real PS scripter, and I know the wrong portions of it are very basic. I would want to spend more time if I had the luxury, but I would really so much appreciate it if you could point me in the right direction.
Thank you so much in advance.
Please see my code below:
# START: Display name and purpose of invoked script
$path = $MyInvocation.MyCommand.Definition
Clear-Host
Write-Host $path
Write-Host " "
Write-Host "This script will allow you to do the following in a single-step process:"
Write-Host "(1) RENAME this computer"
Write-Host "(2) JOIN it to MYDOMAIN"
Write-Host "(3) MOVE it to a target OU"
Write-Host "(4) REBOOT"
Write-Host " "
Pause
# Function: PAUSE
Function Pause ($Message = "Press any key to continue . . . ") {
if ((Test-Path variable:psISE) -and $psISE) {
$Shell = New-Object -ComObject "WScript.Shell"
$Button = $Shell.Popup("Click OK to continue.", 0, "Script Paused", 0)
}
else {
Write-Host -NoNewline $Message
[void][System.Console]::ReadKey($true)
Write-Host
}
Write-Host " "
}
# Function: Define the parameters
Function Define-Parameters {
# Specify new computer name, with validation and TRAP
$WS_NewName = $null
while ($null -eq $WS_NewName) {
[ValidateLength(8,15)]$WS_NewName = [string](Read-Host -Prompt "NEW NAME of computer (8-15 chars.)" )
TRAP {"" ;continue}
}
Write-Host " "
# Domain to join.
$DomainToJoin = 'mydomain.net'
# Specify the target OU, with validation and trap
$baseOU='OU=Offices OU,DC=mydomain,DC=net'
$OU2 = $null
while ($null -eq $OU2) {
[ValidateLength(2,2)]$OU2 = [string](Read-Host -Prompt 'Target OU (TWO-LETTER code for your office)' )
TRAP {"" ;continue}
}
Write-Host " "
$LocDN = "OU=$OU2,$baseOU"
}
# Function: Summary and confirmation screen for defined parameters.
Function Confirm-Parameters {
Write-Host "==========================================================================="
Write-Host "Please confirm that you are joining this computer to
$DomainToJoin (MYDOMAIN)"
Write-Host "with the following parameters:"
Write-Host ""
Write-Host ""
Write-Host "Computer's NEW NAME: $WS_NewName"
# Write-Host "Domain to Join: $DomainToJoin"
Write-Host "TARGET mission OU: $OU2"
}
# Call Define-Parameters Function
Define-Parameters
# Call Confirm-Parameters Function
Confirm-Parameters
<#
Some more code here
#>
# FINAL COMMAND if all else works: Join the computer to the domain, rename it, and restart it.
# Add-Computer -DomainName $DomainToJoin -OUPath $LocDN -NewName $WS_NewName -Restart
In your code, you have a lot of things defined very strangely. Your functions create a new scope and the variables you're trying to define therein will disappear after calling them unless you change the variable scope (in this case, to $script: or $global:). Also, to use functions, you need to define them first (your Pause doesn't actually do anything)
Here's something you can do with your Define-Parameters function (I suggest looking at Get-Verb)
# Domain to join.
$DomainToJoin = 'mydomain.net'
# Function: Define the parameters
Function Get-Parameters {
do {
$global:WS_NewName = Read-Host -Prompt 'NEW NAME of computer (8-15 chars)'
} until ($WS_NewName.Length -gt 7 -and $WS_NewName.Length -lt 16)
''
do {
$global:OU2 = Read-Host -Prompt 'Target OU (TWO-LETTER code for your office)'
} until ($OU2 -match '^[a-z]{2}$')
''
$OU2 = "OU=$global:OU2,OU=Offices OU,DC=mydomain,DC=net"
}
I'd strongly recommend moving away from the ISE to do your testing and test in an actual powershell console.
Perhaps Try/Catch block instead of trap?
Try {
[ValidatePattern('^\w{8,15}$')]$compname=read-host 'enter comp name' -ErrorAction Stop
[ValidatePattern('^\w{2}$')]$OU=read-host 'enter OU name' -ErrorAction Stop
}
Catch {
$ErrorMessage = $_.Exception.Message
$ErrorLineNumber = $_.InvocationInfo.ScriptLineNumber
$ErrorCommandName = $_.InvocationInfo.InvocationName
Write-Error -Message "The error message was: <$ErrorMessage>, script line: <$ErrorLineNumber>, command name: <$ErrorCommandName>"
exit 255
}