Adding members to local groups by SID in multiple languages - powershell

I'm new to powershell/scripting/life in general, but finally I got a problem that is worthy of asking for help:
I've various Windows localizations in environment - English, Finnish and Russian in current environment, but with possibilities to have other Scandinavian/European localizations. I need to add Authenticated users to Administrators group. I can script it in English:
NET LOCALGROUP Administrators "Authenticated Users" /add,
but I won't know all localized names. For example, in Russian it would be "Administratori" and "Proshedshie Proverku." In cyrilic, that I'm not that strong with anyway.
Of course, I know SIDs - S-1-5-32-544 for Administrators and S-1-5-11 for Authenticated users. However, running
NET LOCALGROUP S-1-5-32-544 S-1-5-11 /add returns error that group doesn't exist. Ok, so I found a script to check it -
$objUser = New-Object System.Security.Principal.NTAccount("kenmyer")
$strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
$strSID.Value
This returns expected value, so far so good. Then I tried to double check it -by running line to get name from SID -
$Admin = (Get-WMIObject -Class Win32_Group -Filter "LocalAccount=True and SID='S-1-5-32-544'").Name
$Auth = (Get-WMIObject -Class Win32_Group -Filter "LocalAccount=True and SID='S-1-5-11'").Name
And $Admin = Administratori (as it should be), while $Auth = nothing. There is no name. And that's where I stopped. I tried this in English environment as well - still got "no such group" message. Running first command I wrote, with both names in English - Works perfectly Ok.
Any ideas?
Upd:
Perhaps I can't explain properly what I'm trying to do, so let the script do the talking:
#Task: to add "Authenticated users" to "Administrators" group in any languange OS.
$objSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")
$objgroup = $objSID.Translate( [System.Security.Principal.NTAccount])
$objgroupnameAdm = ($objgroup.Value).Split("\")[1]
$objSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-11")
$objgroup = $objSID.Translate( [System.Security.Principal.NTAccount])
$objgroupnameAuth = ($objgroup.Value).Split("\")[1]
#Administratörer
#Autentiserade användare
net localgroup $objgroupnameAdm $objgroupnameAuth /add
I try this on Swedish Win7 right now. So result is:
net.exe : Syntaxen för kommandot är:
At line:13 char:4
+ net <<<< localgroup $objgroupnameAdm $objgroupnameAuth /add
+ CategoryInfo : NotSpecified: (Syntaxen för kommandot är::String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
I've tried also putting $objgroupnameAuth in quotes, as it contains two words, but that gives same result. Definig variables as string - no changes, and replacing $objGroupNameAdm with actual value - no changes.
If I couldn't do it in English Windows, I would assume that it's just functionally impossible.

I use this method to translate from SID to localized name:
.SYNOPSIS
Adds the "NT AUTHORITY\Interactive security principal to the local computer Administrators group"
.DESCRIPTION
THis script uses a SID translation to receive the localized name for the Interactive principal and the Administrators group, then adds the principal to the group using the localized names.
# Translate the S-1-5-32-544 (.\Administrators) SID to a group name, the name varies depending on the language version of Windows.
$sid2 = 'S-1-5-32-544'
$objSID2 = New-Object System.Security.Principal.SecurityIdentifier($sid2)
$localadminsgroup = (( $objSID2.Translate([System.Security.Principal.NTAccount]) ).Value).Split("\")[1]
# Translate the S-1-5-4 (NT AUTHORITY\Interactive) SID to an account name, the name varies depending on the language version of Windows.
$sid1 = 'S-1-5-4'
$objSID1 = New-Object System.Security.Principal.SecurityIdentifier($sid1)
$interactive = (( $objSID1.Translate([System.Security.Principal.NTAccount]) ).Value).Split("\")[1]
# Add the security principal name to the local administrators group. (used old style of adding group members due to compatibility reasons)
try {
Write-Host "Adding security principal: $interactive to the $localadminsgroup group..."
$group = [ADSI]"WinNT://$env:computername/$localadminsgroup,group"
$ismember = "False"
#($group.Invoke("Members")) | ForEach-Object {
If ($interactive -match $_.GetType.Invoke().InvokeMember("Name", 'GetProperty', $null, $_, $null)) {
$ismember = "True"
}
}
If ($ismember -eq "True") {
write-host "user $interactive is already a member of $localadminsgroup"
}
Else {
$result = $group.Add("WinNT://NT AUTHORITY/$interactive,user")
write-host "user $interactive is added to $localadminsgroup"
}
}
Catch {
write-host $_.Exception.Message
}
It's not entirely tailored towards what you need but I'm sure you can make it work.
Regards,
Koen.

The SID S-1-5-11 is used for Authenticated Users (Well known SIDs). That is a BUILTIN group that cannot be modified. Other groups like this are Everyone, or Anonymous, etc.
This type of group doesn't exist in the "physical" sense or the word, i.e. there is no object created either in the local SAM nor in the Active Directory.
They are entirely generated and managed by Windows.
You receive the SID in your session depending on how you connected and/or logged on.
Therefore making a WMI Win32_Group request, or using Get-ADGroup won't return anything.
You can invoke Get-ADAccountAuthorizationGroup, to see if a particular identity is member of such groups.
You can use the SID to retrieve create a create a System.Security.Principal.NTAccount object pointing to Authenticated Users:
$auth = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-11")
$name = $auth.Translate([System.Security.Principal.NTAccount])
UPDATE:
I could not add the localized name of $auth to that group. Looks like only the English version works.

Question is simple, once asked correctly, and answer is to be found here: Microsoft Supprt: NET /ADD command .
If NET limit is 20 characters, and "Autentiserade användare" is 24 characters, it's not supposed to work. Workaround is to be found in the same link.

Related

Grabbing assigned VM names by entering username in Vmware Horizon (PowerCLI)

I've been working on a PowerShell code to grab VM names from all desktop pools.
I'm using PowerCLI with PowerShell 7 installed.
I have managed to get an output of all the users and their assigned machines. But, I'm having difficulties in optimizing the code in a way that I can input a single user name and it will only show me that user's assigned VM.
Here is the code I've got so far
#Import-Module VMware.VimAutomation.Core
#Import-Module Activedirectory
Connect-VIServer -server servername123 -Force
Connect-HVServer -server server.name.123
$uname = Read-Host -Prompt 'Input the user name you wish to find:' #User-Input
$Global:DefaultHVServers
$query = New-Object "Vmware.Hv.QueryDefinition"
$query.queryEntityType = 'SessionLocalSummaryView'
$qSrv = New-Object "Vmware.Hv.QueryServiceService"
$PCs = ($qSRv.QueryService_Query($global:DefaultHVServers[0].ExtensionData,$query) |
Select -ExpandProperty Results |
Select -ExpandProperty NamesData |
Select-Object -Property UserName,MachineOrRDSServerName)
$PCs | % {"$($_.UserName.Split("\")[1]) `t`t $($_.MachineOrRDSServerName) "}
In the last line of the code, I am formatting the table to remove unnecessary content.
$PCs | % {"$($_.UserName.Split("\")[1]) `t`t $($_.MachineOrRDSServerName) "}
Can someone help me in grabbing username from console and only displaying the VMs that they are assigned to?
I have tried googling for a solution but couldn't find anything relevant.
Thanks!!
Note: I have declared a variable uname but haven't used it yet. I'm unsure how can I use it in this usecase.
After digging around more in the deepest subreddits, I found a post that solved my question.
https://www.reddit.com/r/vmware/comments/d547nt/horizon_view_powercli_help/
Below is the code which utilizes QueryFilterEquals
from VMware.Hv.Equals class to grab usernames and their properties.
I'm skipping the connections portion of the code, it is the same mentioned in the question.
#Get User Input for UserName
$UserName = Read-Host -Prompt 'Input the user name you wish to find:'
#Create Horizon Services object
$HorizonServerServices = $global:DefaultHVServers[0].ExtensionData
#Create Query Definition object with EntityType SessionLocalSummaryView
$HorizonQuery = New-Object VMware.Hv.QueryDefinition
$HorizonQuery.QueryEntityType = 'SessionLocalSummaryView'
#Create Query Filter Object
$QueryFilterEquals = New-Object VMware.Hv.QueryFilterEquals
$QueryFilterEquals.MemberName = 'namesData.userName'
$QueryFilterEquals.value = "domain.loc\$UserName"
$HorizonQuery.Filter = $QueryFilterEquals
$HorizonQueryService = New-Object VMware.Hv.QueryServiceService
$SearchResult = $HorizonQueryService.QueryService_Query($HorizonServerServices, $HorizonQuery)
if ($SearchResult.Results)
{
$SearchResult.Results.Namesdata
}
We do not have to delete the query at the end as it doesn't consume any server-side resources. It is a virtual query. Refer to the link given below for detailed info on how QueryService works.
Refer to: https://vdc-download.vmware.com/vmwb-repository/dcr-public/e2e25628-4ed2-43fc-8bad-54fb86f3bb0f/8e4d2491-c740-4778-ac43-ba8fc0ec8175/doc/queries-landing.html

Language independent way to find all the users with admin access to a computer via powershell

I have a powershell script that can find all users, domain and local, that have admin access to the current computer. The script works fine, until you change the language of the computer. I would like to know if there are other ways to go about this. Below is my current script
$strComputer = "."
$computer = [ADSI]("WinNT://" + $strComputer + ",computer")
$Group = $computer.psbase.children.find("Administrators")
$Props = $Group.psbase.invoke("Members") | %{$_.GetType().InvokeMember("Adspath", 'GetProperty', $null, $_, $null)}
If you doa Write-Host $Props you can get an output of all the users formatted as {DOMAIN}/{USERNAME}
Thanks in advance!
You can use the well-known SID of the group to find it. There is some discussion in a blog post here, but that uses VBScript and WMI (winmgmts:\\).
If you want to use WinNT:// like you already are, then this will search through the local groups for the group with the SID S-1-5-32-544, which corresponds to the Administrators group.
$strComputer = "."
$computer = [ADSI]("WinNT://" + $strComputer + ",computer")
$Group = $computer.Children |
Where {
(New-Object System.Security.Principal.SecurityIdentifier $_.objectSid.Value,0).ToString()
-eq "S-1-5-32-544"
} | Select -First 1
That New-Object bit is because the objectSid property is presented as a byte array, so you need to create a SecurityPrincipal object with it to be able to convert it to a string to do the comparison.

resolve ForeignSecurityPrincipals to distinguishedName in PowerShell

I have a ForeignSecurityPrincipals and need to resolve it to the distinguishedName but am not sure how.
I have this code to get the NTAccount:
$m = "CN=S-1-1-11-1111111111-1111111111-1111111111-1111111,CN=ForeignSecurityPrincipals,DC=one,DC=two,DC=company,DC=com"
$member = [ADSI]("LDAP://" + $m)
$sid = New-Object System.Security.Principal.SecurityIdentifier ($member.objectSid[0], 0)
$sid.Translate([System.Security.Principal.NTAccount]).value
You can bind to an object using the SID (and then get the distinguishedName), but you have to know at least the DNS name of the domain:
$user = [ADSI]"LDAP://$domaindns/<SID=$($sid.Value)>"
To get the DNS name of the domain, you need to examine all the trusts your domain has and store the DNS name and the SID of the domain in a list. Then you can match the domain portion of the user's SID with your list and get the DNS name (a user's SID will start with the domain's SID).
This page has some info on pulling all of the trusts, but the method he ends up using is WMI, which may not work, depending on your permissions. It didn't for me. You can do the same with ADSI, but I haven't done it. It's a starting point at least.
Update:
You can try this:
$DomainSIDList = #{}
$Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
#Get trusts from each domain in the forest -- this will include forest trusts
ForEach($Domain in $Forest.Domains) {
$adsisearcher = New-Object system.directoryservices.directorysearcher
$adsisearcher.SearchRoot = [ADSI]"LDAP://CN=System,$($Domain.GetDirectoryEntry().distinguishedName)"
$adsisearcher.Filter = "(objectclass=trustedDomain)"
ForEach($ExtDomain in $adsisearcher.FindAll()) {
$name = $ExtDomain.Properties["name"][0]
"Found $($name)"
$sid = New-Object System.Security.Principal.SecurityIdentifier ($ExtDomain.Properties["securityidentifier"][0], 0)
if (-not $DomainSIDList.Contains($sid.Value)) {
"Adding $($sid.Value), $($name)"
$DomainSIDList.Add($sid.Value, $name)
}
}
}
Once that runs, $DomainSIDList will contain a list of trusted domains. It'll contain all the domains in the forest, which you don't really need, but that's not a big deal.

Change AD user attributes of users in group

I copied and pasted together this script that looks at all the member in a certain group, and then changes all of their company names. I would now like to expand this script to be able to ask for which attribute of the users you want to change.
Clear-Host
$GPNAME = Read-Host 'Provide the Group Name here'
#$whatwhat = Read-Host 'Which Attribute do you want to change | Put a - infront'
$attrib = Read-Host 'Give new company name here' #This should later change to "Give new info of Attribute you want to change"
$description = "-description"
$offeece = "-office"
$department = "-department"
$company = "-company"
$Userslist = Get-ADGroupMember $GPNAME
ForEach($User in $Userslist)
{
$user1 = $User.name
#Write-Host $user1
get-aduser -filter { name -eq $User1 } | set-aduser -company $attrib
}
at the end of the script i would like to change the -company with $whatwhat.
I have tried it, but no matter how, with the - or without, with ' and " around the - or the $whatwhat, it keeps on giving me this error.
Set-ADUser : A positional parameter cannot be found that accepts argument 'newly'
can anyone assist my to expand this script a little bit by giving the user options to choose from, or allowing him/her to type: company or description or office
you could use invoke-expression cmdlet
$cmd="get-aduser -filter { name -eq $User1 } | set-aduser $whatwhat $attrib -whatif"
invoke-expression $cmd
test :
PS>$what=read-host "attribute to change :" attribute to change ::
company
PS>$c="set-aduser test -$what 'testcpny' -whatif"
PS>iex $c
WhatIf : Opération « Set » en cours sur la cible « CN=TEST,CN=Users,DC=....,DC=com ».

Powershell connecting and searching multiple domains

I am trying to locate the group memberships for a specified user account. One domain's user account is often a member of a group in the other domains (some domains require different admin account). Using get-QAQgroup, I can successfully search each domain individually, but when I try to loop through the domains, I can only find results in the domain that I am logged into.
#Script to change domains and look for group memberships for a specified user account.
$domains = "dom1.ad.state.company.com","dom2.ad.state.company.com","dom3.ad.state.company.com","dom4.ad.state.company.com","corporate.state.company.com","OddNamedDom.com"
$CRED=GET-CREDENTIAL
$userAcc = read-host "Enter domain\username for Group Membership Search"
foreach ($domain in $domains)
{
write-host "In the domain $domain "," $userAcc is a direct member of..."
Get-QADGroup -service $domain -Credential $cred -Containsmember $userAcc | select name
} #foreach domain
Connect-QADService -Service 'dom1.ad.state.company.com'
When I run the script I get results for dom1 (domain I am logged into) and the rest throw the following errors. I am not sure why the "Ref 1:.." lines are pointing to 'dom1'. I thought that might be the source of issue. I have copied the Powershell output below showing the error messages.
In the domain dom1.ad.state.company.com dom1\brownd2.admin.dom1 is a direct member of...
Name
----
DOM1-G-ITS-DS-Company Services
DOM1PGUELFP00003-Exmerge-R
DOMPGUELFP00003-Exmerge-C
ITSPPTBOSHFS003-FSSHARE-C
Domain Users
In the domain dom2.ad.state.company.com dom1\brownd2.admin.dom1 is a direct member of...
Get-QADGroup : 0000202B: RefErr: DSID-03100742, data 0, 1 access points
ref 1: 'dom1.ad.state.company.com'
At C:\TestScripts\tGet-UserAllMemberships.ps1:24 char:6
+ Get-QADGroup -service $domain -Credential $cred -Containsmember $userAcc | ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-QADGroup], DirectoryAccessException
+ FullyQualifiedErrorId : Quest.ActiveRoles.ArsPowerShellSnapIn.DirectoryAccess.DirectoryAccessException,Quest.ActiveRoles.ArsPowerShel
lSnapIn.Powershell.Cmdlets.GetGroupCmdlet
In the domain dom3.ad.state.company.com dom1\brownd2.admin.dom1 is a direct member of...
Get-QADGroup : 0000202B: RefErr: DSID-03100742, data 0, 1 access points
ref 1: 'dom1.ad.state.company.com'
At C:\TestScripts\tGet-UserAllMemberships.ps1:24 char:6
+ Get-QADGroup -service $domain -Credential $cred -Containsmember $userAcc | ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-QADGroup], DirectoryAccessException
+ FullyQualifiedErrorId : Quest.ActiveRoles.ArsPowerShellSnapIn.DirectoryAccess.DirectoryAccessException,Quest.ActiveRoles.ArsPowerShel
lSnapIn.Powershell.Cmdlets.GetGroupCmdlet
There is a similar set of errors for each domain that I am checking. I have not posted the full list of error messages.
If I change the order of the domains in the array, the errors and the results of the one successful domain just change order to match the array. I thought that it might just be successful for the first iteration of the loop. Not the case though.
I know that the account is a member of groups in Dom2 and not in any groups in Dom3. If I take the commands out of the foreach loop and run individual for each domain in the console I do get the expected results. Based on the individual results, I had thought that this would be straight forward example to do in a loop, but I am not connecting correctly to the domains.
What can I change?
Here is a solution using System.DirectoryServices.AccountManagement Namespace, adapted to PowerShell from C# code. It's a kind of recursive solution. In Find Recursive Group Membership (Active Directory) using C#, I give a recursive solution (using basic ADSI avaible from PowerShell 1.0) that also works with distribution groups.
# Retreiving a principal context for the administrator on the Global Catalog
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$domainContext = New-Object DirectoryServices.AccountManagement.PrincipalContext([DirectoryServices.AccountManagement.ContextType]::Domain, "VMESS01:3268" , "administrator", "adminPasswd")
# Retreive the groups
try {
$userPrincipal = [DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($domainContext, "jpb")
$groups = $userPrincipal.GetAuthorizationGroups()
foreach($group in $groups)
{
$group.name;
}
}
finally {
$pc.domainContext()
}