Power shell using the Trim function - powershell

HI I have the following line to check variables from two different systems.
foreach ($val in $stafflist)
{
$found = 0
foreach ($user in $aduser)
if ((($val.Surname.trim() -eq $user.surname.trim()) -And ($val.'First Name'.trim() -eq $user.Givenname.trim())) -or (($val.Surname.trim() -eq $user.surname.trim()) -And ($val.'Known As'.trim() -eq $user.Givenname.trim())))
{
$found = 1
try
{
if ($user.EmployeeID -ne $null)
{
$val.'First Name' + " " + $val.'Known As' + " " + $val.surname + " EmployeedID already set as " + $user.EmployeeID | Out-File $outfile -Append
}
else
{
set-aduser -Identity $user -EmployeeID $val.'Person Number'
$val.'First Name' + " " + $val.'Known As' + " " + $val.surname + " Employeed set to " + $user.EmployeeID| Out-File $outfile -Append
}
} ## end of Try
Catch
{
$val.'First Name' +" " + $val.surname + "Can not be updated in AD" | Out-File $outfile -Append
}
}
So this checks each user in two lists ($stafflist and $aduser) against each other searching for matches, and when it finds a match it updates the employee ID in Active directory with the ID from the HR database.
The trouble is it has an or operator in it to account for the fact that in the HR database system either the "first name" or "known as name" or indeed both might be filled in and need to be checked.
I want to use the .Trim function in case some one has left in white spaces, but it throws and error if i place it against a variable and that variable end up as null, which is often the case with the "known as name" variable.
Can any one suggest the most efficient way to do this check and trim for each variable.
I could trim all the variables upfront after checking they are not null.
or test if they are null and if so pass them through different test strings to avoid errors but that all means more lines of code and slower execution of code.
If any one has a concise way to achieve this I would be grateful for the suggestion.

In the end I am just adding this to the start of the loop
if ($val.Surname -eq $null){} else {$val.Surname = $val.Surname.trim()}
if ($val.'First Name' -eq $null){} else {$val.'First Name' = $val.'First Name'.trim()}
if ($val.'Known As' -eq $null){} else {$val.'Known As' = $val.'Known As'.trim()}
So it early on checks for null values if not null trims it and stores it back to the variable.

Related

Nested if and for loop errors

would any of you be able to help me with the below code
$pcname = "jwang"
#We set $test as a string to be called later
$test = get-adcomputer -filter "name -like '$pcname'" | select -ExpandProperty name
#We define a null string to test our IF statement
$nothing = $null
$number = 1
if ($test -ne $null) {
Write-Host "$pcname is currently in use"
for($number = 1; ($test = get-adcomputer -filter "name -like '$pcname + $number'" | select -ExpandProperty name) -ne $null; $number++ ){
Write-Host $pcname + $number
}
}
else {
Write-host "AD lookup complete, new PC name will be $pcname"
}
The IF statement works correctly, but the trouble starts when I add the nested FOR loop.
The end goal is to have AD tell me if the name is available or not.
Right now in AD there is
JWANG
JWANG1
JWANG2
JWANG3
JWANG4
I want the for loop to eventuelly tell me that "JWANG5" is available, any help is appreciated, thank you
The immediate problem with your code is that $pcname + $number - an expression - isn't enclosed in $(...), the subexpression operator inside your expandable (double-quoted) string ("...").
Therefore, use $($pcname + $number) or, relying on mere string interpolation, $pcname$number
An aside re testing for $null:
It is advisable to make $null the LHS for reliably null testing (if ($null -ne $test)), because on the RHS it acts as a filter if the LHS happens to be an array - see about_Comparison_Operators.
However, often - such as when objects are being returned from a command, you can simply use implicit to-Boolean conversion (if ($test)) - see the bottom section of this answer for the complete rules.
Taking a step back:
Querying AD multiple times is inefficient, and since you're already using the -like operator, you can use it with a wildcard pattern to find all matching names at once:
$pcname = 'jwang'
# Note the use of "*" to match all names that *start with* $pcname
$names = get-adcomputer -filter "name -like '$pcname*'" | select -ExpandProperty name
if ($names) { # at least one name found
Write-Host "$pcname is currently in use"
# Find the highest number embedded in the matching names.
$seqNums = [int[]] ($names -replace '\D')
# The new sequence number is the next number following the currently highest.
$nextSeqNum = 1 + [Linq.Enumerable]::Max($seqNums)
# Form the new name and output it.
$pcname + $nextSeqNum
}
else {
Write-host "AD lookup complete, new PC name will be $pcname"
}

PowerShell Show Menu

I have the next code written in PowerShell.
When I run the code, the Menu is shown, I enter the option 1..9 and the selected option is not called, the menu is shown again and again(see the screenshot).
When I enter an option I want to be called that function related to each option entered then to display the message "The function has been called" and also to display the menu to enter a new option. (see the scrennshot - code in C++)
Any idea ?
function PORII
{
Write-Host " PORII was called"
}
function DXD-BODY
{
Write-Host " DXD BODY was called"
}
function DXD-PAINT
{
Write-Host " DXD PAINT was called"
}
function DXD-PTO
{
Write-Host " DXD PTO was called"
}
function DXD-TCF
{
Write-Host " DXD TCF was called"
}
function FIS-SERVERS
{
Write-Host " FIS SERVERS was called"
}
function SERVERS
{
Write-Host " SERVERS was called"
}
function Acronis
{
Write-Host " Acronis was called"
}
function Menu
{
param([string]$Title = 'Menu')
Write-Host " ==================== $Title ==================== "
while (1)
{
Clear-Host
Write-Host " Press 1 for PORII: "
Write-Host " Press 2 for DXD BODY: "
Write-Host " Press 3 for DXD PAINT: "
Write-Host " Press 4 for DXD PTO: "
Write-Host " Press 5 for DXD TCF: "
Write-Host " Press 6 for FIS SERVERS: "
Write-Host " Press 7 for SERVERS: "
Write-Host " Press 8 for Acronis Images: "
Write-Host " Press 9 for Exit: "
$a = Read-Host -Prompt "`n Enter your option "
if (($a -eq 1) -or ($a -eq 2) -or ($a -eq 3) -or ($a -eq 4) -or ($a -eq 5) -or ($a -eq 6) -or ($a -eq 7) -or ($a -eq 8) -or ($a -eq 9))
{
switch($a)
{
1{PORII}
2{DXD-BODY}
3{DXD-PAINT}
4{DXD-PTO}
5{DXD-TCF}
6{FIS-SERVERS}
7{SERVERS}
8{Acronis-Images}
9{Exit}
}
}
else
{
continue
}
}
}
Menu
Your functions are being called, but that isn't obvious because Clear-Host is called right after, discarding the called function's output.
Apart from that, your code can be streamlined:
Instead of the if (($a -eq 1) -or ($a -eq 2) ... conditional, you can add a default branch to your switch statement.
Also note that Read-Host always returns a string, whereas your conditionals operate on numbers; thanks to PowerShell's automatic type conversions, this isn't a problem in your particular case (with explicit or implied equality comparison), but it's something to keep in mind.
Through the use of a hash table you can simplify your function while making it easier to maintain.
Function Menu {
param([string]$Title = 'Menu')
while ($TRUE) {
$OptionHT = #{
1="PORII"
2="DXD-BODY"
3="DXD-PAINT"
4="DXD-PTO"
5="DXD-TCF"
6="FIS-SERVERS"
7="SERVERS"
8="Acronis-Images"
9="Exit"
}
Write-Host " ==================== $Title ==================== "
For ($Cntr = 1 ; $Cntr -lt $($OptionHT.Count) + 1; $Cntr++) {
Write-Host "Press $Cntr for $($OptionHT.$($Cntr)):"
}
$a = Read-Host -Prompt "`n Enter your option "
If (($a.length) -eq 1 -and ([byte][char]$a) -ge 49 -and
([byte][char]$a) -le 57) {
& ($OptionHT.[int]$a)
}
} # End While ($True)
} # End Function Menu
By placing your options in the hash table you only have a single point to make changes for called function names.
The if statement vs switch eliminates any value other than a single single number from 1 to 9 (note use of ASCII values to verify number input) from being processed. And since we have eliminated invalid inputs a single statement can be used to search the hash table for the function to execute.
UPDATE: Per the comments below you'll have to either trap the EXIT (9) with an If statement and exit or Create another Function and call it something like Exit-Program and place the Exit command there, I tested a function and it works.
Note: I didn't clear the console between writes of the menu so you could see your selection as mentioned in the comments you can add it where you deem necessary.
HTH

Modify Multi Hashtables

I want to create a script to create VDIs. Basically it's ready, but now I want to modify it to work with gridviews to make it more usable.
My goal is, that you may get a gridview like this code:
$id = 1
$table = #( #{'ID'=$id ++; 'VM Name'="VM0001"; 'Assigned User'=$null},
#{'ID'=$id ++; 'VM Name'="VM0002"; 'Assigned User'=$null},
#{'ID'=$id ++; 'VM Name'="VM0003"; 'Assigned User'=$null} )
$out = $null
$out = $table.ForEach({[PSCustomObject]$_}) |
Select ID, 'VM Name', 'Assigned User' |
Out-GridView -Title "VM Creator" -OutputMode Single -OutVariable selectedID
if ($selectedID -eq $null -or $out.Count -eq 0) {
Write-Host -BackgroundColor Black -ForegroundColor Red "Error: nothing is choosed"
}
if ($selectedID.'Assigned User' -eq $null) {
Write-Host -BackgroundColor Black -ForegroundColor Green "Debug: Input UserName for Desktop" $selectedID.'VM Name'
$newUser = Read-Host "Input UserName"
}
Now when you select an entry without a assigned User you should be able to add a name into the hashtable.
When I tried this:
$table.Add("123", "VM0004", "Pete")
I got the following error (in German):
Für "Add" und die folgende Argumenteanzahl kann keine Überladung gefunden
werden: "3".
In Zeile:8 Zeichen:1
+ $table.Add("123", "VM0004", "Pete")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Your $table is an array of hashtables, so you need to append another hashtable:
$table += #{'ID'="123"; 'VM Name'="VM0004"; 'Assigned User'="Pete"}
The Add() method can't be used here, because you can't append to a fixed size array (+= works because it doesn't actually append to the array but creates a new array with increased size, copies the elements from the existing array, puts the new value(s) in the free slots, then assigns the new array to the variable).
To be able to modify values of selected rows I'd suggest to use an array of custom objects right away (you're creating them for the gridview anyway).
$table = [PSCustomObject]#{'ID'=$id++; 'VM Name'="VM0001"; 'Assigned User'=$null},
[PSCustomObject]#{'ID'=$id++; 'VM Name'="VM0002"; 'Assigned User'=$null},
[PSCustomObject]#{'ID'=$id++; 'VM Name'="VM0003"; 'Assigned User'=$null}
With that you can update values of a selected row like this:
$selected = $table | Out-GridView -PassThru
$selected | ForEach-Object {
$_.'Assigned User' = 'Chris'
}

AD user creation with SamAccountName availability check loop

I'm learning Powershell by making a script that will hopefully automate everything that needs to be done when we get a new hire or a consultant. Currently I'm working on the part that will create the AD account. Below are the variables specific to this portion of the script.
#Variables for preliminary SamAccountName, modifiers and initials array
$PrelSamAccountName = ("se" + [string]$GivenName.Substring(0,3) + [string]$SurName.Substring(0,3)).ToLower()
$Modifier1 = 1
$Modifier2 = 0
$InitialsArray = #("x","y","z")
Here is the loop. I cut out a bunch of parameters on New-ADUser to make it less cluttered.
try {
#Checks if preliminary SamAccountName is taken or not
$ADCheck = Get-ADUser -Filter {SamAccountName -eq $PrelSamAccountName}
#Creates new user
New-ADUser -Name $Name -SamAccountName $PrelSamAccountName
} catch {
#Replaces final character in preliminary SamAccountName with "1++"
while (($ADCheck | Select-Object -ExpandProperty SamAccountName) -eq $PrelSamAccountName) {
$PrelSamAccountName = ([string]$PrelSamAccountName.Substring(0,7) + ($Modifier1++)).ToLower()
}
#Changes $Initials from $null to x/y/z if an existing user has identical name as new user
while (($ADCheck | Select-Object -ExpandProperty Name) -eq $Name) {
$Initials = $InitialsArray[$Modifier2++]
$Name = $GivenName + " " + $Initials + " " + $SurName
}
}
Everything is working as intended, except for the fact that a new user is created every other time I run the loop. Ideally I would want it to create a new user every time it is run. :)
I'm assuming it has something to do with the placement of the $ADCheck variable, but after having rewritten this portion multiple times I simply can't get it to work. Any ideas?
Thanks in advance.
You have some logical problems in here:
Follow this Approach:
Pseudocode:
while (user exist) {create new username}
new-aduser new username
PS Code:
function new-sam
{
#creates new sam
}
$sam = "username"
$a=$false
while (!$a)
{
try {
$a = [bool](Get-ADUser -Identity $sam )
}
catch {
$a = $false; $sam = new-sam
}
}
new-aduser ....

how to create a display with arrays

I want to create table where it offers the user some options like change the ip to static and dynamic and i have a hash table like this. I just wanted to know the other ways to create this table without having to spam the $box variable
$Box=#("г","="," ","¬","¦","-","L","¦","¦")
Write-Host $Box[0]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[3]
Write-Host $Box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Title " "$box[2] $box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[4]
Write-host $box[8]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[7]
write-host $Box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[4]
Write-host $box[4]$box[2]$box[2]$box[2]$box[2]$box[2] $MenuItems[0] $box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[4]
Write-host $Box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[4]
Write-host $box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$MenuItems[1]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[4]
Write-host $Box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[4]
Write-host $box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$MenuItems[2]$Box[2]$box[2]" "$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[4]
Write-host $box[6]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[5]
$UserInput = Read-Host "Please make a selection [1-3]"
switch($UserInput)
{
1 {Set-DHCP}
2 {Set-StaticIP}
3 {exit}
default {Main}
}
You can consolidate that code a lot by using .NET string formatting. For example, the first two lines would look like the following:
$Lines = #(); # Create an empty array
$Lines += ('{0}' + '{1}'*33 + '{2}') -f $Box[0], $box[1], $box[3]; # Format and add the first line
$Lines += ('{0}' + '{1}'*9 + '{2} ' + '{1}'*10 + '{0}') -f $Box[4], $box[2], $Title; # Format and add the second line
...
...
...
Write-Host -Object ($Lines -join "`n"); # Write out all lines, joined by a line separator.