{"id":534744,"date":"2025-09-30T17:42:59","date_gmt":"2025-09-30T17:42:59","guid":{"rendered":"https:\/\/www.ninjaone.com\/?post_type=script_hub&#038;p=534744"},"modified":"2025-09-30T17:42:59","modified_gmt":"2025-09-30T17:42:59","slug":"creer-une-alerte-de-changement-d-ip-wan-avec-un-script-powershell","status":"publish","type":"script_hub","link":"https:\/\/www.ninjaone.com\/fr\/script-hub\/creer-une-alerte-de-changement-d-ip-wan-avec-un-script-powershell\/","title":{"rendered":"Comment cr\u00e9er une alerte de changement d&rsquo;IP WAN avec un script PowerShell"},"content":{"rendered":"<p>De nos jours, avec les environnements informatiques dispers\u00e9s, il est essentiel de comprendre et de v\u00e9rifier l&rsquo;adresse IP WAN d&rsquo;un appareil pour maintenir la s\u00e9curit\u00e9, les politiques de <a href=\"https:\/\/www.ninjaone.com\/fr\/it-hub\/remote-access\/georeperage\/\">g\u00e9ofencing<\/a> et les exigences de conformit\u00e9. Un d\u00e9calage entre l&rsquo;IP WAN pr\u00e9vue et actuelle peut indiquer une mauvaise configuration du <a href=\"https:\/\/www.ninjaone.com\/fr\/it-hub\/endpoint-security\/qu-est-ce-qu-un-vpn-reseau-prive-virtuel\/\">VPN<\/a>, un changement de r\u00e9seau non autoris\u00e9 ou m\u00eame un appareil compromis. L&rsquo;automatisation de la surveillance des IP WAN \u00e0 l&rsquo;aide de <a href=\"https:\/\/www.ninjaone.com\/it-hub\/endpoint-management\/what-is-powershell\/\">PowerShell<\/a> permet aux professionnels de l&rsquo;informatique et aux <a href=\"https:\/\/www.ninjaone.com\/fr\/quest-ce-quun-msp\/\">fournisseurs de services g\u00e9r\u00e9s (MSP)<\/a> de rester proactifs sans avoir \u00e0 v\u00e9rifier manuellement chaque terminal.<\/p>\n<p>Cet article pr\u00e9sente un script PowerShell con\u00e7u pour cr\u00e9er des alertes de changement d&rsquo;IP WAN, afin de s&rsquo;assurer que les appareils se trouvent l\u00e0 o\u00f9 ils sont cens\u00e9s \u00eatre, virtuellement parlant.<\/p>\n<h2>Contexte<\/h2>\n<p>Les entreprises d\u00e9ploient souvent des appareils dans des bureaux distants, des installations de travail \u00e0 domicile ou m\u00eame \u00e0 l&rsquo;\u00e9tranger. Avec une telle d\u00e9centralisation, le p\u00e9rim\u00e8tre du r\u00e9seau devient fluide et le suivi des adresses IP externes devient \u00e0 la fois crucial et difficile.<\/p>\n<p>Bien que les pare-feux d&rsquo;entreprise et les outils <a href=\"https:\/\/www.ninjaone.com\/it-hub\/endpoint-security\/what-is-security-information-and-event-management-siem\/\">SIEM<\/a> offrent un certain niveau de <a href=\"https:\/\/www.ninjaone.com\/fr\/it-hub\/remote-access\/qu-est-ce-que-le-suivi-de-la-geolocalisation\/\">g\u00e9olocalisation<\/a> ou d&rsquo;alertes bas\u00e9es sur l&rsquo;IP, ils sont g\u00e9n\u00e9ralement surdimensionn\u00e9s ou trop centralis\u00e9s pour de nombreuses PME ou des terminaux dispers\u00e9s. C&rsquo;est l\u00e0 qu&rsquo;intervient ce script d&rsquo;alerte de changement d&rsquo;IP WAN bas\u00e9 sur PowerShell. L\u00e9ger, compatible avec NinjaOne et hautement configurable, il permet aux administrateurs informatiques de v\u00e9rifier les IP WAN par rapport aux valeurs attendues et de stocker ces informations dans des champs personnalis\u00e9s \u00e0 des fins d&rsquo;audit ou d&rsquo;historique.<\/p>\n<h2>Le script<\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\">#Requires -Version 3\r\n\r\n&lt;#\r\n.SYNOPSIS\r\n    Alert when a device's current WAN IP address deviates from expected IP(s). Expected IP(s) can be provided at runtime or via a Custom Field. Multiple IPs should be comma-separated.\r\n\r\n.DESCRIPTION\r\n    Alert when a device's current WAN IP address deviates from expected IP(s). Expected IP(s) can be provided at runtime or via a Custom Field. Multiple IPs should be comma-separated.\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 -ExpectedWANIP\r\n    Enter the expected WAN IP(s) for the device. CIDR blocks are not supported. If providing multiple expected IPs, they should be comma-separated, for example: 150.23.12.13,150.23.12.14\r\n\r\n.PARAMETER -ExpectedWANIPFromCustomFieldName \r\n    Name of the Custom Field to retrieve the expected WAN IP(s) from. CIDR blocks are not supported. If storing multiple IPs in this field, they should be comma-separated, for example: 150.23.12.13,150.23.12.14\r\n    \r\n.PARAMETER -WANIPHistoryCustomFieldName\r\n    Name of the Custom Field where the current IP address will be written. If the field is already populated, the current IP will be appended to this field with a timestamp.\r\n\r\n.EXAMPLE\r\n    -ExpectedWANIP \"10.0.0.2\",\"104.190.27.83\"\r\n    \r\n    [Info] Checking IP(s) entered at runtime.\r\n\r\n    [Info] Testing 10.0.0.2:\r\n    [Info] 10.0.0.2 is a valid IP address.\r\n\r\n    [Info] Testing 104.190.27.83:\r\n    [Info] 104.190.27.83 is a valid IP address.\r\n\r\n    [Info] Current IP address: 104.190.27.83\r\n    [Info] 104.190.27.83 is an expected IP address for WIN10PRO-1809!\r\n\r\n.EXAMPLE\r\n    -ExpectedWANIP \"129.200.10.21\"\r\n    \r\n    [Info] Checking IP(s) entered at runtime.\r\n\r\n    [Info] Testing 129.200.10.21:\r\n    [Info] 129.200.10.21 is a valid IP address.\r\n\r\n    [Info] Current IP address: 104.190.27.83\r\n    [Alert] No expected IPs match the current IP 104.190.27.83.\r\n\r\n.EXAMPLE\r\n    -ExpectedWANIP \"104.190.27.83\" -WANIPHistoryCustomFieldName \"Multiline\"\r\n    \r\n    [Info] Checking IP(s) entered at runtime.\r\n\r\n    [Info] Testing 104.190.27.83:\r\n    [Info] 104.190.27.83 is a valid IP address.\r\n\r\n    [Info] Current IP address: 104.190.27.83\r\n    [Info] 104.190.27.83 is an expected IP address for WIN10PRO-1809!\r\n\r\n    [Info] Writing current IP to custom field 'Multiline'\r\n    [Info] Successfully wrote to custom field 'Multiline':\r\n    2025-01-24 10:33:32 AM | 104.190.27.83\r\n\r\n.EXAMPLE\r\n    -ExpectedWANIPFromCustomFieldName \"Text\"\r\n\r\n    [Info] Retrieving data from 'Text' custom field.\r\n    [Info] Checking IP(s) retrieved from custom field 'Text'.\r\n\r\n    [Info] Testing 100.0.2.2:\r\n    [Info] 100.0.2.2 is a valid IP address.\r\n\r\n    [Info] Testing 104.190.27.83:\r\n    [Info] 104.190.27.83 is a valid IP address.\r\n\r\n    [Info] Current IP address: 104.190.27.83\r\n    [Info] 104.190.27.83 is an expected IP address for WIN10PRO-1809!\r\n\r\n.EXAMPLE \r\n    -ExpectedWANIPFromCustomFieldName \"Text\" -WANIPHistoryCustomFieldName \"Multiline\"\r\n\r\n    [Info] Retrieving data from 'Text' custom field.\r\n    [Info] Checking IP(s) retrieved from custom field 'Text'.\r\n\r\n    [Info] Testing 100.0.2.2:\r\n    [Info] 100.0.2.2 is a valid IP address.\r\n\r\n    [Info] Testing 104.190.27.83:\r\n    [Info] 104.190.27.83 is a valid IP address.\r\n\r\n    [Info] Current IP address: 104.190.27.83\r\n    [Info] 104.190.27.83 is an expected IP address for WIN10PRO-1809!\r\n\r\n    [Info] Writing current IP to custom field 'Multiline'\r\n    [Info] Successfully wrote to custom field 'Multiline':\r\n    2025-01-24 10:34:34 AM | 104.190.27.83\r\n\r\n.NOTES\r\n    Minimum OS Architecture Supported: Windows 8, Windows Server 2012\r\n    Release Notes: Initial Release\r\n#&gt;\r\n\r\n[CmdletBinding()]\r\nparam (\r\n    [Parameter()]\r\n    [string[]]$ExpectedWANIP,\r\n\r\n    [Parameter()]\r\n    [string]$ExpectedWANIPFromCustomFieldName,\r\n\r\n    [Parameter()]\r\n    [string]$WANIPHistoryCustomFieldName\r\n)\r\n\r\nbegin {\r\n    function Test-IsElevated {\r\n        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()\r\n        $p = New-Object System.Security.Principal.WindowsPrincipal($id)\r\n        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)\r\n    }\r\n\r\n    if ($env:ExpectedWANIP -and $env:ExpectedWANIP -ne 'null'){\r\n        $ExpectedWANIP = $env:ExpectedWANIP\r\n    }\r\n\r\n    if ($env:ExpectedWANIPFromCustomFieldName -and $env:ExpectedWANIPFromCustomFieldName -ne 'null'){\r\n        $ExpectedWANIPFromCustomFieldName = $env:ExpectedWANIPFromCustomFieldName\r\n    }\r\n\r\n    if ($env:WANIPHistoryCustomFieldName -and $env:WANIPHistoryCustomFieldName -ne 'null'){\r\n        $WANIPHistoryCustomFieldName = $env:WANIPHistoryCustomFieldName\r\n    }\r\n\r\n    if (-not $ExpectedWANIP -and -not $ExpectedWANIPFromCustomFieldName){\r\n        Write-Host \"[Error] Please provide either the WAN IP address you are expecting this machine to have, or the name of a custom field to retrieve the expected IPs from.\"\r\n        exit 1\r\n    }\r\n\r\n    function Test-IPAddress {\r\n        param(\r\n            [string]$IP\r\n        )\r\n\r\n        Write-Host \"[Info] Testing $IP`:\"\r\n        switch ($IP) {\r\n            # check IP for a slash, which indicates CIDR notation was used\r\n            {$_ -match \"\/\"} {\r\n                Write-Host \"[Error] This script does not support CIDR notation. If you need to expect multiple IP addresses, you can use commas to separate them.\"\r\n                exit 1\r\n            }\r\n            # check IP for invalid characters (anything other than a digit or a period) \r\n            {$_ -match \"[^\\d\\.]\"} {\r\n                Write-Host \"[Error] Invalid characters detected in IP address.\"\r\n                exit 1\r\n            }\r\n            # check if partial IP address was provided\r\n            {$_ -notmatch \"\\d+\\.\\d+\\.\\d+\\.\\d+\"} {\r\n                Write-Host \"[Error] Partial IP addresses are not allowed. Please include all 4 octets in each expected IP.\"\r\n                exit 1\r\n            }\r\n        }\r\n\r\n        # validate the IP by casting to [ipaddress], if it does not error then it should be a valid IP\r\n        try{\r\n            $ReturnIP = ([ipaddress]$IP).IPAddressToString\r\n        }\r\n        catch{\r\n            Write-Host \"[Error] $IP is not a valid IP address. Please make sure all expected IP addresses are valid, and remove any leading zeros.\"\r\n            exit 1\r\n        }\r\n        \r\n        # in some cases, the above will not fail but the ReturnIP value will not match the input IP exactly\r\n        # if these values don't match exactly, this is likely due to leading zeros in the given IP, as ReturnIP will not have any leading zeros\r\n        if ($ReturnIP -ne $IP){\r\n            Write-Host \"[Error] Error while testing $IP. This is likely due to the IP address containing leading zeros. Please remove any leading zeros in your expected IP addresses.\"\r\n            exit 1\r\n        }\r\n\r\n        Write-Host \"[Info] $IP is a valid IP address.`n\"\r\n        return $ReturnIP\r\n    }\r\n\r\n    function Get-NinjaProperty {\r\n        [CmdletBinding()]\r\n        Param(\r\n            [Parameter(Mandatory = $True, ValueFromPipeline = $True)]\r\n            [String]$Name,\r\n            [Parameter()]\r\n            [String]$Type,\r\n            [Parameter()]\r\n            [String]$DocumentName\r\n        )\r\n        \r\n        # Initialize a hashtable for 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 types that require options to be retrieved\r\n        $NeedsOptions = \"DropDown\", \"MultiSelect\"\r\n        \r\n        # If a document name is provided, retrieve the property value from the document\r\n        if ($DocumentName) {\r\n            # Throw an error if the type is \"Secure\", as it's not a valid type in this context\r\n            if ($Type -Like \"Secure\") { throw [System.ArgumentOutOfRangeException]::New(\"$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            # Notify the user that the value is being retrieved from a Ninja document\r\n            Write-Host \"Retrieving value from Ninja Document...\"\r\n            $NinjaPropertyValue = Ninja-Property-Docs-Get -AttributeName $Name @DocumentationParams 2&gt;&amp;1\r\n        \r\n            # If the property type requires options, retrieve them\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 no document name is provided, retrieve the property value directly\r\n            $NinjaPropertyValue = Ninja-Property-Get -Name $Name 2&gt;&amp;1\r\n    \r\n            # If the property type requires options, retrieve them\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 exception if there was an error retrieving the property value or options\r\n        if ($NinjaPropertyValue.Exception) { throw $NinjaPropertyValue }\r\n        if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions }\r\n        \r\n        # Handle the property value based on its type\r\n        switch ($Type) {\r\n            \"Attachment\" {\r\n                # Convert JSON formatted property value to a PowerShell object\r\n                $NinjaPropertyValue | ConvertFrom-Json\r\n            }\r\n            \"Checkbox\" {\r\n                # Convert the value to a boolean\r\n                [System.Convert]::ToBoolean([int]$NinjaPropertyValue)\r\n            }\r\n            \"Date or Date Time\" {\r\n                # Convert a Unix timestamp to local date and time\r\n                $UnixTimeStamp = $NinjaPropertyValue\r\n                $UTC = (Get-Date \"1970-01-01 00:00:00\").AddSeconds($UnixTimeStamp)\r\n                $TimeZone = [TimeZoneInfo]::Local\r\n                [TimeZoneInfo]::ConvertTimeFromUtc($UTC, $TimeZone)\r\n            }\r\n            \"Decimal\" {\r\n                # Convert the value to a double (floating-point number)\r\n                [double]$NinjaPropertyValue\r\n            }\r\n            \"Device Dropdown\" {\r\n                # Convert JSON formatted property value to a PowerShell object\r\n                $NinjaPropertyValue | ConvertFrom-Json\r\n            }\r\n            \"Device MultiSelect\" {\r\n                # Convert JSON formatted property value to a PowerShell object\r\n                $NinjaPropertyValue | ConvertFrom-Json\r\n            }\r\n            \"Dropdown\" {\r\n                # Convert options to a CSV format and match the GUID to retrieve the display name\r\n                $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header \"GUID\", \"Name\"\r\n                $Options | Where-Object { $_.GUID -eq $NinjaPropertyValue } | Select-Object -ExpandProperty Name\r\n            }\r\n            \"Integer\" {\r\n                # Convert the value to an integer\r\n                [int]$NinjaPropertyValue\r\n            }\r\n            \"MultiSelect\" {\r\n                # Convert options to a CSV format, then match and return selected items\r\n                $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header \"GUID\", \"Name\"\r\n                $Selection = ($NinjaPropertyValue -split ',').trim()\r\n        \r\n                foreach ($Item in $Selection) {\r\n                    $Options | Where-Object { $_.GUID -eq $Item } | Select-Object -ExpandProperty Name\r\n                }\r\n            }\r\n            \"Organization Dropdown\" {\r\n                # Convert JSON formatted property value to a PowerShell object\r\n                $NinjaPropertyValue | ConvertFrom-Json\r\n            }\r\n            \"Organization Location Dropdown\" {\r\n                # Convert JSON formatted property value to a PowerShell object\r\n                $NinjaPropertyValue | ConvertFrom-Json\r\n            }\r\n            \"Organization Location MultiSelect\" {\r\n                # Convert JSON formatted property value to a PowerShell object\r\n                $NinjaPropertyValue | ConvertFrom-Json\r\n            }\r\n            \"Organization MultiSelect\" {\r\n                # Convert JSON formatted property value to a PowerShell object\r\n                $NinjaPropertyValue | ConvertFrom-Json\r\n            }\r\n            \"Time\" {\r\n                # Convert the value from seconds to a time format in the local timezone\r\n                $Seconds = $NinjaPropertyValue\r\n                $UTC = ([timespan]::fromseconds($Seconds)).ToString(\"hh\\:mm\\:ss\")\r\n                $TimeZone = [TimeZoneInfo]::Local\r\n                $ConvertedTime = [TimeZoneInfo]::ConvertTimeFromUtc($UTC, $TimeZone)\r\n        \r\n                Get-Date $ConvertedTime -DisplayHint Time\r\n            }\r\n            default {\r\n                # For any other types, return the raw value\r\n                $NinjaPropertyValue\r\n            }\r\n        }\r\n    }\r\n\r\n    function Set-NinjaProperty {\r\n        [CmdletBinding()]\r\n        Param(\r\n            [Parameter(Mandatory = $True)]\r\n            [String]$Name,\r\n            [Parameter()]\r\n            [String]$Type,\r\n            [Parameter(Mandatory = $True, ValueFromPipeline = $True)]\r\n            $Value,\r\n            [Parameter()]\r\n            [String]$DocumentName,\r\n            [Parameter()]\r\n            [Switch]$Piped\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        # 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 = \"Attachment\", \"Checkbox\", \"Date\", \"Date or Date Time\", \"Decimal\", \"Dropdown\", \"Email\", \"Integer\", \"IP Address\", \"MultiLine\", \"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\"\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                $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            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                Write-Host -Object \"[Error] Failed to set custom field.\"\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\nprocess {\r\n    if (-not (Test-IsElevated)) {\r\n        Write-Host -Object \"[Error] Access Denied. Please run with Administrator privileges.\"\r\n        exit 1\r\n    }\r\n\r\n    # test WAN IP from script variable and store in IPFromRuntime variable\r\n    if ($ExpectedWANIP){\r\n        Write-Host \"[Info] Checking IP(s) entered at runtime.`n\"\r\n        # separate and sanitize IPs from script variable, in case they have quotes around them or spaces \r\n        $ExpectedWANIP = ($ExpectedWANIP -split \",\").Trim(' \"')\r\n\r\n        # create empty list to add each valid IP to\r\n        $IPFromRuntime = [System.Collections.Generic.List[string]]::New()\r\n\r\n        # add each valid IP to the list variable\r\n        foreach ($IP in $ExpectedWANIP){\r\n            $value = Test-IPAddress $IP\r\n            $IPFromRuntime.Add($value)\r\n        }\r\n    }\r\n\r\n    # test WAN IP from Custom Field and store in IPFromCustomField variable\r\n    if ($ExpectedWANIPFromCustomFieldName){\r\n        try{\r\n            Write-Host \"[Info] Retrieving data from '$ExpectedWANIPFromCustomFieldName' custom field.\"\r\n            $CustomFieldValue = Get-NinjaProperty -Name $ExpectedWANIPFromCustomFieldName -Type \"TEXT\"\r\n        }\r\n        catch{\r\n            Write-Host \"[Error] Error retrieving Custom Field data.\"\r\n            Write-Host \"$($_.Exception.Message)\"\r\n            exit 1\r\n        }\r\n\r\n        if ($null -eq $CustomFieldValue){\r\n            Write-Host \"[Error] The custom field '$ExpectedWANIPFromCustomFieldName' is blank. Please make sure this field contains expected IP addresses.\"\r\n            exit 1\r\n        }\r\n        \r\n        Write-Host \"[Info] Checking IP(s) retrieved from custom field '$ExpectedWANIPFromCustomFieldName'.`n\"\r\n        # separate and sanitize IPs retrieved from Custom Field, in case they have quotes around them or spaces\r\n        $CustomFieldValue = ($CustomFieldValue -split \",\").Trim(' \"')\r\n\r\n        # create empty list to add each valid IP to\r\n        $IPFromCustomField = [System.Collections.Generic.List[string]]::New()\r\n\r\n        # add each valid IP to the list variable\r\n        foreach ($IP in $CustomFieldValue){\r\n            $value = Test-IPAddress $IP\r\n            $IPFromCustomField.Add($value)\r\n        }\r\n    }\r\n\r\n    # if IPs were provided in both the custom field and script variable, and they are not the exact same, error out\r\n    # if Compare-Object has any output, it means there are differences between the two objects\r\n    if (\r\n        ($IPFromRuntime -and $IPFromCustomField) -and \r\n        (Compare-Object -ReferenceObject $IPFromRuntime -DifferenceObject $IPFromCustomField)\r\n    ){\r\n        Write-Host \"[Error] IPs differ between the Custom Field value ($($IPFromCustomField -join \", \")) and the expected IPs in the script variable ($($IPFromRuntime -join \", \")). These values must match.\"\r\n        exit 1\r\n    }\r\n    else{\r\n        # otherwise, whether they are the same or only one was provided, create array out of the possible verified IP variables, get the unique values\r\n        $ExpectedIP = (@($IPFromRuntime, $IPFromCustomField) | Select-Object -Unique)\r\n    }\r\n\r\n    # get current IP address\r\n    $CurrentIP = try{\r\n        (Invoke-WebRequest -Uri \"api.ipify.org\" -UseBasicParsing).Content\r\n    }\r\n    catch{\r\n        Write-Host \"[Error] Error retrieving current IP address.\"\r\n        Write-Host \"$($_.Exception.Message)\"\r\n        exit 1\r\n    }\r\n\r\n    Write-Host \"[Info] Current IP address: $CurrentIP\"\r\n\r\n    # check if IP addresses match\r\n    if ($CurrentIP -in $ExpectedIP){\r\n        Write-Host \"[Info] $CurrentIP is an expected IP address for $env:computername!`n\"\r\n    }\r\n    else{\r\n        Write-Host \"[Alert] None of the expected IPs match the current WAN IP address $CurrentIP.`n\"\r\n    }\r\n\r\n    # write current IP to custom field section\r\n    if ($WANIPHistoryCustomFieldName){\r\n        $timestamp = Get-Date -Format \"yyyy-MM-dd\"\r\n\r\n        # read current value to determine how to set the new value\r\n        try{\r\n            $currentValue = Get-NinjaProperty -Name $WANIPHistoryCustomFieldName -ErrorAction Stop\r\n        }\r\n        catch{\r\n            Write-Host \"[Error] Error reading current value of custom field '$WANIPHistoryCustomFieldName'.\"\r\n            Write-Host \"$($_.Exception.Message)\"\r\n            exit 1\r\n        }\r\n\r\n        # set up value to write to the custom field, if any\r\n        if ($currentValue){\r\n            # if the CF does not already contain the current IP in the most recent (topmost) entry, the new value to set should be the new IP with a timestamp at the top, with the rest of the current content of the CF after it\r\n            if (($currentValue | Select-Object -First 1) -notmatch $CurrentIP){\r\n                $valueToSet = $timestamp + \" | $CurrentIP`n\" + ($currentValue -join \"`n\")\r\n            }\r\n            else{\r\n                Write-Host \"[Info] Current IP $CurrentIP is already the most recent IP in the '$WANIPHistoryCustomFieldName' custom field. No new value will be written.\"\r\n            }\r\n        }\r\n        else{\r\n            # if the CF does not have content, we will add the current IP along with a timestamp\r\n            $valueToSet = $timestamp + \" | $CurrentIP\"\r\n        }\r\n\r\n        if ($valueToSet){\r\n            try{\r\n                Write-Host \"[Info] Writing current IP to custom field '$WANIPHistoryCustomFieldName'.\"\r\n                Set-NinjaProperty -Name $WANIPHistoryCustomFieldName -Type \"MULTILINE\" -Value $valueToSet\r\n                Write-Host \"[Info] Successfully wrote to custom field '$WANIPHistoryCustomFieldName'`:\"\r\n                $valueToSet | Write-Host\r\n            }\r\n            catch{\r\n                Write-Host \"[Error] Error writing '$valueToSet' to custom field '$WANIPHistoryCustomFieldName'.\"\r\n                Write-Host \"$($_.Exception.Message)\"\r\n                exit 1\r\n            }\r\n        }\r\n    }\r\n\r\n    exit 0\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 :<\/p>\n<ol>\n<li>\n<h3>Entr\u00e9es et param\u00e8tres<\/h3>\n<ul style=\"list-style-type: disc;\">\n<li><strong>-ExpectedWANIP :<\/strong> Accepte une ou plusieurs adresses IP directement au moment de l&rsquo;ex\u00e9cution.<\/li>\n<li><strong>-ExpectedWANIPFromCustomFieldName :<\/strong> R\u00e9cup\u00e8re les adresses IP attendues d&rsquo;un champ personnalis\u00e9 NinjaOne.<\/li>\n<li><strong>-WANIPHistoryCustomFieldName :<\/strong> Un champ NinjaOne dans lequel les journaux historiques des adresses IP seront ajout\u00e9s.<\/li>\n<\/ul>\n<\/li>\n<li>\n<h3>Validation des PI d&rsquo;entr\u00e9e<\/h3>\n<p>Chaque IP fournie, qu&rsquo;elle provienne de param\u00e8tres ou de champs personnalis\u00e9s, est rigoureusement valid\u00e9e :<\/p>\n<ul style=\"list-style-type: disc;\">\n<li>Pas de notation CIDR<\/li>\n<li>Pas de caract\u00e8res non valides<\/li>\n<li>Doit \u00eatre une adresse <a href=\"https:\/\/www.ninjaone.com\/fr\/it-hub\/remote-access\/qu-est-ce-que-le-protocole-ipv4-definition-vue-d-ensemble\/\">IPv4<\/a> compl\u00e8te<\/li>\n<li>Pas de z\u00e9ros initiaux<\/li>\n<\/ul>\n<\/li>\n<li>\n<h3>D\u00e9tection de l&rsquo;IP du r\u00e9seau \u00e9tendu actuel<\/h3>\n<p>Le script utilise\u00a0<a href=\"https:\/\/api.ipify.org\/\" target=\"_blank\" rel=\"noopener\">api.ipify.org<\/a>\u00a0pour d\u00e9terminer l&rsquo;adresse IP publique actuelle de l&rsquo;appareil.<\/li>\n<li>\n<h3>Logique de comparaison<\/h3>\n<p>Il compare ensuite l&rsquo;adresse IP actuelle du r\u00e9seau \u00e9tendu \u00e0 la liste des adresses IP attendues. Si une correspondance est trouv\u00e9e, un message de confirmation est enregistr\u00e9. Si ce n&rsquo;est pas le cas, un message d&rsquo;alerte est d\u00e9clench\u00e9.<\/li>\n<li>\n<h3>Enregistrement historique<\/h3>\n<p>Si un champ personnalis\u00e9 pour l&rsquo;historique de l&rsquo;IP du r\u00e9seau \u00e9tendu est sp\u00e9cifi\u00e9, l&rsquo;IP actuelle (accompagn\u00e9e d&rsquo;un horodatage) est ajout\u00e9e, \u00e0 moins qu&rsquo;elle n&rsquo;existe d\u00e9j\u00e0 en tant qu&rsquo;entr\u00e9e la plus r\u00e9cente.<\/li>\n<li>\n<h3>Conditions de sortie<\/h3>\n<p>Le script se termine de mani\u00e8re \u00e9l\u00e9gante avec des messages d&rsquo;erreur clairs si :<\/p>\n<ul style=\"list-style-type: disc;\">\n<li>Les deux m\u00e9thodes de saisie (param\u00e8tre et champ personnalis\u00e9) sont en d\u00e9saccord.<\/li>\n<li>Le formatage IP \u00e9choue.<\/li>\n<li>Il n&rsquo;est pas ex\u00e9cut\u00e9 avec des privil\u00e8ges d&rsquo;administrateur.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h2>Cas d&rsquo;utilisation potentiels<\/h2>\n<h3>Cas de figure\u00a0: D\u00e9ploiement d&rsquo;une entreprise MSP multi-site<\/h3>\n<p>Imaginez un fournisseur de services g\u00e9r\u00e9s (MSP) qui g\u00e8re les appareils d&rsquo;un client ayant des bureaux \u00e0 New York, Toronto et Londres. Chaque bureau utilise des adresses IP publiques statiques. Pour s&rsquo;assurer que les appareils n&rsquo;ont pas \u00e9t\u00e9 d\u00e9plac\u00e9s ou expos\u00e9s \u00e0 des r\u00e9seaux non fiables :<\/p>\n<ul>\n<li>Le MSP configure chaque appareil avec une IP attendue via un champ personnalis\u00e9.<\/li>\n<li>Le script est programm\u00e9 pour s&rsquo;ex\u00e9cuter quotidiennement via NinjaOne.<\/li>\n<li>Si un appareil signale une adresse IP inattendue (par exemple, s&rsquo;il est connect\u00e9 \u00e0 un r\u00e9seau Wi-Fi malveillant dans un caf\u00e9), le script alerte le technicien et enregistre l&rsquo;IP dans le champ \u00ab\u00a0historique\u00a0\u00bb.<\/li>\n<\/ul>\n<p>Cela aide l&rsquo;entreprise MSP \u00e0 d\u00e9tecter les comportements de l&rsquo;informatique parall\u00e8le, les modifications non autoris\u00e9es du r\u00e9seau ou les indicateurs pr\u00e9coces d&rsquo;appareils compromis.<\/p>\n<h2>Comparaisons<\/h2>\n<table>\n<tbody>\n<tr>\n<td style=\"text-align: center;\"><strong>M\u00e9thode<\/strong><\/td>\n<td style=\"text-align: center;\"><strong>Avantages<\/strong><\/td>\n<td style=\"text-align: center;\"><strong>Inconv\u00e9nients<\/strong><\/td>\n<\/tr>\n<tr>\n<td><strong>Pare-feu\/SEIM Surveillance IP<\/strong><\/td>\n<td>Alertes centralis\u00e9es en temps r\u00e9el<\/td>\n<td>Co\u00fbteux, non sp\u00e9cifique aux terminaux<\/td>\n<\/tr>\n<tr>\n<td><strong>Contr\u00f4les manuels<\/strong><\/td>\n<td>Simple pour les diagnostics ponctuels<\/td>\n<td>Non extensible ou non reproductible<\/td>\n<\/tr>\n<tr>\n<td><strong>Script PowerShell (cet article)<\/strong><\/td>\n<td>L\u00e9ger, personnalisable, facile \u00e0 int\u00e9grer dans un syst\u00e8me de gestion des ressources humaines comme NinjaOne<\/td>\n<td>N\u00e9cessite des connaissances en mati\u00e8re de script, pas de tableau de bord central \u00e0 moins qu&rsquo;il ne soit int\u00e9gr\u00e9<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Par rapport aux solutions centralis\u00e9es, ce script excelle en termes de portabilit\u00e9 et de simplicit\u00e9, en particulier dans les environnements o\u00f9 les outils d&rsquo;entreprise ne sont pas viables.<\/p>\n<h2>Questions fr\u00e9quentes<\/h2>\n<h3>Q\u00a0: Puis-je utiliser la notation CIDR pour d\u00e9finir les adresses IP attendues ?<\/h3>\n<p>Non. Le script ne prend en charge que les adresses IPv4 individuelles, s\u00e9par\u00e9es par des virgules.<\/p>\n<h3><strong>Q\u00a0: Que se passe-t-il si l&rsquo;adresse IP actuelle correspond, mais dans un format diff\u00e9rent (par exemple, avec des z\u00e9ros en t\u00eate) ?<\/strong><\/h3>\n<p>Le script valide et normalise l&rsquo;entr\u00e9e, assurant une correspondance exacte sans z\u00e9ros en t\u00eate.<\/p>\n<h3><strong>Q\u00a0: Que se passe-t-il si le param\u00e8tre d&rsquo;ex\u00e9cution et le champ personnalis\u00e9 sont tous deux utilis\u00e9s et diff\u00e9rents ?<\/strong><\/h3>\n<p>Le script g\u00e9n\u00e8re une erreur afin d&rsquo;\u00e9viter toute ambigu\u00eft\u00e9 et d&rsquo;assurer la coh\u00e9rence des donn\u00e9es.<\/p>\n<h3><strong>Q\u00a0: \u00c0 quelle fr\u00e9quence dois-je ex\u00e9cuter ce script ?<\/strong><\/h3>\n<p>Dans la plupart des environnements, il suffit de l&rsquo;ex\u00e9cuter une fois par jour ou par d\u00e9marrage.<\/p>\n<h2>Implications<\/h2>\n<p>En surveillant et en enregistrant les changements d&rsquo;IP du r\u00e9seau \u00e9tendu, les entreprises peuvent se faire une id\u00e9e des risques potentiels en mati\u00e8re de s\u00e9curit\u00e9, tels que<\/p>\n<ul>\n<li><strong>D\u00e9placement des appareils :<\/strong>\u00a0D\u00e9placement non autoris\u00e9 de mat\u00e9riel.<\/li>\n<li><strong>\u00c9checs VPN :<\/strong>\u00a0Connexions VPN mal configur\u00e9es ou abandonn\u00e9es.<\/li>\n<li><strong>Usurpation de r\u00e9seau :<\/strong>\u00a0Les appareils rejoignent des r\u00e9seaux malveillants ou malhonn\u00eates.<\/li>\n<\/ul>\n<p>Au fil du temps, le journal historique peut aider \u00e0 \u00e9tablir une corr\u00e9lation entre les changements de propri\u00e9t\u00e9 intellectuelle et les tickets d&rsquo;assistance, les examens de conformit\u00e9 ou les r\u00e9ponses aux incidents.<\/p>\n<h2>Recommandations<\/h2>\n<ul>\n<li><strong>Exploiter les champs personnalis\u00e9s<\/strong> : Utilisez les champs personnalis\u00e9s de NinjaOne pour centraliser les adresses IP attendues et le suivi de l&rsquo;historique.<\/li>\n<li><strong>Automatiser l&rsquo;ex\u00e9cution<\/strong> : Programmez ce script via <a href=\"https:\/\/www.ninjaone.com\/fr\/blog\/definition-de-la-surveillance-et-gestion-a-distance\/\">RMM<\/a> pour qu&rsquo;il s&rsquo;ex\u00e9cute quotidiennement ou sur des d\u00e9clencheurs sp\u00e9cifiques.<\/li>\n<li><strong>Alerte<\/strong>\u00a0: Combinez avec des e-mails ou des d\u00e9clencheurs webhook dans votre plateforme d&rsquo;automatisation pour informer le service informatique lorsque des adresses IP inattendues sont d\u00e9tect\u00e9es.<\/li>\n<li><strong>Documenter les IP attendues<\/strong> : Mainteniez un registre \u00e0 jour des bonnes adresses IP connues afin d&rsquo;\u00e9viter les faux positifs.<\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>Pour les professionnels de l&rsquo;informatique et les MSP qui cherchent \u00e0 am\u00e9liorer la visibilit\u00e9 de leur r\u00e9seau sans d\u00e9ployer de solutions lourdes, ce script PowerShell offre une approche \u00e9l\u00e9gante et pragmatique. Combin\u00e9 \u00e0 l&rsquo;automatisation et aux champs personnalis\u00e9s de NinjaOne, il transforme un contr\u00f4le routinier de l&rsquo;IP WAN en un puissant outil d&rsquo;audit et de s\u00e9curit\u00e9.<\/p>\n<p>Que vous g\u00e9riez des travailleurs \u00e0 distance ou des clients multi-sites, l&rsquo;int\u00e9gration d&rsquo;une alerte de changement d&rsquo;IP WAN dans votre bo\u00eete \u00e0 outils vous permet de ne pas \u00eatre pris au d\u00e9pourvu par des changements silencieux de la posture du r\u00e9seau.<\/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":[4286,4285],"class_list":["post-534744","script_hub","type-script_hub","status-publish","hentry","script_hub_category-windows"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/script_hub\/534744","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=534744"}],"wp:attachment":[{"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/media?parent=534744"}],"wp:term":[{"taxonomy":"script_hub_category","embeddable":true,"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/operating_system?post=534744"},{"taxonomy":"use_cases","embeddable":true,"href":"https:\/\/www.ninjaone.com\/fr\/wp-json\/wp\/v2\/use_cases?post=534744"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}