Use Alternate Credentials for PowerShell ADSI Provider WinNT Query - powershell

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") | % {
...
}

Related

Trying to run a script block locally as admin using powershell

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 ..."

Powershell integration with Office365

I would like some comments on the integration code that I am developing... trying to integrate Office365 to Solarwinds:
import-Module Office365Alerts
$Username = 'XXXXX#XXX.XXX'
$Password = 'XXXXXXXXXXXXXXXX'
$credential = New-Object -TypeName pscredential -ArgumentList $Username, ($Password | ConvertTo-SecureString -AsPlainText -Force) -ErrorAction Stop
$alerts = Get-Office365ServiceHealth -Credential $credential -ErrorAction Stop | Select-Object -Property * | Where-Object Service -like '*Exchange*'
foreach($a in $alerts){
[regex]$regex = '\bCurrent status:\s?.*\s'
$Mess = $a.LatestMessage
if($Mess -match $regex){
foreach($m in $mess){
Write-Host "Message:Title: $($a.Title)"
Write-Host "Message:Impact: $($a.UserImpact)"
Write-Host "Message:Start Time: $($a.StartTime)"
Write-Host "Message:Last Update: $($a.LastUpdate)"
Write-Host "Message: $($Matches.Values)"
}
Write-Host "Statistic: 1"
}
}
if($a -eq $null){
Write-Host "Message: Service is Healthy"
Write-Host "Statistic: 0"
The SolarWinds Powershell monitors are limited to 10 returned metric pairs (message and statistic), if there are more than 10 alerts returned, it will break. AS you are returning 5 messages, which appear to be identical in foreach($a in $alerts) loop, they won't have unique names, also probably causing you problems.
Are you running the script in Local Host or Remote Host execution mode? IF you are running it on a host without the Office365 commandlets, it'll fail.
Have you enabled debug logging, within the template? It's under Advanced at the top of the template editing view. Logs can be found in ProgramData\SolarWinds\Logs\APM, take note of your templateID or its componentIDs, as the logs will be referenced by them.

Trying to script profile deletion for remote workstations

I work in a large company and that is global and I would like to be able to run a script that will allow me to clean up old profile remotely. I found this script but it doesn't seem to do anything. Does anyone have thought to why. I am a novice in powershell so any help would be appreciated. Thanks in advance!
function Remove-ProfileWD {
<#
.SYNOPSIS
Interactive menu that allows a user to connect to a local or remote computer and remove a local profile.
.DESCRIPTION
Presents an interactive menu for user to first make a connection to a remote or local machine. After making connection to the machine,
the user is presented with all of the local profiles and then is asked to make a selection of which profile to delete. This is only valid
on Windows Vista OS and above for clients and Windows 2008 and above for server OS.
.EXAMPLE
Remove-ProfileWF
Description
-----------
Presents a text based menu for the user to interactively remove a local profile on local or remote machine.
#>
#Prompt for a computer to connect to
(
[Parameter(Mandatory=$true)]
$Computer = Read-Host "Please enter a computer name"
)
#Test network connection before making connection
If ($computer -ne $Env:Computername) {
If (!(Test-Connection -comp $computer -count 1 -quiet)) {
Write-Warning "$computer is not accessible, please try a different computer or verify it is powered on."
Break
}
}
Do {
#Gather all of the user profiles on computer
Try {
[array]$users = Get-WmiObject -ComputerName $computer Win32_UserProfile -filter "LocalPath Like 'C:\\Users\\%'" -ea stop
}
Catch {
Write-Warning "$($error[0]) "
Break
}
#Cache the number of users
$num_users = $users.count
Write-Host -ForegroundColor Green "User profiles on $($computer):"
Write-Host -ForegroundColor Green "User profile Last Use ate"
#Begin iterating through all of the accounts to display
For ($i=0;$i -lt $num_users; $i++) {
Write-Host -ForegroundColor Green "$($i): $(($users[$i].localpath).replace('C:\Users\','')) $(get-item \\$computer\C`$\users\$(($users[$i].localpath).replace('C:\Users\',''))| Foreach {$_.LastWriteTime}) "
}
Write-Host -ForegroundColor Green "q: Quit"
#Prompt for user to select a profile to remove from computer
Do {
$account = Read-Host "Select a number to delete local profile or 'q' to quit"
#Find out if user selected to quit, otherwise answer is an integer
If ($account -NotLike "q*") {
$account = $account -as [int]
}
}
#Ensure that the selection is a number and within the valid range
Until (($account -lt $num_users -AND $account -match "\d") -OR $account -Like "q*")
If ($account -Like "q*") {
Break
}
Write-Host -ForegroundColor Yellow "Deleting profile: $(($users[$account].localpath).replace('C:\Users\',''))"
#Remove the local profile
($users[$account]).Delete()
Write-Host -ForegroundColor Green "Profile: $(($users[$account].localpath).replace('C:\Users\','')) has been deleted"
#Configure yes choice
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Remove another profile."
#Configure no choice
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No","Quit profile removal"
#Determine Values for Choice
$choice = [System.Management.Automation.Host.ChoiceDescription[]] #($yes,$no)
#Determine Default Selection
[int]$default = 0
#Present choice option to user
$userchoice = $host.ui.PromptforChoice("","Remove Another Profile?",$choice,$default)
}
#If user selects No, then quit the script
Until ($userchoice -eq 1)
}

Create a script which disables a Windows Account on a target host. Only Admin can execute this action

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.

Powershell Verify local Credentials

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.