I am trying to write a powershell script that runs a specific code block as a domain admin and moves a computer to a specific OU.
If I run it as a domain admin, it works fine, but the problem is it usually runs it as a local admin; which obviously won't add the computer to the domain.
So I added the credentials as part of the script, but it doesn't seem to be working.
Here is my code:
CLS
$command = {
# Specify, or prompt for, NetBIOS name of computer.
$Name = $env:COMPUTERNAME
# Retrieve Distinguished Name of current domain.
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Root = $Domain.GetDirectoryEntry()
$Base = ($Root.distinguishedName)
# Use the NameTranslate object.
$objTrans = New-Object -comObject "NameTranslate"
$objNT = $objTrans.GetType()
# Initialize NameTranslate by locating the Global Catalog.
$objNT.InvokeMember("Init", "InvokeMethod", $Null, $objTrans, (3, $Null))
# Retrieve NetBIOS name of the current domain.
$objNT.InvokeMember("Set", "InvokeMethod", $Null, $objTrans, (1, "$Base"))
$NetBIOSDomain = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $objTrans, 3)
# Retrieve Distinguished Name of specified object.
# sAMAccountName of computer is NetBIOS name with trailing "$" appended.
$objNT.InvokeMember("Set", "InvokeMethod", $Null, $objTrans, (3, "$NetBIOSDomain$Name$"))
$ComputerDN = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $objTrans, 1)
#Bind to computer object in AD.
$Computer = [ADSI]"LDAP://$ComputerDN"
#Specify target OU.
$TargetOU = "OU=Block-Policies,OU=Windows 10,OU=LAPTOPS,OU=COMPUTERS,OU=COMPUTER-SYSTEMS,DC=domain,DC=com"
#Bind to target OU.
$OU = [ADSI]"LDAP://$TargetOU"
# Move computer to target OU.
$Computer.psbase.MoveTo($OU)
}
#Credentials
$domain = "domain.com"
$password = "2093dhqwoe3212" | ConvertTo-SecureString -asPlainText -Force
$username = "$domain\DomainAdmin"
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
#Run the command with escalation
Invoke-Command -Credential credential -ComputerName localhost -ScriptBlock {$command}
I know the credentials work because if I manually type them in and run the script, it works. I have tried using invoke-command as well as
start-job -ScriptBlock {$command} -Credential $credential
Neither seem to be working for me.
The start-job seems to go through, but doesn't actually move the computer. The invoke-command gives me an error.
"[localhost] Connecting to remote server localhost failed with the following error message: The client cannot connect to the destination specified in the request ..."
Related
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.
Is it possible to set machine level "My Computer" access and launch permissions from PowerShell?
The equivalent of
DComPerm.exe -ma set name permit level:l,r
DComPerm.exe -ml set name permit level:l,r
I am looking for a solution using PowerShell v 3.0. The target servers are Windows Server 2008 R2 and 2012.
I have found a number of references for setting the DCOM application security settings. However I can't figure out how to set it at the machine or top level.
https://janbk.wordpress.com/2015/03/12/automating-dcom-acl-with-powershell/
Alternative to using DcomPerm.exe and SetAcl.exe in powershell
We have been using WMI to set Launch Permissions.
Refer: https://rkeithhill.wordpress.com/2013/07/25/using-powershell-to-modify-dcom-launch-activation-settings/
This stopped working after windows security patches rolled out (patch #: 4012212, 4012213, and 4012213)
We converted WIM powershell script to use CIM and that took care of setting launch permissions on DCOM objects & works with the security patches. Code is below for reference:
$ComponentName = "TestComponent" #--- change value as needed
$Username = "Username" #--- change value as needed
$Domain = "Domain" #--- change value as needed
# If you already have a CimSession that you used to get the security descriptor, you can leave this line out and use the existing one:
$CimSession = New-CimSession localhost
Grant-DComAccessToUser -ComponentName $ComponentName -Username $Username -Domain $Domain
# Cleanup
$CimSession | Remove-CimSession
function Grant-DComAccessToUser {
param(
[Parameter(Mandatory=$true)][string] $ComponentName,
[Parameter(Mandatory=$true)][string] $Username,
[string] $Domain
)
$DCom = Get-CimInstance -Query "SELECT * from Win32_DCOMApplicationSetting WHERE Description LIKE '$ComponentName%'"
$GetDescriptor = Invoke-CimMethod -InputObject $DCom -MethodName "GetLaunchSecurityDescriptor";
$ExistingDacl = $GetDescriptor.Descriptor.DACL | Where {$_.Trustee.Name -eq $Username}
if ($ExistingDacl)
{
$ExistingDacl.AccessMask = 11
}
else
{
$NewAce = New-DComAccessControlEntry -Domain $Domain -Username $Username
$GetDescriptor.Descriptor.DACL += $NewAce
}
Invoke-CimMethod -InputObject $DCom -MethodName "SetLaunchSecurityDescriptor" -Arguments #{Descriptor=$GetDescriptor.Descriptor};
}
function New-DComAccessControlEntry {
param(
[Parameter(Mandatory=$true)][string] $Username,
[string] $Domain
)
# Create the Win32_Trustee instance
$Trustee = New-Object ciminstance $CimSession.GetClass("root/cimv2", "Win32_Trustee")
$Trustee.Name = $Username
$Trustee.Domain = $Domain
# Create the Win32_ACE instance
$Ace = New-Object ciminstance $CimSession.GetClass("root/cimv2", "Win32_ACE")
$Ace.AceType = [uint32] [System.Security.AccessControl.AceType]::AccessAllowed
$Ace.AccessMask = 11
$Ace.AceFlags = [uint32] [System.Security.AccessControl.AceFlags]::None
$Ace.Trustee = $Trustee
$Ace
}
You can change this script: https://gallery.technet.microsoft.com/scriptcenter/Grant-Revoke-Get-DCOM-22da5b96. It works with application permissions using registry path "HKCR:\AppID\$ApplicationID" and registry keys "AccessPermission", "LaunchPermission".
You should use registry path "HKLM:SOFTWARE\Microsoft\Ole" and registry keys "DefaultAccessPermission", "DefaultLaunchPermission", "MachineAccessRestriction", "MachineLaunchRestriction".
More info in "Configuring Remote DCOM" chapter: https://books.google.ru/books?id=rbpNppFdipkC&pg=PT211&lpg=PT211&dq=dcom+grant+local+launch+permission+powershell&source=bl&ots=5ZfeVca5NA&sig=9lMN_VeymG8cf73KT062QTsWWkc&hl=ru&sa=X&ved=0ahUKEwikn73f6YLcAhVEDSwKHUftCwkQ6AEIfDAI#v=onepage&q&f=true
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.
So in my script I want to not only have the user enter and store credentials in a variable but be able to verify that the password matches the admin password on the target system. So far the only way I have found to do this is by putting the actual password unecrypted in the script and comparing it to the one the user enters. That is a huge security flaw and to remedy it I was wondering if I could get the admin password using a gwmi query (SID?) as an object and compare that to the secure string the user enters.
Here is my flawed code I am using right now.
Do
{
$password = $null
$password = read-host "Enter the Administrator Password" -assecurestring
$AdminPass = ConvertTo-SecureString "adminpassword" -AsPlainText -Force
$pwd1_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
$pwd2_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($AdminPass))
if ($pwd1_text -cne $pwd2_text) {Write-Host -ForegroundColor Red "Incorrect Password"; $password = $null}
$count ++
$tries = 3 - $count
if ($password -eq $null) {Write-Host -ForegroundColor Yellow "$tries Attempts Remaining"}
if ($count -eq 3) {Write-Host -ForegroundColor Red "$count Unsuccessful Password Attempts. Exiting..."; exit}
}While ($password -eq $null)
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "$ComputerName\Administrator",$password
Here's a function I wrote that tests a PSCredential object, against a Domain or a local Machine:
function Test-Credential {
<#
.SYNOPSIS
Takes a PSCredential object and validates it against the domain (or local machine, or ADAM instance).
.PARAMETER cred
A PScredential object with the username/password you wish to test. Typically this is generated using the Get-Credential cmdlet. Accepts pipeline input.
.PARAMETER context
An optional parameter specifying what type of credential this is. Possible values are 'Domain' for Active Directory accounts, and 'Machine' for local machine accounts. The default is 'Domain.'
.OUTPUTS
A boolean, indicating whether the credentials were successfully validated.
.NOTES
Created by Jeffrey B Smith, 6/30/2010
#>
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.Management.Automation.PSCredential]$credential,
[parameter()][validateset('Domain','Machine')]
[string]$context = 'Domain'
)
begin {
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::$context)
}
process {
$DS.ValidateCredentials($credential.GetNetworkCredential().UserName, $credential.GetNetworkCredential().password)
}
}
If you want to test against local accounts on a remote machine, you'll need to load this function on the remote machine and test the credential against the 'local' machine via remoting (Invoke-Command), but it should be possible.
What am I trying to do?
Hi! I am writing a script that can accept 2 parameters, ComputerName and CheckWhatFile. The script will then access the computer (file server) specified by ComputerName and look for open file handles of CheckWhatFile.
The problem is the script needs to be executed by an administrative user. Our admins login as a non-privileged account. I want it to be as simple as clicking to run the script and only being prompted for the Get-Credentials box to enter there privileged account. I cannot use Invoke-Command unless you can find a way for it to not require having remote management turned on. The code below works when executed from a privileged PowerShell prompt that is started with runas /user: powershell.exe.
What I need help with
Help me find how to execute the 2 lines of code starting with netfile as a different user.
My code is:
param([string]$ComputerName = $null,[string]$CheckWhatFile = $null)
Import-Module ActiveDirectory
$Credentials = Get-Credential #Get Powerful Credentials
$netfile = [ADSI]"WinNT://$ComputerName/LanmanServer"
$netfile.Invoke("Resources") | foreach {
try
{
$Id = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
$ItemPath = $_.GetType().InvokeMember("Path", 'GetProperty', $null, $_, $null)
$UserName = $_.GetType().InvokeMember("User", 'GetProperty', $null, $_, $null)
$LockCount = $_.GetType().InvokeMember("LockCount", 'GetProperty', $null, $_, $null)
if($ItemPath -eq $CheckWhatFile)
{
$Culprit = Get-ADUser -Filter {SamAccountName -eq $UserName} -Credential $Credentials
Write-Host -ForegroundColor White -NoNewLine "Go Find "
Write-Host -ForegroundColor Yellow -NoNewLine $Culprit.Name
Write-Host -ForegroundColor White " and tell them to close the file!"
}
}
catch
{
}
}
Notes:
I have seen some examples with executing ADSI provider queries as a different user but they all relate to LDAP based queries not WinNT.
As it stands, the only way you're going to be able to do that is to have them RDP to the file server using the Admin credentials and run the script there. That's the only way they're going to get a powershell console to be able to see that output. Without powershell remoting enabled you can only get a local session, and that means either a local logon or RDP.
Something like this might work:
$provider = "WinNT://$ComputerName/LanmanServer"
$cred = Get-Credential
$netfile = New-Object DirectoryServices.DirectoryEntry(
$provider, $cred.UserName, $cred.GetNetworkCredential().Password
)
$netfile.Invoke("Resources") | % {
...
}