PowerShell Connect-VIServer with multiple potential credentials - powershell

I am work in a data center and am currently working on creating a script that will connect to multiple esx hosts. Unfortunately, the username/passwords are mostly the same but some of them are different. I want to check for a failed connection, and then retry with another set of credentials. I've got three or four different sets that I need to try before I let the host be listed as not accessible.
Currently, I have my script successfully connecting using the most common credentials without any problem. Now I want to upgrade the script to be able to test all of the possible credentials if it fails with the main one.
In my research to find a solution, I came across try/catch but I'm thinking that is going to get pretty nasty with three or four possible layers of repeated commands.
So here is what I have so far with a try/catch idea going:
$goodhost | ForEach-Object {
try {
$sessions = #(Connect-VIServer -Server $_ -User 'user' -Password $psw -
ErrorAction SilentlyContinue)
}
catch {
if ($Error[0] -ccontains "incorrect user name") {
$sessions = #(Connect-VIServer -Server $_ -User 'user1' -Password $psw1
-ErrorAction SilentlyContinue)
}
}
}
My current thought is, that to do a try/catch for three different possible username/password pairs, would basically require me to do 3 nested repeats of this same thing. In otherwords, the following:
connect using main credential
if fail
Connect using second credential
if fail
connect using third credential
if fail
connect using fourth credential
I guess my question is, is this the best way to tackle this problem?

#Jawad provided the insight I was looking for. While a dictionary won't work in this case it was close enough that I feel his suggestion lead me to the solution. I will edit this once I have finished writing the new code.

Related

Double-Hop Errors when running Skype for Business Cmdlets

I am attempting to automate the Skype for Business Server installation process in Powershell, I have a script that remotes into specified machines and begins preparing them as Front-End servers. The problem lies when certain SfB cmdlets (SfB commands are all of the form "verb-Cs...", ex. Get-CsUser or Get-CsPool) are run in remote sessions, they throw the double-hop error:
Exception: Active Directory error "-2147016672" occurred while searching for domain controllers in domain...
This is after running Enable-CsComputer, which enables the computer's role-based off its definition in the topology (topology was published successfully). The user object is in all required groups (RTCUniversalServerAdmins, Schema Admins, CsAdministrators & Local Admin rights on all SfB Servers). Oddly enough, the command 'Import-CsConfiguration -localstore" does not throw errors, and it's in the same remote session. There may be other local or domain groups that I need to be in, but I cannot pinpoint exactly which and have not seen them documented in the Skype build guides. Skype commands that have parameters to specify targets or just pull data, such as Get-CsPool or Get-CsAdForest, do not have errors because they are run in the local scope. The Enable-CsComputer has no parameter for the computer name, it has to be executed from that machine itself.
Enabling CredSSP delegation on each server is not an option, and I'm not understanding why there is a "second hop" in this command! If the second hop was a resource on a file server or database, that would make sense, and be easy to solve, but in this case, I can't track it. Can anyone tell me what I may be missing?
Here's a code sample to try and illustrate. From the jumbox I get the pool data to create an array, and a session is opened to each machine:
$ServerArray =get-cspool -identity $poolName
$i=0
$SessionArray = #{}
foreach($server in $ServerArray.Computers){$SessionArray[$i] = new-PsSession -ComputerName $server}
foreach($session in $SessionArray.values){
invoke-Command -session $session -scriptBlock {
#remote commands:
import-csConfiguration -<config file path> -localstore; #no errors
enable-CsReplica; #no errors
enable-cscomputer; #double hop error here
}}
If I log into that machine and run the same command, it executes fine but the intention of the project is to automate it on an arbitrary number of machines.
It looks like it's just trying to authenticate to a domain controller, which is reasonable. You'll have to approach this like any other double-hop issue.
Microsoft has an article dedicated to the double hop issue, and has a few solutions other than CredSSP that you can look at: Making the second hop in PowerShell Remoting

i'am trying to remove a user from a local group throught AD (powershell)

i'm trying to develop a script that remove a domain user from local administrators group (i can use computer management from ad but its a graphical interface i need to do it with commands) for now i'm using invoke command to remotely connect to machines and remove their users from local admins group .
im using this command : Invoke-Command -ComputerName $line2.split(";")[0] -ScriptBlock { net localgroup "administrators" $using:notadmin /DELETE } -Credential $Cred
the problem here if a the machine is not online i need to wait until it will be online , i'm searching how to remove users from local group (administrators for example ) through ad
is there a command to do that ?
I see two approaches:
If you would like to use Group Policy, you may check for: Restricted groups.
https://www.petri.com/manage-local-active-directory-groups-using-group-policy-restricted-groups
Another option would be to incoroporate Test-Connection in your script, validating if computer is online. If it is - execute the script, if it is not, store it in another list with offline machines.
Then later run the script against the offline machine list ... and so on until all the computers are being covered.
P.S. And yes, as suggested in the commments, consider using remove-localgroupmember, if your powershell version support it.
Again, depends of the case.
Hope it helps!
$RemoteComputer = "yourComputer"
$Computer = [ADSI]("WinNT://$RemoteComputer,computer")
$Group = $Computer.PSBase.Children.Find("Administrators")
ForEach ($User in (Get-Content
"c:\users\administrator.domain\desktop\localadmin.txt"))
{ $Group.Remove("WinNT://$User")
}
i tired this code and it really helped me thnx for help

Need a Way to Test a user against remote server using Powershell

My only objective is to validate the user account against bunch servers. I am using below commands to do it.
$creds2= Get-Credential
$servers = Get-Content ('C:\Users\vishnuvardhan.chapal\Documents\Widnows Servers success in 139 and 445.txt')
$servers | ForEach-Object {Get-WmiObject Win32_ComputerSystem -ComputerName $_ -Credential $creds2} | Out-GridView
Here, I am encountering two problems.
1) In the Grid view, I am just getting the hostname but without FQDN like shown in below screenshot.
2) Above screen is only for succeeded servers and for failed ones (for the servers, where authentication is failing) I am getting the output in Powershell window like below screen.
Now, my goal is to combine both the output's in at single place. Is it possible? If yes, How to do it? Please shed some light to it.
Apart from above is there any way to test it more easily, i mean a direct command to test the user authentication against a remote server??
FYI...My only goal for this exercise is to validate user authentication not to get some details from a remote computer.
Out-GridView is not a good way to handle these things. Recommended to convert that into JSON or some kind of a format and then parse it in files or however you wish to.
There are multiple ways to check that but error handling will solve your issue:
try
{
$creds2= Get-Credential
$servers = Get-Content ('C:\Users\vishnuvardhan.chapal\Documents\Widnows Servers success in 139 and 445.txt')
$servers
foreach($server in $servers)
{
try
{
Get-WmiObject Win32_ComputerSystem -ComputerName $Server -Credential $creds2
}
catch
{
"Error in accessing the server - $Server with the given credential. Kindly validate."
}
}
}
catch
{
$_.Exception.Message
}
So within the loop also I have added a try catch because if one server is failing, it will proceed with the next server from the list and that will capture the error with server name along with the message.
Hope it helps.

Having trouble binding to Active Directory with specified credentials

As part of my current role, I frequently find myself having to work with objects in one of my organisation's resource forests. At the moment in order to do that, I use an RDP session connected to a server within that forest, and authenticate to it with a specific "Admin" account in that forest.
I'm starting to find this tedious, and so I've been trying to come up with a nice profile.ps1 which will get me a DirectoryEntry for the resource forest that I can work on with Powershell (v2.0) on my local workstation instead, and save me the tedium of constantly re-establishing RDP sessions.
So I've got some code in my profile.ps1 which looks like this:
$resforest = "LDAP://DC=ldap,DC=path,DC=details"
$creds = Get-Credential -credential "RESOURCE_FOREST\my_admin_account"
$username = $creds.username
$password = $creds.GetNetworkCredential().password
$directoryentry = New-Object System.DirectoryServices.DirectoryEntry($resforest,$username,$password)
All of this proceeds fine, however, when I come to actually use the entry thus:
$search = New-Object DirectoryServices.DirectorySearcher($directoryentry)
$search.filter = "(&(anr=something_to_look_for))"
$search.findall()
I get a logon failure.
Now, I know the credentials are fine, I can map drives with them from my workstation to machines in the resource forest - and that works fine - so what am I ballsing up here?
PS - Please don't ask me to do anything with Quest's AD cmdlets - they're not allowed here.
Turns out the issue was with the serverless binding I was attempting to do.
If I modify the LDAP path to "LDAP://ldap.path.details/DC=ldap,DC=path,DC=details" then everything works.
Thanks for everyone who at least looked at the question ;)

Powershell Remoting Speeding up a Foreach Loop Hosted Exchange

I have a CSV of email adddresses and Departments that I need to set on Live#edu. The command I currently have looks something like this:
Import-CSV departments.csv | ForEach-Object { Set-User $_.EmailAddress $_.Department }`
The problem is, this operation takes FOREVER.
My first thought is that it would be great to have the ForEach-Object command actually be forwarded over to the remote machine, so that it will only need to create the one pipeline between the two machines, but when I go into the PSSession, there doesn't seem to be any foreach-object available. For reference, How I Import the PSSession is:
Import-PSSession(New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri 'https://ps.outlook.com/powershell' `
-Credential (Get-Credential) `
-Authentication Basic -AllowRedirection)
Is there a better way that I can import the session to allow ForEach-Object to be remote, or to import an aliased version of the remote foreach-object, perhaps as ForEach-Object-Remote, or perhaps does anybody have something better to suggest to streamline this process?
UPDATE:
A Couple Things I've tried:
Using the -AsJob switch on the implicitly remoted command.
Import-CSV departments.csv | ForEach-Object { Set-User $_.EmailAddress $_.Department -AsJob }
This, unfortunately, doesn't work because there are throttling limits in place that don't allow the additional connections. Worse than that, I don't even know that anything went wrong until I check the results, and find that very few of them actually got changed.
Importing the ForEach-Object under a different name.
Turns out that adding a prefix is easy as putting -Prefix RS in the Import-PSSession Command to have things like the ForEach-Object from the Remote Session become ForEach-RSObject in the local session. Unfortunately, this won't work for me, because the server I'm connecting to does not does not have the Microsoft.Powershell ConfigurationName available to me.
UPDATE 2: The Set-User cmdlet seems to be Microsoft provided for Live#edu administration. Its purpose is to set User attributes. It is not a script or cmdlet that I am able to debug. It doesn't take pipeline input, unfortunately, so that would not be able to fix the issue.
As Far as I can tell, the problem is that it has to construct and tear down a pipeline to the remote machine every time this command runs, rather than being able to reuse it. The remote ForEach idea would have allowed me to offload that loop to avoid having to create all those remote pipelines, while the -asJob would have allowed them to all run in parallel. However, it also caused errors to fail silently, and only a few of the records actually get properly updated.
I suspect at this point that I will not be able to speed up this command, but will have to do whatever I can to limit the amount of data that needs to be changed in a particular run by keeping better track of what I have done before (keeping differential snapshots). Makes the job a bit harder.
EDIT: Start-Automate left a very useful help, unfortunately, neither of them work. It is my feeling at this point that I won't find a way to speed this up until my provider gives access to more powershell cmdlets, or the exchange cmdlets are modified to allow multiple pipelines, neither of which I expect to happen any time soon. I am marking his answer as correct, despite the ultimate result that nothing helps significantly. Thanks, Start-Automate.
You can speed up your script and also avoid trying to make two connections to the server by the use of the foreach statement, instead of Foreach-Object.
$departments = #(import-csv .\departments.csv)
foreach ($user in $departments) {
Set-User $user.EmailAddress $user.Department
}
If you need to batch, you could use the for statement, moving forward in each batch
for ($i =0; $i -lt $departments.Count; $i+=3) {
$jobs = #()
$jobs+= Invoke-Command { Set-User $departments[$i].EmailAddress $departments[$i].Department } -AsJob
$jobs+= Invoke-Command { Set-User $departments[$i + 1].EmailAddress $departments[$i + 1].Department } -AsJob
$jobs+= Invoke-Command { Set-User $departments[$i + 2].EmailAddress $departments[$i + 2].Department } -AsJob
$jobs | Wait-job | Receive-job
}
Hope this helps