I already have my credentials stored in an xml file.
$myCredential=Get-Credential -Message "Enter the credentials."
$myCredential | Out-File "C:\cred.xml"
Now, I have a script that prompts and gets new credential when it is run.
$newCredential= Get-Credential -Message "Enter your credential."
So, how do I check if the newly provided credential is matching with the old credential without decrypting the credentials to human understandable actual plain text?
Here is how to securely compare two SecureString objects without decrypting them:
# Safely compares two SecureString objects without decrypting them.
# Outputs $true if they are equal, or $false otherwise.
function Compare-SecureString {
param(
[Security.SecureString]
$secureString1,
[Security.SecureString]
$secureString2
)
try {
$bstr1 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString1)
$bstr2 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString2)
$length1 = [Runtime.InteropServices.Marshal]::ReadInt32($bstr1,-4)
$length2 = [Runtime.InteropServices.Marshal]::ReadInt32($bstr2,-4)
if ( $length1 -ne $length2 ) {
return $false
}
for ( $i = 0; $i -lt $length1; ++$i ) {
$b1 = [Runtime.InteropServices.Marshal]::ReadByte($bstr1,$i)
$b2 = [Runtime.InteropServices.Marshal]::ReadByte($bstr2,$i)
if ( $b1 -ne $b2 ) {
return $false
}
}
return $true
}
finally {
if ( $bstr1 -ne [IntPtr]::Zero ) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr1)
}
if ( $bstr2 -ne [IntPtr]::Zero ) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr2)
}
}
}
You can use the above function to compare the Password property of two PSCredential objects thus:
$theyMatch = Compare-SecureString $cred1.Password $cred2.Password
if ( $theyMatch ) {
...
}
You can use the GetNetworkCredential() Method to get the plain text without saving it anywhere, just validate it.
if ($newCredential.GetNetworkCredential().Password -eq $oldCredential.GetNetworkCredential().Password )
{
return "Password is match"
}
Related
How to control the while loop using boolean in Powershell?
param
(
[Parameter(Mandatory = $true, HelpMessage = "Name. -1 for all names.")]
[string]$name
)
try
{
$selected = $false
while($selected -eq $false)
{
if($name -eq "-1")
{
write-host "Displaying all names"
Get-Name | select name
}
else
{
# do some code here
$selected = $true
}
}
}
catch
{
'Name {0} was not found. Provide a valid name.' -f $name
exit(0)
}
Expected behavior:
User is prompted to enter name
If the user doesn't know the name, they can type -1
When -1 typed, the user is presented with all names
Code doesn't quit while loop
Just set $selected to $true to exit the loop when user enter "-1" in $name.
I don't really understand why you loop ?
So I'm trying to create a custom PsAnalyzer rule for the office, and it's my first time using $ast based commands/variables. As a result I'm at a bit of a loss.
I've been using a couple of sites to get my head around the [System.Management.Automation.Language] object class, namely this, this, and this.
For testing purposes I'm using the function below - dodgy looking params intended.
Function IE {
[cmdletbinding()]
Param(
$Url,
$IEWIDTH = 550,
$IeHeight = 450
)
$IE = new-object -comobject InternetExplorer.Application
$IE.top = 200 ; $IE.width = $IEWIDTH ; $IE.height = $IEHEIGHT
$IE.Left = 100 ; $IE.AddressBar = $FALSE ; $IE.visible = $TRUE
$IE.navigate( "file:///$Url" )
}
Then with the code below, I'd expect $IEWIDTH as the only param to fail.
Function Measure-PascalCase {
<#
.SYNOPSIS
The variables names should be in PascalCase.
.DESCRIPTION
Variable names should use a consistent capitalization style, i.e. : PascalCase.
.EXAMPLE
Measure-PascalCase -ScriptBlockAst $ScriptBlockAst
.INPUTS
[System.Management.Automation.Language.ScriptBlockAst]
.OUTPUTS
[Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
.NOTES
https://msdn.microsoft.com/en-us/library/dd878270(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/ms229043(v=vs.110).aspx
https://mathieubuisson.github.io/create-custom-rule-psscriptanalyzer/
#>
[CmdletBinding()]
[OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
Param
(
[Parameter(Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.Language.ScriptBlockAst]
$ScriptBlockAst
)
Process {
$Results = #()
try {
#region Define predicates to find ASTs.
[ScriptBlock]$Predicate = {
Param ([System.Management.Automation.Language.Ast]$Ast)
[bool]$ReturnValue = $false
if ( $Ast -is [System.Management.Automation.Language.ParameterAst] ){
[System.Management.Automation.Language.ParameterAst]$VariableAst = $Ast
if ( $VariableAst.Left.VariablePath.UserPath -eq 'i' ){
$ReturnValue = $false
} elseif ( $VariableAst.Left.VariablePath.UserPath.Length -eq 3 ){
$ReturnValue = $false
} elseif ($VariableAst.Left.VariablePath.UserPath -cnotmatch '^([A-Z][a-z]+)+$') {
$ReturnValue = $True
}
}
return $ReturnValue
}
#endregion
#region Finds ASTs that match the predicates.
[System.Management.Automation.Language.Ast[]]$Violations = $ScriptBlockAst.FindAll($Predicate, $True)
If ($Violations.Count -ne 0) {
Foreach ($Violation in $Violations) {
$Result = New-Object `
-Typename "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" `
-ArgumentList "$((Get-Help $MyInvocation.MyCommand.Name).Description.Text)",$Violation.Extent,$PSCmdlet.MyInvocation.InvocationName,Information,$Null
$Results += $Result
}
}
return $Results
#endregion
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
}
}
Export-ModuleMember -Function Measure-*
Instead I get:
Line Extent Message
---- ------ -------
6 $Url Variable names should use a consistent capitalization style, i.e. : PascalCase.
7 $IEWIDTH = 550 Variable names should use a consistent capitalization style, i.e. : PascalCase.
8 $IeHeight = 450 Variable names should use a consistent capitalization style, i.e. : PascalCase.
6 $Url Variable names should use a consistent capitalization style, i.e. : PascalCase.
7 $IEWIDTH = 550 Variable names should use a consistent capitalization style, i.e. : PascalCase.
8 $IeHeight = 450 Variable names should use a consistent capitalization style, i.e. : PascalCase.
Any ideas on what I'm doing wrong? I found another site here, if I use that method to test individual variables at a time, I get the results I'd expect to see.
To do that, add the following line at the end of the function IE,
[System.Management.Automation.Language.Parser]::ParseInput($MyInvocation.MyCommand.ScriptContents, [ref]$null, [ref]$null)
Then this code below will give you length numbers.
$stuffast = .\FunctionIe.ps1
$left = $Stuffast.FindAll({$args[0] -is [System.Management.Automation.Language.AssignmentStatementAst]},$true)
$left[0].Left.Extent.Text.Length
$left[0].Left.VariablePath.UserPath.Length
Unlike an AssignmentStatementAst, which has a Left and Right(-hand) value property, the ParameterAst has a Name and DefaultValue property, so you'll want to use Name in your predicate block:
$predicate = {
# ...
if ( $Ast -is [System.Management.Automation.Language.ParameterAst] ) {
[System.Management.Automation.Language.ParameterAst]$VariableAst = $Ast
if ( $VariableAst.Name.VariablePath.UserPath -eq 'i' ) {
$ReturnValue = $false
}
elseif ( $VariableAst.Name.VariablePath.UserPath.Length -eq 3 ) {
$ReturnValue = $false
}
elseif ($VariableAst.Name.VariablePath.UserPath -cnotmatch '^([A-Z][a-z]+)+$') {
$ReturnValue = $True
}
}
# ...
}
Alternatively, flip your predicate-logic around to search for VariableExpressionAst's where the parent node is one of AssignmentStatementAst or ParameterAst:
$predicate = {
param($ast)
$ValidParents = #(
[System.Management.Automation.Language.ParameterAst]
[System.Management.Automation.Language.AssignmentStatementAst]
)
if($ast -is [VariableExpressionAst] -and $ValidParents.Where({$ast -is $_}, 'First')){
[System.Management.Automation.Language.VariableExpressionAst]$variableAst = $ast
# inspect $variableAst.VariablePath here
}
return $false
}
I am attempting to mock up a data structure and logic to handle deferred log writing, and I am running into an odd situation. if ([collections.arrayList]$deferredLog = Get-PxDeferredLogItems) { errors when the returned arrayList is only one item, with
Cannot convert the "logged: 07/20/2019 10:56:29" value of type "System.String" to type "System.Collections.ArrayList".
Once there are two items it's fine. Now I know an array of a single item needs to be forced to be an array because PS is sometimes too clever for it's own good, but I thought the Add method of a [collections.arrayList] produced an arrayList even on the first Add. AM I totally misunderstanding, or is my logic wrong somewhere else?
I also tried casting the results of the function call like this
if ([collections.arrayList]$deferredLog = [collections.arrayList](Get-PxDeferredLogItems)) { but that is even worse, it errors at every call. I also tried initializing the variable to an empty arrayList with Set-PxDeferredLogItems (New-Object collections.arrayList) in Main, rather the n initializing it on the first call of Add-PxDeferredLogItem, but that shows the same behavior, errors until the deferred log has two items. I verified this by changing the Write-Host directly after the erroring line to Write-Host "Process deferred items $($deferredLog.count)", and I get errors till it shows a 2, then everything works as expected.
function Get-PxDeferredLogItems {
return $script:deferredLogItems
}
function Set-PxDeferredLogItems {
param (
[collections.arrayList]$deferredLog
)
[collections.arrayList]$script:deferredLogItems = $deferredLog
}
function Add-PxDeferredLogItem {
param (
[string]$item
)
if (-not $script:deferredLogItems) {
[collections.arrayList]$script:deferredLogItems = New-Object collections.arrayList
}
[void]$script:deferredLogItems.Add($item)
}
function Add-PxLogContent {
param (
[string]$string
)
function Assert-PxWrite {
if ((Get-Random -minimum:1 -maximum:4) -gt 1) {
return $true
} else {
return $false
}
}
if ([collections.arrayList]$deferredLog = Get-PxDeferredLogItems) {
Write-Host "Process deferred items"
:deferredItemsWrite do {
if (Assert-PxWrite) {
Write-Host "$($deferredLog[0]) ($(Get-Date))"
$deferredLog.RemoveAt(0)
} else {
break :deferredItemsWrite
}
} while ($deferredLog.count -gt 0)
if ($deferredLog.count -eq 0) {
$deferredLogPending = $false
} else {
$deferredLogPending = $true
}
Set-PxDeferredLogItems $deferredLog
} else {
$deferredLogPending = $false
}
if (-not $deferredLogPending) {
if (Assert-PxWrite) {
Write-Host $string
} else {
Add-PxDeferredLogItem $string
}
} else {
Add-PxDeferredLogItem $string
}
}
### MAIN
$startTime = Get-Date
$endTime = $startTime + (New-TimeSpan -minutes:2)
Clear-Host
do {
Add-PxLogContent "logged: $(Get-Date)"
Start-SLeep -s:5
} while ((Get-Date) -lt $endTime)
if ($deferredLog = Get-PxDeferredLogItems) {
foreach ($item in $deferredLog) {
Write-Host "!$item"
}
}
EDIT: It seems to be related to passing the arrayList, as this works as expected when there are no functions involved.
Clear-Host
$deferredLogItems = New-Object collections.arrayList
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
[void]$deferredLogItems.Add('One')
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
[void]$deferredLogItems.Add('Two')
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
[void]$deferredLogItems.Add('Three')
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
function Get-DeferredLogItems {
if (-not $script:deferredLogItems) {
[collections.arrayList]$script:deferredLogItems = New-Object collections.arrayList
}
[void]$script:deferredLogItems.Add("$($script:deferredLogItems.count + 1)")
return $script:deferredLogItems
}
$script:deferredLogItems = $localDeferredLogItems = $null
foreach ($i in 1..5) {
[collections.arrayList]$localDeferredLogItems = Get-DeferredLogItems
Write-Host "$($localDeferredLogItems.getType()) $($localDeferredLogItems.count) $localDeferredLogItems"
}
EDIT2: Replaced the line # reference with the actual offending line of code, because Confusion.
EDIT3: For what it's worth, forcing the results of Get-DeferredLogItems into an array DOES work. But I still wonder why that's even needed. Why does the Add method produce a correct arrayList at first add but this gets #%#$ed up by PowerShell when passed as a return value?
I'm just learning switch to make my logic a bit cleaner, and it seems to work except I'm having trouble determining if my Read-Host value is a number (for the access point number to select).
## Give option to reset all aps on site
$continueVal = Read-Host "`nSpecify AP # to see more details or type 'Reset' to reset all APs in Store $Store"
## Start switch
$event = switch ($continueVal) {
[int]{
$apNumber = $continueVal
Query-AP($apNumber)
}
'Reset' {
Manage-Prelim($e = 2)
}
default {
Repeat
}
}
When I was using If/Else/ElseIf I'd use if($continueVal -gt 0) which would work, but still dirty. With switch it seems that -gt 0 is improper syntax and fails. How would I effectively check if the value of $continueVal is a number to pass it to the next function as $apNumber?
I don't want to pre-validate as possible options can come through as an integer or a string.
Here is another approach that uses parameters and parameter sets:
# testscript.ps1
[CmdletBinding(DefaultParameterSetName = "APNumber")]
param(
[Parameter(Mandatory = $true,ParameterSetName = "APNumber")]
[Int] $APNumber,
[Parameter(Mandatory = $true,ParameterSetName = "Controller")]
[String] $Controller,
[Parameter(Mandatory = $true,ParameterSetName = "Reset")]
[Switch] $Reset
)
switch ( $PSCmdlet.ParameterSetName ) {
"APNumber" {
"You specified -APNumber with value '$APNumber'"
break
}
"Controller" {
"You specified -Controller with value '$Controller'"
break
}
"Reset" {
"You specified -Reset"
break
}
}
This script is simple to use. Example usage:
testscript -APNumber 3
testscript -Controller "foo"
testscript -Reset
If you omit any parameters, it will prompt for the -APNumber parameter (since it specifies that as the default parameter set).
Now that I understand your question more, this can be done with switch -regex and parsing. Here is a short example:
do {
$response = Read-Host "Enter a response"
$valid = $true
switch -regex ( $response ) {
'^AP Number \d+$' {
$arg = [Regex]::Match($_,'\d+$').Value -as [Int]
Write-Host "You entered 'AP Number $arg'"
break
}
'^Controller \S+$' {
$arg = [Regex]::Match($_,'\S+$').Value
Write-Host "You entered 'Controller $arg'"
break
}
'^Reset$' {
Write-Host "You entered 'Reset'"
break
}
default {
$valid = $false
Write-Host "Invalid entry"
}
}
}
until ( $valid )
Note that this is more code than the parameter version, more complex, and you can't automate it.
I have created an app for back up and restore of computers. I also allows modification of ADObjects through the use of custom Profile.ps1 file. The app runs fine in the ISE with no errors and works properly no errors in Windows 7. However, when I try to run it in a newly imaged Windows 10 machine I get "Property Can Not Be Found" errors on all my object properties.
The thing is I can read all the properties when I fill comboboxes fine. The error only occurs when the form is submitted. I will attach 1 of the forms I am having a problem with. Again it runs fine in Windows 7, but not Windows 10.
Could this be a problem with Microsoft updates?
Also, yes, I am setting Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted.
Error message:
The property 'company' cannot be found on this object. Verify that the
property exist and can be set.
+ $CO.company = $company
+ Categoryinfo :InvalidOperation: (:) [] RuntimeExeption
Code:
. \\iotsdsp01pw\installs$\MoveToOU\PcDeployment\Profile.ps1
#region Validation Functions
function Validate-IsEmail ([string]$Email) {
return $Email -match "^(?("")("".+?""#)|(([0-9a-zA-Z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-zA-Z])#))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,6}))$"
}
function Validate-IsURL ([string]$Url) {
if ($Url -eq $null) {
return $false
}
return $Url -match "^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_]*)?$"
}
function Validate-IsName ([string]$Name, [int]$MaxLength) {
if ($MaxLength -eq $null -or $MaxLength -le 0) {
#Set default length to 40
$MaxLength = 40
}
return $Name -match "^[a-zA-Z''-'\s]{1,$MaxLength}$"
}
function Validate-IsIP ([string]$IP) {
return $IP -match "\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"
}
function Validate-IsEmptyTrim ([string]$Text) {
if ($text -eq $null -or $text.Trim().Length -eq 0) {
return $true
}
return $false
}
function Validate-IsEmpty ([string]$Text) {
return [string]::IsNullOrEmpty($Text)
}
function Validate-IsDate ([string]$Date) {
return [DateTime]::TryParse($Date, [ref](New-Object System.DateTime))
}
#endregion
$No_Load = {
$NewForm.Close()
#Initialize variables
$dateTime = Get-Date -Format G
$userName = (Get-WmiObject -Class Win32_ComputerSystem -Property UserName).UserName
$computerName = $env:computername
#Varables for display
$distinguishedName = (Get-dn computer cn $computerName)
$computerObject = (Get-ADObject $distinguishedName)
$organizationalUnit = (Get-ADObject "OU=Agencies, DC=state, DC=in, DC=us")
#Initialize Form Controls
$lblUserNameNewNo.Text = $userName
$lblComputerNameNewNo.Text = $computerName
$lblPhysicalLocationNewNo.Text = $computerObject.location
$txtBillingCodeNewNo.Text = $computerObject.departmentNumber
$comboboxAccountTypeNewNo.Text = $computerObject.extensionAttribute15
$comboboxOrganizationalUnitNewNo.Text = $computerObject.company
Load-ComboBox -ComboBox $comboboxOrganizationalUnitNewNo ($organizationalUnit.children | %{ $_.OU })
}
#region Control Helper Functions
function Load-ComboBox {
Param (
[ValidateNotNull()]
[Parameter(Mandatory = $true)]
[System.Windows.Forms.ComboBox]$ComboBox,
[ValidateNotNull()]
[Parameter(Mandatory = $true)]
$Items,
[Parameter(Mandatory = $false)]
[string]$DisplayMember,
[switch]$Append
)
if (-not $Append) {
$ComboBox.Items.Clear()
}
if ($Items -is [Object[]]) {
$ComboBox.Items.AddRange($Items)
} elseif ($Items -is [Array]) {
$ComboBox.BeginUpdate()
foreach ($obj in $Items) {
$ComboBox.Items.Add($obj)
}
$ComboBox.EndUpdate()
} else {
$ComboBox.Items.Add($Items)
}
$ComboBox.DisplayMember = $DisplayMember
}
#Validation
function ParameterValidate {
Param (
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[ValidateLength(1, 10)]
[String]$Text
)
return $true
}
$comboboxOrganizationalUnitNewNo_Validating = [System.ComponentModel.CancelEventHandler]{
#Check if the Name field is empty
$result = Validate-IsEmptyTrim $comboboxOrganizationalUnitNewNo.Text
if ($result -eq $true) {
#Mark a failure only if the Validation failed
$script:ValidationFailed = $true
#Display an error message
$errorprovider1.SetError($comboboxOrganizationalUnitNewNo, "Please select agency.");
} else {
#Clear the error message
$errorprovider1.SetError($comboboxOrganizationalUnitNewNo, "");
}
}
$txtBillingCodeNewNo_Validating = [System.ComponentModel.CancelEventHandler]{
#Check if the Name field is empty
$result = Validate-IsEmptyTrim $txtBillingCodeNewNo.Text
if ($result -eq $true) {
#Mark a failure only if the Validation failed
$script:ValidationFailed = $true
#Display an error message
$errorprovider1.SetError($txtBillingCodeNewNo, "Please enter billing code.");
} else {
#Clear the error message
$errorprovider1.SetError($txtBillingCodeNewNo, "");
}
}
$comboboxAccountTypeNewNo_Validating = [System.ComponentModel.CancelEventHandler]{
$result = Validate-IsEmptyTrim $comboboxAccountTypeNewNo.Text
if ($result -eq $true) {
#Mark a failure only if the Validation failed
$script:ValidationFailed = $true
#Display an error message
$errorprovider1.SetError($comboboxAccountTypeNewNo, "Please enter agency type.");
} else {
#Clear the error message
$errorprovider1.SetError($comboboxAccountTypeNewNo, "");
}
}
$control_Validated = {
#Pass the calling control and clear error message
$errorprovider1.SetError($this, "");
}
$No_FormClosing = [System.Windows.Forms.FormClosingEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.FormClosingEventArgs]
#Validate only on OK Button
if ($No.DialogResult -eq "OK") {
#Init the Validation Failed Variable
$script:ValidationFailed = $false
#Validate the Child Control and Cancel if any fail
$No.ValidateChildren()
#Cancel if Validation Failed
$_.Cancel = $script:ValidationFailed
}
}
#Events
$buttonColor_Click = {
#TODO: Place custom script here
$colordialog1.ShowDialog()
}
$linklblViewListNewNo_LinkClicked = [System.Windows.Forms.LinkLabelLinkClickedEventHandler]{
Start-Process "http://billingcodes.iot/"
}
$btnSubmitNewNo_Click = {
#TODO: Place custom script here
$company = $comboboxOrganizationalUnitNewNo.Text
$departmentNumber = $txtBillingCodeNewNo.Text
$accountType = $comboboxAccountTypeNewNo.Text
if ($accountType -eq "Seat") {
$accountType = " "
}
#Varables for Set-ADObject
$computerObject.company = $company
$computerObject.departmentNumber = $departmentNumber
$computerObject.extensionAttribute15 = $accountType
try {
$computerObject.SetInfo()
[Environment]::Exit(1)
} catch {
$labelDialogRedNewNo.Text = "AD computer object not found"
}
}
This is your culprit (from what I can see):
$No_Load = {
...
$computerObject = (Get-ADObject $distinguishedName)
...
}
...
$btnSubmitNewNo_Click = {
...
$computerObject.company = $company
...
}
You assign a computer object to the variable $computerObject in one scriptblock, and try to change one of its properties in another scriptblock. However, to be able to share a variable between scriptblocks you need to make it a global variable, otherwise you have two separate local variables that know nothing about each other.
$No_Load = {
...
$global:computerObject = Get-ADObject $distinguishedName
...
}
...
$btnSubmitNewNo_Click = {
...
$global:computerObject.company = $company
...
}
BTW, I doubt that this ever worked in Windows 7, since it failed with the same error on my Windows 7 test box.