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
Related
So, as the title says: I am trying to write up a PowerShell script that checks to see if any user accounts have been added to the Local Administrators group or have been added as an Administrator on the local machine. I have been using Get-EventLog and Get-WinEvent in an attempt to accomplish what I a trying to do. The problem I am having is isolating or extracting the information I want out of the event logs.
This is what I have so far:
$Date = ((Get-Date).AddDays(-1)).Date
$Events = Get-WinEvent -FilterHashtable #{
StartTime = $Date
LogName = 'Security'
ProviderName = 'Microsoft-Windows-Security-Auditing'
ID = 4732
}
I figure, if I can get the Username of the account that was added; which group or permissions it was given; and the date it was created, I can selectively output that information for each log over the last 24 hours. I'm not sure if I should be trying to use Get-Item or Get-Content, or if there is another way I should be trying to tackle this.
Unless you have powershell v6.0+ installed, you'll need to use -FilterXPath instead:
$Alerts = Get-WinEvent -LogName Security -FilterXPath #'
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">*[System[
EventID=4732 and
TimeCreated[timediff(#SystemTime) <= 604800000]]] and
*[EventData[Data[#Name="TargetDomainName"]='BUILTIN']] and
*[EventData[Data[#Name='TargetUserName']='Administrators']]
</Select>
</Query>
</QueryList>
'#
You can use -FilterHashTable if you are running powershell v6.0 or higher. I would use something like this to check for those events. My servers don't all have v6, so I would have to run it remotely like so:
#Requires -Version 6.0
$alerts = Get-WinEvent -ComputerName $computername -FilterHashtable #{
StartTime = ((Get-Date).AddDays(-1)).Date
LogName = 'Security'
ProviderName = 'Microsoft-Windows-Security-Auditing'
ID = 4732
TargetDomainName='Builtin' ## These fields require
TargetUserName='Administrators' ## Powershell v6.0+
}
I'll include how I convert event log's local SIDs to usernames for reports, since it's a pain
# Process event(s)
if ($alerts) { foreach ($event in $alerts) {
# Try and convert SID to account name:
$sid = $event.Properties[1].Value.Value
$localuser = ([System.Security.Principal.SecurityIdentifier]::new($sid)
).Translate([System.Security.Principal.NTAccount])).Value
}
# Use SID in case of failure
if (!$localuser){$localuser=$event.Properties[1].Value}
# Example action
Write-Warning "User $localuser added to local Administrators group on $computername by user $($event.Properties[7].Value)\$($event.Properties[6].Value)"
}}
# Outputs
WARNING: User Server01\local-user01 added to local Administrators group on Server01 by user DOMAIN\AdminUser01
I am trying to create an array of objects, and inside each object I want a property called something like "valid paths" indicating where those objects can be found, but I'm having trouble adding new paths after creating the array.
For example:
$Guids = $null
$Guids = #()
$Guids += [pscustomobject]#{
Guid = '{374DE290-123F-4565-9164-39C4925E467B}'
Paths = #()
}
$Guids += [pscustomobject]#{
Guid = '{7d83ee9b-2244-4e70-b1f5-5393042af1e4}'
Paths = #()
}
Here I am creating two objects indicating the guids I am looking for. Then to find and add the paths to the array, I do this:
$Rootpath = "HKU:\$($CurrentUser.PSChildName)"
$ShellFolderspath = "$RootPath\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
$UserShellFolderspath = "$Rootpath\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
#region 1 - Identify registry keys to update
New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS -ErrorAction SilentlyContinue
#Adding Downloads Guids to array
ForEach ($object in $Guids)
{
#Check Shell Folders Path
If ($Item = Get-ItemProperty $ShellFolderspath -Name $object.Guid -ErrorAction Stop)
{
"$($object.guid) found in Shell Folders path"
$Object.paths += $Item.PSpath
}
#Check User Shell Folders Path
If ($Item = Get-ItemProperty $UserShellFolderspath -Name $object.Guid -ErrorAction Stop)
{
"$($object.guid) found in User Shell Folders path"
$Object.paths += $Item.PSpath
}
}
I've googled over and over and can't find any examples of anyone trying to do this. Any help would be appreciated!
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()
}
I'm trying to search AD for all machines in a given OU that have 'TC' in their name, this is what I have so far, but its returning all machines, I need it to return just the machines with 'TC' in their name.
$root = ([adsi]'LDAP://OU=PCs,OU=Student Computers,DC=student,DC=belfastmet,DC=int','objectCategory=computer')
$searcher = new-object System.DirectoryServices.DirectorySearcher($root)
$searcher.filter = "(objectCategory=computer)"
$searcher.pageSize=1000
$searcher.propertiesToLoad.Add("name")
$computers = $searcher.findall()
$computers | foreach {$_.properties.name}
Not really sure what I should be doing from this point, I am a Powershell Newbie.
You have two options. You can get all computers and then filter using Powershell cmdlets, or your ldap filter reflects what you want (better). Try this:
$searcher.filter = "(&(objectCategory=computer)(cn=TN*))"
With ActiveDirectoryModule, you could find machines in a specific OU using filter and limit the search to the OU (assuming YourDomain.com\YourOU in the example below) you want with SearchBase:
$adcomputers = Get-ADComputer -Filter {name -like "TC*"} -Searchbase "OU=YourOU,DC=YourDomain,DC=com"
If you have the AD module available to you, you can do this with a single cmdlet.
get-adcomputer -filter {name -like "*TC*"}
I am running this script as Admin and It does create the folders requred, just does not set the appropriate permissions.
$Users = Get-Content "D:\New_Users.txt"
ForEach ($user in $users)
{
$newPath = Join-Path "F:\Users" -childpath $user
New-Item $newPath -type directory
$UserObj = New-Object System.Security.Principal.NTAccount("DOMAIN",$user)
$acl = Get-Acl $newpath
$acl.SetAccessRuleProtection($True, $False)
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("O1OAK\$user","AppendData,CreateDirectories,CreateFiles,DeleteSubdirectoriesAndFiles,ExecuteFile,ListDirectory,Modify,Read,ReadAndExecute,ReadAttributes,ReadData,ReadExtendedAttributes,ReadPermissions,Synchronize,Traverse,Write,WriteAttributes,WriteData,WriteExtendedAttributes","ContainerInherit, ObjectInherit","None","Allow")
$acl.SetAccessRule($accessRule)
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("NT AUTHORITY\SYSTEM","FullControl","ContainerInherit, ObjectInherit","None","Allow")
$acl.SetAccessRule($accessRule)
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Administrators","FullControl","ContainerInherit, ObjectInherit","None","Allow")
$acl.SetAccessRule($accessRule)
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("1OAK\$user","Delete","ContainerInherit, ObjectInherit","None","Allow")
$acl.removeAccessRule($accessRule)
$acl.SetOwner($UserObj)
$acl | Set-Acl $newpath
}
The first error in a string of 3 that I get is below. I think it is the most important and will fix the other 2.
Exception calling "SetAccessRule" with "1" argument(s): "Some or all identity references could not be translated."
At D:\DOMAIN\IT\IT Private\User Drives\user_folders.ps1:12 char:20
+ $acl.SetAccessRule <<<< ($accessRule)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
The error is pretty self explanatory: Some or all identity references could not be translated.
This means the account couldn't be found. So what you have to do is verify your accounts. Since you're adding 4 ACE's, you'll need to identify which is invalid.
The easiest way to do this is to debug through, line by line using the ISE or PowerGUI.
I tried your code with "NT AUTHORITY\SYSTEM" and "BUILTIN\Administrators" and it works so the issue is with "O1OAK\$user" or "1OAK\$user". You likely have an invalid account in your text file.
a gotch with the user ID is that AD truncates the username, so a user with a long name "j_reallylongname" will have a samid (Security Account Manager (SAM) account name) which is truncated. (j_reallylong)
so when fetching usernames, make sure you verify against the AD before using it.
When i've got the upns, so i run a dsget query to get the samid then use that to build the identity reference.
Adding this in case any C#/ASP.NET developers get this (which is my scenario, and I found this post).
I am using .NET Core in a corporate environment, and I need to check UserGroups as part of security. The code is like (where "user" is a ClaimsPrincipal):
var windowsIdentity = user.Identity as WindowsIdentity;
if( windowsIdentity is null )
throw new Exception( $"Invalid Windows Identity {user.Identity.Name}" );
return windowsIdentity.Groups
.Select( g => g.Translate( typeof( NTAccount ) ).Value );
Anyway, someone in charge of groups deleted a group I was part of, and the AD replication lag caused me to get the error in the title. A logoff and/or reboot worked just fine.
For me it was a case of where i verified whether the script execution knew the password by using $user = Get-Credential "username". i had to turn my $user into $user.UserName To give the script parameters the value they were expecting