Import users to local with powershell windows server 2008 r2 - windows

I got this script:
$Users = Import-Csv C:\Users\Administrator\Desktop\userImport\userTest.csv
$Users | % {
# Setting data
$computer = [ADSI]"WinNT://."
$userGroup = [ADSI]"WinNT://./Users,Group"
# Create user itself
$createUser = $computer.Create("User",$_.userid)
# Set password (print1!)
$createUser.SetPassword($_.password)
$createUser.SetInfo()
# Create extra data
$createUser.Description = "Import via powershell"
$createUser.FullName = $_.'full name'
$createUser.SetInfo()
# Set standard flags (Password expire / Password change / Account disabled)
$createUser.UserFlags = 64 + 65536 # ADS_UF_PASSWD_CANT_CHANGE + ADS_UF_DONT_EXPIRE_PASSWD
$createUser.SetInfo()
# Adduser to standard user group ("SERVER02\Users")
$userGroup.Add($createUser.Path)
}
But I get the error:
A member could not be added to or removed from the local group because the member does not exist. How Can I possible fix it??

try changing the . with the computer name here:
$computer = [ADSI]"WinNT://."
as
$compname = hostname
$computer = [ADSI]"WinNT://$compname"

Related

How to fix security within WinSCP SFTP scripts in PowerShell with hard-coded passwords

So my organization has tasked me with cleaning up some of the security issues in regards to some automated scripts that have hard coded passwords within the scripts that are running as automated tasks. One such task contains SFTP scripts that export and import files to and from with the password, host name, credentials, port, and everything exposed within the script. As a result, I would like to first see about how to call such credentials within a separate file that can be hidden and two see about encryption and salting it later. But my main focus is getting them out of the script in case traffic is every intercepted. Here is what the PowerShell code looks like:
param (
$localPath = 'E:\FTP\SchooLinks\course_requests.csv',
$remotePath = '/schoolinks_exports/course_planning/course_requests.csv'
)
try
{
# Load WinSCP .NET assembly
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "<domain_name>"
UserName = "<username>"
Password = "<password>"
SshHostKeyFingerprint = "<fingerprint>"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult =
$session.GetFiles($remotePath, $localPath, $False, $transferOptions)
# Throw on any error
$transferResult.Check()
# Print results
foreach ($transfer in $transferResult.Transfers)
{
Write-Host "Download of $($transfer.FileName) succeeded"
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}
catch
{
Write-Host "Error: $($_.Exception.Message)"
exit 1
}
Another one that we have looks like this:
param (
$localPath = 'E:\FTP\TalentEd\SkywardApplicantExportSQL.txt',
$remotePath = '/SkywardApplicantExportSQL.txt'
)
try
{
# Load WinSCP .NET assembly
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "<domain>"
UserName = "<username>"
Password = "<password>"
SshHostKeyFingerprint = "<sha_fingerprint>"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult =
$session.GetFiles($remotePath, $localPath, $False, $transferOptions)
# Throw on any error
$transferResult.Check()
# Print results
foreach ($transfer in $transferResult.Transfers)
{
Write-Host "Download of $($transfer.FileName) succeeded"
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}
catch
{
Write-Host "Error: $($_.Exception.Message)"
exit 1
}
I am familiar with Python and json and calling stuff within a json file similar to the following:
import json
with open('secrets.json','r') as f:
config = json.load(f)
and calling it with (config['object']['nested_element']) within the Python script.
I would like to do something similar with PowerShell, however I have very limited knowledge to PowerShell.
Yeppers, of course, never store creds in clear text in files.
There are several ways to store credentials for use. Secure file (xml, etc..), the registry, or Windows Credential Manager and this is well documented on Microsoft sites, as well as in many articles all over the web and via Q&A's on StackOverflow.
Just search for 'securely store credentials PowerShell'
Sample results...
Working with Passwords, Secure Strings and Credentials in Windows
PowerShell
How to run a PowerShell script against multiple Active Directory
domains with different credentials
Accessing Windows Credentials Manager from PowerShell
Save Encrypted Passwords to Registry for PowerShell
...and/or the modules via the MS powershellgallery.com directly installable from your PowerShell environments.
Find-Module -Name '*cred*' |
Format-Table -AutoSize
<#
# Results
Version Name Repository Description
------- ---- ---------- -----------
2.0 CredentialManager PSGallery Provides access to credentials in the Windows Credential Manager
2.0.168 VcRedist PSGallery A module for lifecycle management of the Microsoft Visual C++ Redistributables. Downloads the supp...
1.3.0.0 xCredSSP PSGallery Module with DSC Resources for WSMan CredSSP.
1.1 VPNCredentialsHelper PSGallery A simple module to set the username and password for a VPN connection through PowerShell. Huge tha...
1.0.11 pscredentialmanager PSGallery This module allows management and automation of Windows cached credentials.
4.5 BetterCredentials PSGallery A (compatible) major upgrade for Get-Credential, including support for storing credentials in Wind...
1.0.4 WindowsCredential PSGallery Management module for Windows Credential Store.
...
#>
So many thanks to #postanote and #Martin Prikryl I was able to figure this out.
You can basically use a config.xml file with contents similar to this:
<Configuration>
<localPath>insert_local_file_path</localPath>
<remotePath>insert_remote_file_path</remotePath>
<Protocol>[WinSCP.Protocol]::Sftp</Protocol>
<HostName>insert_hostname</HostName>
<UserName>username</UserName>
<Password>mypassword</Password>
<SshHostKeyFingerPrint>fingerprint</SshHostKeyFingerPrint>
</Configuration>
From here you can use the following at the beginning of your template:
# Read XML configuration file
[xml]$config = Get-Content ".\config.xml"
param (
$localPath = $config.Configuration.localPath
$remotePath = $config.Configuration.remotePath
)
try
{
# Load WinSCP .NET assembly
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = $config.Configuration.Protocol
HostName = $config.Configuration.HostName
UserName = $config.Configuration.UserName
Password = $config.Configuration.Password
SshHostKeyFingerprint = $config.Configuration.SshHostKeyFingerprint
}
I have more SFTP templates here people can use at
https://github.com/Richard-Barrett/ITDataServicesInfra/tree/master/SFTP

Get Property guid

This is the context: I'm trying to set a bunch of properties to the group "Authenticated Users". For that, I've written the following script:
# GETTING AUTHENTICATED USERS SID
$sid1 = "S-1-5-11"
$objSID1 = New-Object System.Security.Principal.SecurityIdentifier($sid1)
# GETTING AUTHENTICATED ACL
$acl = Get-Acl -Path "AD:DC=*****,DC=*****"
# CREATING RULE ATTTIBUTES
$objectGuid = New-Object Guid 5f332e20-9eaa-48e7-b8c4-f4431fef859a
$identity = [System.Security.Principal.IdentityReference] $objSID1
$adRights = [System.DirectoryServices.ActiveDirectoryRights] "ReadProperty,WriteProperty"
$type = [System.Security.AccessControl.AccessControlType] "Allow"
$inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "Descendents"
$inheritedobjectguid = New-Object Guid bf967aba-0de6-11d0-a285-00aa003049e2
# CREATING THE NEW RULE
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $identity, $adRights, $type, $objectGuid, $inheritanceType, $inheritedobjectguid
# SETTING THE NEW RULE
$acl.AddAccessRule($ace)
Set-Acl -AclObject $acl "AD:DC=*****,DC=*****"
And the final result should be this:
One important thing is that what I'm trying to set, as can be seen in the second image, is a property and not a permission. And that property doesn't have the same GUID in all the computers because I create it with another script before this one.
The question is the following:
In the code where I set $objectGuid variable I've hardcoded the GUID I need. What I need to know is if there is any way to get the GUID of the property using PowerShell.
You can retrieve the GUID of an attribute from the Schema:
Query the schemaNamingContext for an attributeSchema object
Filter on ldapDisplayName, the attribute name shown by the GUI
Grab the schemaIDGUID attribute value and use that in the ACE
I'll use the RSAT ActiveDirectory module for simplicity here, but you can do this with any ldap client:
$attrSchemaParams = #{
SearchBase = (Get-ADRootDSE).schemaNamingContext
Filter = "ldapDisplayName -eq 'pwmEventLog' -and objectClass -eq 'attributeSchema'"
Properties = 'schemaIDGUID'
}
$pwmEventLogSchema = Get-ADObject #attrSchemaParams
$pwmEventLogGUID = $pwmEventLogSchema.schemaIDGuid -as [guid]
Now use $pwmEventLogGUID in place of $objectGuid

How can I set the "Environment" > "Start the following program at logon" property on a local user using powershell?

Background:
I have a server with Windows 2008 R2 installed running as a terminal server session host. I have a long list of local users set-up and configured as remote desktop users. When the users remotely log on using remote desktop connection, a program automatically starts up. When the user closes that program, the session ends. This all works fine if I set it up manually.
My Question:
I have written a script to add a list of local users automatically and setup and configure the properties. The problem is that nowhere can I find how to set the "Environment" > "Start the following program at logon" properties. (See image for the properties I want to set)
A sample portion of my current script is as follow:
$computer = "localhost"
$userName = "aTestUser"
$objComputer = [ADSI]"WinNT://$computer"
$objUser = $objComputer.Create('user', $userName)
$objUser.SetPassword("Password")
$objUser.PSBase.InvokeSet('Description', "Some description for $userName")
$objUser.PSBase.InvokeSet('userflags', 512)
$objUser.PSBase.InvokeSet('passwordExpired', 1)
$objUser.SetInfo();
I also tried this command which doesn't work:
$objUser.PSBase.InvokeSet("TerminalServicesInitialProgram", "C:\programs\a_test_program.exe")
I have searched on Microsoft's MSDN site and Google and StackOverflow but could not find this specific property.
I found a solution here.
$ou = [adsi]"WinNT://127.0.0.1"
$user = $ou.psbase.get_children().find("test")
$user.PSBase.InvokeSet("TerminalServicesInitialProgram", "C:\logoff.bat")
$user.setinfo()
Okay, so I finally got it working. Seems like you have to first create the user then open it again for editing before the InvokeSet sets the TerminalServicesInitialProgram property.
I am not sure, maybe someone can share some experience or explanation.
Thank you to everyone for your help and assistance.
Working Code:
# Read the CSV file and create the users
# The CSV file structure is:
# UserName,FullName,Description
$Users = Import-Csv -Path "C:\Users.csv"
foreach ($User in $Users)
{
# adds user
$computer = "localhost"
$username = $User.UserName
#$username = "atest001"
$fullname = $User.FullName
#$fullname = "My Name"
$description = $User.Description
#$description = "A new user description"
$password = "MyGreatUnbreakableSecretPassword"
$objComputer = [ADSI]"WinNT://$computer"
$objUser = $objComputer.Create('user', $username)
$objUser.SetPassword($password)
$objUser.PSBase.InvokeSet("Description", $description)
$objUser.PSBase.InvokeSet('userflags', 65536)
$objUser.SetInfo();
# set password not to expire
#wmic USERACCOUNT WHERE "Name = '$username'" SET Passwordexpires=FALSE
# Add to groups
$group = [ADSI]"WinNT://./Power Users,group"
$group.Add("WinNT://$username,user")
$group = [ADSI]"WinNT://./WW_Users,group"
$group.Add("WinNT://$username,user")
$ou = [adsi]"WinNT://127.0.0.1"
$user = $ou.psbase.get_children().find($username)
$user.PSBase.InvokeSet("TerminalServicesInitialProgram", "C:\Program Files (x86)\Wonderware\InTouch\view.exe c:\program files (x86)\archestra\framework\bin\sibanyegold-kdce_app_tse1_test")
$user.PSBase.InvokeSet("MaxConnectionTime", 120)
$user.PSBase.InvokeSet("MaxDisconnectionTime", 1)
$user.PSBase.InvokeSet("MaxIdleTime", 30)
$user.PSBase.InvokeSet("BrokenConnectionAction", 1)
$user.PSBase.InvokeSet("ReconnectionAction", 1)
$user.PSBase.InvokeSet("FullName", $fullname)
$user.setinfo()
}

Can you create an anonymous-access Windows share all from PowerShell?

Windows makes it difficult to create a network share with anonymous access (in other words, users who the share-hosting machine does not know about can access). The net share ShareName=C:\DesiredShareSource /GRANT:EVERYONE,FULL gives access to Everyone, but that does not include anonymous access (e.g. non-domain joined users, WITHOUT prompting credentials).
I know there's a way to do this from a GUI (https://serverfault.com/questions/272409/setting-up-an-anonymous-windows-server-2008-network-share), but is there a way changing security policies and creating anonymous network shares can be done strictly from PowerShell?
EDIT
This is what happens when I run the WMI script posted by Ansgar Wiechers. I get an exception but the share mounts successfully:
However, when I try and connect to the share from another box on the same network, I am still prompted for a username and password, as seen below:
Again, I want anonymous access (no username and password) to be set up all from command line.
Here is the exact code I am using in testingAnonShare.ps1, on a Win7 system:
$path = 'C:\Users\<REDACTED>\Desktop\Attempt'
$name = 'testinganon'
$description = 'share description'
function Get-Trustee($sid) {
$trustee = ([wmiclass]'Win32_Trustee').CreateInstance()
$trustee.SID = ([wmi]"Win32_SID.SID='$sid'").BinaryRepresentation
return $trustee
}
function New-FullAce($sid) {
$ace = ([wmiclass]'Win32_ACE').CreateInstance()
$ace.AccessMask = 2032127 # full control
$ace.AceFlags = 3 # container inherit + object inherit
$ace.AceType = 0 # access allowed
$ace.Trustee = Get-Trustee $sid
return $ace
}
$sd = ([wmiclass]'Win32_SecurityDescriptor').CreateInstance()
$sd.ControlFlags = 4
$sd.DACL = (New-FullAce 'S-1-1-0'),
(New-FullAce 'S-1-5-7')
$wmi = Get-WmiObject Win32_Share -List
$wmi.Create($path, $name, 0, $null, $description, '', $sd) | Out-Null
All examples create a share called test mapped to a path D:\test, granting full access to Anonymous and Everyone.
Windows Server 2012 R2 and newer
To create a share with everyone having Full access this is the command
New-SmbShare -Name 'test' -path 'D:\test' -FullAccess 'ANONYMOUS LOGON','Everyone'
To update an existing share to have the same permission is a little more complicated. First, assume the share name is test. Here is the code to change it to the same permissions as above.
Get-SmbShare -Name test |
Set-SmbShare -SecurityDescriptor 'O:BAG:DUD:(A;;FA;;;AN)(A;;FA;;;WD)'
To get the SecurityDescriptor string, create a share test like you want it and run the following command.
(get-smbshare -Name Test).SecurityDescriptor
Backward compatible (NET SHARE)
This can also be done with net share
net share test=D:\test /GRANT:"ANONYMOUS LOGON,FULL" /GRANT:"Everyone,FULL"
In addition to New-SmbShare (Windows Server 2012 or newer) and net share you can also use WMI for creating network shares.
$path = 'C:\DesiredShareSource'
$name = 'sharename'
$description = 'share description'
function Get-Trustee($sid) {
$trustee = ([wmiclass]'Win32_Trustee').CreateInstance()
$trustee.SID = ([wmi]"Win32_SID.SID='$sid'").BinaryRepresentation
return $trustee
}
function New-FullAce($sid) {
$ace = ([wmiclass]'Win32_ACE').CreateInstance()
$ace.AccessMask = 2032127 # full control
$ace.AceFlags = 3 # container inherit + object inherit
$ace.AceType = 0 # access allowed
$ace.Trustee = Get-Trustee $sid
return $ace
}
$sd = ([wmiclass]'Win32_SecurityDescriptor').CreateInstance()
$sd.ControlFlags = 4
$sd.DACL += (New-FullAce 'S-1-1-0').PSObject.BaseObject
$sd.DACL += (New-FullAce 'S-1-5-7').PSObject.BaseObject
$wmi = Get-WmiObject Win32_Share -List
$wmi.Create($path, $name, 0, $null, $description, '', $sd) | Out-Null
S-1-1-0 and S-1-5-7 are the well-known SIDs of the Everyone and Anonymous groups respectively.
Appending each ACE separately to the DACL property is required to make the code work with PowerShell v2. In more recent version you can assign the ACEs as an array, and you also don't need to unwrap the base object:
$sd.DACL = (New-FullAce 'S-1-1-0'), (New-FullAce 'S-1-5-7')
To actually enable anonymous access to shares you also need to make three changes to the local security policy (source):
Start secpol.msc.
Navigate to Security Settings → Local Policies → Security Options.
Change the following settings:
Accounts: Guest account status → Enabled
Network access: Let Everyone permissions apply to anonymous users → Enabled
Network access: Shares that can be accessed anonymously → sharename
Note that I did not have to change the setting Network access: Restrict anonymous access to Named Pipes and Shares to enable anonymous access from Windows 7 to an anonymous share on Server 2012 R2, but I did have to add NTFS permissions for the Everyone group.
$acl = Get-Acl -Path 'C:\DesiredShareSource'
$ace = New-Object Security.AccessControl.FileSystemAccessRule(
'Everyone', 'ReadAndExecute', 'ContainerInherit, ObjectInherit', 'None', 'Allow'
)
$acl.AddAccessRule($ace)
Get-Acl -Path 'C:\DesiredShareSource' -AclObject $acl
I'm not aware of a way to make the policy changes with a script. You may be able to cook up something by wrapping secedit in PowerShell, but whenever I had to deal with secedit it turned out to be … bothersome, so I wouldn't recommend it. In a domain environment you can deploy local security policy settings via group policies, though.

I want to add a user to a Group based on name form a CVS file

I have the following code that can make a user however when I write the group in the CSV file the user is not added to said group.
I am able to run this right on the server.
The group is in a sub-group of a much larger group. so I am also not sure how to inform Powershell I want a group of a group.
this is the path would this work?
BILOMNI.BILPROMETRIC.ROOT/EasyServe_OU/EasyServeChannel_OU/Channel_CenterUsers_OU
I also have the group unique name
RequestingAccess_CenterUsers_GG
$computer = $ENV:COMPUTERNAME;
$users = Import-Csv "C:\Users.csv";
Foreach ($user in $users)
{
#for ($i=0; $i -le 2000; $i++)
#{
# Grab required info
$userName = $user.User
$objOu = [ADSI]"WinNT://$computer"
$Group = $user.Group
# Create user
$objUser = $objOU.Create("User", $userName + $i)
$objUser.setpassword($user.password)
$objUser.SetInfo()
# Find target group and add the user to it
$de = [ADSI]"WinNT://$computer/$Group,Group"
$de.add([ADSI]"WinNT://$computer/$userName")
# }
}
The following exception occurred while retrieving member "add": "An invalid dn syntax has been specified.
"
At C:\Users\dennis.hayden\Desktop\makingbilusers.ps1:20 char:12
+ $de.add <<<< ([ADSI]"LDAP://$computer/$userName")
+ CategoryInfo : NotSpecified: (:) [], ExtendedTypeSystemException
+ FullyQualifiedErrorId : CatchFromBaseGetMember
Below is the CSV file I am use:
User,Group,password
Masstestuser,RequestingAccess_CenterUsers_GG,P#ssWord
Can you try to change all your Wint:// by LDAP:// and modify a bit your code like the following :
# Find target group and add the user to it
$de = [ADSI]"LDAP://$computer/$Group,Group"
$user=[ADSI]"LDAP://$computer/$userName"
$de.add($user.Path)
# Commit
$de.setinfo()

Resources