Enter-PSSession, try with multiple credentials - powershell

I need to create a script that requires multiple credentials for establishing the connection to the computers.
I have tried to achieve this with a "try catch" and "if else", but the script looks pretty ugly and is not scalable if i need to insert more credentials.
There is a better way to achive the same results?
#credentiasl for non domain conputers and domain joined
$credential01 = Get-Credential domain 1\administrador
$credentiall02 = Get-Credential domain 2\administrador
$credentiall03 = Get-Credential workgroup\administrador
$error.clear()
#try to establish a remote sesion to the pc with the credentials01
try { etsn -ComputerName sldb04 -Credential $credential01 }
#if there is an error try to establish a remote sesion to the pc with the credentials02
catch
{
etsn -ComputerName sldb04 -Credential $credential02
#if the second credential is also wrong try the third
If ($? -eq $false )
{
etsn -ComputerName sldb04 -Credential $credential03
}
}

As a best practice, don't attempt to guess the right account. This creates noise in the security logs and might lead into unexpected account lockouts.
Record the proper credentials for each environment and use it. You could store the credential mappings in a hash table like so,
# Add computer names and respective admin accounts into a hash table
$ht = #{"computer01"="domain1\admin";"computer02"="domain2\admin";"computer03"="group\admin" }
# What's the account for computer01?
$ht["computer01"]
domain1\admin
# Print all computers and admin account names
$ht.GetEnumerator() | % { $("Logon to {0} as {1}" -f $_.name, $_.value) }
Logon to computer01 as domain1\admin
Logon to computer03 as group\admin
Logon to computer02 as domain2\admin
This can easily be extended by creating another a hashtable that stores credentials. Like so,
# Create empty hashable
$creds = #{}
# Iterate computer/account hashtable and prompt for admin passwords
$ht.GetEnumerator() | % {
$c= get-credential $_.value
# Add only accounts that don't yet exist on the hashtable
if(-not $creds.Contains($_.value)) {
$creds.Add($_.value, $c) }
}
# What's the account for domain1\admin?
$creds["domain1\admin"]
UserName Password
-------- --------
domain1\admin System.Security.SecureString

Related

While Loop with Break Statement in PowerShell [duplicate]

I am trying to build my own script to check some Windows services (status and start mode) and I am facing an issue on the IF ...
For example even if the service is "Running", it will never run the code inside the IF...
let me share my code below (I am a newbie on powershell so be gentle xD)
For info, I will do more actions inside the IF and ELSE, it is just for the example.
# import computers list, 1 by line
$Computers = get-content .\computers.txt
# define variable of services we want to check
$ServiceNetbios = "netbt"
# define variable to ask credentials
$Cred = Get-Credential
# declare Function to open a session a remote computer
Function EndPSS { Get-PSSession | Remove-PSSession }
EndPSS
########################################################
# BEGINNING OF SCRIPT #
# by xxx #
# 2022-02-03 #
########################################################
# loop for each computer imported from the file
foreach ($computer in $computers) {
# show name of computer in progress
$computer
# connect remotely to the computer
$session = New-PSSession -ComputerName $computer -Credential $Cred
# check Netbios service
$StatusServiceNetbios = Invoke-Command -Session $session -ScriptBlock { Get-Service -Name $Using:ServiceNetbios | select -property * }
# Check Netbios service started or not
write-host $StatusServiceNetbios.Status
if ($StatusServiceNetbios.Status -eq 'Running')
{
Write-host "IF Running"
}
else
{
write-host "IF NOT Running"
}
EndPSS
}
and what return my script :
computername
Running (<= the variable $StatusServiceNetbios.Status )
IF NOT Running (<= the ELSE action)
Thanks you in advance for your help,
this drive me crazy and maybe this is very simple...
To complement Cpt.Whale's helpful answer, this is likely to be caused by the serialization and deserialization done by Invoke-Command:
using namespace System.Management.Automation
$service = Get-Service netbt
$afterInvokeCmd = [PSSerializer]::Deserialize(([PSSerializer]::Serialize($service)))
$service.Status -eq 'Running' # => True
$afterInvokeCmd.Status -eq 'Running' # => False
$afterInvokeCmd.Status.Value -eq 'Running' # => True
$afterInvokeCmd.Status.ToString() -eq 'Running' # => True
To put some context to my answer, this is a nice quote from about_Remote_Output that can better explain why and what is happening:
Because most live Microsoft .NET Framework objects (such as the objects that PowerShell cmdlets return) cannot be transmitted over the network, the live objects are "serialized". In other words, the live objects are converted into XML representations of the object and its properties. Then, the XML-based serialized object is transmitted across the network.
On the local computer, PowerShell receives the XML-based serialized object and "deserializes" it by converting the XML-based object into a standard .NET Framework object.
However, the deserialized object is not a live object. It is a snapshot of the object at the time that it was serialized, and it includes properties but no methods.
This is probably because of the way powershell creates service objects - (Get-Service netbt).Status has a child property named Value:
$StatusServiceNetbios.Status
Value
-----
Running
# so Status is never -eq to 'Running':
$StatusServiceNetbios.Status -eq 'Running'
False
# use the Value property in your If statement instead:
$StatusServiceNetbios.Status.Value -eq 'Running'
True

Azure DevOps Get Current User ObjectId

Is there a way to get the ObjectId of the Service Principal that is currently executing an Azure PowerShell task in Azure DevOps at all?
I am creating a resource, and then want to apply permissions for the 'current' user.. but can't work out how to get the current user ObjectId / ApplicationId
Is this possible?
Ok - based of the above, I've made a little function - it may work for a lot of cases:
function Get-CurrentUserObjectID {
$ctx = Get-AzContext
#This is different for users that are internal vs external
#We can use Mail for users and guests
$User = Get-AzADUser -Mail $ctx.Account.id
if (-not $user) { #Try UPN
$User = Get-AzADUser -UserPrincipalName $ctx.Account.Id
}
if (-not $User) { #User was not found by mail or UPN, try MailNick
$mail = ($ctx.Account.id -replace "#","_" ) + "#EXT#"
$User = Get-AzADUser | Where-Object { $_MailNick -EQ $Mail}
}
Return $User.id
}
There seems to be two ways of doing this depending on if it's a user, or a service principal:-
Get-AzADUser
Get-AzADServicePrincipal
These i believe are in the Az.Resources module. So, to give you the ObjectId (for permissions), you could take a two step approach like this:
$x = (Get-AzContext).Account.Id
$x
> df6fc4f6-cb05-4301-91e3-11d93d7fd43d # ApplicationId
$y = Get-AzADServicePrincipal -ApplicationId $x
$y.Id
> c3588e6a-b48b-4111-8241-3d6bd726ca40 # ObjectId
I can't get anything to work reliably with standard users though.. if your user is created in the AzureAD directly, and not external (i.e. gmail.com, outlook.com, etc) this should work:
$x = (Get-AzContext).Account.Id
$x
> sample#your-domain.onmicrosoft.com # UserPrincipalName
$y = Get-AzADUser -UserPrincipalName $x
$y.Id
> c5d4339b-48dc-4190-b9fb-f5397053844b # ObjectId
If your user is external, and has the weird your.email.address_outlook.com#EXT##your-domain.onmicrosoft.com as the UserPrincipalName you'll need to solve that with a bit of string manipulation i think 😕.
But! You shouldn't be scripting things with user accounts anyway, so it probably doesn't matter 😆.
Note: I have not tried this in Azure DevOps, you will probs need to upgrade the PowerShell packages, but i think the same commands should exist as Get-AzureRmADUser, and Get-AzureRmADServicePrincipal. Please let me know.

Using Powershell and ADSI to set local passwords

I'm trying to automate setting a bunch of local user accounts' password on a Windows 2008 server. I've tried a few things and this works if I don't use a variable for the username like this:
$user = [adsi]"WinNT://$computer/SomeUserName"
My script block is below... any ideas what I'm doing wrong?
$accounts = Get-Content c:\userlist.txt
$computer = SomeComputerName
$password = "MyPassword"
Foreach($account in $accounts)
{
$user = [adsi]"WinNT://$computer/$account"
$user.SetPassword("$Password")
$user.SetInfo()
}
The error I get when I use the $account variable for the user (from the text file list) is:
The following exception occurred while retrieving member "SetInfo": "The group name could not be found.
Thanks for any help...
It seems that your machine tries to resolve the $account value to a local group name.
You can specify that it is a User object you want, by following the account name with a comma and the string user:
$user = [adsi]"WinNT://$computer/$account,user"

Search GC replicas and find AD account

I have an issue which is to do with AD replication. We use a 3rd party app the create accounts in AD and then a powershell script (called by the app) to create the exchange accounts.
In the 3rd party app we can not tell which GC the ad account has been created on and therefore have to wait 20 minutes for replication to happen.
What I am trying to do is find which GC the account has been created on or is replicated to and connect to that server using....
set-adserversettings -preferredserver $ADserver
I currently have the below script and what I can't work out is to get it to stop when it finds the account and assign that GC to the $ADserver variable. The write-host line is only there for testing.
$ForestInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$GCs = $ForestInfo.FindAllGlobalCatalogs()
Import-module activedirectory
ForEach ($GC in $GCs)
{
Write-Host $GC.Name
Get-aduser $ADUser
}
TIA
Andy
You can check whether Get-ADUser returns more than 0 objects to determine whether the GC satisfied your query. After that, use Set-ADServerSettings -PreferredGlobalCatalog to configure the preference
You will need to specify that you want to search the Global Catalog and not just the local directory. The Global Catalog is accessible from port 3268 on the DC, so it becomes something like:
$ForestInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$GCs = $ForestInfo.FindAllGlobalCatalogs()
Import-module ActiveDirectory
$ADUserName = "someusername"
$ADDomainDN = "DC=child,DC=domain,DC=tld"
$FinalGlobalCatalog = $null
foreach ($GC in $GCs)
{
$GCendpoint = "{0}:3268" -f $GC.Name
$SearchResult = Get-ADUser -LDAPFilter "(&(samaccountname=$ADUserName))" -Server $GCEndpoint -SearchBase $ADDomainDN -ErrorAction SilentlyContinue
if(#($SearchResult).Count -gt 0){
$FinalGlobalCatalog = $GC
break
}
}
if($FinalGlobalCatalog){
Write-Host "Found one: $($FinalGlobalCatalog.Name)"
Set-ADServerSettings -PreferredGlobalCatalog $FinalGlobalCatalog.Name
} else {
Write-Host "Unable to locate GC replica containing user $ADUserName"
}

Need to check for the existence of an account if true skip if false create account

I am trying to create local user on all servers and I want to schedule this as a scheduled task so that it can run continually capturing all new servers that are created.
I want to be able to check for the existence of an account and if true, skip; if false, create account.
I have imported a module called getlocalAccount.psm1 which allows me to return all local accounts on the server and another function called Add-LocaluserAccount
which allows me to add local accounts these work with no problems
when I try and run the script I have created the script runs but does not add accounts
Import-Module "H:\powershell scripts\GetLocalAccount.psm1"
Function Add-LocalUserAccount{
[CmdletBinding()]
param (
[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[string[]]$ComputerName=$env:computername,
[parameter(Mandatory=$true)]
[string]$UserName,
[parameter(Mandatory=$true)]
[string]$Password,
[switch]$PasswordNeverExpires,
[string]$Description
)
foreach ($comp in $ComputerName){
[ADSI]$server="WinNT://$comp"
$user=$server.Create("User",$UserName)
$user.SetPassword($Password)
if ($Description){
$user.Put("Description",$Description)
}
if ($PasswordNeverExpires){
$flag=$User.UserFlags.value -bor 0x10000
$user.put("userflags",$flag)
}
$user.SetInfo()
}
}
$usr = "icec"
$rand = New-Object System.Random
$computers = "ServerA.","ServerB","Serverc","ServerD","ServerE"
Foreach ($Comp in $Computers){
if (Test-Connection -CN $comp -Count 1 -BufferSize 16 -Quiet){
$admin = $usr + [char]$rand.next(97,122) + [char]$rand.next(97,122) + [char]$rand.next(97,122) + [char]$rand.next(97,122)
Get-OSCLocalAccount -ComputerName $comp | select-Object {$_.name -like "icec*"}
if ($_.name -eq $false) {
Add-LocalUserAccount -ComputerName $comp -username $admin -Password "password" -PasswordNeverExpires
}
Write-Output "$comp online $admin"
} Else {
Write-Output "$comp Offline"
}
}
Why bother checking? You can't create an account that already exists; you will receive an error. And with the ubiquitous -ErrorAction parameter, you can determine how that ought to be dealt with, such as having the script Continue. Going beyond that, you can use a try-catch block to gracefully handle those exceptions and provide better output/logging options.
Regarding your specific script, please provide the actual error you receive. If it returns no error but performs no action check the following:
Event Logs on the target computer
Results of -Verbose or -Debug output from the cmdlets you employ in your script
ProcMon or so to see what system calls, if any, happen.
On a sidenote, please do not tag your post with v2 and v3. If you need a v2 compatible answer, then tag it with v2. Piling on all the tags with the word "powershell" in them will not get the question answered faster or more effectively.
You can do a quick check for a local account like so:
Get-WmiObject Win32_UserAccount -Filter "LocalAccount='true' and Name='Administrator'"
If they already exist, you can either output an error (Write-Error "User $UserName Already exists"), write a warning (Write-Warning "User $UserName Already exists"), or simply silently skip the option.
Please don't use -ErrorAction SilentlyContinue. Ever. It will hide future bugs and frustrate you when you go looking for them.
This can very easily be done in one line:
Get-LocalUser 'username'
Therefore, to do it as an if statement:
if((Get-LocalUser 'username').Enabled) { # do something }
If you're not sure what the local users are, you can list all of them:
Get-LocalUser *
If the user is not in that list, then the user is not a local user and you probably need to look somewhere else (e.g. Local Groups / AD Users / AD Groups
There are similar commands for looking those up, but I will not outline them here