Delete the disabled accounts since 90 days based on custom attribute value - powershell

I move automatically all ad disabled accounts in OU adding the date of deactivation in extensionattribute4 with this the script :
import-module activedirectory
$timer = (Get-Date)
$TargetOU = "OU=Disabled Accounts,DC=domain,DC=lan"
$DisabledAccounts = get-aduser -filter { enabled -eq $false } -SearchBase "OU=Test,OU=EMEA,DC=domain,DC=lan"
ForEach ($account in $DisabledAccounts) {
set-aduser -Identity $account.distinguishedName -add #{extensionAttribute4="$timer"}
}
ForEach ($account in $DisabledAccounts) {
Move-ADObject -Identity $account.distinguishedName -TargetPath $TargetOU
But when I want to remove the ad disabled accounts with the reference the date of extensionattribute4 less 90 days with the script :
import-module activedirectory
$DaysInactive = 90
$time = (Get-Date).Adddays(-($DaysInactive))
$DisabledAccounts = get-aduser -filter { extensionattribute4 -lt $time -and enabled -eq $false } -SearchBase "OU=Disabled Accounts,DC=domain,DC=lan"
ForEach ($account in $DisabledAccounts) {
Remove-ADObject -Identity $account.distinguishedName
}
I have got an error :
get-aduser : Invalid type 'System.DateTime'.
Parameter name: extensionattribute4
At C:\removedisabledadaccounts.ps1:4 char:21
+ $DisabledAccounts = get-aduser -filter { extensionattribute4 -lt $time -and enab ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADUser], ArgumentException
+ FullyQualifiedErrorId : Invalid type 'System.DateTime'.
Parameter name: extensionattribute4,Microsoft.ActiveDirectory.Management.Commands.GetADUser

The error indicates you are trying to do an operation that the attribute does not accept. When you populated the field in your earlier operation you converted the date to a string with #{extensionAttribute4="$timer"}. I can't imagine those attributes are stored as anything other than strings anyway. In fact trying to store the date object ends in similar failure.
Kudos for using -Filter but I am sure this is something beyond the -Filter/-LDAPFilter so you should just have to do some post processing.
Get-ADUser -Filter {enabled -eq $false} -SearchBase "OU=Disabled Accounts,DC=domain,DC=lan" -Properties extensionattribute4 |
Where-Object{$time -ge $_.extensionattribute4}
Since we need to work with that attribute we need to be sure it is returned in the -Properties list.

Related

PowerShell to Remove ADGroup from Active Directory

start-transcript -path c:\docs\MyTranscript.txt
$WhenChangedDate = ((get-date).addmonths(-12)) #has not been modified in over a year
$domain = "Domain1"
$emptygroups = Get-ADGroup -Filter * -Properties members, whenchanged -server $domain| Where-Object {($_.members.count -eq 0) -and ($_.whenchanged -le $WhenChangedDate)} | Select-Object -last 10
#$emptygroups = Get-ADGroup -Filter * -Properties members, whenchanged -server $domain | Where-Object { ($_.members.count -eq 0) -and ($_.whenchanged -le $WhenChangedDate) -and ($_.name -notlike '*CTX*')} | Select-Object -last 10
$emptygroups.name | %{REmove-adgroup $_ -Confirm:$false -WhatIf}
Stop-transcript
I'm getting the below error for Domain1. However, it runs successfully on Domain2. Any ideas?
Remove-ADGroup : Cannot validate argument on parameter 'Identity'. The argument is null. Provide a valid value for the argument, and then try running the command again. At line:1 char:38
+ $emptygroups.name | %{REmove-adgroup $_ -Confirm:$false -WhatIf}
+ ~~
+ CategoryInfo : InvalidData: (:) [Remove-ADGroup], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.
Commands.RemoveADGroup
What you're trying to do can be accomplished using the following LDAPFilter:
"(&(!member=*)(whenChanged<=$date)(!name=*CTX*))"
& All conditions must be met.
!member=* Group without members.
whenChanged<=$date WhenChanged lower than a specified date.
!name=*CTX* Name not like CTX.
$domain = "Domain1"
$date = [datetime]::Today.AddYears(-1).ToString('yyyyMMddHHmmss.sZ')
Get-ADGroup -LDAPFilter "(&(!member=*)(whenChanged<=$date)(!name=*CTX*))" -Server $domain |
Select-Object -Last 10 |
Remove-ADGroup -Confirm:$false -WhatIf

Updating Active Directory Manager Attribute from .csv Using PowerShell

I'm not so good at PowerShell. I have this script which worked before that I've have used to update the manager attribute in Active Directory, however, now after just replacing the file its no longer working
$Users = Import-CSV C:\Documents\Managers.csv
ForEach ($User in $Users) {
$user= Get-ADUser -Filter "displayname -eq '$($Users.EmployeeFullName)'"|select -ExpandProperty samaccountname
$manager=Get-ADUser -Filter "displayname -eq '$($Users.'Line Manager Fullname')'"|select -ExpandProperty DistinguishedName
Set-ADUser -Identity $user -Replace #{manager=$manager}
}
when running the script it returns:
Set-ADUser : Cannot validate argument on parameter 'Identity'. The argument is null. Provide a valid value for the argument, and then try running the command again.
At line:6 char:22
+ Set-ADUser -Identity $user -Replace #{manager=$manager}
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Set-ADUser], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.SetADUser
the "EmployeeFullName" and "Line Manager FullName" format in the .csv is set as i.e. joe bloggs, see below CSV file example.
EmployeeFullname Line Manager Fullname
---------------- ---------------------
Kieran Rhodes Tracey Barley
Lewis Pontin Tracey Barley
Lizzy Wilks Rodney Bennett
I also noticed if I remove and try to retype "$Users.EmployeeFullName" it no longer picks up the "EmployeeFullName" from the csv file.
Anyone know what I'm doing wrong here please?
Below would be an example:
$Users = Import-CSV C:\Documents\Managers.csv
ForEach ($User in $Users) {
$user = Get-ADUser -Filter "displayname -eq '$($User.EmployeeFullName)'"
$manager = (Get-ADUser -Filter "displayname -eq '$($User.'Line Manager Fullname')'").DistinguishedName
Set-ADUser -Identity $User -Replace #{manager=$manager}
}
Note: you don't need to dig down to the samAccountName property because Set-ADUser will take the full fidelity object for the -Identity argument. You also don't need to use Select... -ExpandProperty you can just parenthetically dot reference the distinguishedName property.
Also you can use the instancing capability in the AD cmdlets:
$Users = Import-CSV C:\Documents\Managers.csv
ForEach ( $User in $Users )
{
$User = Get-ADUser -Filter "displayname -eq '$($User.EmployeeFullName)'" -Properties Manager
$Manager = (Get-ADUser -Filter "displayname -eq '$($User.'Line Manager Fullname')'").DistinguishedName
$User.Manager = $Manager
Set-ADUser -Instance $User
}
In this case you call back the Manager property set it with a simple assignment statement than give the $User variable as the argument to the -Instance parameter of Set-ADUser

Fixing simple Get-ADComputer : The server has returned the following error: invalid enumeration context

Why did the below simple AD cmdlets fail? I'm trying to get all Windows Server OS that is online in my AD domain, into variable.
$Servers = Get-ADComputer -Filter { Enabled -eq $True -and OperatingSystem -like "*Server*" } -Properties OperatingSystem -SearchBase "DC=DOMAIN,DC=com" |
Where-Object { Test-Connection $_.Name -Count 1 -Quiet } |
Select-Object -ExpandProperty Name
However, it is error:
Get-ADComputer : The server has returned the following error: invalid enumeration context.
+ $Servers = Get-ADComputer -Filter { Enabled -eq $True -and OperatingS ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-ADComputer], ADException
+ FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.GetADComputer
How to make it works?
Get-ADComputer makes a paged query to Active Directory, and then starts outputting the results one-by-one.
If PowerShell takes too long between initially sending the query and then subsequently asking for the next page of the result set, the page cursor on the DC basically times out to free up resources and you get invalid enumeration context.
Instead of piping the output directly to Where-Object { Test-Connection ... }, which in turn "backs up" the pipeline every time we're waiting for a ping to timeout, make the Get-ADComputer call in a nested pipeline (enclose it it ()):
$Servers = (Get-ADComputer -Filter { Enabled -eq $True -and OperatingSystem -like "*Server*" } -Properties OperatingSystem -SearchBase "DC=DOMAIN,DC=com") |
Where-Object { Test-Connection $_.Name -Count 1 -Quiet } |
Select-Object -ExpandProperty Name

msExchDelegateListBL attribute - The attribute cannot be modified because it is owned by the system

When attempting to clear msExchDelegateListBL for AD User then I got the following the error message.
Get-ADUser -Identity "User01" -Properties * | set-aduser -clear msExchDelegateListBL
Message :
set-aduser : The attribute cannot be modified because it is owned by the system
At line:1 char:49
+ ... ity "User01" -Properties * | set-aduser -clear msExchDelegateListBL
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (CN=User01...,DC=corp:ADUser) [Set-ADUser], ADException
+ FullyQualifiedErrorId : ActiveDirectoryServer:8369,Microsoft.ActiveDirectory.Management.Commands.SetADUser
LAST UPDATE :
$userDN = Get-ADUser -Filter {(Enabled -eq $true -and sAMAccountName -like "TEST*") -or (Enabled -eq $true -and sAMAccountName -like "PROD*")} -SearchBase "OU=COMPANY,DC=contoso,DC=local" -SearchScope Subtree -Properties * |select-object distinguishedname,samaccountname
foreach($userToClean in $userDN) {
$delegates = Get-ADUser $userToClean.samaccountname -Properties msExchDelegateListBL | select -ExpandProperty msExchDelegateListBL
Write-Host “======================================================”
write-host “List of Delegated accounts that are backlinked:” $Delegates
Write-Host “======================================================”
foreach ($delegate in $delegates) {
Set-ADUser $delegate -Remove #{msExchDelegateListLink = "$($userToClean.distinguishedname)"}
}
Write-Host “======================================================”
Write-Host “If the following get-aduser cmdlet searching for backlinds is empty, then all delegated backlinks have been removed”
Get-ADUser $userToClean.samaccountname -Properties msExchDelegateListBL | select -ExpandProperty msExchDelegateListBL
Write-Host “======================================================”
}
The msExchDelegateListBL attribute is adjusted by the system after a user is removed from (or added to) the msExchDelegateListLink of the delegate.
When users are granted permission to a shared mailbox, the default behaviour of automapping means that the shared mailbox has msExchDelegateListLink set to the DN of the users, and the backlink property (msExchDelegateLinkListBL hidden in AD by default) on each user is populated with the DN of the shared mailbox. Whenever the link attribute is updated, the backlink is automatically updated.
I found a good read about that, including PowerShell code.
For your question I suggest to scroll down to To remove all BLs all at once chapter and adapt the code in there to suit your needs as you have done in your edit.
Personally, I would change the top line in your code into
$userDN = Get-ADUser -Filter "Enabled -eq 'True'" -SearchBase "OU=COMPANY,DC=contoso,DC=local" -SearchScope Subtree |
Where-Object { $_.SamAccountName -match '^(TEST|PROD)' }
since user properties SamAccountName and DistinguishedName are returned by the Get-ADUSer cmdlet by default.

How do I pass SamAccountName from a given user name, so that I can set -ProtectedfromAccidentalDeletion#$true

I inherited several PowerShell scripts, to place a user on LitigationHold in Exchange, as well as set the user's AD protect from accidental deletion to $true.
They all work separately, but one uses User Full name, and the other uses the SAM account name. I am trying to marry scripts so that I can just use the Full name, but I can't seem to pass the SAMAccountName .
My attempt at combining the codes:
foreach ($user in ("Name 1", "Name 2", "Name 3"))
{
$mailuser = Get-Mailbox $user -DomainController A1.Domain l -ErrorAction SilentlyContinue| Select *;
if ($mailuser -eq $null)
{
Write-Host "$user was not found. Check for misspellings."
}
else
{
if($mailuser.LitigationHoldDate -ne $null)
{
Set-Mailbox $user -LitigationHoldEnabled:$true -Confirm:$False -Force:$True -DomainController A1.Domain;
Write-Host "$user is now placed on hold.";
$userinfo = {
Get-ADUser $user -Server A1.Domain
};
Set-ADObject -Identity $userinfo.SamAccountName -ProtectedFromAccidentalDeletion:$true;
$i = Get-ADUser $user -Properties description -Server A1.Domain |
%{ $_.description } |
Set-ADUser $userinfo -Server A1.Domain -Replace #{
description="8/19/2019 - Security Hold, please contact admin before modifying `r`n | $($i)"
}
}
else{
Write-Host "$user is already on litigation hold as of $($mailuser.LitigationHoldDate) by $($mailuser.LitigationHoldOwner)."
}
}
}
To take list of Display names and get usernames:
foreach ($user in ("Name 1", "Name 2", "Name 3"))
{
$userinfo = Get-ADUser -filter { DisplayName -like $user } -Server A1.Domain ;
if ($userinfo -ne $null)
{
Get-ADUser -filter { DisplayName -like $user } -Server A1.Domain | ft SamAccountName -HideTableHeaders
}
else
{
Write-Host "$user is not available"
}
}
To Add Lit Hold into AD Description
foreach ($user in ("Name 1", "Name 2", "Name 3"))
{
$mailuser = Get-Mailbox $user -DomainController A1.Domain -ErrorAction SilentlyContinue| Select *;
if($mailuser -eq $null)
{
Write-Host "$user was not found. Check for misspellings."
}
else
{
if ($mailuser.LitigationHoldDate -eq $null)
{
$i = Get-ADUser $user -Properties description -Server A1.Domain | %{ $_.description};
Set-ADUser $user -Server A1.Domain -Replace #{
description="Security Hold, please contact the Gnome before modifying `r`n | $($i)"
}
}
else
{
Write-Host "$user is already on litigation hold as of $($mailuser.LitigationHoldDate) by $($mailuser.LitigationHoldOwner)."
}
}
}
To take a list of Display names and set Lit Hold:
foreach ($user in ("Name 1", "Name 2", "Name 3""))
{
$mailuser = Get-Mailbox $user -DomainController A1.Domain -ErrorAction SilentlyContinue| Select *;
if ($mailuser -eq $null)
{
Write-Host "$user was not found. Check for misspellings."
}
else
{
if($mailuser.LitigationHoldDate -eq $null)
{
Set-Mailbox $user -LitigationHoldEnabled:$true -Confirm:$False -Force:$True -DomainController A1.Domain ;
Write-Host "$user is now placed on hold."
}
else
{
Write-Host "$user is already on litigation hold as of $($mailuser.LitigationHoldDate) by $($mailuser.LitigationHoldOwner)."
}
}
}
To take a list of usernames and protect against accidental deletion:
"User1", "User2", "User3" | Get-aduser -Server A1.Domain | Set-ADObject -ProtectedFromAccidentalDeletion:$true
I want to account to be set to LitigationHold, AD protect from accidental deletion, also reflect security hold on description.
This is the error message I get when I run it:
FN LN is now placed on hold.
Set-ADObject : Cannot validate argument on parameter 'Identity'. The argument is null. Provide a valid value for the argument, and then try running the
command again.
At line:9 char:25
+ Set-ADObject -Identity $userinfo.SamAccountName -ProtectedFromAccide ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Set-ADObject], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.SetADObject
Set-ADUser : A positional parameter cannot be found that accepts argument 'Get-ADUser $user -Server A1.domain '.
At line:12 char:1
+ Set-ADUser $userinfo -Server A1.domain -Replace # ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-ADUser], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.ActiveDirectory.Management.Commands.SetADUser
I was trying to see if I can bypass the -Identify flag by giving it the user full name, but Set-ADObject will only take an object, not a string.
--edit 3--
Replying to #Seth about -Identity flag is a parameter:
changed part of the code to give -Identity an ADObject:
$userinfo = Get-ADUser {DisplayName -like $user} -Server A1.domain};
Set-ADObject $userinfo -ProtectedFromAccidentalDeletion:$true;
The error message is as follows:
Get-ADUser : Cannot evaluate parameter 'Identity' because its argument is specified as a script block and there is no input. A script block cannot be
evaluated without input.
At line:8 char:26
+ $userinfo = Get-ADUser {DisplayName -like $user} -Server A1.domain ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [Get-ADUser], ParameterBindingException
+ FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.ActiveDirectory.Management.Commands.GetADUser
Set-ADObject : Cannot evaluate parameter 'Identity' because its argument is specified as a script block and there is no input. A script block cannot be
evaluated without input.
At line:9 char:15
+ Set-ADObject $userinfo -ProtectedFromAccidentalDeletion:$true;
+ ~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [Set-ADObject], ParameterBindingException
+ FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.ActiveDirectory.Management.Commands.SetADObject
Set-ADUser : A positional parameter cannot be found that accepts argument 'Get-ADUser {DisplayName -like $user} -Server A1.domain'.
At line:12 char:1
+ Set-ADUser $userinfo -Server A1.domain -Replace # ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-ADUser], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.ActiveDirectory.Management.Commands.SetADUser
I think at this point the issue boils down to: Set-ADUser uses SamAccountName, and I can't seem to parse out the SamAccountName from the user ADObject. I can get the SamAccountName by calling a table from the ADObject, but it will not pass into Set-ADUser correctly.
You're currently not doing any verification on whenever you actually get a proper ADUser object. You simply assume that the assignment to $i/$userinfo will be successful. Your $name contains the display name for a user. So for example it would be "John Doe".
This works for Get-Mailbox because it supports Name, Alias, Distinguished name (DN), Canonical DN, <domain name>\<account name>, Email address, GUID, LegacyExchangeDN, SamAccountName, User ID or user principal name (UPN) as values for Identity. It has to do this to an extend because some values might or might not be available to identify a mailbox. Get-ADUser on the other hand has a much more strict approach only supporting distinguished name, GUID (objectGUID), security identifier (objectSid), SAM account name (sAMAccountName). Hence using the display name to find an AD account isn't supported. Your colleague used a filter to just use it anyway which is one solution $userinfo = Get-ADUser -filter { DisplayName -like $user } -Server A1.Domain ;.
If you really want to go with the display name you'll need to either look for it like that or use the DistinguishedName property of the mailbox to get the owner. So you'd do something like:
$mailbox = Get-Mailbox test
$adObject = Get-AdUser -Identity $mailbox.DistinguishedName
Obviously you should check whenever the property actually exists on the mailbox object as it might be disconnected. Also rather than using $user again and again you might want to use actual object (e.g. the mailbox or AD Object) to make sure you only need to verify your search results once.
You should also be able to just use one Set-AdUser call instead of that very weird call you're currently doing. You find the user, iterate the descriptions for the user (an object only has one) and add to it. Doing something like Set-AdUser $adObject -Description "New Description | $($adObject.Description)" would be much shorter and clearer. Spreading it some more might even improve it more.