Add User to Local Group - powershell

This function should run on Windows Server 2003 and 2008 R2
Using the command line to execute it line by line is SUCCESSFULL! Execution by script fails.
function addUser2Group([string]$user,[string]$group)
{
$cname = gc env:computername
$objUser = [ADSI]("WinNT://$user")
$objGroup = [ADSI]("WinNT://$cname/$group,group")
$members = $objGroup.PSBase.Invoke('Members')
$found = $false
foreach($m in $members)
{
if($m.GetType().InvokeMember('Name', 'GetProperty', $null, $m, $null) -eq $user)
{
$found = $true
}
}
if(-not $found)
{
$objGroup.PSBase.Invoke('Add',$objUser.PSBase.Path)
}
$members = $objGroup.PSBase.Invoke('Members')
$found = $false
foreach($m in $members)
{
if($m.GetType().InvokeMember('Name', 'GetProperty', $null, $m, $null) -eq $user)
{
$found = $true
}
}
return $found
}
addUser2Group('MyGlobalMonitoringUser',"SomeDBGroup")
It should add a user to a local group. But it only gives me the following error:
Exception calling "Invoke" with "2" argument(s): "Unknown error (0x80005000)"
+ $members = #($objGroup.PSBase.Invoke <<<< ("Members"))
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Edit: the error message that occurs with /add is
The following exception occurred while retrieving member "Add": "Unknown error (0x80005000)"
Code is:
function addUser2Group([string]$user,[string]$group)
{
$cname = gc env:computername
try
{
([adsi]"WinNT://$cname/$group,group").Add("WinNT://$cname/$user,user")
}
catch
{
write2log($_)
return $false
}
return $true
}

Why go through the pain of reflection when PowerShell will do it for you? Example:
$group = [ADSI]"WinNT://./Power Users,group"
$group.Add("WinNT://SYSTEM,user")
The above adds the SYSTEM local account to the local Power Users group. I am not sure why you are getting the specific error above, you might get it with this abbreviated syntax as well. The particular COM interface that is being used is IADsGroup - reference here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa706021.aspx
Note: Because you are actually consuming COM objects wrapped in .NET objects, it is a good idea to call the Dispose method on any ADSI objects that are created when you are finished with them.

Why wouldn't you use net localgroup /add in your script instead of all that nasty looking WMI? PowerShell is a shell, not an operating system :)

Observations and assumptions
There are a couple of assumptions that are made based on the code.
You only passed ONE parameter object with two values. Use the Param statement
Call the function differently addUser2Group -user 'MyGlobalMonitoringUser' -group "SomeDBGroup"
Validate the parameters passed. They should be checked for empty/null at least.
This would only work if you had the $group variable assigned a value before the script ran.
Based on the fact that the incorrect parameter passing the value of the $group variable remains empty. It then caused the rest of the code to fail always returning the value of $False.
Proposed solution
Recommended Reading:
Simplify your PowerShell Script with Parameter Validation
Include the Param "switch" in the function
Change the way you call the function.
Here's a copy of the code that works.
function addUser2Group
{
# Added the Param Switch
Param(
[string]$user,
[string]$group
)
$cname = gc env:computername
$objUser = [ADSI]("WinNT://$user")
$objGroup = [ADSI]("WinNT://$cname/$group,group")
#$members = $objGroup.Invoke('Members')
$found = $false
foreach($m in $members)
{
if($m.GetType().InvokeMember('Name', 'GetProperty', $null, $m, $null) -eq $user)
{
$found = $true
}
}
if(-not $found)
{
$objGroup.PSBase.Invoke('Add',$objUser.PSBase.Path)
}
$members = $objGroup.PSBase.Invoke('Members')
$found = $false
foreach($m in $members)
{
if($m.GetType().InvokeMember('Name', 'GetProperty', $null, $m, $null) -eq $user)
{
$found = $true
}
}
return $found
}
addUser2Group -user 'testing' -group "Administrators"

•0x80005000 ("The specified directory service attribute or value does not exist").
Parameter binding or Environmental culprit perhaps?
As for the issue with net localgroup: examine the error message carefully:
The following exception occurred while retrieving member "Add"
Evidentally the /add flag is not being properly set as it is being interpreted as a member name, but since no code is provided, can't say why.

Just to add a more uptodate approach roughly five years later:
$group = Get-LocalGroup SomeDBGroup
$user = Get-LocalUser MyGlobalMonitoringUser
Add-LocalGroupMember $group $user

Related

How to check if computer name is already exist in the domain My script used in each pc

My script used in each pc in our network to rename pc but i want to check domain is the new name is already exist
Any help please
$Searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher
$searcher = [adsisearcher]"(&(objectCategory=computer)(objectClass=computer)(name=$global:NewComputerName))"
$searcher.PropertiesToLoad.AddRange(('name'))
$searchResult = $searcher.FindAll()
if($searchResult.count -eq 1)
{
$Result = $true
}
else
{
$Result = $False
}
So then i use this to check if the result is true or false
elseif ($Result -match 'true')
{
$msgBoxInput7 = [System.Windows.Forms.MessageBox]::Show('computer is exist', 'OK')
switch ($msgBoxInput7) {
'OK' {
$groupbox1.ResumeLayout()
$form1.ResumeLayout()
$form1.add_FormClosed($Form_Cleanup_FormClosed)
}
}
}
Please help me this code doesn’t work
Try to use Get-ADComputer (as Theo said in the comment):
if(get-adcomputer -filter "Name -eq 'nameToFind'"){
"Exists"
}else{
"Not exists"
}
If you will receive error like:
The term 'Get-ADComputer' is not recognized as the name of a cmdlet, function etc..
You need to import the module before this command using Import-Module ActiveDirectory
You may even receive another issue:
Get-ADComputer : Unable to find a default server with Active Directory Web Services running
If so, please add parameter -Server with AD server name, example:
if(get-adcomputer -Server "DC" -filter "Name -eq 'nameToFind'"){
"Exists"
}else{
"Not exists"
}

PowerShell Script ErrorActionPreference [duplicate]

I'm trying to export a list of all users with no photo from our Exchange Online account using powershell. I cannot get it to work and have tried various methods.
Get-UserPhoto returns this exception when there is no profile present.
Microsoft.Exchange.Data.Storage.UserPhotoNotFoundException: There is no photo stored here.
First of all I tried use Errorvariable against the command but received:
A variable that cannot be referenced in restricted language mode or a Data section is being referenced. Variables that can be referenced include the following: $PSCulture, $PSUICulture, $true, $false, and $null.
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : VariableReferenceNotSupportedInDataSection
+ PSComputerName : outlook.office365.com
Next I tried try, catch but the non-terminating error never calls the catch despite various methods followed online about setting $ErrorActionPreference first of all.
Any ideas ? Here is the script:
$UserCredential = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session
$timestamp = $timestamp = get-date -Format "dd/M/yy-hh-mm"
$outfile = "c:\temp\" + $timestamp + "UserswithoutPhotos.txt"
$resultslist=#()
$userlist = get-user -ResultSize unlimited -RecipientTypeDetails usermailbox | where {$_.accountdisabled -ne $True}
Foreach($user in $userlist)
{
try
{
$user | get-userphoto -erroraction stop
}
catch
{
Write-Host "ERROR: $_"
$email= $user.userprincipalname
$name = $user.DisplayName
$office = $user.office
write-host "User photo not found...adding to list : $name , $email, $office"
$resultslist += $user
}
}
$resultslist | add-content $outfile
$resultslist
PowerShell's error handling is notoriously tricky, especially so with cmdlets that use implicit remoting via a locally generated proxy script module.
The following idiom provides a workaround based on temporarily setting $ErrorActionPreference to Stop globally (of course, you may move the code for restoring the previous value outside the foreach loop), which ensures that even functions from different modules see it:
try {
# Temporarily set $ErrorActionPreference to 'Stop' *globally*
$prevErrorActionPreference = $global:ErrorActionPreference
$global:ErrorActionPreference = 'Stop'
$user | get-userphoto
} catch {
Write-Host "ERROR: $_"
$email= $user.userprincipalname
$name = $user.DisplayName
$office = $user.office
write-host "User photo not found...adding to list : $name, $email, $office"
$resultslist += $user
} finally {
# Restore the previous global $ErrorActionPreference value
$global:ErrorActionPreference = $prevErrorActionPreference
}
As for why this is necessary:
Functions defined in modules do not see the caller's preference variables - unlike cmdlets, which do.
The only outside scope that functions in modules see is the global scope.
For more information on this fundamental problem, see this GitHub issue.
You can throw your own error like so:
try {
$error.Clear()
$user | Get-UserPhoto
if ($error[0].CategoryInfo.Reason -eq "UserPhotoNotFoundException") {throw "UserPhotoNotFoundException" }
} catch {
#code
}

Exchange Online - Get-UserPhoto - how to catch non terminating error?

I'm trying to export a list of all users with no photo from our Exchange Online account using powershell. I cannot get it to work and have tried various methods.
Get-UserPhoto returns this exception when there is no profile present.
Microsoft.Exchange.Data.Storage.UserPhotoNotFoundException: There is no photo stored here.
First of all I tried use Errorvariable against the command but received:
A variable that cannot be referenced in restricted language mode or a Data section is being referenced. Variables that can be referenced include the following: $PSCulture, $PSUICulture, $true, $false, and $null.
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : VariableReferenceNotSupportedInDataSection
+ PSComputerName : outlook.office365.com
Next I tried try, catch but the non-terminating error never calls the catch despite various methods followed online about setting $ErrorActionPreference first of all.
Any ideas ? Here is the script:
$UserCredential = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session
$timestamp = $timestamp = get-date -Format "dd/M/yy-hh-mm"
$outfile = "c:\temp\" + $timestamp + "UserswithoutPhotos.txt"
$resultslist=#()
$userlist = get-user -ResultSize unlimited -RecipientTypeDetails usermailbox | where {$_.accountdisabled -ne $True}
Foreach($user in $userlist)
{
try
{
$user | get-userphoto -erroraction stop
}
catch
{
Write-Host "ERROR: $_"
$email= $user.userprincipalname
$name = $user.DisplayName
$office = $user.office
write-host "User photo not found...adding to list : $name , $email, $office"
$resultslist += $user
}
}
$resultslist | add-content $outfile
$resultslist
PowerShell's error handling is notoriously tricky, especially so with cmdlets that use implicit remoting via a locally generated proxy script module.
The following idiom provides a workaround based on temporarily setting $ErrorActionPreference to Stop globally (of course, you may move the code for restoring the previous value outside the foreach loop), which ensures that even functions from different modules see it:
try {
# Temporarily set $ErrorActionPreference to 'Stop' *globally*
$prevErrorActionPreference = $global:ErrorActionPreference
$global:ErrorActionPreference = 'Stop'
$user | get-userphoto
} catch {
Write-Host "ERROR: $_"
$email= $user.userprincipalname
$name = $user.DisplayName
$office = $user.office
write-host "User photo not found...adding to list : $name, $email, $office"
$resultslist += $user
} finally {
# Restore the previous global $ErrorActionPreference value
$global:ErrorActionPreference = $prevErrorActionPreference
}
As for why this is necessary:
Functions defined in modules do not see the caller's preference variables - unlike cmdlets, which do.
The only outside scope that functions in modules see is the global scope.
For more information on this fundamental problem, see this GitHub issue.
You can throw your own error like so:
try {
$error.Clear()
$user | Get-UserPhoto
if ($error[0].CategoryInfo.Reason -eq "UserPhotoNotFoundException") {throw "UserPhotoNotFoundException" }
} catch {
#code
}

Get-ADUser via Scheduled Task on PCs Not Working

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

PowerShell Remove the last error

In my PowerShell script I try to do some error handling. However, I'm depending on an advanced function that uses the Try/Catch clauses. So once in a while the code block in the function fails and goes to the Catch clause after generating an error. At this point the variable $Error is filled with one error.
If I then consult within my script the variable $Error it tells me there's one record, which is correct. But I would like to know if it's possible to only delete the last error within the function in the Catch clause? So I can keep my $Error variable clear for the script errors.
The problem is within Get-ADTSProfileHC. I tried to delete the last error with $Error[0] | Remove-Item but it failed.
The function:
Function Get-ADusersHC {
[CmdletBinding(SupportsShouldProcess=$True)]
Param(
[Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true,Position=0)]
[String[]] $OU
)
Begin {
Function Get-ADOUNameHC {
$CanonicalName = $_.CanonicalName
[System.Collections.ArrayList]$Pieces = $CanonicalName.split(“/”)
$Pieces.Remove($Pieces[-1])
$OU = $Pieces -join '\'
$OU -replace ($Pieces[0],$Pieces[0].ToUpper())
}
Function Get-ADManagerDisplayNameHC {
$m = Get-ADObject -Identity $_.manager -Properties displayName,cn
if($m.ObjectClass -eq "user") { $m.displayName } Else{ $m.cn }
}
Function Get-ADTSProfileHC {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,Position=0)]
[String] $DistinguishedName,
[parameter(Mandatory=$true,Position=1)]
[ValidateNotNullOrEmpty()]
[ValidateSet('UserProfile','AllowLogon','HomeDirectory','HomeDrive')]
[String]$Property
)
Begin {
$User = [ADSI]"LDAP://$DistinguishedName"
}
Process {
Try {
Switch ($Property) {
'AllowLogon' {if ($($User.psbase.InvokeGet('allowLogon')) -eq '1'){$True}else{$False}}
'HomeDirectory' {$User.psbase.InvokeGet('TerminalServicesHomeDirectory')}
'HomeDrive' {$User.psbase.InvokeGet('TerminalServicesHomeDrive')}
'UserProfile' {$User.psbase.InvokeGet('TerminalServicesProfilePath')}
}
}
Catch {
# When we receive an error, it means the field has never been used before and is blank
# this is due to an error in the AD (same problem with the Quest CmdLet), AllowLogon is
# always 'TRUE' but we don't set it because we can't read it sometimes so we write 'blanks'
Write-Output $null
}
}
}
}
Process {
Foreach ($_ in $OU) {
Write-Verbose "Function Get-HCADusersNoManager > OU: $_"
Write-Verbose "Function Get-HCADusersNoManager > Manager field empty"
Get-ADUser -SearchBase $_ -Filter 'SAMAccountName -eq "shenn"' -Properties * |
#Get-ADUser -SearchBase $_ -Filter * -Properties * |
Foreach {
$Properties = ([Ordered] #{
"Creation date" = $_.whenCreated;
"Display name" = $_.displayName;
"CN name" = $_.name;
"Last name" = $_.sn;
"First name" = $_.givenName;
"Logon name" = $_.sAMAccountName;
"Manager" = if($_.manager){Get-ADManagerDisplayNameHC};
"Employee ID" = $_.EmployeeID;
"HeidelbergcCement Billing ID" = $_.extensionAttribute8
"Type of account" = $_.employeeType;
"OU" = Get-ADOUNameHC;
"Notes" = $_.info -replace "`n"," ";
"E-mail" = $_.EmailAddress;
"Logon script" = $_.scriptPath;
"TS User Profile" = Get-ADTSProfileHC $_.DistinguishedName 'UserProfile';
"TS Home directory" = Get-ADTSProfileHC $_.DistinguishedName 'HomeDirectory';
"TS Home drive" = Get-ADTSProfileHC $_.DistinguishedName 'HomeDrive';
"TS Allow logon" = Get-ADTSProfileHC $_.DistinguishedName 'AllowLogon'
})
$Object = New-Object -TypeName PSObject -Property $Properties
Write-Output $Object
}
}
}
}
Two easy ways to do this:
$error.Remove($error[0])
$Error.RemoveAt(0)
Don't forget to check there is an error first.
$Error.Remove($error[$Error.Count-1])
If errors variable is empty, you will not get any exception
I hope it helps