i have the following code snippet where i change the values in a column (named G) of a csv to Y if the integer value is greater then 1 and to N if it is equal to 1 and smaller.
ForEach-Object {if ($_.G -gt '1') {$_.G = 'Y'} if ($_.G -le '1') {$_.G = 'N'} $_}
It works fine with the exception of negative numbers. I always get a Y. I don't have any idea. Example data:
F,G
item1, -58
item2, -77
item3, 562
Does anyone have an idea?
Regards, Hubertus
In order to evaluate the $_.G property as a number you need to specify the type as [int]. Example using your code:
$testObject = New-Object PSObject -Property #{
G='-1'
}
$testObject| %{
if ([int]$_.G -gt 1)
{
$out = "{0} is greater than 1" -f $_.G
Write-Host $out -ForegroundColor Green
[string]$_.G = "Y"
}
elseif ([int]$_.G -le 1)
{
$out = "{0} is Less than 1" -f $_.G
Write-Host $out -ForegroundColor Green
[string]$_.G = "N"
}
}
Note: In order to assign $_.G as a string you have to change the type to [string]. In my opinion, I would use another property to indicate "Y/N" instead of flipping the type back and forth on the property.
The left side of -le or -gt controls the type for both sides, int32 (integer) in this case. You probably want an else in there, to not look at the G again after changing it.
'G
-1
1
2' |
convertfrom-csv |
ForEach-Object {
if (1 -le $_.G)
{$_.G = 'Y'}
else
{$_.G = 'N'}
$_
}
G
-
N
Y
Y
I have a cellpainting event and am trying to correct/clean this IF statement up. I think I'm getting lost in my parenthess. Is someone able to take a second look at this? Thanks for your time.
My end goal is the IF statemant to be: Column 1 date older than 42 days or not $null and column 4 value = "SEP"
$datagridview1_CellPainting=[System.Windows.Forms.DataGridViewCellPaintingEventHandler]{
$SEPreturnlimit = "{0:MM/dd/yyyy}" -f (get-date).AddDays(-42)
if ((($_.ColumnIndex -eq 1 -and ([datetime]$_.Value -le $SEPreturnlimit -and [datetime]$_.Value.ToString() -ne $null))) -and ($datagridview1.rows .Cells[4].Value -eq "SEP")) #Column 1 date older than 42 days or not $null **and** column 4 value = "SEP"
{
$this.Rows[$_.RowIndex] | %{ $_.DefaultCellStyle.BackColor = 'crimson' } #Color Row
}
I would split the if conditions into separate nested ifs and also add a try..catch
$datagridview1_CellPainting = [System.Windows.Forms.DataGridViewCellPaintingEventHandler] {
if ($_.ColumnIndex -eq 1 -and $datagridview1.rows.Cells[4].Value -eq 'SEP') {
$SEPreturnlimit = (Get-Date).AddDays(-42).Date # set to 42 days back at midnight
try {
# if we succeed in parsing out a datetime object
$date = [datetime]$_.Value
# test if we have a DateTime object and if that date is older than the reference date
if (($date) -and $date-le $SEPreturnlimit) {
# cannot check this myself, but shouldn't that simply be
# $this.Rows[$_.RowIndex].DefaultCellStyle.BackColor = 'crimson'
$this.Rows[$_.RowIndex] | ForEach-Object{ $_.DefaultCellStyle.BackColor = 'crimson' } #Color Row
}
}
catch { <# do nothing #> }
}
}
I think I fixed the placement, but check the logic. Have a look at it this way
if
(
(
$_.ColumnIndex -eq 1 -and
(
[datetime]$_.Value -le
$SEPreturnlimit -and
[datetime]$_.Value.ToString() -ne
$null
)
) -and
(
$datagridview1.rows .Cells[4].Value -eq
'SEP'
)
) #Column 1 date older than 42 days or not $null **and** column 4 value = "SEP"
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 ...
I am trying to compare two dates and decide based on the number of hours gap but the comparison doesn't seem to work. I tried to cast TotalHours to [int] but no luck. What it surprises me is that I get the same result using -gt or -lt.... :?
it must be something simple but I dont see it.
here the code
$stamp = "14/11/2015 14:20:57"
$rightNow = Get-Date
$stampDateTime = [datetime]::ParseExact($stamp,'dd/MM/yyyy HH:mm:ss',[Globalization.CultureInfo]::InvariantCulture)
$difference = NEW-TIMESPAN –Start $stampDateTime –End $rightNow
if ([int]$difference.TotalHours -gt 24 ) { $ok = "OK" }
if ($ok){
Write-Host "VALID"
}else{
Write-Host "BAD"
}
Your if ($ok) is the issue. All strings when evaluated as booleans will return true.
Changing it so that you are using boolean would be one way to address it.
if ([int]$difference.TotalHours -gt 24 ) { $ok = $true }
if ($ok){
Write-Host "VALID"
}else{
Write-Host "BAD"
}
You should also set a known value at the beginning of the script to use as a flag. This is important as the variable state will persist between executions in a PowerShell session.
# assume false
$ok = $false
Depending on your needs you don't need to keep this logic. You can bypass the second if all together because as presented it is redundant.
if ([int]$difference.TotalHours -gt 24 ) {
Write-Host "VALID"
}else{
Write-Host "BAD"
}
I understand this might just be the simple example you used to make the question.
Consider the following to understand what happened. Any non-zero length string will resolve to $true when cast as a boolean.
PS C:\> [boolean]"OK"
True
PS C:\> [boolean]"VeryBad"
True
PS C:\> [boolean]""
False
I have been provided a PS script which calls a simple SQL query, displays the results and plays a voice... supposedly. This is designed to go into a BI gadget which just displays a number from the PS script.
The correct count from the query is not displaying and is for some reason stuck on 2.
$sql = "<simple COUNT query>"
$items = invoke-sql -server "<server name>" -database "<db name>" -user "<uname>" -password "<password>" -sql $sql -timeout 180
$day = (get-date).DayOfWeek.Value__
# $items[0] = 10
if ($day -ge 1 -and $day -le 5)
{
$date = Get-Date
if ($date.Hour -ge 8 -and $date.Hour -lt 17)
{
if ($items[0] -gt 0)
{
$voice = New-Object -Com sapi.spvoice
$voice.Rate = 1
$voice.volume = 100
Do {
Start-Sleep -s 1
$Counter = Get-Date
}
Until ($Counter.Second/15 -is [int])
if ($items[0] -gt 1)
{
[void]$voice.Speak("New Ticket.")
}
else
{
[void]$voice.Speak("New Ticket.")
}
}
}
}
Write-Output $items.Count
The SQL component I have no issues with and definitely works correctly when run, but I have no experience with PowerShell scripting so can someone please assist?
$Items.count is going to give you the number of records in your result set. Technically these are DataRow objects and $Items is a DataTable. If you are getting multiple rows back, you need to determine WHICH row you want to show.
Basically the way to would query the returned DataTable is:
($Items[x]).Column
Where x is the 0-indexed row you want to return and Column is the name of the column you want to return. So if you want the ItemCount column from the second row you would use:
($Items[1]).ItemCount