I have a question regarding the script.
I found a script that generates a password, but how to do it so that, for example, you can set the length of the password and probably some characters when generating, if you do not set it only after that, let it generate a default password, for some length
function Get-RandomCharacters($length, $characters) {
$random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length }
$private:ofs=""
return [String]$characters[$random]
}
function Scramble-String([string]$inputString){
$characterArray = $inputString.ToCharArray()
$scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length
$outputString = -join $scrambledStringArray
return $outputString
}
$password = Get-RandomCharacters -length 5 -characters 'abcdefghiklmnoprstuvwxyz'
$password += Get-RandomCharacters -length 1 -characters 'ABCDEFGHKLMNOPRSTUVWXYZ'
$password += Get-RandomCharacters -length 1 -characters '1234567890'
$password += Get-RandomCharacters -length 1 -characters '!"§$%&/()=?}][{##*+'
Write-Host $password
$password = Scramble-String $password
Write-Host $password
There are more ways than one to generate a password.
For instance this one:
function New-Password {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateRange(8, 128)]
[int]$TotalLength = 10,
[int]$Digits = 3,
[int]$Symbols = 2
)
# the number of symbols must be => 0 and <= $TotalLength
$Symbols = [math]::Max(0,[math]::Min($Symbols, $TotalLength))
# same for the number of digits
$Digits = [math]::Max(0,[math]::Min($Digits, $TotalLength))
$alphas = $TotalLength - ($Digits + $Symbols)
if ($alphas -lt 0) {
throw "Too many digits or symbols for the total password length"
}
$list = [System.Collections.Generic.List[char]]::new()
if ($Digits -gt 0) { $list.AddRange([char[]]([char[]]'0123456789' | Get-Random -Count $Digits)) }
if ($Symbols -gt 0) { $list.AddRange([char[]]([char[]]'!##$%^&*()_-+=[{]};:<>|./?~' | Get-Random -Count $Symbols)) }
if ($alphas -gt 0) { $list.AddRange([char[]]([char[]]'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' | Get-Random -Count $alphas)) }
($list | Sort-Object {Get-Random}) -join ''
}
Usage:
$password = New-Password -TotalLength 12 -Symbols 4 -Digits 2 # or without parameters to accept the defaults
Or this:
$TotalLength = 10
$password = ([char[]]([char]33..[char]95) + ([char[]]([char]97..[char]126)) + 0..9 | Sort-Object {Get-Random})[0..$TotalLength] -join ''
Or this:
Add-Type -AssemblyName System.Web
# the first parameter specifies the total password length.
# the second one specifies the minimum number of non-alphanumeric characters
$password = [System.Web.Security.Membership]::GeneratePassword(10, 3)
Note: Apparently you cannot use this last method in .NET Core as it does not support System.Web.dll. See this github issue
How about condensing down into a smaller function:
Function Generate-Password
{
-join ('abcdefghkmnrstuvwxyzABCDEFGHKLMNPRSTUVWXYZ23456789$%&*#'.ToCharArray() | Get-Random -Count 12) # Add characters and/or password length to suit your organisation's requirements
}
And then call whenever you need it:
$password = Generate-Password
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
replace line
function Get-RandomCharacters($length, $characters) {
with:
function Get-RandomCharacters($length = 8, $characters = 'abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890') {
it defines default length to 8 chars, and charset as abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890
Related
I will create 100 random AD users via script. But , I am getting the following error message for some random users.
New-ADUser : The password does not meet the length, complexity, or history requirement of the domain.
Get-ADDefaultDomainPasswordPolicy
output :
ComplexityEnabled : True
DistinguishedName : DC=contoso,DC=local
LockoutDuration : 00:00:00
LockoutObservationWindow : 69.10:39:00
LockoutThreshold : 5
MaxPasswordAge : 90.00:00:00
MinPasswordAge : 3.00:00:00
MinPasswordLength : 8
objectClass : {domainDNS}
objectGuid : 346664da-c908-470e-9fc3-5487983c92ae
PasswordHistoryCount : 12
ReversibleEncryptionEnabled : False
Here is my script :
$UserList = Import-CSV -Path C:\temp\CreateUsers.csv
$targetOU = 'OU=Test,DC=contoso,DC=local'
$upnDomain = 'contoso.local'
$Path = "C:\temp\output.csv"
foreach ($Person in $UserList) {
#If username does not exist
if(-not(Get-ADUser -Filter "SamAccountName -eq '$($Person.Sam)'")) {
$PassWord = -join ([char[]]"!##$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" |Get-Random -Count 10)
$useritems = #{
GivenName = $Person.Firstname
Surname = $Person.LastName
AccountPassword = ConvertTo-SecureString -String $Password -AsPlainText -force
ChangePasswordAtLogon = $false
Enabled = $true
DisplayName = "$($Person.Firstname) $($Person.Lastname)"
Name = "$($Person.Firstname) $($Person.Lastname)"
SamAccountName = $Person.Sam
UserPrincipalName = "$($Person.Sam)#$upnDomain"
}
New-ADUser #useritems -Path $targetOU
Add-Content -Path $Path -Value "Username: $($Person.Sam) Password: $Password"
}
}
To avoid creating passwords that do not comply to the domains password complexity rules, you could use this helper function:
function Test-DomainPassword {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$Password,
[Parameter(Mandatory = $false)]
[string]$AccountSamAccountName = $null,
[Parameter(Mandatory = $false)]
[string]$AccountDisplayName = $null
)
# [Microsoft.ActiveDirectory.Management.ADEntity]
$PasswordPolicy = Get-ADDefaultDomainPasswordPolicy -ErrorAction SilentlyContinue
If ($Password.Length -lt $PasswordPolicy.MinPasswordLength) {
Write-Verbose "Password '$Password' is too short. Minimal length is $($PasswordPolicy.MinPasswordLength)"
return $false
}
if (![string]::IsNullOrWhiteSpace($AccountSamAccountName) -and $Password -match $AccountSamAccountName) {
Write-Verbose "The password '$Password' includes the users SamAccountName"
return $false
}
if (![string]::IsNullOrWhiteSpace($AccountDisplayName)) {
# if ANY PART of the display name that is split by the characters below, the password should fail the complexity rules.
$tokens = $AccountDisplayName.Split(",.-,_ #`t")
foreach ($token in $tokens) {
if (($token) -and ($Password -match "$token")) {
Write-Verbose "The password '$Password' includes (part of) the users DisplayName"
return $false
}
}
}
if ($PasswordPolicy.ComplexityEnabled) {
# check for presence of
# - Uppercase: A through Z, with diacritic marks, Greek and Cyrillic characters
if ($Password -cnotmatch "[A-Z\p{Lu}\s]") {
Write-Verbose "The password '$Password' is missing Uppercase characters"
return $false
}
# - Lowercase: a through z, sharp-s, with diacritic marks, Greek and Cyrillic characters
if ($Password -cnotmatch "[a-z\p{Ll}\s]") {
Write-Verbose "The password '$Password' is missing Lowercase characters"
return $false
}
# - Base 10 digits (0 through 9)
if ($Password -notmatch "[\d]") {
Write-Verbose "The password '$Password' is missing digits (0-9)"
return $false
}
# - Nonalphanumeric characters: ~!##$%^&*_-+=`|\(){}[]:;”‘<>,.?/
if ($Password -notmatch "[^\w]") {
Write-Verbose "The password '$Password' is missing Nonalphanumeric characters: ~!##$%^&*_-+=`|\(){}[]:;`"'<>,.?/"
return $false
}
}
return $true
}
and use that in a loop:
do {
$PassWord = -join ([char[]]"!##$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" | Get-Random -Count 10)
} until (Test-DomainPassword -Password $PassWord)
If you add the -Verbose switch to the function call, you will see the reason why a password would fail.
Of course, this cannot test if a password meets the domains history requirement, that remains a trial and error thing..
Or you could re-use the code from Test-DomainPassword and create a helper function that returns a valid password like this:
function New-DomainPassword {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $false)]
[int]$Length = 10,
[Parameter(Mandatory = $false)]
[string]$SamAccountName = $null,
[Parameter(Mandatory = $false)]
[string]$DisplayName = $null
)
# [Microsoft.ActiveDirectory.Management.ADEntity]
$passwordPolicy = Get-ADDefaultDomainPasswordPolicy -ErrorAction SilentlyContinue
$passwordLength = [math]::Max($Length, $passwordPolicy.MinPasswordLength)
$count = 1
while ($true) {
Write-Verbose ("Generating valid password attempt: {0}" -f $count++)
$password = -join ([char[]]"!##$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" | Get-Random -Count $passwordLength)
if (![string]::IsNullOrWhiteSpace($SamAccountName) -and $password -match $SamAccountName) {
continue # bad password, skip and try another one
}
if (![string]::IsNullOrWhiteSpace($DisplayName)) {
# if ANY PART of the display name that is split by the characters below, the password should fail the complexity rules.
$tokens = $DisplayName.Split(",.-,_ #`t")
$bad = foreach ($token in $tokens) {
if (($token) -and ($password -match $token)) { $true; break }
}
if ($bad) { continue } # bad password, skip and try another one
}
if ($passwordPolicy.ComplexityEnabled) {
# check for presence of
# - Uppercase: A through Z, with diacritic marks, Greek and Cyrillic characters
if ($password -cnotmatch "[A-Z\p{Lu}\s]") {
continue # bad password, skip and try another one
}
# - Lowercase: a through z, sharp-s, with diacritic marks, Greek and Cyrillic characters
if ($password -cnotmatch "[a-z\p{Ll}\s]") {
continue # bad password, skip and try another one
}
# - Base 10 digits (0 through 9)
if ($password -notmatch "[\d]") {
continue # bad password, skip and try another one
}
# - Nonalphanumeric characters: ~!##$%^&*_-+=`|\(){}[]:;”‘<>,.?/
if ($password -notmatch "[^\w]") {
continue # bad password, skip and try another one
}
}
# apparently all tests succeeded, so break out of the while loop
break
}
# return the new password
$password
}
and use it like this:
$UserList = Import-CSV -Path C:\temp\CreateUsers.csv
$targetOU = 'OU=Test,DC=contoso,DC=local'
$upnDomain = 'contoso.local'
$Path = "C:\temp\output.csv"
foreach ($Person in $UserList) {
#If username does not exist
if(-not(Get-ADUser -Filter "SamAccountName -eq '$($Person.Sam)'")) {
$accountName = $Person.Sam
$displayName = "$($Person.Firstname) $($Person.Lastname)"
$Password = New-DomainPassword -Length 10 -SamAccountName $accountName -DisplayName $displayName
$useritems = #{
GivenName = $Person.Firstname
Surname = $Person.LastName
AccountPassword = ConvertTo-SecureString -String $Password -AsPlainText -force
ChangePasswordAtLogon = $false
Enabled = $true
DisplayName = $displayName
Name = "$($Person.Firstname) $($Person.Lastname)"
SamAccountName = $accountName
UserPrincipalName = "$($Person.Sam)#$upnDomain"
Path = $targetOU
}
New-ADUser #useritems
Add-Content -Path $Path -Value "Username: $accountName Password: $Password"
}
}
Your random password generator is very poor and you do not guarantee that it meets your requirements. There is a .NET function, that you can use to generate random passwords: .GeneratePassword(Int32, Int32).
The first argument specifies the length of the password and the second argument specifies the minimum number of special characters.
You can use the following PowerShell function to generate a random password according to your needs:
function Get-RandomPassword {
param($Length = 8) # Default length of 8, if not provided
# Generate a random password with at least one special character
$password = [System.Web.Security.Membership]::GeneratePassword($Length, 1)
# Check password complexity
if ($password -cnotmatch '[A-Z]' -or $password -cnotmatch '[a-z]' -or $password -cnotmatch '\d') {
# Recursively generate a new password if the complexity requirements are not met
return Get-RandomPassword -Length $Length
} else {
# Return the password, if the complexity requirements are met
return $password
}
}
Example usage:
PS C:\> Get-RandomPassword -Length 10
A8$JfE1e%i
The function generates a random password of the provided length and with at least one special character. It then checks if all other (your individual) requirements are met. If not, the function will call itself recursively, until a password is found, that meets all complexity criteria.
To be honest, it's not the best design, as it might run forever in theory, but you will rather win a lottery than seeing this function running forever ;-)
I have a powershell script that measures download time on some pages, however I get the error above, I am unsure what I am doing wrong
error is
Cannot bind argument to parameter 'InputObject' because it is null.
function ResponseTime($CommonName,$URL, $environment)
{
$Times = 5
$i = 0
$TotalResponseTime = 0
Write-HOst $URL
While ($i -lt $Times) {
$Request = New-Object System.Net.WebClient
$Request.UseDefaultCredentials = $true
$Start = Get-Date
Write-HOst $URL
$PageRequest = $Request.DownloadString($URL)
$TimeTaken = ((Get-Date) - $Start).TotalMilliseconds
$Request.Dispose()
$i ++
$TotalResponseTime += $TimeTaken
}
$AverageResponseTime = $TotalResponseTime / $i
Write-Host Request to $CommonName took $AverageResponseTime ms in average -ForegroundColor Green
$details = #{
Date = get-date
AverageResponseTime = $AverageResponseTime
ResponseTime = $Destination
Environment = $environment
}
$results += New-Object PSObject -Property $details
$random = Get-Random -minimum 1 -maximum 30
Start-Sleep -s $random
}
#PRODUCTION
ResponseTime -commonname 'app homepage' -URL 'https://url1' -environment 'PRODUCTION'
ResponseTime -commonname 'department homepage' -URL 'https://url2' -environment 'PRODUCTION'
$results | export-csv -Path c:\so.csv -NoTypeInformation
Reviewing your last edit, it seems that $results simply returns $null (As your error says)
The only line setting $results is $results += New-Object PSObject -Property $details
It is not in the scope of your Export-CSV call and - even if it would, $results could be empty, if this line is not called.
You should IMHO set it to e.g. an ArrayList like follows:
$results = New-Object -TypeName System.Collections.ArrayList
And add items to it via
$times = ResponseTime -commonname '' #etc
$results.Add($times) | Out-Null
This gives you an ArrayList - even if there are no items in it - which can easily be transformed to CSV and other formats.
#Clijsters has given the correct answer; i.e. the issue being the scope of your $results variable.
This answer just provides a bit of a code review to help you with other bits going forwards...
function Get-ResponseTime {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$CommonName
,
[Parameter(Mandatory = $true)]
[string]$URL
,
[Parameter(Mandatory = $true)]
[string]$Environment
,
[Parameter(Mandatory = $false)]
[int]$Times = 5
)
[System.Int64]$TotalResponseTime = 0
[System.Diagnostics.Stopwatch]$stopwatch = New-Object 'System.Diagnostics.Stopwatch'
Write-Verbose "Processing URL: $URL"
1..$times | foreach-object {
[System.Net.WebClient]$Request = New-Object 'System.Net.WebClient'
$Request.UseDefaultCredentials = $true
Write-Verboset "Call $_ to URL: $URL"
$stopwatch.Restart()
$PageRequest = $Request.DownloadString($URL)
$stopwatch.Stop()
$TimeTaken = $stopwatch.Elapsed.TotalMilliseconds
$Request.Dispose()
$TotalResponseTime += $TimeTaken
}
$AverageResponseTime = $TotalResponseTime / $Times
Write-Verbose "Request to $CommonName took $AverageResponseTime ms on average"
$details = #{
Date = get-date
AverageResponseTime = $AverageResponseTime
#ResponseTime = $Destination #this is not declared anywhere / don't know what this field's for
Environment = $environment
}
Write-Output (New-Object 'PSObject' -Property $details)
#do you really want a delay here? Doesn't make much sense... may make sense to include a delay in the above loop; i.e. to stagger your tests?
#$random = Get-Random -minimum 1 -maximum 30
#Start-Sleep -s $random
}
#PRODUCTION
[PSObject[]]$results = #(
(Get-ResponseTime -commonname 'app homepage' -URL 'https://url1' -environment 'PRODUCTION' -Verbose)
,(Get-ResponseTime -commonname 'department homepage' -URL 'https://url2' -environment 'PRODUCTION' -Verbose)
)
$results | Export-Csv -LiteralPath 'c:\so.csv' -NoTypeInformation
Use verb-noun function names (e.g. Get-Item). What is the naming convention for Powershell functions with regard to upper/lower case usage?
Use "Cmdlets" (Advanced Functions) instead of (Basic) Functions; they're basically the same thing, only tagged with [Cmdletbinding()]. The reason for this you get support for functionality such as verbose output. http://www.lazywinadmin.com/2015/03/standard-and-advanced-powershell.html
Use a stopwatch to time processes (you could also use measure-command; but any output would be suppressed / consumed by the measure-command function). Timing a command's execution in PowerShell
Have your cmdlet output its values to the pipeline via Write-Output (or you can leave off the function name; any output caused by placing a variable with nothing to process it will be fed to the pipeline; i.e. write-object $a is the same as a line solely consisting of $a).
Capture the output into your $results variable outside of the function, and handle the results there.
I've been browsing the web trying to find a way if possible to email a low disk space alert from a Gmail account to a shared mail box using power shell but Im struggling with a query I've managed to piece together.
$EmailFrom = "FromEmail#Gmail.com"
$EmailTo = "ToEmail#Gmail.com"
$SMTPServer = "smtp.gmail.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("Username", "Password");
$Computers = "Local Computer"
$Subject = "Disk Space Storage Report"
$Body = "This report was generated because the drive(s) listed below have less than $thresholdspace % free space. Drives above this threshold will not be listed."
[decimal]$thresholdspace = 50
$tableFragment = Get-WMIObject -ComputerName $computers Win32_LogicalDisk `
| select __SERVER, DriveType, VolumeName, Name, #{n='Size (Gb)' ;e={"{0:n2}" -f ($_.size/1gb)}},#{n='FreeSpace (Gb)';e={"{0:n2}" -f ($_.freespace/1gb)}}, #{n='PercentFree';e={"{0:n2}" -f ($_.freespace/$_.size*100)}} `
| Where-Object {$_.DriveType -eq 3} `
| ConvertTo-HTML -fragment
$regexsubject = $Body
$regex = [regex] '(?im)<td>'
if ($regex.IsMatch($regexsubject)) {$smtpclinet.send($fromemail, $EmailTo, $Subject, $Body)}
Script runs but nothing happens, any help would be fantastic!!!
My version would be longer because I'd have made a substitute for Send-MailMessage such that swapping between mine and Send-MailMessage is trivial.
This is one possible way of doing it. There are good uses for the Fragment parameter on ConvertTo-Html, but not much of a justification to do so here.
This is a script and expected to be a .ps1 file. Mandatory things I don't really want to hard-code beyond a default are set in the param block.
param(
[String[]]$ComputerName = $env:COMPUTERNAME,
[Decimal]$Theshold = 0.5,
[PSCredential]$Credential = (Get-Credential)
)
#
# Supporting functions
#
# This function acts in much the same way as Send-MailMessage.
function Send-SmtpMessage {
param(
[Parameter(Mandatory = $true, Position = 1)]
[String[]]$To,
[Parameter(Mandatory = $true, Position = 2)]
[String]$Subject,
[String]$Body,
[Switch]$BodyAsHtml,
[String]$SmtpServer = $PSEmailServer,
[Int32]$Port,
[Switch]$UseSSL,
[PSCredential]$Credential,
[Parameter(Mandatory = $true)]
[String]$From
)
if ([String]::IsNullOrEmtpy($_)) {
# I'd use $pscmdlet.ThrowTerminatingError for this normally
throw 'A value must be provided for SmtpServer'
}
# Create a mail message
$mailMessage = New-Object System.Net.Mail.MailMessage
# Email address formatting si validated by this, allowing failure to kill the command
try {
foreach ($recipient in $To) {
$mailMessage.To.Add($To)
}
$mailMessage.From = $From
} catch {
$pscmdlet.ThrowTerminatingError($_)
}
$mailMessage.Subject = $Subject
$mailMessage.Body = $Body
if ($BodyAsHtml) {
$mailMessage.IsBodyHtml = $true
}
try {
$smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $Port)
if ($UseSSL) {
$smtpClient.EnableSsl = $true
}
if ($psboundparameters.ContainsKey('Credential')) {
$smtpClient.Credentials = $Credential.GetNetworkCredential()
}
$smtpClient.Send($mailMessage)
} catch {
# Return errors as non-terminating
Write-Error -ErrorRecord $_
}
}
#
# Main
#
# This is inserted before the table generated by the script
$PreContent = 'This report was generated because the drive(s) listed below have less than {0} free space. Drives above this threshold will not be listed.' -f ('{0:P2}' -f $Threshold)
# This is a result counter, it'll be incremented for each result which passes the threshold
$i = 0
# Generate the message body. There's not as much error control around WMI as I'd normally like.
$Body = Get-WmiObject Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $ComputerName | ForEach-Object {
# PSCustomObject requires PS 3 or greater.
# Using Math.Round below means we can still perform numeric comparisons
# Percent free remains as a decimal until the end. Programs like Excel expect percentages as a decimal (0 to 1).
[PSCustomObject]#{
ComputerName = $_.__SERVER
DriveType = $_.DriveType
VolumeName = $_.VolumeName
Name = $_.Name
'Size (GB)' = [Math]::Round(($_.Size / 1GB), 2)
'FreeSpace (GB)' = [Math]::Round(($_.FreeSpace / 1GB), 2)
PercentFree = [Math]::Round(($_.FreeSpace / $_.Size), 2)
}
} | Where-Object {
if ($_.PercentFree -lt $Threshold) {
$true
$i++
}
} | ForEach-Object {
# Make Percentage friendly. P2 adds % for us.
$_.PercentFree = '{0:P2}' -f $_.PercentFree
$_
} | ConvertTo-Html -PreContent $PreContent | Out-String
# If there's one or more warning to send.
if ($i -gt 0) {
$params = #{
To = "ToEmail#Gmail.com"
From = "FromEmail#Gmail.com"
Subject = "Disk Space Storage Report"
Body = $Body
SmtpServer = "smtp.gmail.com"
Port = 587
UseSsl = $true
Credential = $Credential
}
Send-SmtpMessage #params
}
Let me preface by noting this is only my third day with powershell, sorry if this is common knowledge.
This is to create a randomly generated password with requirements. I've set the minimums to 2 for each value, but it doesn't always have a minimum of 2 of each when the password is generated. I think it may be because of the Get-Random -count 10 is pulling from the pool that the rest of the string has created. I’m unsure how i would force it to create a password of 10-12 characters with a minimum of each requirement specified. Can/Should i validate the password after it's been created?
$ErrorActionPreference = "silentlycontinue"
function OnApplicationLoad {
#Note: This function is not called in Projects
#Note: This function runs before the form is created
#Note: To get the script directory in the Packager use: Split-Path $hostinvocation.MyCommand.path
#Note: To get the console output in the Packager (Windows Mode) use: $ConsoleOutput (Type: System.Collections.ArrayList)
#Important: Form controls cannot be accessed in this func
else{Stop-Process -name powershell.exe}
return $true #return true for success or false for failure
}
function OnApplicationExit {
#Note: This function is not called in Projects
#Note: This function runs after the form is closed
$script:ExitCode = 0 #Set the exit code for the Packager
}
#######Static Password Resources######
$caps = [char[]] "ABCDEFGHJKMNPQRSTUVWXY"
$lows = [char[]] "abcdefghjkmnpqrstuvwxy"
$nums = [char[]] "2346789"
$spl = [char[]] "#%$+<=>?"
$ofs = ""
####################Starts code############################
function Call-test_pff {
$form1 = New-Object 'System.Windows.Forms.Form'
$form1.ClientSize = '514, 640'
$form1.Name = "form1"
$form1.Text = "Password tool"
$form1.add_Load($form1_Load)
#Add Icon to window
$Icon = [system.drawing.icon]::ExtractAssociatedIcon($PSHOME + "\powershell.exe")
$form1.icon = $Icon
#form1: Button1 "Generate password"
$button1 = New-Object system.windows.forms.button
$button1.location = "10, 25"
$button1.size = "125, 35"
$button1.text = "Generate password"
$button1.add_click({
$first = Get-Random -Minimum 2
$second = Get-Random -Minimum 2
$third = Get-Random -Minimum 2
$fourth = Get-Random -Minimum 2
$pwd = [string](#($nums | Get-Random -Count $first) + #($lows | Get-Random -Count $second) + #($caps | Get-Random -Count $third) + #($spl | Get-Random -Count $fourth) | Get-Random -Count 10)
$textbox1.text = $pwd
})
$form1.controls.add($button1)
#form1: Label4: "Password"
$label4 = New-Object system.windows.forms.label
$label4.location = "10, 475"
$label4.size = "200, 20"
$label4.text = "Password:"
$form1.controls.add($label4)
#form1: password box "Password"
$textbox1 = New-Object system.windows.forms.textbox
$textbox1.location = "10, 500"
$textbox1.size = "200, 30"
$textbox1.multiline = $true
$textbox1.readonly = $true
$form1.controls.add($textbox1)
#####################Stop Code Here#####################
#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($Form_StateCorrection_Load)
#Clean up the control events
$form1.add_FormClosed($Form_Cleanup_FormClosed)
#Show the Form
return $form1.ShowDialog()
} #End Function
#Call OnApplicationLoad to initialize
if((OnApplicationLoad) -eq $true)
{
#Call the form
Call-test_pff | Out-Null
#Perform cleanup
OnApplicationExit
}
Assuming that your problem is in this code:
$first = Get-Random -Minimum 2
$second = Get-Random -Minimum 2
$third = Get-Random -Minimum 2
$fourth = Get-Random -Minimum 2
$pwd = [string](#($nums | Get-Random -Count $first) + #($lows | Get-Random -Count $second) + #($caps | Get-Random -Count $third) + #($spl | Get-Random -Count $fourth) | Get-Random -Count 10)
You have two related problems:
You're specifying no maximum for get-random, so you are getting very large numbers in most cases. As a result, $nums|get-random -count $first is always going to return the whole array (but in a random order) - you're asking for a random collection of (for example) 1000 selections from a list of fewer than 10.
Because of the above, Your trailing get-random -count 10 is picking 10 random characters from the entire search space ($caps, $lows,$lows,$spl)
So, you need to do two things:
Get a subset of each of your character types by limiting get-random to both a minimum and maximum.
Ensure that you get the correct distribution of characters in that final line. This can't be done with the current final line, because in theory you could end up with 10 upper-case characters if the random distribution fell that way
To solve the first, by limiting your counts to just the length of those source arrays:
$caps = [char[]] "ABCDEFGHJKMNPQRSTUVWXY"
$lows = [char[]] "abcdefghjkmnpqrstuvwxy"
$nums = [char[]] "2346789"
$spl = [char[]] "#%$+<=>?"
$ofs = ""
$first = Get-Random -Minimum 2 -Maximum $nums.Length;
$second = Get-Random -Minimum 2 -Maximum $lows.Length;
$third = Get-Random -Minimum 2 -Maximum $caps.Length;
$fourth = Get-Random -Minimum 2 -Maximum $spl.Length;
But this still doesn't solve the second problem, because you will be passing more than 50 characters into that final get-random -count10`, very easily excluding one or more required character types.
If you can deal with a set number of each type of character (in this case, 2 numbers, 3 upper, 3 lower, 2 special), do the following:
$caps = [char[]] "ABCDEFGHJKMNPQRSTUVWXY"
$lows = [char[]] "abcdefghjkmnpqrstuvwxy"
$nums = [char[]] "2346789"
$spl = [char[]] "#%$+<=>?"
$ofs = ""
$first = $nums | Get-Random -count 2;
$second = $caps | Get-Random -count 3;
$third = $lows | Get-Random -count 3;
$fourth = $spl | Get-Random -count 2;
$pwd = [string](#($first) + #($second) + #($third) + #($fourth) | Get-Random -Count 10)
$pwd
This picks random items in the correct quantity from each group, then randomizes the order of those characters.
I'm new to powershell. I read some lines on www.powershell.com. Now I need your help to solve a problem. I want to read the UUID from clients in the Network. Therefore I created a document "pcs.txt" where all PCs are stored.
$pc = Get-Content pcs.txt #Read content of file
$cred = Get-Credential “domain\user”
for ($i=0; $i -lt $pc.length; $i++) {
$Result=test-connection -ComputerName $pc[$i] -Count 1 -Quiet
If ($Result -eq 'True')
{
$uuid = (Get-WmiObject Win32_ComputerSystemProduct -ComputerName $pc[$i] -Credential $cred).UUID
$Ausgabe=$pc[$i] + ';'+$uuid
$Ausgabe
}
else
{
$Ausgabe=$pc[$i] + '; UUID nicht erhalten'
$Ausgabe
}
}
First I test if the ping works. When the ping works I try to get the uuid.
Sometimes I don't get the uuid even if the ping worked. So I would like to code a timeout, which say -> go to next pc when you don't have the uuid after 2 seconds.
Can you help me please?
Alas, there is no timeout parameter for Get-WmiObject commandlet. There is a feature request in MS Connect, but it is from 2011 and still open.
A workaround, which I haven't tested is available by using System.Management. I'll copy-and-paste it here in case the link goes dead. (And I hate SO answers that only contain links to resouces that may or may not exist...)
Function Get-WmiCustom([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15){
$ConnectionOptions = new-object System.Management.ConnectionOptions
$EnumerationOptions = new-object System.Management.EnumerationOptions
$timeoutseconds = new-timespan -seconds $timeout
$EnumerationOptions.set_timeout($timeoutseconds)
$assembledpath = "\\" + $computername + "\" + $namespace
#write-host $assembledpath -foregroundcolor yellow
$Scope = new-object System.Management.ManagementScope $assembledpath, $ConnectionOptions
$Scope.Connect()
$querystring = "SELECT * FROM " + $class
#write-host $querystring
$query = new-object System.Management.ObjectQuery $querystring
$searcher = new-object System.Management.ManagementObjectSearcher
$searcher.set_options($EnumerationOptions)
$searcher.Query = $querystring
$searcher.Scope = $Scope
trap { $_ } $result = $searcher.get()
return $result
}
I found a good workaround!
http://theolddogscriptingblog.wordpress.com/2012/05/11/wmi-hangs-and-how-to-avoid-them/
Here my working code:
$pc = Get-Content pcs.txt #FILE FROM THE HARDDISK
$cred = Get-Credential “DOMAIN\USER” #
for ($i=0; $i -lt $pc.length; $i++)
{
$Result=test-connection -ComputerName $pc[$i] -Count 1 -Quiet
If ($Result -eq 'True')
{
$WMIJob = Get-WmiObject Win32_ComputerSystemProduct -ComputerName $pc[$i] -Credential $cred -AsJob
$Timeout=Wait-Job -ID $WMIJob.ID -Timeout 1 # the Job times out after 1 seconds.
$uuid = Receive-Job $WMIJob.ID
if ($uuid -ne $null)
{
$Wert =$uuid.UUID
$Ausgabe=$pc[$i] + ';'+$Wert
$Ausgabe
}
else
{
<#$b = $error | select Exception
$E = $b -split (:)
$x = $E[1]
$Error.Clear() #>
$Ausgabe=$pc[$i] + '; got no uuid'
$Ausgabe
}
}
else
{
$Ausgabe='PC not reached through ping.'
$Ausgabe
}
}
I hope I can help somebody with that