{"id":531428,"date":"2025-09-24T09:55:02","date_gmt":"2025-09-24T09:55:02","guid":{"rendered":"https:\/\/www.ninjaone.com\/?post_type=script_hub&#038;p=531428"},"modified":"2025-09-24T09:55:02","modified_gmt":"2025-09-24T09:55:02","slug":"creer-une-nouvelle-alerte-utilisateur-avec-powershell","status":"publish","type":"script_hub","link":"https:\/\/www.ninjaone.com\/fr\/script-hub\/creer-une-nouvelle-alerte-utilisateur-avec-powershell\/","title":{"rendered":"Comment cr\u00e9er une nouvelle alerte utilisateur avec PowerShell"},"content":{"rendered":"<p>Le contr\u00f4le de la <strong>cr\u00e9ation de nouveaux comptes utilisateur<\/strong> est un aspect fondamental du maintien d&rsquo;un environnement informatique s\u00e9curis\u00e9. Que vous prot\u00e9giez un domaine <a href=\"https:\/\/www.ninjaone.com\/it-hub\/endpoint-management\/what-is-active-directory\/\">Active Directory<\/a> d&rsquo;entreprise ou que vous g\u00e9riez des postes de travail autonomes, la d\u00e9tection de la cr\u00e9ation d&rsquo;utilisateurs non autoris\u00e9s ou inattendus peut s&rsquo;av\u00e9rer essentielle pour pr\u00e9venir les menaces internes, les erreurs de configuration ou m\u00eame les violations externes. Cet article explore une solution bas\u00e9e sur PowerShell con\u00e7ue pour alerter les professionnels de l&rsquo;informatique lorsque de nouveaux comptes utilisateurs sont cr\u00e9\u00e9s dans un d\u00e9lai donn\u00e9, en fournissant une int\u00e9gration optimale avec NinjaOne pour une visibilit\u00e9 centralis\u00e9e.<\/p>\n<h2>Contexte<\/h2>\n<p>Dans les environnements d&rsquo;entreprise, les administrateurs informatiques et les fournisseurs de services g\u00e9r\u00e9s (MSP) ont souvent du mal \u00e0 suivre chaque changement de compte, en particulier dans les infrastructures hybrides. Des acteurs malveillants peuvent cr\u00e9er de nouveaux comptes en guise de portes d\u00e9rob\u00e9es, ou des changements l\u00e9gitimes peuvent passer inaper\u00e7us, laissant des failles de s\u00e9curit\u00e9. Windows offre des possibilit\u00e9s d&rsquo;audit par le biais des <a href=\"https:\/\/www.ninjaone.com\/fr\/blog\/comment-lire-les-journaux-d-evenements-windows\/\">journaux d&rsquo;\u00e9v\u00e9nements<\/a> et d&rsquo;Active Directory, mais l&rsquo;extraction et l&rsquo;utilisation de ces donn\u00e9es par programme n&rsquo;est pas triviale.<\/p>\n<p>Ce script PowerShell r\u00e9sout ce probl\u00e8me en automatisant la d\u00e9tection des nouveaux comptes utilisateurs et en poussant \u00e9ventuellement les donn\u00e9es d&rsquo;alerte dans un champ personnalis\u00e9 multiligne de NinjaOne. Il prend en charge \u00e0 la fois les <a href=\"https:\/\/www.ninjaone.com\/fr\/it-hub\/endpoint-security\/qu-est-ce-qu-un-controleur-de-domaine\/\">contr\u00f4leurs de domaine<\/a> (DC) et les syst\u00e8mes autonomes, ce qui le rend adaptable \u00e0 divers environnements Windows. Ce script est un script PowerShell avanc\u00e9 d&rsquo;alerte pour les nouveaux utilisateurs et un outil de conformit\u00e9 fiable pour l&rsquo;audit et la surveillance.<\/p>\n<h2>Le script<\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\">#Requires -Version 5.1\r\n\r\n&lt;#\r\n.SYNOPSIS\r\n    Finds and alerts on new user accounts created within a specified time frame, measured in minutes. If ran on a domain controller, it will use Active Directory to find new users, otherwise it will use the event log to find new users.\r\n.DESCRIPTION\r\n    This script finds and alerts on new user accounts created within a specified time frame, measured in minutes.\r\n    It can be used to monitor user creation activity in an Active Directory environment or an individual system.\r\n\r\n    On a domain controller, it retrieves new users from Active Directory. If the Active Directory Recycle Bin is enabled, it will also retrieve deleted users that were created within the specified time frame.\r\n    On an individual system, it retrieves new users from the event log. The event log will not retain these events indefinitely.\r\n\r\n    When running on an individual system, it checks if auditing is enabled for User Account Management.\r\n    If auditing is not enabled, the following will need to be run in an elevated PowerShell\/cmd session:\r\n    auditpol.exe \/set \/subcategory:{0CCE9235-69AE-11D9-BED3-505054503030} \/success:enable\r\n    This will enable auditing for user account management events, including user creation required to track new user creation events.\r\nBy using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https:\/\/www.ninjaone.com\/terms-of-use.\r\n    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. \r\n    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. \r\n    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. \r\n    Warranty Disclaimer: The script is provided \u201cas is\u201d and \u201cas available\u201d, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. \r\n    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. \r\n    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. \r\n    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).\r\n\r\n.PARAMETER TimeFrameInMinutes\r\n    The time frame in minutes to check for new users.\r\n    Minimum value is 5 minutes.\r\n    Maximum value is 525600 minutes (1 year).\r\n.PARAMETER MultilineCustomFieldName\r\n    The name of the multiline custom field to set with the new user information.\r\n    This field will be populated with the names and creation times of the new users.\r\n\r\n.EXAMPLE\r\n    -TimeFrameInMinutes 10000 -MultilineCustomFieldName \"Multiline\"\r\n\r\n    This example checks for new users created within the last 10000 minutes and sets the multiline custom field \"Multiline\" with the user information.\r\n    Example output:\r\n    [Alert] New users created within the last 10000 minutes:\r\n    [Alert] Username: test, Full Name: test test, Created On: 05\/20\/2025 13:13:35\r\n    [Alert] Username: anewaccount, Full Name: Another New Account, Created On: 05\/20\/2025 15:01:10\r\n\r\n    [Info] Attempting to set Custom Field 'Multiline'.\r\n    [Info] Successfully set Custom Field 'Multiline'!\r\n\r\n.EXAMPLE\r\n    -TimeFrameInMinutes 2440\r\n\r\n    This example checks for new users created within the last 1440 minutes (24 hours).\r\n    Example output:\r\n    [Alert] New users created within the last 1440 minutes:\r\n    [Alert] Username: test, Full Name: test test, Created On: 05\/20\/2025 13:13:35\r\n    [Alert] Username: anewaccount, Full Name: Another New Account, Created On: 05\/20\/2025 15:01:10\r\n\r\n.NOTES\r\n    Minimum OS Architecture Supported: Windows 10, Windows Server 2016\r\n    Release Notes: Initial release\r\n#&gt;\r\n\r\n[CmdletBinding()]\r\nparam (\r\n    $TimeFrameInMinutes,\r\n    [string]$MultilineCustomFieldName\r\n)\r\n\r\nbegin {\r\n    # Import the script variables\r\n    if ($env:timeFrameInMinutes) { $TimeFrameInMinutes = $env:timeFrameInMinutes }\r\n    if ($env:multilineCustomFieldName) { $MultilineCustomFieldName = $env:multilineCustomFieldName }\r\n\r\n    function Test-IsElevated {\r\n        [CmdletBinding()]\r\n        param ()\r\n\r\n        # Get the current Windows identity of the user running the script\r\n        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()\r\n\r\n        # Create a WindowsPrincipal object based on the current identity\r\n        $p = New-Object System.Security.Principal.WindowsPrincipal($id)\r\n\r\n        # Check if the current user is in the Administrator role\r\n        # The function returns $True if the user has administrative privileges, $False otherwise\r\n        # 544 is the value for the Built In Administrators role\r\n        # Reference: https:\/\/learn.microsoft.com\/en-us\/dotnet\/api\/system.security.principal.windowsbuiltinrole\r\n        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]'544')\r\n    }\r\n\r\n    function Test-IsDomainJoined {\r\n        # Check the PowerShell version to determine the appropriate cmdlet to use\r\n        try {\r\n            if ($PSVersionTable.PSVersion.Major -lt 3) {\r\n                return $(Get-WmiObject -Class Win32_ComputerSystem).PartOfDomain\r\n            }\r\n            else {\r\n                return $(Get-CimInstance -Class Win32_ComputerSystem).PartOfDomain\r\n            }\r\n        }\r\n        catch {\r\n            Write-Host -Object \"[Error] Unable to validate whether or not this device is a part of a domain.\"\r\n            Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n            exit 1\r\n        }\r\n    }\r\n\r\n    function Test-IsDomainController {\r\n        # Determine the method to retrieve the operating system information based on PowerShell version\r\n        try {\r\n            $OS = if ($PSVersionTable.PSVersion.Major -lt 3) {\r\n                Get-WmiObject -Class Win32_OperatingSystem -ErrorAction Stop\r\n            }\r\n            else {\r\n                Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop\r\n            }\r\n        }\r\n        catch {\r\n            Write-Host -Object \"[Error] Unable to validate whether or not this device is a domain controller.\"\r\n            Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n            exit 1\r\n        }\r\n\r\n        # Check if the ProductType is \"2\", which indicates that the system is a domain controller\r\n        if ($OS.ProductType -eq \"2\") {\r\n            return $true\r\n        }\r\n    }\r\n\r\n    function Set-NinjaPropertyValue {\r\n        [CmdletBinding()]\r\n        Param(\r\n            [Parameter(Mandatory = $True)]\r\n            [String]$Name,\r\n            [Parameter(Mandatory = $True, ValueFromPipeline = $True)]\r\n            $Value,\r\n            [Parameter()]\r\n            [String]$Type,\r\n            [Parameter()]\r\n            [String]$DocumentName,\r\n            [Parameter()]\r\n            [Switch]$Piped\r\n        )\r\n\r\n        if ($Type -eq \"Date Time\") { $Type = \"DateTime\" }\r\n        if ($Type -match \"[-]\") { $Type = $Type -replace '-' }\r\n        if ($Type -match \"[\/]\") { $Type = $Type -replace '\/' }\r\n\r\n        # Remove the non-breaking space character\r\n        if ($Type -eq \"WYSIWYG\") {\r\n            $Value = $Value -replace '\u00a0', '&amp;nbsp;'\r\n        }\r\n\r\n        if ($Type -eq \"DateTime\" -or $Type -eq \"Date\") {\r\n            $Type = \"Date or Date Time\"\r\n        }\r\n\r\n        # Measure the number of characters in the provided value\r\n        $Characters = $Value | ConvertTo-Json | Measure-Object -Character | Select-Object -ExpandProperty Characters\r\n\r\n        # Throw an error if the value exceeds the character limit of 200,000 characters\r\n        if ($Piped -and $Characters -ge 200000) {\r\n            throw [System.ArgumentOutOfRangeException]::New(\"Character limit exceeded: the value is greater than or equal to 200,000 characters.\")\r\n        }\r\n\r\n        if (!$Piped -and $Characters -ge 45000) {\r\n            throw [System.ArgumentOutOfRangeException]::New(\"Character limit exceeded: the value is greater than or equal to 45,000 characters.\")\r\n        }\r\n\r\n        # Initialize a hashtable for additional documentation parameters\r\n        $DocumentationParams = @{}\r\n\r\n        # If a document name is provided, add it to the documentation parameters\r\n        if ($DocumentName) { $DocumentationParams[\"DocumentName\"] = $DocumentName }\r\n\r\n        # Define a list of valid field types\r\n        $ValidFields = \"Checkbox\", \"Date\", \"Date or Date Time\", \"DateTime\", \"Decimal\", \"Dropdown\", \"Email\", \"Integer\", \"IP Address\", \"MultiLine\",\r\n        \"MultiSelect\", \"Phone\", \"Secure\", \"Text\", \"Time\", \"URL\", \"WYSIWYG\"\r\n\r\n        # Warn the user if the provided type is not valid\r\n        if ($Type -and $ValidFields -notcontains $Type) { Write-Warning \"$Type is an invalid type. Please check here for valid types: https:\/\/ninjarmm.zendesk.com\/hc\/en-us\/articles\/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality\" }\r\n\r\n        # Define types that require options to be retrieved\r\n        $NeedsOptions = \"Dropdown\", \"MultiSelect\"\r\n\r\n        # If the property is being set in a document or field and the type needs options, retrieve them\r\n        if ($DocumentName) {\r\n            if ($NeedsOptions -contains $Type) {\r\n                $NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2&gt;&amp;1\r\n            }\r\n        }\r\n        else {\r\n            if ($NeedsOptions -contains $Type) {\r\n                $NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2&gt;&amp;1\r\n            }\r\n        }\r\n\r\n        # Throw an error if there was an issue retrieving the property options\r\n        if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions }\r\n\r\n        # Process the property value based on its type\r\n        switch ($Type) {\r\n            \"Checkbox\" {\r\n                # Convert the value to a boolean for Checkbox type\r\n                $NinjaValue = [System.Convert]::ToBoolean($Value)\r\n            }\r\n            \"Date or Date Time\" {\r\n                # Convert the value to a Unix timestamp for Date or Date Time type\r\n                $Date = (Get-Date $Value).ToUniversalTime()\r\n                $TimeSpan = New-TimeSpan (Get-Date \"1970-01-01 00:00:00\") $Date\r\n                [long]$NinjaValue = $TimeSpan.TotalSeconds\r\n            }\r\n            \"Dropdown\" {\r\n                # Convert the dropdown value to its corresponding GUID\r\n                $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header \"GUID\", \"Name\"\r\n                $Selection = $Options | Where-Object { $_.Name -eq $Value } | Select-Object -ExpandProperty GUID\r\n\r\n                # Throw an error if the value is not present in the dropdown options\r\n                if (!($Selection)) {\r\n                    throw [System.ArgumentOutOfRangeException]::New(\"Value is not present in dropdown options.\")\r\n                }\r\n\r\n                $NinjaValue = $Selection\r\n            }\r\n            \"MultiSelect\" {\r\n                $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header \"GUID\", \"Name\"\r\n                $Selections = New-Object System.Collections.Generic.List[String]\r\n                if ($Value -match \"[,]\") {\r\n                    $Value = $Value -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ }\r\n                }\r\n\r\n                $Value | ForEach-Object {\r\n                    $GivenValue = $_\r\n                    $Selection = $Options | Where-Object { $_.Name -eq $GivenValue } | Select-Object -ExpandProperty GUID\r\n\r\n                    # Throw an error if the value is not present in the dropdown options\r\n                    if (!($Selection)) {\r\n                        throw [System.ArgumentOutOfRangeException]::New(\"Value is not present in dropdown options.\")\r\n                    }\r\n\r\n                    $Selections.Add($Selection)\r\n                }\r\n\r\n                $NinjaValue = $Selections -join \",\"\r\n            }\r\n            \"Time\" {\r\n                # Convert the value to a Unix timestamp for Date or Date Time type\r\n                $LocalTime = (Get-Date $Value)\r\n                $LocalTimeZone = [TimeZoneInfo]::Local\r\n                $UtcTime = [TimeZoneInfo]::ConvertTimeToUtc($LocalTime, $LocalTimeZone)\r\n\r\n                [long]$NinjaValue = ($UtcTime.TimeOfDay).TotalSeconds\r\n            }\r\n            default {\r\n                # For other types, use the value as is\r\n                $NinjaValue = $Value\r\n            }\r\n        }\r\n\r\n        # Set the property value in the document if a document name is provided\r\n        if ($DocumentName) {\r\n            $CustomField = Ninja-Property-Docs-Set -AttributeName $Name -AttributeValue $NinjaValue @DocumentationParams 2&gt;&amp;1\r\n        }\r\n        else {\r\n            try {\r\n                # Otherwise, set the standard property value\r\n                if ($Piped) {\r\n                    $CustomField = $NinjaValue | Ninja-Property-Set-Piped -Name $Name 2&gt;&amp;1\r\n                }\r\n                else {\r\n                    $CustomField = Ninja-Property-Set -Name $Name -Value $NinjaValue 2&gt;&amp;1\r\n                }\r\n            }\r\n            catch {\r\n                throw $_.Exception.Message\r\n            }\r\n        }\r\n\r\n        # Throw an error if setting the property failed\r\n        if ($CustomField.Exception) {\r\n            throw $CustomField\r\n        }\r\n    }\r\n\r\n    function Get-OSVersion {\r\n        # Get the OS version information\r\n        return [System.Environment]::OSVersion.Version\r\n    }\r\n\r\n    function Test-IsSystem {\r\n        [CmdletBinding()]\r\n        param ()\r\n\r\n        # Get the current Windows identity of the user running the script\r\n        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()\r\n\r\n        # Check if the current identity's name matches \"NT AUTHORITY*\"\r\n        # or if the identity represents the SYSTEM account\r\n        return $id.Name -like \"NT AUTHORITY*\" -or $id.IsSystem\r\n    }\r\n\r\n    # If time frame in minutes exists, validate that it is an integer\r\n    # If it does not exist, error out because it is required\r\n    if (-not [string]::IsNullOrWhiteSpace($TimeFrameInMinutes)) {\r\n        # Convert the value to an integer\r\n        try {\r\n            $TimeFrameInMinutes = [int]$TimeFrameInMinutes\r\n        }\r\n        catch {\r\n            Write-Host -Object \"[Error] Error while converting the value for 'Time Frame In Minutes' to an integer:\"\r\n            Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n            Write-Host -Object \"[Error] The value for 'Time Frame In Minutes' must be an integer between 5 and 525600.\"\r\n            exit 1\r\n        }\r\n\r\n        # Validate the TimeFrameInMinutes is within the valid range\r\n        if ($TimeFrameInMinutes -lt 5 -or $TimeFrameInMinutes -gt 525600) {\r\n            Write-Host -Object \"[Error] An invalid value was provided for 'Time Frame In Minutes': '$TimeFrameInMinutes'\"\r\n            Write-Host -Object \"[Error] The value for 'Time Frame In Minutes' must be an integer between 5 and 525600.\"\r\n            exit 1\r\n        }\r\n    }\r\n    else {\r\n        Write-Host -Object \"[Error] The value for 'Time Frame In Minutes' is required and must be provided.\"\r\n        exit 1\r\n    }\r\n\r\n    # Validate the custom field name if it exists\r\n    if ($MultilineCustomFieldName) {\r\n        # Error if the custom field name is empty\r\n        if ([string]::IsNullOrWhiteSpace($MultilineCustomFieldName)) {\r\n            Write-Host -Object \"[Error] The value for 'Multiline Custom Field Name' cannot be empty.\"\r\n            exit 1\r\n        }\r\n\r\n        # Trim whitespace from the custom field name\r\n        $MultilineCustomFieldName = $MultilineCustomFieldName.Trim()\r\n\r\n        # Validate that the field name contains only alphanumeric characters\r\n        if ($MultilineCustomFieldName -match \"[^0-9A-Z]\") {\r\n            Write-Host -Object \"[Error] The 'Multiline Custom Field Name' of '$MultilineCustomFieldName' is invalid as it contains invalid characters.\"\r\n            Write-Host -Object \"[Error] Please provide a valid multiline custom field name to save the results, or leave it blank.\"\r\n            Write-Host -Object \"[Error] https:\/\/ninjarmm.zendesk.com\/hc\/en-us\/articles\/360060920631-Custom-Field-Setup\"\r\n            exit 1\r\n        }\r\n    }\r\n\r\n    # Get the OS version information\r\n    try {\r\n        $osVersion = Get-OSVersion -ErrorAction Stop\r\n    }\r\n    catch {\r\n        Write-Host -Object \"[Error] Failed to retrieve the OS version information.\"\r\n        Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n        exit 1\r\n    }\r\n\r\n    # Check if the OS version is supported\r\n    if ($osVersion.Major -lt 10 -or ($osVersion.Major -eq 10 -and $osVersion.Build -lt 14393)) {\r\n        Write-Host -Object \"[Error] This script requires Windows 10 or Windows Server 2016 or later.\"\r\n        exit 1\r\n    }\r\n\r\n}\r\nprocess {\r\n    # Check if the script is running with administrative privileges\r\n    if (-not (Test-IsElevated)) {\r\n        Write-Host -Object \"[Error] This script requires administrative privileges. Please run as an administrator.\"\r\n        exit 1\r\n    }\r\n\r\n    # Error if not running as system\r\n    if (-not (Test-IsSystem)) {\r\n        Write-Host -Object \"[Error] This script must be run as the SYSTEM account. Please run as the SYSTEM account.\"\r\n        exit 1\r\n    }\r\n\r\n    $ExitCode = 0\r\n\r\n    # Get the current date and time\r\n    try {\r\n        $currentDateTime = Get-Date -ErrorAction Stop\r\n    }\r\n    catch {\r\n        Write-Host -Object \"[Error] Failed to retrieve the current date and time.\"\r\n        Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n        exit 1\r\n    }\r\n\r\n    try {\r\n        # Calculate the start date and time based on the specified time frame\r\n        $startDateTime = $currentDateTime.AddMinutes(-$TimeFrameInMinutes)\r\n    }\r\n    catch {\r\n        Write-Host -Object \"[Error] Failed to calculate the start date and time.\"\r\n        Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n        exit 1\r\n    }\r\n\r\n    # Get the list of new users created within the specified time frame\r\n    # If host is a DC, get new users from Active Directory\r\n    # Otherwise, get new users from the event log\r\n    if (Test-IsDomainController) {\r\n        Write-Host -Object \"`n[Info] This host is a domain controller. Checking for new users in Active Directory created within the last $TimeFrameInMinutes minutes.\"\r\n\r\n        $IsDomainController = $true\r\n\r\n        # Initialize a list to hold new users\r\n        $newUsers = [System.Collections.Generic.List[PSCustomObject]]::new()\r\n\r\n        # Check if the AD Recycle Bin is enabled\r\n        try {\r\n            $ADRecycleBinEnabled = (Get-ADOptionalFeature -Filter { Name -eq \"Recycle Bin Feature\" } -ErrorAction Stop).FeatureScope\r\n        }\r\n        catch {\r\n            Write-Host -Object \"[Error] Failed to check if the AD Recycle Bin is enabled.\"\r\n            Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n            exit 1\r\n        }\r\n\r\n        if (-not $ADRecycleBinEnabled) {\r\n            Write-Host -Object \"[Warning] The Active Directory Recycle Bin is not enabled. Deleted users cannot be tracked until the Recycle Bin is enabled. This may lead to less accurate results.\"\r\n        }\r\n        else {\r\n            try {\r\n                # If the Recycle Bin is enabled, try to find deleted users created within the time frame\r\n                $deletedADUsers = Get-ADObject -Filter {\r\n                    ObjectClass -eq \"User\" -and\r\n                    ObjectClass -ne \"Computer\" -and\r\n                    IsDeleted -eq $TRUE -and\r\n                    WhenCreated -ge $startDateTime\r\n                } -Properties SamAccountName, WhenCreated -IncludeDeletedObjects -ErrorAction Stop\r\n\r\n                # Format the deleted users to include only the necessary properties and add a status property\r\n                $deletedADUsers = $deletedADUsers | Select-Object Name,\r\n                SamAccountName,\r\n                @{\r\n                    Name       = \"WhenCreated\"\r\n                    Expression = { $_.WhenCreated.ToShortDateString() + \" \" + $_.WhenCreated.ToLongTimeString() }\r\n                },\r\n                @{\r\n                    Name       = \"Status\"\r\n                    Expression = { \"Deleted\" }\r\n                }\r\n            }\r\n            catch {\r\n                Write-Host -Object \"[Error] Failed to retrieve deleted users from Active Directory.\"\r\n                Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n                exit 1\r\n            }\r\n        }\r\n\r\n        try {\r\n            # Find new users in AD created within the time frame\r\n            $newUsersInAD = Get-ADUser -Filter { WhenCreated -ge $startDateTime } -Properties WhenCreated -ErrorAction Stop\r\n\r\n            # Format new users in AD to include only the necessary properties and add a status property\r\n            $newUsersInAD = $newUsersInAD | Select-Object Name,\r\n            SamAccountName,\r\n            @{\r\n                Name       = \"WhenCreated\";\r\n                Expression = { $_.WhenCreated.ToShortDateString() + \" \" + $_.WhenCreated.ToLongTimeString() }\r\n            },\r\n            @{\r\n                Name       = \"Status\";\r\n                Expression = {\r\n                    if ($_.Enabled) {\r\n                        \"Enabled\"\r\n                    }\r\n                    else {\r\n                        \"Disabled\"\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        catch {\r\n            Write-Host -Object \"[Error] Failed to retrieve users from Active Directory.\"\r\n            Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n            exit 1\r\n        }\r\n\r\n        # Add each new user in AD to the list of new users\r\n        foreach ($user in $newUsersInAD) {\r\n            $newUsers.Add($user)\r\n        }\r\n\r\n        # Add each deleted AD user that was created within the time frame to the list of new users\r\n        foreach ($user in $deletedADUsers) {\r\n            # Modify the name of the user to only include the first line before adding it to the list\r\n            $user.Name = $user.Name -split \"`n\" | Select-Object -First 1\r\n            $newUsers.Add($user)\r\n        }\r\n    }\r\n    else {\r\n        Write-Host -Object \"`n[Info] Checking for new local users created within the last $TimeFrameInMinutes minutes in the event log.\"\r\n\r\n        # Warn if domain-joined that only local users will be included\r\n        if (Test-IsDomainJoined) {\r\n            Write-Host -Object \"`n[Warning] This host is domain-joined but not a domain controller. Only local users will be included in the results.\"\r\n            Write-Host -Object \"[Warning] Please run this script on a domain controller if you'd like to alert on domain user accounts.\"\r\n        }\r\n\r\n        # Check if user account creation auditing is enabled\r\n        $AuditPolicy = (Get-ItemProperty HKLM:\\Security\\Policy\\PolAdtEv\\ -ErrorAction SilentlyContinue).\"(default)\"\r\n\r\n        if ($AuditPolicy) {\r\n            # If enabled, the value of this should be 1 (success only) or 3 (success and failure)\r\n            $UserCreationAuditingValue = $AuditPolicy[102]\r\n\r\n            if ($UserCreationAuditingValue -ne 1 -and $UserCreationAuditingValue -ne 3) {\r\n                Write-Host -Object \"[Error] Auditing is not enabled for User Account Management. Please enable it to track new user creation events.\"\r\n                Write-Host -Object \"[Info] After auditing is enabled, only new user creation events after the change will be tracked.\"\r\n                Write-Host -Object \"[Info] To enable auditing, run the following command in an elevated PowerShell\/cmd session:\"\r\n                Write-Host -Object \"[Info] auditpol.exe \/set \/subcategory:'{0CCE9235-69AE-11D9-BED3-505054503030}' \/success:enable\"\r\n                exit 1\r\n            }\r\n        }\r\n        else {\r\n            Write-Host -Object \"[Error] Failed to check if auditing is enabled for User Account Management. This is usually due to the script not running as the SYSTEM account.\"\r\n            exit 1\r\n        }\r\n\r\n        # Get new users from the event log\r\n        # Event ID 4720 corresponds to \"A user account was created\"\r\n        $eventLog = @(Get-WinEvent -FilterHashtable @{LogName = \"Security\"; Id = 4720; StartTime = $startDateTime } -ErrorAction SilentlyContinue)\r\n\r\n        # Process each event log entry to extract the user information\r\n        $newUsers = foreach ($event in $eventLog) {\r\n            # Convert the event to an XML object\r\n            $eventXML = [xml]$event.ToXml()\r\n\r\n            # Extract the username from the XML\r\n            $userName = $eventXml.Event.EventData.Data | Where-Object { $_.Name -eq \"TargetUserName\" } | Select-Object -ExpandProperty '#text'\r\n\r\n            # If a username is found, attempt to retrieve the local user's full name from their username\r\n            # Otherwise, error and continue to the next event log entry\r\n            if ($userName) {\r\n                try {\r\n                    $localUser = Get-LocalUser -Name $UserName -ErrorAction Stop\r\n\r\n                    # Determine the status of the user account\r\n                    $status = if ($localUser.Enabled) { \"Enabled\" } else { \"Disabled\" }\r\n\r\n                    if (-not [string]::IsNullOrWhiteSpace($localUser.FullName)) {\r\n                        $fullName = $localUser.FullName\r\n                    }\r\n                    elseif (-not [string]::IsNullOrWhiteSpace($localUser.Name)) {\r\n                        $fullName = $localUser.Name\r\n                    }\r\n                    else {\r\n                        $fullName = \"Unknown\"\r\n                    }\r\n                }\r\n                catch [Microsoft.PowerShell.Commands.UserNotFoundException] {\r\n                    # If the user is not found, it may have been deleted or renamed, so set the status to \"No Longer Exists\"\r\n                    $fullName = \"Unknown\"\r\n                    $Status = \"No Longer Exists\"\r\n                }\r\n                catch {\r\n                    Write-Host -Object \"[Error] Unable to retrieve the full name of the user '$userName'.\"\r\n                    Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n                    $fullName = \"Unknown\"\r\n                    $Status = \"Unknown\"\r\n                    $ExitCode = 1\r\n                }\r\n\r\n                # Create a custom object with the user information\r\n                [PSCustomObject]@{\r\n                    UserName    = $userName\r\n                    Name        = $fullName\r\n                    Status      = $status\r\n                    WhenCreated = $event.TimeCreated.ToShortDateString() + \" \" + $event.TimeCreated.ToLongTimeString()\r\n                }\r\n            }\r\n            else {\r\n                Write-Host -Object \"[Error] Failed to extract the username from the event data.\"\r\n                $ExitCode = 1\r\n                continue\r\n            }\r\n        }\r\n    }\r\n\r\n    # If new users are found not from AD, filter out any duplicate users in the list, keeping the most recently created instance only\r\n    if ($newUsers.Count -gt 1 -and -not $IsDomainController) {\r\n        # Group the users by username and select the most recent entry for each group\r\n        $newUsers = $newUsers | Group-Object -Property UserName | ForEach-Object {\r\n            $_.Group | Sort-Object -Property WhenCreated -Descending | Select-Object -First 1\r\n        }\r\n    }\r\n\r\n    # Check if any new users were found\r\n    if ($newUsers) {\r\n        Write-Host -Object \"`n[Alert] New users have been created within the last $TimeFrameInMinutes minutes:`n\"\r\n\r\n        # Format the new users object for output, showing the Enabled accounts first, then the Disabled accounts\r\n        $newUsers = $newUsers | Sort-Object -Property { $_.Status -eq \"Enabled\"; $_.Status -eq \"Disabled\" }, WhenCreated -Descending | Select-Object @{\r\n            Name       = \"Username\"\r\n            Expression = { if ($IsDomainController) { $_.SamAccountName } else { $_.UserName } }\r\n        }, @{\r\n            Name       = \"Full Name\"\r\n            Expression = { $_.Name }\r\n        }, @{\r\n            Name       = \"Created On\"\r\n            Expression = { $_.WhenCreated }\r\n        }, Status\r\n\r\n        # Format it as a list and trim the whitespace\r\n        $newUsers = ($newUsers | Format-List | Out-String).Trim()\r\n\r\n        # Output to the activity feed\r\n        $newUsers | Out-Host\r\n    }\r\n    elseif ($MultilineCustomFieldName) {\r\n        Write-Host -Object \"[Info] No new users have been created within the last $TimeFrameInMinutes minutes. The custom field '$MultilineCustomFieldName' will not be modified.\"\r\n    }\r\n    else {\r\n        Write-Host -Object \"[Info] No new users have been created within the last $TimeFrameInMinutes minutes.\"\r\n    }\r\n\r\n    # Write to custom field if there are new users\r\n    if ($newUsers -and $MultilineCustomFieldName) {\r\n        try {\r\n            Write-Host -Object \"`n[Info] Attempting to set the custom field '$MultilineCustomFieldName'.\"\r\n\r\n            Set-NinjaPropertyValue -Name $MultilineCustomFieldName -Value $newUsers -Type \"MultiLine\"\r\n\r\n            Write-Host -Object \"[Info] Successfully set the custom field '$MultilineCustomFieldName'!\"\r\n        }\r\n        catch {\r\n            Write-Host -Object \"[Error] Error setting the custom field '$MultilineCustomFieldName'.\"\r\n            Write-Host -Object \"[Error] $($_.Exception.Message)\"\r\n            $ExitCode = 1\r\n        }\r\n    }\r\n\r\n    exit $ExitCode\r\n}\r\n\r\nend {\r\n    \r\n    \r\n    \r\n}<\/pre>\n<p>&nbsp;<\/p>\n\n<h2>Description d\u00e9taill\u00e9e<\/h2>\n<p>Voici comment fonctionne le script, divis\u00e9 en \u00e9tapes logiques :<\/p>\n<h3>1. Validation des entr\u00e9es<\/h3>\n<ul>\n<li>Accepte deux param\u00e8tres :\n<ul>\n<li><strong><code>TimeFrameInMinutes<\/code><\/strong>: La fen\u00eatre de temps \u00e0 v\u00e9rifier (de 5 minutes \u00e0 1 an).<\/li>\n<li><strong><code>MultilineCustomFieldName<\/code><\/strong>: Le champ personnalis\u00e9 NinjaOne pour stocker les r\u00e9sultats (facultatif).<\/li>\n<\/ul>\n<\/li>\n<li>Effectue des v\u00e9rifications strictes concernant la configuration minimale du syst\u00e8me d&rsquo;exploitation (Windows 10\/Server 2016), la version de PowerShell, le formatage appropri\u00e9 et les autorisations (doit \u00eatre ex\u00e9cut\u00e9 en tant que SYSTEM et avec des droits d&rsquo;administrateur).<\/li>\n<\/ul>\n<h3>2. D\u00e9tection de l&rsquo;environnement<\/h3>\n<ul>\n<li>D\u00e9termine si le syst\u00e8me :\n<ul>\n<li>Est reli\u00e9 \u00e0 un domaine.<\/li>\n<li>Est un contr\u00f4leur de domaine (DC).<\/li>\n<li>L&rsquo;audit de la gestion des comptes d&rsquo;utilisateurs est activ\u00e9 (important pour les non-DC).<\/li>\n<\/ul>\n<\/li>\n<li>En fonction de l&rsquo;environnement, il choisit entre Active Directory et les journaux d&rsquo;\u00e9v\u00e9nements locaux comme source de donn\u00e9es.<\/li>\n<\/ul>\n<h3>3. D\u00e9tection d&rsquo;un nouvel utilisateur<\/h3>\n<ul>\n<li><strong>Mode de contr\u00f4leur de domaine<\/strong>:\n<ul>\n<li>Recherche dans Active Directory tous les utilisateurs cr\u00e9\u00e9s apr\u00e8s la date de d\u00e9but calcul\u00e9e <strong><code>date de d\u00e9but<\/code><\/strong>.<\/li>\n<li>V\u00e9rifie \u00e9ventuellement la corbeille AD pour les utilisateurs r\u00e9cemment cr\u00e9\u00e9s et supprim\u00e9s.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Mode autonome\/non DC :<\/strong>\n<ul>\n<li>Lit le journal des \u00e9v\u00e9nements de Windows Security pour l&rsquo;ID d&rsquo;\u00e9v\u00e9nement 4720 (cr\u00e9ation d&rsquo;un compte d&rsquo;utilisateur).<\/li>\n<li>Extrait les noms d&rsquo;utilisateur et les enrichit avec des d\u00e9tails provenant de <strong><code>Get-LocalUser<\/code><\/strong>.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3>4. Formatage et enregistrement des donn\u00e9es<\/h3>\n<ul>\n<li>Les r\u00e9sultats sont pr\u00e9sent\u00e9s sous forme d&rsquo;objets propres et lisibles :\n<ul>\n<li>Nom d&rsquo;utilisateur<\/li>\n<li>Nom complet<\/li>\n<li>Le temps de la cr\u00e9ation<\/li>\n<li>Statut du compte (activ\u00e9, d\u00e9sactiv\u00e9, supprim\u00e9)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3>5. Int\u00e9gration de NinjaOne<\/h3>\n<ul>\n<li>Si un champ personnalis\u00e9 valide est fourni, il utilise la fonction <strong><code>Set-NinjaPropertyValue<\/code><\/strong> pour enregistrer les donn\u00e9es dans la plateforme NinjaOne.<\/li>\n<li>Veille \u00e0 ce qu&rsquo;aucune modification ne soit apport\u00e9e aux champs personnalis\u00e9s si aucun utilisateur n&rsquo;est trouv\u00e9.<\/li>\n<\/ul>\n<h2>Cas d&rsquo;utilisation potentiels<\/h2>\n<h3>\u00c9tude de cas\u00a0: Environnements du domaine de surveillance MSP<\/h3>\n<p>Imaginez qu&rsquo;une entreprise MSP g\u00e8re un domaine pour un client du secteur financier. Il d\u00e9ploit ce script pour qu&rsquo;il s&rsquo;ex\u00e9cute toutes les heures sur tous les contr\u00f4leurs de domaine via NinjaOne. Un jour, le script les avertit qu&rsquo;un utilisateur nomm\u00e9 <strong><code>jsmith_temp<\/code><\/strong> a \u00e9t\u00e9 cr\u00e9\u00e9 au cours des 30 derni\u00e8res minutes. Cette situation \u00e9tait inattendue et, apr\u00e8s enqu\u00eate, ils ont d\u00e9couvert qu&rsquo;un outil d&rsquo;automatisation des ressources humaines avait mal fonctionn\u00e9 pendant l&rsquo;onboarding. Sans ce script, ce compte aurait pu passer inaper\u00e7u et pr\u00e9senter un risque potentiel.<\/p>\n<h2>Comparaisons<\/h2>\n<table>\n<tbody>\n<tr>\n<td><strong>M\u00e9thode<\/strong><\/td>\n<td><strong>Avantages<\/strong><\/td>\n<td><strong>Inconv\u00e9nients<\/strong><\/td>\n<\/tr>\n<tr>\n<td><strong>Ce script PowerShell<\/strong><\/td>\n<td>Journalisation centralis\u00e9e, int\u00e9gration de NinjaOne, prise en charge \u00e0 la fois les DC et en local<\/td>\n<td>N\u00e9cessite une ex\u00e9cution au niveau du syst\u00e8me, une configuration pour l&rsquo;audit<\/td>\n<\/tr>\n<tr>\n<td>Surveillance manuelle du journal des \u00e9v\u00e9nements<\/td>\n<td>Natif, aucun script n&rsquo;est n\u00e9cessaire<\/td>\n<td>N\u00e9cessite beaucoup de main-d&rsquo;\u0153uvre, manque d&rsquo;automatisation<\/td>\n<\/tr>\n<tr>\n<td>Outils SIEM (par exemple, Splunk)<\/td>\n<td>\u00c9volutif, facile \u00e0 corr\u00e9ler<\/td>\n<td>Configuration co\u00fbteuse et complexe<\/td>\n<\/tr>\n<tr>\n<td>Alertes \u00e9lectroniques AD<\/td>\n<td>Simple<\/td>\n<td>Pas de suivi historique, manque de formatage<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>En comparaison, le script PowerShell offre une solution interm\u00e9diaire abordable et extensible, id\u00e9ale pour les MSP et les entreprises de taille moyenne.<\/p>\n<h2>Questions fr\u00e9quentes<\/h2>\n<h3>Question 1\u00a0: Dois-je l&rsquo;ex\u00e9cuter en tant que SYST\u00c8ME ?<\/h3>\n<p>Oui. Ce script n\u00e9cessite des autorisations de niveau SYST\u00c8ME pour acc\u00e9der aux journaux d&rsquo;\u00e9v\u00e9nements et aux champs personnalis\u00e9s de NinjaOne.<\/p>\n<h3>Question 2\u00a0: Que se passe-t-il si l&rsquo;audit n&rsquo;est pas activ\u00e9 ?<\/h3>\n<p>Sur les syst\u00e8mes non CD, le script v\u00e9rifie et vous indique comment activer l&rsquo;audit \u00e0 l&rsquo;aide de <strong><code>auditpol.exe<\/code><\/strong>.<\/p>\n<h3>Question 3\u00a0: Fonctionne-t-il sur les machines d&rsquo;un groupe de travail ?<\/h3>\n<p>Oui, tant que l&rsquo;audit est activ\u00e9 et que le syst\u00e8me r\u00e9pond aux exigences du syst\u00e8me d&rsquo;exploitation.<\/p>\n<h3>Question 4\u00a0: Puis-je programmer cela avec NinjaOne ?<\/h3>\n<p>Absolument. Il est con\u00e7u pour fonctionner via le module de script de NinjaOne avec des param\u00e8tres et peut envoyer des alertes vers des champs personnalis\u00e9s pour faciliter la cr\u00e9ation de rapports.<\/p>\n<h2>Implications<\/h2>\n<p>L&rsquo;ex\u00e9cution r\u00e9guli\u00e8re de ce script PowerShell introduit une surveillance proactive des comptes dans votre dispositif de s\u00e9curit\u00e9. L&rsquo;identification des cr\u00e9ations de comptes inattendues peut aider \u00e0 pr\u00e9venir l&rsquo;escalade des privil\u00e8ges, \u00e0 maintenir la conformit\u00e9 <a href=\"https:\/\/www.ninjaone.com\/blog\/hipaa-compliance\/\">(<\/a><a href=\"https:\/\/www.ninjaone.com\/blog\/hipaa-compliance\/\">HIPAA<\/a><a href=\"https:\/\/www.ninjaone.com\/blog\/hipaa-compliance\/\">, SOX, etc.) et \u00e0 soutenir les audits internes. Lorsqu&rsquo;il est int\u00e9gr\u00e9 \u00e0 NinjaOne, ce script permet une visibilit\u00e9 centralis\u00e9e et coh\u00e9rente sur les appareils et les locataires, ce qui permet aux MSP de r\u00e9agir plus rapidement et en toute confiance.<\/a><\/p>\n<h2>Recommandations<\/h2>\n<ul>\n<li><strong>Ex\u00e9cuter en tant que SYST\u00c8ME<\/strong>: Veillez \u00e0 ex\u00e9cuter le script avec les privil\u00e8ges SYST\u00c8ME, ce qui est particuli\u00e8rement important pour acc\u00e9der aux journaux de s\u00e9curit\u00e9.<\/li>\n<li><strong>Param\u00e8tres d&rsquo;audit<\/strong>: Pour obtenir des donn\u00e9es fiables, activez toujours l&rsquo;audit de la <strong>gestion des comptes d&rsquo;utilisateurs<\/strong> sur les syst\u00e8mes locaux.<\/li>\n<li><strong>Validation des champs<\/strong>: Utilisez uniquement des noms alphanum\u00e9riques pour les champs personnalis\u00e9s de NinjaOne afin d&rsquo;\u00e9viter les probl\u00e8mes de format.<\/li>\n<li><strong>Ajustement du cadre temporel<\/strong>: Ajuster <strong><code>TimeFrameInMinutes<\/code><\/strong> en fonction de la fr\u00e9quence du script : par exemple 1440 pour une fr\u00e9quence quotidienne, 60 pour une fr\u00e9quence horaire.<\/li>\n<li><strong>Gestion des erreurs<\/strong>\u00a0: Surveillez les journaux de sortie du script \u00e0 la recherche de <strong><code>[Erreur]<\/code><\/strong> pour d\u00e9tecter les erreurs de configuration.<\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>La cr\u00e9ation d&rsquo;un nouveau compte utilisateur est une op\u00e9ration sensible qui n\u00e9cessite visibilit\u00e9 et contr\u00f4le. Ce script PowerShell offre un moyen intelligent et fiable de cr\u00e9er des alertes de nouvel utilisateur avec un minimum de frais g\u00e9n\u00e9raux. Associ\u00e9 \u00e0 NinjaOne, il devient encore plus puissant : il permet d&rsquo;obtenir des informations exploitables et des historiques sur l&rsquo;ensemble de votre infrastructure. Que vous soyez un administrateur informatique solitaire ou que vous g\u00e9riez une \u00e9quipe au sein d&rsquo;une entreprise MSP, cette solution vous aide \u00e0 garder une longueur d&rsquo;avance en mati\u00e8re de s\u00e9curit\u00e9 des comptes utilisateurs.<\/p>\n","protected":false},"author":35,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"_relevanssi_hide_post":"","_relevanssi_hide_content":"","_relevanssi_pin_for_all":"","_relevanssi_pin_keywords":"","_relevanssi_unpin_keywords":"","_relevanssi_related_keywords":"","_relevanssi_related_include_ids":"","_relevanssi_related_exclude_ids":"","_relevanssi_related_no_append":"","_relevanssi_related_not_related":"","_relevanssi_related_posts":"","_relevanssi_noindex_reason":"","_lmt_disableupdate":"no","_lmt_disable":""},"operating_system":[4212],"use_cases":[4289],"class_list":["post-531428","script_hub","type-script_hub","status-publish","hentry","script_hub_category-windows","use_cases-gestion-des-utilisateurs-et-des-acces"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/script_hub\/531428","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/script_hub"}],"about":[{"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/types\/script_hub"}],"author":[{"embeddable":true,"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/users\/35"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/comments?post=531428"}],"wp:attachment":[{"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/media?parent=531428"}],"wp:term":[{"taxonomy":"script_hub_category","embeddable":true,"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/operating_system?post=531428"},{"taxonomy":"use_cases","embeddable":true,"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/use_cases?post=531428"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}