I'm looking at creating a local administrator on a handful of machines (>30). I don't really want to use GPO if I can get away with it. LAPS is a little overkill for what I need.
I found a nice script online but it only creates the user and doesn't add them to the administrators group. Can anyone see the error?
#Define variables
$computers = Get-Content C:\Computers.txt
#$computers = Import-CSV C:\Computers.txt | select Computer
$username = "Admin"
$password = "Password99"
$fullname = "Admin"
$local_security_group = "Administrators"
$description = "Description"
Foreach ($computer in $computers) {
$users = $null
$comp = [ADSI]"WinNT://$computer"
#Check if username exists
Try {
$users = $comp.psbase.children | select -expand name
if ($users -like $username) {
Write-Host "$username already exists on $computer"
} else {
#Create the account
$user = $comp.Create("User", "$username")
$user.SetPassword("$password")
$user.Put("Description", "$description")
$user.Put("Fullname", "$fullname")
$user.SetInfo()
#Set password to never expire
#And set user cannot change password
$ADS_UF_DONT_EXPIRE_PASSWD = 0x10000
$ADS_UF_PASSWD_CANT_CHANGE = 0x40
$user.userflags = $ADS_UF_DONT_EXPIRE_PASSWD + $ADS_UF_PASSWD_CANT_CHANGE
$user.SetInfo()
#Add the account to the local admins group
$group = ([ADSI]"WinNT://$computer/$local_security_group,group")
$username = [ADSI]"WinNT://$Computer/$username,user"
#Validate whether user account has been created or not
$users = $comp.psbase.children | select -expand name
if ($users -like $username) {
Write-Host "$username has been created on $computer"
} else {
Write-Host "$username has not been created on $computer"
}
}
}
Catch {
Write-Host "Error creating $username on $($computer.path): $($Error[0].Exception.Message)"
}
}
In your code you are not actually adding the user to the group.
Here you are actually retrieving a group object, but you are not doing anything with it.
#Add the account to the local admins group
$group = ([ADSI]"WinNT://$computer/$local_security_group,group")
$username = [ADSI]"WinNT://$Computer/$username,user"
First you must remove the assignment to $username. Then you must invoke a method on the $group object to add the user:
#Add the account to the local admins group
$group = ([ADSI]"WinNT://$computer/$local_security_group,group")
$computerHostName = (Get-WmiObject -ComputerName $computer Win32_ComputerSystem).Name
$group.Add([ADSI]"WinNT://$computerHostName/$username,user")
There is a catch here. Notice I use Get-WmiObject to get the hostname from the computer. When using the Add() method, the computer name must be the unqualified hostname. For example server-01, and NOT server-01.domain.lan
If you want to retrieve the ADSI object for the user later, I recommend assigning it to a different variable name, like this:
$adsiUser = [ADSI]"WinNT://$Computer/$username,user"
Related
Hello dear community.
We got MS RDS Terminal Server and we would like to remove User Profile Disks for deleted and (maybe) disabled users.
I would like one script with following tasks:
Get all UPDs from path
Check UPD Owner
Check if the owner exist in AD and if is enabled
Remove UPD if owner not exist in ad or if is enabled
$updtest = Get-ChildItem -Path C:\updtest -Force
foreach($UPD in $updtest) {
##Get User Name
$UPD = $UPD.ToString()
$UPD = $UPD.Trim("UVHD-")
$UPD = $UPD.Trim(".vhdx")
$objSID = New-Object System.Security.Principal.SecurityIdentifier `
($UPD)
$objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
$userTest = $objUser.Value
$userTest = $userTest.Trim("Domain\")
$AdUser = [bool] (Get-ADUser -Filter {SamAccountName -eq $userTest })
if ($AdUser -eq $false) {Write-Host "User $userTest exist"}
else {Write-Host "User $AdUser exist"}
In step no. 3 is getting harder, because some users are not right trimmed (first letter missing). $UPD and $userTest have to be trimmed because Get-ADUser don't accept "DOMAIN" and New-Object System.Security.Principal.SecurityIdentifier don't accept ".vhdx" and "UVHD-"
Is there any another possibility to do easier this tasks?
It is possible to check if SID exists in AD without any conversion?
Thank you in advance!
I want to free up some C Drive space on my servers by removing user profiles that from C:\users who haven't logged into the server in the last 6 months. I connect to the servers using PowerShell Cim commands.
So far I have only found the Get-CimInstance -CimSession $CimSession -ClassName Win32_UserProfile command that will list users profiles but it doesn't list the last logon time for each user. Is there another command that can be used to list UserProfiles with LastLogon? Once I have that list I want to delete any profile that hasn't logged into the server in the last 6 months.
How to delete user profiles older than a specified number of days in Windows
This PowerShell script sample shows how to delete user profiles older than a specified number of days.
Example 1:
C:\Script\RemoveLocalUserProfile.ps1 -ListUnusedDay 1
Example 2:
C:\Script\RemoveLocalUserProfile.ps1 -DeleteUnusedDay 1 -ExcludedUsers “marry”
# Begin Script
If ($ProfileInfo -eq $null)
{
Write-Warning -Message "The item not found."
}
Else
{
Foreach ($RemoveProfile in $ProfileInfo)
{
#Prompt message
$Caption = "Remove Profile"
$Message = "Are you sure you want to remove profile '$($RemoveProfile.LocalPath)'?"
$Choices = [System.Management.Automation.Host.ChoiceDescription[]]`
#("&Yes", "&No")
[Int]$DefaultChoice = 1
$ChoiceRTN = $Host.UI.PromptForChoice($Caption, $Message, $Choices, $DefaultChoice)
Switch ($ChoiceRTN)
{
0
{
Try {$RemoveProfile.Delete(); Write-Host "Delete profile '$($RemoveProfile.LocalPath)' successfully."}
Catch {Write-Host "Delete profile failed." -ForegroundColor Red}
}
1 {break}
}
}
$ProfileInfo|Select-Object #{Expression = {$_.__SERVER}; Label = "ComputerName"}, `
#{Expression = {$_.ConvertToDateTime($_.LastUseTime)}; Label = "LastUseTime"},`
#{Name = "Action"; Expression = {If (Test-Path -Path $_.LocalPath)
{"Not Deleted"}
Else
{"Deleted"}
}
}
}
# End Script
Similar approaches can be see here:
https://community.spiceworks.com/how_to/124316-delete-user-profiles-with-powershell
https://www.business.com/articles/powershell-manage-user-profiles
Take care when deleting profiles, you don't want to hit machine special accounts. The Win32_UserProfile class has a LastUseTime property you can rely on.
$session = New-CimSession -ComputerName $cn
$gcimParams = #{
'CimSession' = $session
'ClassName' = 'Win32_UserProfile'
'Filter' = 'RefCount<1 and Special="false" and Loaded="false"'
}
$profileList = (Get-CimInstance #gcimParams).Where{$PSItem.LastUseTime -lt (Get-Date).AddMonths(-6)}
foreach ($user in $profileList)
{
$user | Remove-CimInstance -CimSession $session
}
Setup:
We are all in domain environment. Active Directory is Windows Server 2012 R2
Client Workstations are mix of Windows 10 versions (1703 and 1709)
Need to create user "Servis" on all workstation and place it to local Administrator group. I will input a text file with the host names.
This is the script im trying to make it work, but no success.
The user is created, but user is not added to local admin group.
This is the error i get :
Error creating Service23 on WinNT://WS-TEST: The following exception occurred while retrieving member "add": "The network path was not found.
$computers = Get-Content -path C:\Scripts\CreateLocalUser\New\Computers.txt
$username = "Servis"
$password = "P4$$w0rd!##"
Foreach ($computer in $computers) {
$users = $null
$computer = [ADSI]"WinNT://$computername"
Try {
$users = $computer.psbase.children | select -expand name
if ($users -like $username) {
Write-Host "$username already exists" -ForegroundColor Green
}
Else {
$user_obj = $computer.Create("user", "$username")
$user_obj.SetPassword($password)
$user_obj.SetInfo()
$user_obj.Put("description", "$username")
$user_obj.SetInfo()
$user_obj.psbase.invokeset("AccountDisabled", "False")
$user_obj.SetInfo()
$users = $computer.psbase.children | select -expand name
if ($users -like $username) {
Write-Host "$username has been created on $($computer.name)"
$group = [ADSI]("WinNT://" + $computername + "/administrators,group")
$group.add("WinNT://" + $computername + "/" + $username + ",user")
}
Else {
Write-Host "$username has not been created on $($computer.name)"
}
}
}
Catch {
Write-Host "Error creating $username on $($computer.path): $($Error[0].Exception.Message)"
}
}
Once you've defined your credential correctly with a password in a SecureString format creating a local admin is a 2 step process:
Create a user
Add that user to the Local Administrators group
These few lines are the powershell code needed:
$username = "Servis"
$password = ConvertTo-SecureString -AsPlainText "P4$$w0rd!##" -Force
New-LocalUser "$username" -Password $password -FullName "$username" -Description "Local admin $username"
Add-LocalGroupMember -Group "Administrators" -Member "$username"
If you want make you script more robust you should consider to create some Pester tests and add these checks to your script:
Validate if your file exists
Validate the content of the file (if is an IP or a FQDN with a regex)
Test if the host is reachable (e.g. Test-Connection -computername $computer -count 1 -quiet) before connecting
Using invoke-command with computername and argumentlist (username, password)
Check if the user was created successfully and is a member of the administrator group if not throwing an error
Write a log file to keep trace of all the tasks completed (by who, when on what host and so on).
I wrote a blog post on www.scriptinglibrary.com on few better ways of completing this task if you're interested.
I am trying to create user on remote machine using Powershell. Once account created I want to add that in local admin group.
Account is getting created but it is not getting added in admin group. Below is the code that I am using.
cls
$username = "test_user"
$password = "password"
$computer1 = hostname
$users = $null
$computer = [ADSI]“WinNT://$computer1”
Try {
$users = $computer.psbase.children | select -expand name
if ($users -like $username) {
Write-Host "$username already exists"
} Else {
$user_obj = $computer.Create(“user”, “$username”)
$user_obj.SetPassword($password)
$user_obj.SetInfo()
$user_obj.Put(“description”, “$username”)
$user_obj.SetInfo()
$user_obj.psbase.invokeset(“AccountDisabled”, “False”)
$user_obj.SetInfo()
$users = $computer.psbase.children | select -expand name
if ($users -like $username) {
Write-Host "$username has been created on $($computer.name)"
$group = [ADSI]("WinNT://"+$env:COMPUTERNAME+"/administrators,group")
$group.add("WinNT://$env:localhost/$username,user")
} Else {
Write-Host "$username has not been created on $($computer.name)"
}
}
} Catch {
Write-Host "Error creating $username on $($computer.path): $($Error[0].Exception.Message)"
}
What am I doing wrong?
$env:computername is your local computer. $env:localhost doesn't exist. $computer1 is the variable you defined of the computer to you are adding the user to earlier.
$group = [ADSI]("WinNT://$computer1/administrators,group")
$group.add("WinNT://$computer1/$username,user")
I use this
$computername = "computername" # place computername here for remote access
$username = 'user'
$password = 'P#ssw0rd1' #password
$desc = 'Local admin account'
$computer = [ADSI]"WinNT://$computername,computer"
$user = $computer.Create("user", $username)
$user.SetPassword($password)
$user.Setinfo()
$user.description = $desc
$user.setinfo()
$user.UserFlags = 65536
$user.SetInfo()
$group = [ADSI]("WinNT://$computername/administrators,group")
$group.add("WinNT://$username,user")
That is a whole lot of code, just to do this.
Invoke-Command -ComputerName SomeRemoteComputerName -ScriptBlock {
net user SomeNewUserName SomePassword
net localgroup administrators SomeNewUserName /add
}
Yeppers, I know, it's not all pure PoSH. Sure, you can do this with via more code in PoSH (way more in ), but sometimes you just need to get stuff done.
But vs doing this from scratch (well, unless you are just trying to learn stuff). There is a whole set of pre-built scripts and module for you to leverage. See:
'gallery.technet.microsoft.com/scriptcenter/site/search?f%5B0%5D.Type=RootCategory&f%5B0%5D.Value=localaccount'
Of course if all machines were on PoSH v5+, then you just use the built-in cmdlets for local user / group management.
'learn.microsoft.com/en-us/powershell/module/microsoft.powershell.localaccounts/?view=powershell-5.1'
As for the other question:
'check service account exist or not'
Assuming you are asking if this is for a remote computer, then it's the same approach.
Invoke-Command -ComputerName SomeRemoteComputerName -ScriptBlock {
Get-Service -Name SomeServiceName
Get-Service -DisplayName SomeServiceDisplayName
}
I have a task set up via GPO that is running a PowerShell script. I have found out that Get-ADUser can only be used if the module is available on the machine you are running the script on. Is there another way of seeing if a user is in a specific group when running on several client machines that I do not want to install anything extra on? This was easy with VBScript, but I'm not sure how else to do it with PowerShell. Here is the one line that is not working on machines without AD:
If ((Get-ADUser $User -Properties memberof).memberof -like "CN=GROUP*")
With VBScript, the following works on all machines:
If IsMember("GROUP") Then
First you need to get the DistinguishedName(See Function Above) of the user, and your DomainController name, then you can use [ADSI] type without the active directory module, like this:
DC = Name of your DomainController //DC/$UserDN
Function Get-DN ($SAMName)
{
$root = [ADSI]''
$searcher = new-object System.DirectoryServices.DirectorySearcher($root)
$searcher.filter = "(&(objectClass=user)(sAMAccountName= $SAMName))"
$user = $searcher.findall()
if ($user.count -gt 1)
{
$count = 0
foreach($i in $user)
{
write-host $count ": " $i.path
$count = $count + 1
}
$selection = Read-Host "Please select item: "
Return $user[$selection].path
}
else
{
return $user[0].path
}
}
$GroupName = "MyGroup"
$UserDN = Get-DN $env:USERNAME
$user = [adsi]$UserDN
if ($User.memberOf -match $GroupName)
{
"The User is Member of $GroupName"
}
else
{
"The User is not Member of $GroupName"
}