powershellcsvactive-directoryactive-directory-groupdate-comparison

Removing a user from a group after N days


I am trying to create a PowerShell script that will be executed via a task every day. The task will check if a user has been a member of an AD group longer than 7 days. If so, that user will be removed. The data is imported from a CSV file in which we will insert the date the user was added to the group:

samaccountname,Date
user 1,20/01/2022
user2,06/02/2022

This is the code I wrote:

$users = "C:\Path to CSV\File.csv"

Import-Module ActiveDirectory
Import-Csv $users

foreach ($user in $Users) 
{
    $CheckDate2 = get-date ($user.'Date').AddDays(7)
    $CheckDate1 = get-date ($user.'Date')

    if ($CheckDate1 -lt $CheckDate2){
        exit
    }
    else{
        Remove-ADGroupMember -Identity "Group Name" -Members $user.samaccountname -Confirm:$false
    }
}

This is the errors I am receiving:

You cannot call a method on a null-valued expression.
At line:8 char:1
+ $CheckDate2 = get-date ($user.'Date').AddDays(7)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Get-Date : Cannot bind parameter 'Date' to the target. Exception setting "Date": "Cannot convert null to type "System.DateTime"."
At line:9 char:24
+ $CheckDate1 = get-date ($user.'Date')
+                        ~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (:) [Get-Date], ParameterBindingException
    + FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.GetDateCommand
 
Remove-ADGroupMember : Cannot validate argument on parameter 'Members'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At line:16 char:68
+ ... tity "Group Name" -Members $user.samaccountname -Confir ...
+                                              ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Remove-ADGroupMember], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.RemoveADGroupMember

Solution

  • Your foreach loop is enumerating $users, which has a value of "C:\Path to CSV\File.csv". This means there will be one iteration of the loop where $user has a value of "C:\Path to CSV\File.csv". Since the [String] class does not have a Date property, this...

    ($user.'Date').AddDays(7)
    

    ...is effectively this...

    ($null).AddDays(7)
    

    ...hence the error. You are also not doing anything with the output of Import-Csv $users, so you need to collect it somewhere for processing; either store it in a variable...

    $usersPath = "C:\Path to CSV\File.csv"
    
    Import-Module ActiveDirectory
    $users = Import-Csv $usersPath
    
    foreach ($user in $users)
    {
        # ...
    

    ...or, even better, loop directly over the output to process records as they're read...

    $usersPath = "C:\Path to CSV\File.csv"
    
    Import-Module ActiveDirectory
    
    foreach ($user in Import-Csv $usersPath)
    {
        # ...
    

    I renamed your initial $users variable to $usersPath for clarity; reusing a variable in that way can be confusing and unexpected.

    Even after fixing the loop you still need to change your parentheses from this...

    get-date ($user.'Date').AddDays(7)
    

    ...to this...

    (get-date $user.'Date').AddDays(7)
    

    ...to create a [DateTime] using the value of $user.'Date' and then call AddDays(7) on the result. Note that you don't need to quote a property that doesn't contains spaces in the name, so you can simplify that to...

    (get-date $user.Date).AddDays(7)
    

    Also, you are comparing a user's Date with Date + 7d, which will always be $true. Instead, you want to test if today is beyond the 7-day membership limit...

    $today = (Get-Date).Date # Ignore the TimeOfDay portion so the expiration happens on a day boundary
    $userAddedDate = Get-Date ($user.Date)
    $maxGroupMembershipTime = New-TimeSpan -Day 7
    
    if ($today - $userAddedDate -gt $maxGroupMembershipTime)
    # Alternative: if ($userAddedDate + $maxGroupMembershipTime -lt $today)
    {
        # ...
    

    Finally, you are conditionally exiting the loop, which means no further user records nor anything else in the script will be processed. To skip that user you would replace exit with continue, or just let control fall through to the end of the foreach block...

    $usersPath = "C:\Path to CSV\File.csv"
    $today = (Get-Date).Date # Ignore the TimeOfDay portion so the expiration happens on a day boundary
    $maxGroupMembershipTime = New-TimeSpan -Day 7
    
    Import-Module ActiveDirectory
    
    foreach ($user in Import-Csv $usersPath)
    {
        $userAddedDate = Get-Date ($user.Date)
    
        if ($today - $userAddedDate -gt $maxGroupMembershipTime)
        # Alternative: if ($userAddedDate + $maxGroupMembershipTime -lt $today)
        {
            Remove-ADGroupMember -Identity "Group Name" -Members $user.samaccountname -Confirm:$false
        }
    }
    

    I moved the initialization of $today outside of the loop since there's really no need to set it on each iteration and to ensure every user gets the same date for "today" regardless of when the script executes.