Powershell IF with multiple conditions not working - powershell

It's not making any sense.
If I put this lines in my script
it will still accept a number as 123 even if it's supposed to be just between 1 and 32.
It's the line until ($setSubNetMask -cle 32 -and $setSubNetMask -cge 1) I'm having the problem with.
What's going on here?
do
{
$setSubNetMask = 0
$setSubNetMask = Read-Host -Prompt "Subnetmask (CIDR)"
if ($setSubNetMask -cge 32 -or $setSubNetMask -cle 2)
{
write-host "Felaktig CIDR (1-32)"
}
}
until ($setSubNetMask -cle 32 -and $setSubNetMask -cge 1)

Read-Host is giving you a string. You're comparing it to a number, which is being implicitly converted to a string because it's on the right side of the comparison.
What you want to do is convert to a number first:
do
{
$setSubNetMask = 0
[int]$setSubNetMask = Read-Host -Prompt "Subnetmask (CIDR)"
if ($setSubNetMask -cge 32 -or $setSubNetMask -cle 2)
{
write-host "Felaktig CIDR (1-32)"
}
}
until ($setSubNetMask -cle 32 -and $setSubNetMask -cge 1)
PowerShell also supports ranges, so your conditionals might better be expressed as:
do
{
$setSubNetMask = 0
$setSubNetMask = Read-Host -Prompt "Subnetmask (CIDR)"
if (1..32 -notcontains $setSubNetMask)
{
write-host "Felaktig CIDR (1-32)"
}
}
until (1..32 -contains $setSubNetMask)
In PowerShell v3 and higher, you can reverse the order using the -in operator instead of -contains, depending on what feels more natural to you:
do
{
$setSubNetMask = 0
$setSubNetMask = Read-Host -Prompt "Subnetmask (CIDR)"
if ($setSubNetMask -notin 1..32)
{
write-host "Felaktig CIDR (1-32)"
}
}
until ($setSubNetMask -in 1..32)
Note that in these examples, I removed the [int] cast; it's not needed and the conversion will be done implicitly in either case.

As #briantist noted, it's due to the Read-Host returning a string value, and then comparing that to an [int].
When comparing dissimilar object types, Powershell will attempt to do the perform the operation by trying to cast the value on the RH side to match the type on the LH side. This means you can implicitly re-cast the strings to [int] by reversing the order of your comparison arguments so that the [int] is on the LH side:
do
{
$setSubNetMask = 0
$setSubNetMask = Read-Host -Prompt "Subnetmask (CIDR)"
if (32 -le $setSubNetMask -or 2 -gt $setSubNetMask)
{
write-host "Felaktig CIDR (1-32)"
}
}
until (32 -le $setSubNetMask -and 1 -le $setSubNetMask)

Related

User input validation in PowerShell

I have this piece of code in PowerShell. I need user input validation to integer with writing output saying what is wrong with the input and expecting correct input.
$FromObj = "Please input object number"
$FromInput = Read-Host $FromObj
while(($FromInput -isnot [int])) {
#I also want to put in the while that the input has to be an integer, -gt 0, and -lt 800
Write-Output "Your input has to be a number."
$FromInput = Read-Host $FromObj
}
if ($FromInput -le 0) {
Write-Output "Your input has to be a number greater than 0!"
$FromInput = Read-Host $FromObj
}
elseif ($FromInput -ge 800) {
Write-Output "Your input has to be a number less than 800!"
$FromInput = Read-Host $FromObj
}
else {
$FromInput = $FromInput -as [int]
}
However, this is not working for me. Could you help me with validating it with writing outputs as the ones displayed above?
I think Nicks's link in comment already provides different right answers to your question i.e.: using regex to match \d+ (digits) or using [int]::TryParse(...) but to explain why your code is not working:
Read-Host will store user input as string unless you manipulate it which is what you were already doing on your last condition (the else {...} statement).
This is why you never get past the $FromInput -isnot [int] condition.
To fix the code you could simply attempt to store user input -as [int] since the beginning and since you have 3 Read-Host on your code you could save the user input attempt -as [int] in a ScriptBlock which then can be easily executed as many times as needed:
$FromObj = "Please input object number"
$giveMeNumber = { (Read-Host $FromObj) -as [int] }
$FromInput = & $giveMeNumber
while($FromInput -isnot [int]) {
Write-Output "Your input has to be a number."
$FromInput = & $giveMeNumber
}
if ($FromInput -le 0) {
Write-Output "Your input has to be a number greater than 0!"
$FromInput = & $giveMeNumber
}
elseif ($FromInput -ge 800) {
Write-Output "Your input has to be a number less than 800!"
$FromInput = & $giveMeNumber
}
Note, even though this works, the statement is not entirely correct since you're breaking out of the while loop after the input is [int] the user could force an incorrect input.
Try this instead which will execute indefinitely until right input:
Clear-Host
$ErrorActionPreference = 'Stop'
$FromObj = "Please input object number"
$scriptBlock = {
try
{
$FromInput = [int](Read-Host $FromObj)
# Note I'm using Write-Host instead of Write-Ouput, this is because
# we don't want to store the invalid inputs messages in the
# $userInput variable.
if ($FromInput -le 0) {
Write-Host "Your input has to be a number greater than 0!"
& $scriptBlock
}
elseif ($FromInput -ge 800) {
Write-Host "Your input has to be a number less than 800!"
& $scriptBlock
}
else {
$FromInput
}
}
catch
{
Write-Host "Your input has to be a number."
& $scriptBlock
}
}
$userInput = & $scriptBlock

validate for a 2-digit number

I need to in powershell
-Ask the user for a 2-digit number
-Validate this number is two numeric digits
-Take into account leading zeroes
-If the number is invalid, have the user try again
It seemed that using
$2digit = read-host
$2digit -match "[0-9][0-9]"
was working but it stopped out of nowhere. Any advice?
You are probably getting an a false result when you entered more than 2 characters.
eg:
This is because you have not specified length.
Resolution:
$2digit = read-host
($2digit.Length -le 2) -and ($2digit -match "[0-9][0-9]")
You can also change your regex pattern
$2digits -match "^[0-9][0-9]$"
^ - start of string or line
$ - end of string or line
I was able to figure it out
do{
do {
write-host -nonewline "Enter the two digit number: "
$2d = read-host
$value = $2d -as [Double]
$ok = $value -ne $NULL
if ( -not $ok ) { write-host "!!ERROR:You must enter numeric values!!" }
}
until ( $ok )
if ($2d.Length -eq 2){$holder = $true}
elseif($2d.Length -ne 2){Write-host "!!ERROR:The number must be 2 digits!!"}
}
while ( $holder -ne $true )
The first do loop will verify that the input is numeric and the second do loop will check it to be 2 numbers.
Regular expressions are your friend. This will only accept digits 0-9 with a length of two. Anything else will not be accepted.
do {
$input = Read-Host -Prompt 'Enter the two digit number(0-9)'
if ( $input -match '^\d{2}$' ) {
break
}
Write-Host "The value must be a two digit number; $input is invalid"
} while ( $true )

Powershell: Getting 0 from Read-Host and Substring comparison with 0 returns false

Im doing a -eq comparison by having a user enter a 3 digit number. If the first number is equal two 0, I need to set a variable to a value. The issue is, 0 is not equaling 0 and returning false.
If $myNumber = 0
This returns false
$myNumber = Read-Host "Enter Number"
$firstNum = $myNumber.Substring(0,1)
if ($firstNum -eq '0') {write-host "True"} else {write-host "False"}
I tried this and still returns false
$myNumber = Read-Host "Enter Number"
$firstNum = $myNumber.Substring(0,1)
if ([int]$firstNum -eq [int]'0') {write-host "True"} else {write-host "False"}
I tried varying combinations but I can never get it to return true, BUT it works with any other number.
If $myNumber = 7
This returns true
$myNumber = Read-Host "Enter Number"
$firstNum = $myNumber.Substring(0,1)
if ($firstNum -eq '7') {write-host "True"} else {write-host "False"}
What am I doing wrong?
The first thing you need to understand is the difference between the number 7 and the character string '007'. Try these exercises.
$s = '007'
if ($s[0] -eq '0') {'007 Starts with zero'} else {'007 Starts with non zero'}
$x = '707'
if ($x[0] -eq '0') {'707 Starts with zero'} else {'707 Starts with non zero'}
$n = 7
if ($n[0] -eq '0') {' 7 Starts with zero'} else {'7 Starts with non zero'}
Next, you need to read from the host without converting numeric input to a number. Try this:
[string]$s = Read-Host 'Input 3 digits'
"Your input was $s"
if ($s[0] -eq '0') {'Input starts with zero'} else {'Input starts with non zero'}
Try inputting 707, then try inputting 007.

PowerShell - Password Generator - How to always include number in string?

I have the following PowerShell script that creates a random string of 15 digits, for use as an Active Directory password.
The trouble is, this works great most of the time, but on some occasions it doesn't use a number or symbol. I just get 15 letters. This is then not usable as an Active Directory password, as it must have at least one number or symbol in it.
$punc = 46..46
$digits = 48..57
$letters = 65..90 + 97..122
$YouShallNotPass = get-random -count 15 `
-input ($punc + $digits + $letters) |
% -begin { $aa = $null } `
-process {$aa += [char]$_} `
-end {$aa}
Write-Host "Password is $YouShallNotPass"
How would I amend the script to always have at least one random number or symbol in it?
Thank you.
You could invoke the Get-Random cmdlet three times, each time with a different input parameter (punc, digit and letters), concat the result strings and shuffle them using another Get-Random invoke:
(Get-Random -Count 15 -InputObject ([char[]]$yourPassword)) -join ''
However, why do you want to reinvent the wheel? Consider using the following GeneratePassword function:
[Reflection.Assembly]::LoadWithPartialName("System.Web")
[System.Web.Security.Membership]::GeneratePassword(15,2)
And to ensure, it contains at least one random number (you already specify the number of symbols):
do {
$pwd = [System.Web.Security.Membership]::GeneratePassword(15,2)
} until ($pwd -match '\d')
As suggested by jisaak, there is no 100% guaranty that the Membership.GeneratePassword Method generates a password that meets the AD complexity requirements.
That's why I reinvented the wheel:
Function Create-String([Int]$Size = 8, [Char[]]$CharSets = "ULNS", [Char[]]$Exclude) {
$Chars = #(); $TokenSet = #()
If (!$TokenSets) {$Global:TokenSets = #{
U = [Char[]]'ABCDEFGHIJKLMNOPQRSTUVWXYZ' #Upper case
L = [Char[]]'abcdefghijklmnopqrstuvwxyz' #Lower case
N = [Char[]]'0123456789' #Numerals
S = [Char[]]'!"#$%&''()*+,-./:;<=>?#[\]^_`{|}~' #Symbols
}}
$CharSets | ForEach {
$Tokens = $TokenSets."$_" | ForEach {If ($Exclude -cNotContains $_) {$_}}
If ($Tokens) {
$TokensSet += $Tokens
If ($_ -cle [Char]"Z") {$Chars += $Tokens | Get-Random} #Character sets defined in upper case are mandatory
}
}
While ($Chars.Count -lt $Size) {$Chars += $TokensSet | Get-Random}
($Chars | Sort-Object {Get-Random}) -Join "" #Mix the (mandatory) characters and output string
}; Set-Alias Create-Password Create-String -Description "Generate a random string (password)"
Usage:
The Size parameter defines the length of the password.
The CharSets parameter defines the complexity where the character U,
L, N and S stands for Uppercase, Lowercase, Numerals and Symbols.
If supplied in lowercase (u, l, n or s) the returned string
might contain any of character in the concerned character set, If
supplied in uppercase (U, L, N or S) the returned string will
contain at least one of the characters in the concerned character
set.
The Exclude parameter lets you exclude specific characters that might e.g.
lead to confusion like an alphanumeric O and a numeric 0 (zero).
Examples:
To create a password with a length of 8 characters that might contain any uppercase characters, lowercase characters and numbers:
Create-Password 8 uln
To create a password with a length of 12 characters that that contains at least one uppercase character, one lowercase character, one number and one symbol and does not contain the characters OLIoli01:
Create-Password 12 ULNS "OLIoli01"
For the latest New-Password version: use:
Install-Script -Name PowerSnippets.New-Password
Command to Generate Random passwords by using existing funciton:
[system.web.security.membership]::GeneratePassword(x,y)
x = Length of the password
y = Complexity
General Error:
Unable to find type [system.web.security.membership]. Make sure that the assembly that contains this type is loaded.
Solution:
Run the below command:
Add-Type -AssemblyName System.web;
Another solution:
function New-Password() {
param(
[int] $Length = 10,
[bool] $Upper = $true,
[bool] $Lower = $true,
[bool] $Numeric = $true,
[string] $Special
)
$upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
$lowerChars = "abcdefghijklmnopqrstuvwxyz"
$numericChars = "0123456789"
$all = ""
if ($Upper) { $all = "$all$upperChars" }
if ($Lower) { $all = "$all$lowerChars" }
if ($Numeric) { $all = "$all$numericChars" }
if ($Special -and ($special.Length -gt 0)) { $all = "$all$Special" }
$password = ""
for ($i = 0; $i -lt $Length; $i++) {
Write-Host "password: [$password]"
$password = $password + $all[$(Get-Random -Minimum 0 -Maximum $all.Length)]
}
$valid = $true
if ($Upper -and ($password.IndexOfAny($upperChars.ToCharArray()) -eq -1)) { $valid = $false }
if ($Lower -and ($password.IndexOfAny($lowerChars.ToCharArray()) -eq -1)) { $valid = $false }
if ($Numeric -and ($password.IndexOfAny($numericChars.ToCharArray()) -eq -1)) { $valid = $false }
if ($Special -and $Special.Length -gt 1 -and ($password.IndexOfAny($Special.ToCharArray()) -eq -1)) { $valid = $false }
if (-not $valid) {
$password = New-Password `
-Length $Length `
-Upper $Upper `
-Lower $Lower `
-Numeric $Numeric `
-Special $Special
}
return $password
}
Flexible enough to set length, turn on/of upper, lower, and numeric, and set the list of specials.
My take on generating passwords in PowerShell, based on what I've found here and in the Internets:
#Requires -Version 4.0
[CmdletBinding(PositionalBinding=$false)]
param (
[Parameter(
Mandatory = $false,
HelpMessage = "Minimum password length"
)]
[ValidateRange(1,[int]::MaxValue)]
[int]$MinimumLength = 24,
[Parameter(
Mandatory = $false,
HelpMessage = "Maximum password length"
)]
[ValidateRange(1,[int]::MaxValue)]
[int]$MaximumLength = 42,
[Parameter(
Mandatory = $false,
HelpMessage = "Characters which can be used in the password"
)]
[ValidateNotNullOrEmpty()]
[string]$Characters = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM##%*-_+:,.'
)
(1..(Get-Random -Minimum $MinimumLength -Maximum $MaximumLength) `
| %{ `
$Characters.GetEnumerator() | Get-Random `
}) -join ''
I preferred this over using System.Web, not to introduce dependencies, which could change with .Net / .Net Core versions.
My variation also allows random password length (in specified range), is fairly concise (apart from the parameters section, which is quite verbose, to enforce some validations and provide defaults) and allows character repetitions (as opposite to the code in the question, which never repeats the same character).
I understand, that this does not guarantee a digit in the password. This however can be addressed in different ways. E.g. as was suggested, to repeat the generation until the password matches the requirements (contains a digit). My take would be:
Generate a random password.
If it does not contain a digit (or always):
Use a random function to get 1 random digit.
Add it to the random password.
Randomize the order of the result (so the digit is not necessarily always at the end).
Assuming, that the above script would be named "Get-RandomPassword.ps1", it could look like this:
$pass = .\Get-RandomPassword.ps1
$pass += (0..9 | Get-Random)
$pass = (($pass.GetEnumerator() | Get-Random -Count $pass.Length) -join '')
Write-Output $pass
This can be generalized, to enforce using any character category:
$sets = #('abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '0123456789', '()-_=+[{]};:''",<.>/?`~')
$pass = .\Get-RandomPassword.ps1 -Characters ($sets -join '')
foreach ($set in $sets) {
$pass += ($set.GetEnumerator() | Get-Random)
}
$pass = (($pass.GetEnumerator() | Get-Random -Count $pass.Length) -join '')
Write-Output $pass
I wrote a secure password generator function in PowerShell, maybe this will be useful to someone.
Similar to the accepted answer, this script also uses Get-Random (twice), and also regular expression matching to ensure the output is secure.
The difference in this script is that the password length can also be randomised.
(To hard set a password length, just set the MinimumPasswordLength and MaximumPasswordLength values to the the same length.)
It also allows an easy to edit character set, and also has a regex to ensure a decent password has been generated with all of the following characteristics:
(?=.*\d) must contain at least one numerical character
(?=.*[a-z]) must contain at least one lowercase character
(?=.*[A-Z]) must contain at least one uppercase character
(?=.*\W) must contain at least one non-word character
The answer to your question about always including a number in your generated output can be solved by checking the output with a regex match (just use the parts of the regex that you need, based on the explanations above), the example here checks for uppercase, lowercase, and numerical:
$Regex = "(?=.*\d)(?=.*[a-z])(?=.*[A-Z])"
do {
$Password = ([string]($AllowedPasswordCharacters |
Get-Random -Count $PasswordLength) -replace ' ')
} until ($Password -cmatch $Regex)
$Password
Here is the full script:
Function GeneratePassword
{
cls
$MinimumPasswordLength = 12
$MaximumPasswordLength = 16
$PasswordLength = Get-Random -InputObject ($MinimumPasswordLength..$MaximumPasswordLength)
$AllowedPasswordCharacters = [char[]]'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!?##£$%^&'
$Regex = "(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W)"
do {
$Password = ([string]($AllowedPasswordCharacters |
Get-Random -Count $PasswordLength) -replace ' ')
} until ($Password -cmatch $Regex)
$Password
}
GeneratePassword
I had the same issue here is the snippet I used to create my alphanumerical password its simple all I have done is used ASCII regex replace to make it nice.
Function Password-Generator ([int]$Length)
{
# Generate passwords just call password-generator(lenght of password)
$Assembly = Add-Type -AssemblyName System.Web
$RandomComplexPassword = [System.Web.Security.Membership]::GeneratePassword($Length,2)
$AlphaNumericalPassword = $RandomComplexPassword -replace '[^\x30-\x39\x41-\x5A\x61-\x7A]+'
Write-Output $AlphaNumericalPassword
}
I've created this. You can choose how many Pwd to create
$howoften = Read-Host "How many would you like to create: "
$i = 0
do{
(-join(1..42 | ForEach {((65..90)+(97..122)+(".") | % {[char]$_})+(0..9)+(".") | Get-Random}))
$i++
} until ($i -match $howoften)
To change the length of the pwd simply edit the "42" in line 4
(-join(1..**42** | ForEach ...

Powershell, check for blank input, zero evaluates as blank/null

I am trying to validate a simple powershell input. I want to allow the number zero as valid input, but blank (just pressing enter) is not valid input. I am having the most difficult time with the validation. Everything works EXCEPT when the user enters the number zero.
I have tried checking the length, checking for null, etc.
Consider the code below.
If you enter 0 (zero) the output is the same as if you just press [enter] (no input)
Pick a number 0-6: 0
You picked 0
blank input
equals zero
too short
$OUchoice = -1
do
{
try{
$OUchoice = Read-Host "Pick a number 0-6"
}
catch{
write-host "invalid input"
}
Write-Host "You picked $OUchoice"
if ($OUchoice -gt 6) {write-host "too high"}
if ($OUchoice -lt 0) {write-host "too low"}
if ($OUchoice -eq "") {write-host "blank input"}
if ($OUchoice -eq 0 ) {write-host "equals zero"}
if ($OUchoice -eq $null ) {write-host "Null"}
if (!($OUchoice)) {write-host "too short"}
}
until (($OUchoice -ge 0) `
-and ($OUchoice -le 6) `
-and (($ouchoice)))
write-host "OK"
I have tried -eq $NULL and -eq "". What is the proper way to allow zero and disallow blank input?
Problem lies in this:
PS> 0 -eq ""
True
When two values of different type are compared second argument is casted to the type of the first. Unfortunately for you:
PS> [int]""
0
empty string when casted to int produces 0. In your case fix is easy: just reverse the order of operands as 0 casted to string would end up being '0':
if ("" -eq $OUchoice) {write-host "blank input"}
This is what I came up with. I ensure that the $OUChoice is an Int when you compare it against other numbers. This ensures that null input is a 0 and also allows for negative values.
$OUchoice = -1
do
{
try{
$OUchoice = Read-Host "Pick a number 0-6"
}
catch{
write-host "invalid input"
}
Write-Host "You picked $OUchoice"
Switch ($OUchoice) {
{
[string]::IsNullOrEmpty($OUchoice)} {
write-host "Null Input"
Break
}
{
[int]$OUchoice -gt 6} {
write-host "too high"
Break
}
{
[int]$OUchoice -lt 0} {
write-host "too low"
Break
}
{
[int]$OUchoice -eq 0 } {
write-host "equals zero"
Break
}
}
}
until (([int]$OUchoice -ge 0) -and ([int]$OUchoice -le 6) -AND (-NOT [string]::IsNullOrEmpty($OUchoice)))
write-host "OK"
Nest your ifs (I didn't test much, but did check the zero and blank cases):
if (-not ($OUchoice -eq ""))
{
if ($OUchoice -gt 6) {write-host "too high"}
if ($OUchoice -lt 0) {write-host "too low"}
if ($OUchoice -eq 0 ) {write-host "equals zero"}
if ($OUchoice -eq $null ) {write-host "Null"}
if (!($OUchoice)) {write-host "too short"}
}
else
{
Write-Output "blank input"
}
Output:
PS C:\users\mattp_000> .\x.ps1
Pick a number 0-6:
You picked
blank input
Pick a number 0-6: 0
You picked 0
equals zero
OK
PS C:\users\mattp_000> .\x.ps1
Pick a number 0-6: 3
You picked 3
OK
PS C:\users\mattp_000>