Basic Loop Help - Powershell - powershell

Complete Powershell Noobie here.
I have a .bat file that I would like to convert to Powershell.
Basically when run; it asks the user to enter their Active Directory credentials. Once validated; it starts a RSAT tool (example: dhcpmgmt.msc) as the elevated domain user.
However, if credentials are incorrect (if %ERRORLEVEL% EQU 5); it does a GOTO an echo "Incorrect username and password" and then loops back requesting the user to enter their credentials again.
When I use:
do
{
Start-Process -FilePath C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Credential (Get-Credential "$env:USERDNSDOMAIN\") -ArgumentList "C:\Windows\System32\dhcpmgmt.msc"
} until ($response -eq $null)
It works. But if I enter an incorrect password; the window closes.
I would like a notification information the user that the Username/Password is incorrect and then re-direct them to enter their credentials again. This would loop until either the user enters the correct credentials or simple clicks the cancel button.
Any help/guidance is highly appreciated. Thanks a bunch in advance!

You can run this in the while loop to keep asking for credentials until they are valid.
Take credentials
Run powershell with new creds
If fails, ask for new credentials. Break out of the loop if no.
while ($true)
{
$userCreds = Get-Credential "$env:USERDNSDOMAIN\"
try {
Start-Process powershell -Credential $userCreds -ArgumentList "C:\Windows\System32\dhcpmgmt.msc" -ErrorAction Stop
break
}
catch {
Write-Output "Invalid Credentials"
Write-Output "Enter new credentials?"
if ((Read-Host) -ne "Yes") { break }
}
}

This should do the needful.
Loop to Get and Check Credential
Try-Catch Credential Check
Raise Messagebox on Failed Credential
Exit script on Cancel.
$Credential=$null
while ($Credential -eq $null) {
$Credential = Get-Credential -Username "$env:USERDNSDOMAIN\" -message "Enter Admin Password"
try {
Start-Process -FilePath cmd.exe /c -Credential $Credential
}
catch {
$Credential=$null
IF (
$([System.Windows.MessageBox]::Show('Invalid UN/PW! ?','Credential Error','OKCancel','Error')
) -eq "Cancel") { EXIT}
}
}
### Your code to launch RSAT tools here:
We use a while loop.
While the credentials are NULL we p, getting the credentials, storing it as a secure string in a variable, then do a try-catch on the test of running a process, if that will try to get the credentials again, and we store the credentials in a variable as a secure string.
Initially, credentials are NULL so we ask for them.
Then we use Try to test the credentials by initiating a process as them.
If that fails we Catchit and 1st set credentials to NULL again, then we Raise a Windows Message Box to the user letting them know the credentials failed and checking if they would like to try again (OK) or Cancel.
If they respond with Cancel, there is no point in continuing the script so we just exit.
Otherwise, we just go ahead with our loop as planned

Related

Pass stored Credential variable to new script

I have successfully built a working simple program that displays a menu and allows a user to choose which script to open and it runs in the same window.
I would like to be able to store credentials so that you would only need to enter them once while using this program and it passes them through to other scripts.
Please can someone advise.
I played around and found a solution. Seems the variables are carried through the session regardless if it runs a new script.
This bit of code sorted it out.
if ($cred -eq $null)
{
try {
Write-Host "`r`n"
$cred = Get-Credential -Credential $username
}
catch {
Write-Host -ForegroundColor Red "`r`nSomething has gone wrong with entering credentials. Please try run the script again or if issues persist please contact the system administrator.`r`n"
Return-ExitRestart
}

Can PowerShell interact with RDP prompts?

I'm currently writing a script to automate a number of checks, I have a number of clients which I want to automatically log into one of their servers or use an app hosted via RDweb.
Right now my script works fine, however, I'm only able to get to the point that it'll start to execute the RDP pointer, I'm wondering if there's a way to hit "connect":
The method I'm currently using to run this:
[System.Diagnostics.Process]::Start("c:\file\path\file.rdp")
Is there a better way to run the .RDP file which will also allow you to "Connect"? I've also attempted to tick the "don't ask me" again, the next day it'll still prompt me with this message.
A solution I've found to start an RDP session that seems to work quite good is the following:
function Connect-RDP {
param (
[Parameter(Mandatory=$true)]
$ComputerName,
[System.Management.Automation.Credential()]
$Credential
)
# take each computername and process it individually
$ComputerName | ForEach-Object {
# if the user has submitted a credential, store it
# safely using cmdkey.exe for the given connection
if ($PSBoundParameters.ContainsKey('Credential'))
{
# extract username and password from credential
$User = $Credential.UserName
$Password = $Credential.GetNetworkCredential().Password
# save information using cmdkey.exe
cmdkey.exe /generic:$_ /user:$User /pass:$Password
}
# initiate the RDP connection
# connection will automatically use cached credentials
# if there are no cached credentials, you will have to log on
# manually, so on first use, make sure you use -Credential to submit
# logon credential
mstsc.exe /v $_ /f
}
}
Then you call it with Connect-rdp -ComputerName myserver -Credential (Get-Credential ).
Maybe you can adjust your script to use this cmdlet instead of your file.rdp.
I found the solution here:
https://www.powershellmagazine.com/2014/04/18/automatic-remote-desktop-connection/
Another way you could try is this:
[void][System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
# Get the ID of the process
$WindowsHandle = Get-Process | Where-Object { $_.MainWindowTitle -Match 'Remote Desktop Connection' } | Select-Object -ExpandProperty Id
# Activate the window
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate($WindowsHandle) | Out-Null
# SendKey to connect
[System.Windows.Forms.SendKeys]::SendWait("%{c}")
%{c} stands for ALT+C
The modifier keys are:
Key | Code
-----------
SHIFT +
CTRL ^
ALT %

Powershell Suppress Certificate Notification

I am writing a script to connect from a Windows 10 Client to a Terminal Server with the RDP-Protocoll.
The thought behind is: On these ThinClients we have about 20 RDP-Files. With about 10 of them, the password needs to be safed.
So it is quite a lot of work if you always have to save the password on every new ThinClient.
But I thought I could solve this problem with a powershell script. I just have to open the connection 1 time successfully and save the credentials and further on the credentials are saved.
I will show my code first:
$Server = "xx.yy.zz.xx"
$User = "DOMAIN\User"
$password = "password"
cmdkey /generic:"$Server" /user:"$User" /pass:"$password"
mstsc /v:"$Server"
This works so far.
But I always get the this Notification:
This is a Symbol-Picture from the Internet, as my Notification is in German. It is exactly the same, just easier to understand.
Even if I install the certificate, the notification keeps popping up.
How can I check the field with Powershell, where it says Don't ask me again for connections to this computer?
Ok i found a solution!
There is a registry-Key generated when you Tick "Dont ask me again..."
Now i just added the necessary Registry Keys with my Powershell Script..
function Test-RegistryValue {
param (
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Path,
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Value
)
try{
Get-ItemProperty -Path $Path | Select-Object -ExpandProperty $Value -ErrorAction Stop | Out-Null
return $true
}
catch{
return $false
}
}
#Certificate warning turn off
$exists = Test-RegistryValue -Path 'HKCU:\Software\Microsoft\Terminal Server Client' -Value 'AuthenticationLevelOverride'
if($exists -eq $False){
reg add "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client" /v "AuthenticationLevelOverride" /t "REG_DWORD" /d 0 /f
}
Like this it works without this certificate notification!

Prevent PowerShell script from being read

I have the below PowerShell script (myscript.ps1) in which I ask for username and password. Depending on the username and password it copies a file to a certain destination.
$credentials = Get-Credential
if ($credentials.Username -eq "user1" -And $credentials.GetNetworkCredential().password -eq "pass1")
{ Copy-Item "test1.pdf" "\test\test1.pdf"; }
else
{ Copy-Item "test2.pdf" "\test\test2.pdf"; }
Requirement: I want to make this file protected so no one can edit it and see the username and password.
PS2EXE
I found a solution found here which converts the PowerShell script to an .exe file. When I originally run the script using PowerShell a dialog box appears allowing me to enter the username and password:
After the .exe is generated and when I run it the credentials dialog box no longer appears. Instead, the console appears saying "Credential:"
I don't know why? I want the credentials form to still appear when running the exe. Any thoughts please?
Q: Why does the EXE prompt with "Credential"?
This isn't an answer to the real question, and is based on guessing/supposition about PS2EXE, but I hope it is useful to clear up some confusion.
Having looked briefly at the PS2EXE page linked above, it seems that this utility encodes the script in Base64 and bundles it with a lightweight (?) custom PowerShell host. When run, I suppose the EXE starts the host, decodes the script and runs it.
The problem is that the Get-Credential cmdlet is running within a PS host that probably can't interact with the desktop. That is, it can't put up the GUI prompt for credentials. It therefore needs to prompt for the Credential property on the command line, explaining why you see that behaviour.
Workaround with Read-Host?
Instead of trying to use Get-Credential to prompt for username and password, you could embrace what PS2EXE seems to be doing and just use Read-Host:
$UserName = Read-Host "Enter username"
$Password = Read-Host "Enter password" -AsSecureString
$Credentials = New-Object System.Management.Automation.PSCredential $UserName,$Password
if ($credentials.Username -eq "user1" -And $credentials.GetNetworkCredential().password -eq "pass1")
{ ... }
Using -AsSecureString will hide the password on the screen. The $Password variable will be of type System.Security.SecureString, which can be used to create a PSCredential object as shown.
You'd need to test this, but it seems that you're able to read from the shell but not from a GUI prompt.
And just to be clear: none of this is anywhere near best-practice security. If you need authentication/authorization for these activities, step back and look at the problem again.
Workaround with two scripts?
It seems that PS2EXE doesn't support -AsSecureString in the same way that normal PowerShell does, i.e. it doesn't hide the characters. A possible workaround for this would be to collect the username and password from the user in one script and then pass them to a PS2EXE-converted script for processing.
Launch-MyScript.ps1:
$Credentials = Get-Credential
& MyScript.exe $Credentials.Username $Credentials.Password
MyScript.exe (coverted with PS2EXE):
param($Username,$Password)
$Credentials = New-Object System.Management.Automation.PSCredential $Username,$Password
if ($Credentials.Username -eq "user1" -and
$Credentials.GetNetworkCredential().password -eq "pass1")
{
...
}
The user runs Launch-MyScript.ps1 and completes the password prompt. Then the EXE is run automatically with the username and password passed in as arguments. Note that, as shown above, the password is a Secure String. Test this; I'm not using PS2EXE so it's a theoretical solution at the moment.
If you can't pass $Password along the pipeline as a Secure String object, you can convert it to text with ConvertFrom-SecureString in the first script, then conver it back with ConvertTo-SecureString in the second one.
According to this article http://windowsitpro.com/powershell/protect-your-powershell-scripts you should first set ur execution policy to AllSigned by Set-ExecutionPolicy AllSigned, then create a certificate using makecert cmdlet.
Then u can sign single script using Set-AuthenticodeSignature cmdlet or use .pfx File to Sign a Script which appears even safer.
Hope it helps a bit.

Logon script causes powershell.exe to launch and close in a loop

I created a logon script that promotes any user that has been on the machine in the last X days to the Administrators group.  I have tested this script successfully and have no issues with execution in any of my tests.  I created a GPO that links this script to a particular OU in my org and I'm finding something like a 25% failure rate to properly execute.
The "failure" is the troublesome part because 1) its only occurs for a relatively small number of users, and because of this 2) I don't understand what is happening conceptually.  Specifically the user gets signed in, and then PowerShell.exe launches and closes immediately, but then continues to do this indefinitely until you force quit powershell - the window takes focus on the desktop and prevents users from working.
When I use Computer Management to remotely view the Administrator group membership on the computer I can see that the script ran successfully (it promotes the users to Admins) but I'm not sure what causes it to respawn, and only for some users.
I can post the script if it will help (its short) but since its "working" most of the time, I'd be inclined to assume some component of PowerShell is failed or failing on these machines.  I'm hoping this kind of behavior is a known, or has been experienced by someone in the community before.
The last point I'll add is that in 2 cases just having the user reboot fixed it.
Script Code:
# Launches elevated PS session if possible.
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
$Threshold = (Get-Date).AddDays(-30)
# Non-builtin regular user SIDs are always prefixed S-1-5-21-
$DomainUserFilter = "SID LIKE 'S-1-5-21-%'"
# Suppress Errors
$ErrorActionPreference = "SilentlyContinue"
# Retrieve user profiles
$DomainProfiles = Get-WmiObject -Class Win32_UserProfile -Filter $DomainUserFilter
foreach($UserProfile in $DomainProfiles)
{
# Check if profile was ever used, skip if not
if(-not $UserProfile.LastUseTime)
{
continue
}
# Convert the datetime string to a proper datetime object
$LastUsed = $UserProfile.ConvertToDateTime($UserProfile.LastUseTime)
# Compare against threshold
if($LastUsed -gt $Threshold)
{
# Resolve user profile SID to account name
$Account = (New-Object System.Security.Principal.SecurityIdentifier $UserProfile.SID).Translate([System.Security.Principal.NTAccount])
if($?)
{
# Add to Administrators group
net localgroup administrators $Account.Value /add
}
}
}
net localgroup administrators “domain users” /delete
exit
I figured this out myself - the only contentious part was the .net stuff at the beginning so I tried to comment that part out:
# Launches elevated PS session if possible.
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
and that worked... I assume it was .NET issues on the machines that were failing to execute this properly, thank you for looking at this.