How do you Re-prompt credential box if username/pw is incorrect?
Import-Module ActiveDirectory
Write-Host "Please enter Admin credential..."
$Creds = Get-Credential
My script runs everyline after even putting wrong username/pw (w error obvously)
I need Get-Credential to appear everytime the user/pass is invalid so I won't rerun the same script everytime I put in wrong information.
UPDATE:
Heres the sample script:
Import-Module ActiveDirectory
Write-Host "Please enter Admin credential..."
$Creds = Get-Credential
$Tpm = Get-wmiobject -ComputerName $wsid -Credential $Creds -Namespace ROOT\CIMV2\Security\MicrosoftTpm -Class Win32_Tpm
I need to get the TPM of another computer and will require to enter user/pw to do the job. What I need is to validate if the user/name pw is correct and if not, reprompt the Get-Credentials screen.
You can't validate credential with get-credential, instead you can use a try catch statement against the get-wmiobject, something like
$c=get-credential
try{
gwmi -ComputerName comp1 -Credential $c -Class win32_bios
}
catch [System.UnauthorizedAccessException]{
"wrong credential retry please"
$c=get-credential
}
function Test-Credentials() {
param (
[Parameter(Mandatory = $false)]
[PsCredential]$Creds
)
$IDX = 0 # Retry Limit Counter
$limit = 3 # Retry Limiter
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain')
while (!$DS.ValidateCredentials($Creds.UserName, $Creds.GetNetworkCredential().password) -and ($IDX -lt $limit)) {
$IDX++
Write-Host " - Failed - " -ForegroundColor Yellow
$Creds = Get-Credential -Message "Validaton Failed: Please Re-enter your credentials" -UserName $Creds.UserName
}
return $Creds
Related
I am working on developing PowerShell script to automate a task on a remote server by using Invoke-Command with WinRM.
The script will take the server IP, test WinRM and "Get-Credential" cmdlet to establish session and use Invoke-Command to run another script on remote server. I have made significant progress of what I want to achieve, however, I am having trouble on how to setup the code so that when I press the "Cancel" or "X" button on Get-Credential prompt it should abort the script and return to the regular PowerShell command line prompt.
Below is what I have so far, I have erased the comments and description of the code to keep the number of words less in here.
function SS
{
Add-Type -AssemblyName System.Windows.Forms
$BInput = [System.Windows.Forms.MessageBox]::Show('Do you want to proceed?', 'Confirmation',[System.Windows.Forms.MessageBoxButtons]::YesNo)
switch ($BInput)
{
"Yes" {
while ($true)
{
$server=Read-Host "Enter Server IP Address"
set-item -Path WSMan:\localhost\Client\TrustedHosts -Value "$server" -Force
if(Test-WSMan -ComputerName $server -ErrorAction SilentlyContinue)
{
Write-Host "$server is accessible, enter credentials to connect"
while ($true)
{
$creden=Get-Credential -Message "Please enter the server credentials that you want to connect"
$serversession = New-Pssession -ComputerName $server -Credential $creden -ErrorAction SilentlyContinue
if(-not($serversession))
{
write-warning "Credentials are not valild, please try again"
}
else
{
write-host "$server is connected, starting the workflow ......"
Invoke-Command -Session $serversession -FilePath "C:\Temp\XXX.ps1"
}
}
Break
}
else
{
write-host "Windows Remote Management (WinRM) protocol is not running, please check service and confirm."
}
}
Get-Pssession | Remove-PSSession
}
"No" {
Break
}
}
}
I understand I have to apply the changes / logic after this line
$creden=Get-Credential -Message "Please enter the server credentials that you want to connect"
But can't seem to find it yet. I looked online and have taken different approaches but no success so far. I would like to have opinions or recommendations on how to tackle this, appreciate your help.
Thanks
What i'm seeing is that you may be thinking too much into it. A simple if statement should do the trick, try:
$creden=Get-Credential -Message "Please enter the server credentials that you want to connect"
if(!$creden){break}
Continuing from my comment.
Try this refactor of your use case.
Point of note: Note fully tested since I do not have an environment at this time to test.
Function Start-WorkFlow
{
<#
.Synopsis
Execute a workflow
.DESCRIPTION
Sets up a WinRM session to a remote host to execute the defined workflow
.EXAMPLE
Start-WorkFlow
.EXAMPLE
swf
.INPUTS
Remote host IPAddress
Remove host credentials
.OUTPUTS
Resutls of teh workflow
.NOTES
V 0.0.1 - Prototype script. Clean-Up before production use
.COMPONENT
Stand-alone script
.ROLE
Administrative actions
.FUNCTIONALITY
Implemetned error logic for each code block
Restrict the user input to only be a proper IPAddress
Validate TCPIP state
Validate WSman state
Establish a new session
Process workflow
Exit session
#>
[cmdletbinding(SupportsShouldProcess)]
[Alias('swf')]
Param
(
)
If ((Read-Host -Prompt 'Do you want to proceed: [Yes/No]') -eq 'No' )
{Break}
Else
{
Do {$RemoteServerIPAddress = (Read-Host -Prompt 'Enter Server IP Address')}
Until ($RemoteServerIPAddress -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
Get-ChildItem -Path 'WSMan:\localhost\Client\TrustedHosts'
Try
{
(Test-Connection -ComputerName $RemoteServerIPAddress -Count 1 -ErrorAction Stop).IPV4Address
# Set-Item -Path 'WSMan:\localhost\Client\TrustedHosts' -Value $RemoteServerIPAddress -Force
Get-ChildItem -Path 'WSMan:\localhost\Client\TrustedHosts'
Try
{
Test-WSMan -ComputerName $RemoteServerIPAddress -ErrorAction Stop
"$RemoteServerIPAddress is accessible, enter credentials to connect"
Do
{
$Creds = $null
$CredMesssage = 'Please enter the remote server credentials that you want to connect.'
$CredMesssage = "$CredMesssage If credentials are not valid, you will be prompted to re-enter them."
$Creds = Get-Credential -Message $CredMesssage
if(-Not $creds)
{
Write-Warning -Message 'Credential request cancelled.'
Start-Sleep -Seconds 3
Exit
}
$NewPSSessionSplat = #{
ComputerName = $RemoteServerIPAddress
Credential = $Creds
Name = 'RemoteSessionName'
ErrorAction = 'Stop'
}
New-PSSession $NewPSSessionSplat
}
Until (Get-PSSession -Name 'RemoteSessionName')
"$RemoteServerIPAddress is connected, starting the workflow ......"
Invoke-Command -Session $RemoteServerSession -FilePath 'C:\Temp\XXX.ps1'
}
Catch
{
Write-Warning -Message 'Session connection results:'
$PSitem.Exception.Message
}
Finally
{
Get-PSSession |
Remove-PSSession -ErrorAction SilentlyContinue
}
}
Catch
{
Write-Warning -Message "
The remote server $RemoteServerIPAddress is not available
Exiting the session."
Start-Sleep -Seconds 3
Exit
}
}
}
Start-WorkFlow
I'm trying to store/retrieve a user's O365 credentials using the following function:
function Access-Credentials {
[CmdletBinding()]
[OutputType([System.Management.Automation.PSCredential])]
param (
[Parameter(Mandatory=$true)]
[String]
$App,
[Switch]
$Change
)
$App = Truncate-Filename -Filename $App
$Filename = $PSScriptRoot + "\SecCred-" + $App + ".bin"
# If we need to change the credentials, or if the current application's credentials don't exist, create
If( $Change -or ((Test-PathEx -Path $Filename) -ne $True) ) {
Try {
Get-Credential | Export-CliXml -Path $Filename
}
Catch [Exception] {
Write-Log -err -text "Fatal error, unable to create credentials XML file: $Filename. Ran into error: $PSItem. Exiting..."
exit
}
}
return Import-CliXml -Path $Filename
}
This gets called on the following line in my script:
$Cred = Access-Credentials -App "OWA Signatures" #Credentials are now stored against a file called "SecCred-OWASignatures.bin" so don't need to be authenticated in future
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $Cred -Authentication Basic –AllowRedirection -ErrorAction SilentlyContinue | Out-Null
Basically, if the credentials already exist, proceed - otherwise, request them and store them.
This works OK. What I'd like to do now is...
a) test if the credentials have authenticated successfully
b) if they haven't, allow the user to attempt to re-enter their credentials three times
c) if credentials are already stored, check them. if they don't authenticate, prompt the user to enter new credentials
I'm not really sure where to begin with this. I've attempted a while loop with an $attempts counter, but this appears to have done nothing - it simply repeatedly requests credentials.
$attempts = 0
While ( (-not($Session)) -or ($attempts -eq 3) ) {
Write-Log -warn -text "The credentials provided have not authenticated. Please try again..."
$Cred = Access-Credentials -Change -App "OWA Signatures" #Credentials are now stored against a file called "SecCred-OWASignatures.bin" so don't need to be authenticated in future
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $Cred -Authentication Basic –AllowRedirection -ErrorAction SilentlyContinue | Out-Null
#Increment the attempts counter
$attempts++
}
If( ($attempts -eq 3) -and (-not($Session)) ) {
Write-Log -err -text "Fatal error: unable to authenticate with O365 after three attempts. Please try different credentials."
exit
}
Could anyone point me in the right direction please?
I want to create a PowerShell script which will disable the windows account, the target host name will be provided as an argument. Only admin should be able to execute this task.
This is what I have tried. Could someone please tell me if this approach is right or is there any better way to do this.
param( [Parameter(Mandatory=$true)] [String] $TargetHost ,
[Parameter(Mandatory=$true)] [String] $TargetUserName ,
[String] $User ,
[String] $Password)
# Set up a trap to properly exit on terminating exceptions
trap [Exception] {
write-error $("TRAPPED: " + $_)
exit 1
}
function DeactivateAccount($TargetHost , $TargetUserName ,$User , $Password){
$TargetHost = $TargetHost #Target Host on which windows account deactivation will be done.
$TargetUserName = $TargetUserName #User Name of Target.
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() #Domain name of the localhost.
$localHost = [System.Net.Dns]::GetHostName()
$localIP = [System.Net.Dns]::GetHostAddresses("$localHost")
#if TargetHost and LocalHost are same.
if($localHost -like $TargetHost -OR $localIP -like $TargetHost) {
if($Domain -eq [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()){
$process = net user $TargetUsername /domain /active:no #Performs the operation on the domain controller in the computer's primary domain.
} else {
$process = net user $TargetUsername /active:no
}
Write-host " $TargetUsername account deactivated "
}
#If TargetHost is remote Host.
else {
$User = $User #Creds to perform admin function.
$Password = $Password
$SecurePassword = new-Object System.Security.SecureString #Convert password into secure string.
$Password.ToCharArray() | % { $SecurePassword.AppendChar($_) }
$Cred = New-Object -typename System.Management.Automation.PSCredential -argumentlist "$User",$securePassword
$newSession = New-PSSession -ComputerName "$TargetHost" -credential $Cred #Used PSSession for persistent connection and credentials to Specify a user account that has permission to perform this action.
$export_username = Invoke-Command -Session $newSession -ScriptBlock {$username=args[1]} # Invoke-Command command uses the Session parameter(here newSession) to run the commands in same session.
if($Domain -eq [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()){
$process = Invoke-Command -Session $newSession -ScriptBlock {net user $username /domain /active:no}
} else {
$process = Invoke-Command -Session $newSession -ScriptBlock {net user $username /active:no}
}
Write-host " $TargetUsername account deactivated "
Remove-PSSession $newSession # Closes Windows PowerShell sessions.
}
if(-not $?) { # Returns true if last command was successful.
Write-Error "Windows Deactivation Failed!!"
exit 1
}
}
DeactivateAccount($TargetHost , $TargetUserName ,$User , $Password)
Couple of things:
Your meant to show some code to show you tried but since you're new to Powershell I'll let that slide :)
Is it a local windows account you are trying to disable or an AD one? For the purpose of this I'll assume local.
Grab this module: https://gallery.technet.microsoft.com/PowerShell-Module-to-255637a3
The dude basically made a module for exactly what you want to do :)
Note: If you have Powershell 5.1+ you won't need the module they added new cmdlets to do this natively.
Credential-wise I wouldn't worry, Powershell can't bypass windows security, it will execute with the permissions of the user that ran the script unless your script specifically gives credentials for another user in the commands.
Let me know how you get on.
I am running Powershell on a remote computer that is not connected to the domain, does not have any modules and is running PS 2.0.
I want to contact the Active Directory of my domain, check if there is an entry for this computer and; if yes, delete that entry.
Checking the AD via ADSI for existance of the computer is easy. However the deleting does not work somehow.
Here is my code so far:
# Variables
$domain = "Test.com"
$Ldap = "LDAP://$domain"
$Global:AdsiSearcher = $Null
# Function to Delete PC
Function DeleteThisPc ()
{
$CurrentSearch = $Global:AdsiSearcher
$One = $CurrentSearch.FindOne()
$OPath = [adsi]$One.Path
$OPath.psbase.DeleteTree()
The Problem lies here. Even though $OPath is of type System.DirectoryServices.DirectoryEntry and the propertylist shows all properties, it does not allow me to delete the object.
Exception calling "DeleteTree" with "0" argument(s): "Logon failure:
unknown user name or bad password.
At C:\TEMP\Domjoin1.1.ps1:49 char:33 $OPath.psbase.DeleteTree <<<< ()
CategoryInfo: NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : DotNetMethodException
Code:
# Function to get a ADSISearcher and set it to the global-AdsiSearcher
Function ConnectAD ()
{
$domain = new-object DirectoryServices.DirectoryEntry($Ldap,"$domain\Bob",'1234')
$filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$ComputerName))"
$AdsiSearch = [adsisearcher]""
$AdsiSearch.SearchRoot = $domain
$AdsiSearch.Filter = $filter
$Global:AdsiSearcher = $AdsiSearch
}
# Main Function
Function Sub_Check-ADComputer()
{
ConnectAD
$CurSearch = $Global:AdsiSearcher.findOne()
if($CurSearch -ne $null)
{
DeleteThisPc
}
}
# Start
Sub_Check-ADComputer
Even though the issue seems to be obvious as the error states:
Logon failure: unknown user name or bad password.
The username and password is the same that I use to get the object from the AD in the first place. So it does work - do I somehow have to give the credentials again when trying to deleteTree() ? I also gave the User FullControl on the OU that the object is stored in.
Edit:
When I do it on another machine with PS 3.0 I get a different Error message:
Exception calling "DeleteTree" with "0" argument(s): "Access is
denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))"
I found the problem.
When using invoke command the variables are not transmitted unless specified by -argumentlist. Another approach I discovered was the following, which is the one I am using now and which works like a charm.
$domain = "DOMAINNAME"
$AdUser = "$domain\JoinDom"
$AdPW = "PASSWORD"
$AdPass = convertto-securestring -string $AdPW -AsPlainText -Force
$AdCred = new-object -typename System.Management.Automation.PSCredential -argumentlist $AdUser,$AdPass
$ThisComputer = $Env:COMPUTERNAME
$RetValue = $true
Function CheckExist ()
{
$ErrorActionPreference = ‘SilentlyContinue’
$Ascriptblock = $ExecutionContext.InvokeCommand.NewScriptBlock("get-adcomputer $ThisComputer")
$Ret = Invoke-Command -ComputerName SERVERNAME -ScriptBlock $Ascriptblock -Credential $AdCred
$ErrorActionPreference = ‘Continue’
return $Ret
}
$ExistBefore = CheckExist
if($ExistBefore -ne $null)
{
$scriptblock = $ExecutionContext.InvokeCommand.NewScriptBlock("Remove-ADComputer $ThisComputer")
Invoke-Command -ComputerName SERVERNAME -ScriptBlock $scriptblock -Credential $AdCred
$ExistAfter = CheckExist
if($ExistAfter -ne $null){$RetValue = $false}
}
if($RetValue -ne $false)
{
Add-computer -domainname $domain -credential $Adcred -OUPath "OU=MyOU,DC=DOMAIN,DC=DE"
Restart-Computer -Force
}
If your domain controller runs Windows Server 2008 or higher you could leverage PowerShell sessions to avoid having to work with ADSI.
Just run the following command:
Enter-PSSession -ComputerName domaincontroller.test.com -Credential (Get-Credential)
Then run Import-Module ActiveDirectory to allow you to use Get-ADComputer and Remove-ADComputer.
I currently have a VBScript that reads a list of servers, and attempts to verify the password of a specific userid. The userid is locally on that server. I am checking to see that the password is not set to the default (I want to make sure it was changed to something else).
The "list of servers" can be a mix of IP addresses, hostnames (like Rocky), or fully qualified DNS names (like rocky.bigcompany.com). The servers are a mixture of physical and virtual devices, and may or may not be on a domain.
The existing VBScript I wrote handles all this, and works fine. I'm trying to re-write this same program in Powershell, and It's not working.
Here's the function I have in VBScript that does what I want:
Function LoginToServer(Computer, username, password)
'this function will log into a server
On Error Resume next
Set locator = CreateObject("WbemScripting.SWbemLocator")
Set wmi = locator.ConnectServer(computer, "root\cimv2", username, password)
'check the error code and see if we logged in successfully
LoginRC = Err.Number
If LoginRC <> 0 Then
msg = "Could not log into server: " & CStr(computer) & " With ID: " & CStr(username)
lfo.lmsg "B", "WARN", msg
Else
msg = "Server: " & CStr(computer) & " Logged in successfully as: " & CStr(username)
lfo.lmsg "B", "INFO", msg
End If
wmi.Security_.ImpersonationLevel = 3
'return the code back to calleer
LoginToServer = LoginRC
End Function
… and here's what I've tried to do in PowerShell:
Param($ComputerName = "LocalHost")
$ErrorActionPreference = "Stop"
# Actual Code starts here
Write-Host "Attempting to ping server: $ComputerName"
$IPResult = Test-Connection -ComputerName $ComputerName -Quiet
if ($IPResult -eq "TRUE") {
Write-Host "Ping OK - now attempting to log in"
try {
$ID = "userid"
$PSW = "password"
$password = ConvertTo-SecureString $PSW -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ($ID, $password)
$sesh = New-PSSession -ComputerName $ComputerName -Credential $cred
} catch {
Write-Host "Error caught"
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
} finally {
$Time = Get-Date
"$Time Computer: $ComputerName ERROR: $ErrorMessage ITEM: $FailedItem" |
Out-File c:\temp\TestCredScript.log -Append
}
} else {
Write-Host "Could not ping server"
}
How do I log into these remote computers with an ID and Password using PowerShell?
Your two code samples do different things. The VBScript code connects via WMI whereas the PowerShell code tries to establish a PowerShell session. For the latter you need PowerShell Remoting enabled, which you probably don't have.
While you probably may want to enable PSRemoting anyway, you can also use WMI from PowerShell. The Get-WmiObject cmdlet allows you to provide credentials and impersonation level, so you don't need to establish a connection first like you need to do with VBScript (if you want to use explicit credentials).
Example querying the Win32_Process class on a remote computer:
$computer = '...'
$username = 'userid'
$password = 'password'
$pw = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object Management.Automation.PSCredential ($username, $pw)
Get-WmiObject -Computer $computer -Namespace 'root\cimv2' -Class Win32_Process -Impersonation 3 -Credential $cred
See here for further information.