How to validate Windows account and password with Perl? - perl

I have solved this in Powershell 2.0, but need to port to Perl 5.10 for a legacy app.
I have Windows service account credentials which cannot INTERACTIVELY LOGON passed to a perl script as $Account and $Password. The $Account variable includes the domain name and AD user account name. My Powershell solution (one-liner) is:
PS C:\> New-PsSession -Credential (new-object -typename System.Management.Automation.PSCredential -argumentlist '$Account', (ConvertTo-SecureString '$Password' -AsPlainText -Force)); exit-PsSession;
If the new PsSession gets created then the account validation passed and I get output to STDOUT like:
Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
1 Session1 localhost Opened Microsoft.PowerShell Available
If the validation failed I get output like this to STDERR:
[localhost] Connecting to remote server failed with the following error message
: Access is denied. For more information, see the about_Remote_Troubleshooting
Help topic.
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:Re
moteRunspace) [], PSRemotingTransportException
+ FullyQualifiedErrorId : PSSessionOpenFailed
I can parse these results in my program. I want to do a similar thing in Perl 5.10 on Windows. All I want to do is test that a password works for an account bearing in mind that interactive logon is denied. Any ideas?

A completely different approach is to contact AD directly using Net::LDAP, but then you have a lot of new challenges.
I also used something like the following (calling win ole) long time ago. But had to abandon on win200X-server i think. If it may be of any help: (Please excuse poor coding)
But this will actually check that the current user is a member of a group.
I presume it may be used to verify username pwd somehow as well.
require Win32::OLE;
sub GetObject {
Win32::OLE->GetObject(#_);
}
my ( $module, $database, $mode ) = #_;
my $env = {%ENV}; #makes a copy, makes it possible to override $ENV for test etc
my $oRoot = GetObject("LDAP://rootDSE");
my $domain_name = $oRoot && $oRoot->Get("defaultNamingContext");
my $domain_host = $oRoot && $oRoot->Get("dnsHostName");
$domain_host .= "/" unless $domain_host =~ /\/$/;
my $strAttributeName ="sAMAccountname"; #could be userPrincipalName, cn etc
my #user_parts = ($user_name); # the last one (i.e. -1) will be the user name
my $alias = $user_name;
my $group_prefix = $system_info_defs->{adAuthGroupPrefix};
my $strADsPath = "LDAP://$domain_host$domain_name";
my $objConnection = Win32::OLE->new("ADODB.Connection")
or do{warn "Cannot create ADODB Connection!";return undef};#die ("Cannot create ADODB object!");
my $objCommand = Win32::OLE->new("ADODB.Command")
or do{warn "Cannot create ADODB command!";return undef};#die ("Cannot create ADODB object!");
$objConnection->{Provider} = ("ADsDSOObject");
$objConnection->Open();
$objCommand->{ActiveConnection} = ($objConnection);
etc. etc.

use a system call from perl i.e. backticks
my $a = `powershell -command "ls;exit;"`;
if ($a =~ /pl$/) {
print "folder contains a perl file";
} else {
print "folder contains no perl file (strange)";
}
if this works then replace the simple ls-command with your more complex text.
Probalby you may need to elaborate on \" " ' \' "" and '' to get it right.

Related

Adding a PC to a Security Group in AD via Powershell without having to install RSAT

a few weeks ago I started setting up my MDT(Microsoft Deployment Toolkit) custom Image. Nearly everything works fine so far except my recent Powershell script which is meant for adding a computer to a specific security group without RSAT Tools. I tested it on a newly installed OS but I keep on getting the Exception as in the Powershell Exception link shown below. I'm not really into Powershell programming and I tested several scripts to get it to work and I ended up with this one but I think I didn't fully get the hang of it.
Any help/advice or alternative is highly appreciated :).
My Powershell Code:
<#
PowerShell to join computer object to Active Directory Group without AD module being imported
This finds the computer object anywhere in AD and adds it to a security group in a known location
#>
#Get computer name
$ComputerName = gc env:computername
#Check to see if computer is already a member of the group
$isMember = new-object DirectoryServices.DirectorySearcher([ADSI]"NameofMYSecurityGroup")
$ismember.filter = “(&(objectClass=computer)(sAMAccountName= $Computername$)(memberof=CN=Computers,DC=MY_DOMAIN,DC=LOCAL))”
$isMemberResult = $isMember.FindOne()
#If the computer is already a member of the group, just exit.
If ($isMemberResult) {exit}
else
#If the computer is NOT a member of the group, add it.
{
$searcher = new-object DirectoryServices.DirectorySearcher([ADSI]"NameofMYSecurityGroup")
$searcher.filter = “(&(objectClass=computer)(sAMAccountName= $Computername$))”
$FoundComputer = $searcher.FindOne()
$P = $FoundComputer | select path
$ComputerPath = $p.path
$GroupPath = "LDAP://CN=Computers,DC=MY_DOMAIN,DC=LOCAL"
$Group = [ADSI]"$GroupPath"
$Group.Add("$ComputerPath")
$Group.SetInfo()
}
it's german by the way but it basically says:
Exception calling "Add" with 1 Arguments: "Unknown Name. (Exception From HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))
AT F:\"SourcePath"
+ $Group.Add("$ComputerPath")
+CategoryInfo :NotSpecified: (:) [], MethodInvocationException
+FullyQuallifiedErrord :CatchFromBaseAdapterMethodInvoke
Exception Link:
Powershell Exception
Untested, but this may help you in the right direction:
$ComputerName = $env:COMPUTERNAME
$GroupDN = 'CN=Computers,DC=MY_DOMAIN,DC=LOCAL'
# initialize the DirectorySearcher
$root = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
$searcher = New-Object System.DirectoryServices.DirectorySearcher($root.defaultNamingContext)
$searcher.SearchScope = 'SubTree'
# Check to see if computer is already a member of the group
$searcher.Filter = "(&(objectCategory=Computer)(objectClass=User)(samaccountname=$ComputerName$)(memberof=$GroupDN))"
$isMember = $searcher.FindOne()
# If the computer is already a member of the group, just exit.
if ($isMember) { exit }
# get the computer object
$searcher.Filter = "(&(objectCategory=Computer)(objectClass=User)(samaccountname=$ComputerName$))"
$ComputerDN = $searcher.FindOne().Properties['distinguishedname']
$ComputerObject = [ADSI]"LDAP://$ComputerDN"
# get the group object
$GroupObject = [ADSI]"LDAP://$GroupDN"
# add the computer to the group
$GroupObject.Add($ComputerObject.AdsPath)
# no need for this $Group.SetInfo()

creating a mapped drive from variables

I had all this working but must have changed something because it doesn't now! I want to pass in variables and then from them create a mapped drive.
$d = "O:";
$s = "\\Server\Folder";
$u = "/user:DOMAIN\UserId password";
net use $d $s $u;
When I run that now, powershell just hangs. I don't get any errors or nothing it just "runs" but doesn't complete. I have to hit the stop script button. If I check net use there is no entry for it in there.
if I manually type net use O: \\Server\Folder /user:DOMAIN\UserId password then it works and creates the mapped drive.
I have tried escaping the backslash as per below:
$d = "O:";$s = "\\\\Server\Folder";$u = "/user:DOMAIN\\UserId password";
also building the string like (with and without the escaped backslashes.):
$n = $d+' '+$s+' '+$u;
net use n;
this method advises the network cannot be found, again, with and without the escaped backslashes. How ever If I echo $n and paste the code it creates the drive so I am totally at a loss!
Calling a CMD command within a PowerShell script you should use Invoke-Expression.
$d = "O:";
$s = "\\Server\Folder";
$u = "/user:DOMAIN\UserId password";
Invoke-Expression "C:\Windows\System32\net.exe use $d $s $u"

Enter-PSSession, try with multiple credentials

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

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"

Trying to add Domain Global group to a remote machine local group in the domain with powershell

I am currently trying to add domain global group (Desktopgr) to my local group (Localgr).
Let me precise this in more details:
I had a machine 2008 server (LDAPCLIENT, ip 10.112.252.222) in a domain (GermanTool.com).
I have a AD (Active directory) server (Vcenter, ip 10.112.252.218).
GermanTool.com is the only domain in the AD server.
The domain has many global group, one of it is 'Desktopgr'.
This group cntains user who can log in to any server in the domain through RDP.
From GUI server management tool, I can add the Domain Global group (Desktopgr) to a member of computer local group (Localgr)
I have already login to the LDAPCLIENT machine as a domain user 'GermanTool\rohit' and password '%%%%%'.
I have removed that group from Localgr. Now I try to write a Powershell script to work the same thing.
I am already logged in as a domain user to the LADPCLIENT machine and both the machines can be pinged from either side and both are in same domain.
I run the script I wrote but getting error as:
Exception calling "Add" with "1" argument(s): "Access is denied.
At C:\script\a.ps1:44 char:20
$LocalGroup.Add <<<< ($DomainGroup.Path)
CategoryInfo : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : CatchFromBaseAdapterMethodInvokeTI
The Script is given bellow:
#######################################################################
#
# File: Add2LocalGr.ps1
# Purpose: Example of adding a Domain group to a Local group
# Author: Rohit Basu
# Date: 12/05/2013
# Get List of Servers from Flat TXT file`Servers.txt`
$Servers = Get-Content Servers.txt
#GermanTool
#"LDAP://ldap.company.com:389/$($dn)"
#$credn = New-Object -TypeNam System.DirectoryServices.DirectoryEntry("LDAP://10.112.252.218:389/GermanTool","rohit","%%%%%")
#Initaliaze the Domain Group Object
$DomainGroup = [ADSI]"WinNT://10.112.252.218/Desktopgr,Group"
#$DomainGroup = "$credn/Globalgroup,Group"
#Name the LogFile and Initialize it
$LogFile = ".\Logs\ServerLog.txt"
New-Item $LogFile -type file -force
ForEach ($Server in $Servers) #Loop through each server
{
$Server
$Server>>$LogFile
#Get Local Group object
$LocalGroup = [ADSI]"WinNT://$Server/Localgr,Group"
"Hi This is to see Localgroup">>$LogFile
$LocalGroup.Path>>$LogFile
"Hi This is to see Globalgroup">>$LogFile
$DomainGroup.Path>>$LogFile
#Assign DomainGroup to LocalGroup
$LocalGroup.Add($DomainGroup.Path)
#Determine if command was successful
If (!$?) #Add failed
{
$Server + " fail: " + $Error[0]>>$LogFile
"">>$LogFile
}
Else #Add succeeded
{
$Server + " success">>$LogFile
"">>$LogFile
$Server + " success"
}
}
#####################################################
The Servers.txt file contains one Ip: 10.112.252.222