{"id":353766,"date":"2024-08-30T11:53:09","date_gmt":"2024-08-30T11:53:09","guid":{"rendered":"https:\/\/www.ninjaone.com\/script-hub\/extensiones-de-archivos-powershell\/"},"modified":"2024-10-13T19:08:32","modified_gmt":"2024-10-13T19:08:32","slug":"extensiones-de-archivos-powershell","status":"publish","type":"script_hub","link":"https:\/\/www.ninjaone.com\/es\/script-hub\/extensiones-de-archivos-powershell\/","title":{"rendered":"Gu\u00eda de uso de PowerShell para informes sobre extensiones de archivos en la gesti\u00f3n de TI"},"content":{"rendered":"<p>En el panorama digital actual, la gesti\u00f3n eficaz de archivos es crucial para los profesionales de TI y los <a href=\"https:\/\/www.ninjaone.com\/what-is-an-msp\/\" target=\"_blank\" rel=\"noopener\">proveedores de servicios gestionados (MSP)<\/a>. La posibilidad de generar informes exhaustivos basados en las extensiones de archivos dentro de directorios espec\u00edficos puede agilizar las operaciones, mejorar la seguridad y ayudar a mantener la higiene del sistema. Este art\u00edculo explora un script de <a href=\"https:\/\/www.ninjaone.com\/it-hub\/endpoint-management\/what-is-powershell\/\" target=\"_blank\" rel=\"noopener\">PowerShell<\/a> dise\u00f1ado para automatizar esta tarea, garantizando que los profesionales de TI puedan localizar y documentar sin esfuerzo archivos por extensi\u00f3n en varios directorios.<\/p>\n<h2><strong>Background\u00a0<\/strong><\/h2>\n<p>Los scripts de PowerShell son herramientas inestimables para los administradores de TI, ya que proporcionan la automatizaci\u00f3n necesaria para gestionar tareas complejas con precisi\u00f3n y facilidad. Este script en particular se centra en generar informes detallados basados en las extensiones de archivos dentro de los directorios especificados por el usuario y sus subdirectorios. Aprovechando este script, los profesionales de TI pueden identificar r\u00e1pidamente tipos de archivo espec\u00edficos, facilitando tareas como auditor\u00edas de software, comprobaciones de conformidad y evaluaciones de seguridad.<\/p>\n<h2>El script para generar informes sobre extensiones de archivos<\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\">#Requires -Version 5.1\r\n\r\n&lt;#\r\n.SYNOPSIS\r\n    Creates a report based on the files found in the directory or subdirectory you specified with your desired extension.\r\n.DESCRIPTION\r\n    Creates a report based on the files found in the directory or subdirectory you specified with your desired extension.\r\n.EXAMPLE\r\n    -Extensions \".exe\" -SearchPaths \"C:\\Users\\tuser\\Downloads\"\r\n    \r\n    Searching C:\\Users\\tuser\\Downloads for files with extension '.exe'...\r\n    No files found with extension .exe!\r\n\r\n\r\nPARAMETER: -Extensions \"exe, .ico\"\r\n    A comma-separated list of extensions to search for. You can use the * character as a wildcard.\r\n\r\nPARAMETER: -SearchPaths \"C:\\Replace\\Me\\With\\Valid\\Path\"\r\n    Enter the starting directories for the search, separated by commas. This will include all subdirectories as well.\r\n\r\nPARAMETER: -MultiLineField \"ReplaceMeWithNameOfMultilineCustomField\"\r\n    Optional multiline field to record search results. Leave blank if unused.\r\n\r\nPARAMETER: -WysiwygField \"ReplaceMeWithNameOfWYSIWYGCustomField\"\r\n    Optional WYSIWYG field to record search results. Leave blank if unused.\r\n\r\nPARAMETER: -ScanSystemDrive\r\n    This will set the system drive (usually drive C:\\) as the starting point for the search.\r\n\r\nPARAMETER: -ScanAllDrives\r\n    This will set all drives (including flash drives) as the starting point for the search.\r\n.NOTES\r\n    Minimum OS Architecture Supported: Windows 10, Windows Server 2016\r\n    Release Notes: Initial Release\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#&gt;\r\n\r\n[CmdletBinding()]\r\nparam (\r\n    [Parameter()]\r\n    [String]$Extensions,\r\n    [Parameter()]\r\n    [String]$SearchPaths,\r\n    [Parameter()]\r\n    [String]$MultiLineField,\r\n    [Parameter()]\r\n    [String]$WysiwygField,\r\n    [Parameter()]\r\n    [Switch]$ScanSystemDrive = [System.Convert]::ToBoolean($env:scanSystemDrive),\r\n    [Parameter()]\r\n    [Switch]$ScanAllDrives = [System.Convert]::ToBoolean($env:scanAllDrives)\r\n)\r\nbegin {\r\n    # Set parameters using dynamic script variables.\r\n    if ($env:fileExtension -and $env:fileExtension -notlike \"null\") { $Extensions = $env:fileExtension }\r\n    if ($env:searchPath -and $env:searchPath -notlike \"null\") { $SearchPaths = $env:searchPath }\r\n    if ($env:multilineCustomField -and $env:multilineCustomField -notlike \"null\") { $MultiLineField = $env:multilineCustomField }\r\n    if ($env:wysiwygCustomField -and $env:wysiwygCustomField -notlike \"null\") { $WysiwygField = $env:wysiwygCustomField }\r\n\r\n    # Check if no extensions were specified and exit with an error if true.\r\n    if (-not $Extensions) {\r\n        Write-Host -Object \"[Error] Missing extension to search for!\"\r\n        exit 1\r\n    }\r\n\r\n    # Verify that WysiwygField and MultiLineField are not the same, exiting with an error if they are.\r\n    if ($WysiwygField -and $MultiLineField -and ($WysiwygField -eq $MultiLineField)) {\r\n        Write-Host -Object \"[Error] Wysiwyg Field and Multiline Field are the same! Custom fields cannot be the same type.\"\r\n        Write-Host -Object \"https:\/\/ninjarmm.zendesk.com\/hc\/en-us\/articles\/18601842971789-Custom-Fields-by-Type-and-Functionality\"\r\n        exit 1\r\n    }\r\n\r\n    # Initialize a list to store the extensions to search for.\r\n    $ExtensionsToSearch = New-Object System.Collections.Generic.List[string]\r\n    # Split the extensions if they are comma-separated and trim whitespace.\r\n    if ($Extensions -match \",\") {\r\n        $Extensions -split \",\" | ForEach-Object { $ExtensionsToSearch.Add($_.Trim()) }\r\n    }\r\n    else {\r\n        $ExtensionsToSearch.Add($Extensions.Trim())\r\n    }\r\n    \r\n    # Initialize a list to keep track of extensions that need to be replaced (adding a leading dot if missing).\r\n    $ExtensionsToReplace = New-Object System.Collections.Generic.List[object]\r\n    $ExtensionsToSearch | ForEach-Object {\r\n        if ($_ -notmatch \"^\\.\") {\r\n            $NewExtension = \".$_\"\r\n\r\n            $ExtensionsToReplace.Add(\r\n                [PSCustomObject]@{\r\n                    Index        = $ExtensionsToSearch.IndexOf(\"$_\")\r\n                    NewExtension = $NewExtension\r\n                }\r\n            )\r\n                \r\n            Write-Warning \"Missing . for extension. Changing extension search to '$NewExtension'.\"\r\n        }\r\n    }\r\n\r\n    # Apply the replacements for extensions that were missing a leading dot.\r\n    $ExtensionsToReplace | ForEach-Object {\r\n        $ExtensionsToSearch[$_.index] = $_.NewExtension \r\n    }\r\n\r\n    # Check if no search locations were specified and exit with an error if true.\r\n    if (!$SearchPaths -and !$ScanSystemDrive -and !$ScanAllDrives) {\r\n        Write-Host -Object \"[Error] Missing somewhere to search!\"\r\n        exit 1\r\n    }\r\n\r\n    # If scanning all drives, ignore specific paths and the system drive flag.\r\n    if ($ScanAllDrives) {\r\n        $ScanSystemDrive = $false\r\n        $SearchPaths = $Null\r\n    }\r\n\r\n    # Initialize a list for paths to search.\r\n    $PathsToSearch = New-Object System.Collections.Generic.List[string]\r\n    # Split the search paths if they are comma-separated and trim whitespace.\r\n    if ($SearchPaths -match \",\") {\r\n        $SearchPaths -split \",\" | ForEach-Object { $PathsToSearch.Add($_.Trim()) }\r\n    }\r\n    elseif ($SearchPaths) {\r\n        $PathsToSearch.Add($SearchPaths)\r\n    }\r\n\r\n    # Add the system drive to the search paths if specified.\r\n    if ($ScanSystemDrive) {\r\n        if ($env:SystemDrive -notmatch '^[A-Z]:\\\\$' -and $env:SystemDrive -match '^[A-Z]:$') {\r\n            $PathsToSearch.Add(\"$env:SystemDrive\\\")\r\n        }\r\n        else {\r\n            $PathsToSearch.Add($env:SystemDrive)\r\n        }\r\n    }\r\n\r\n    # Add all filesystem drives to the search paths if scanning all drives.\r\n    if ($ScanAllDrives) {\r\n        Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Free -and $_.Used } | ForEach-Object {\r\n            if ($_.Root -notmatch '^[A-Z]:\\\\$' -and $_.Root -match '^[A-Z]:$') {\r\n                $PathsToSearch.Add(\"$($_.Root)\\\")\r\n            }\r\n            else {\r\n                $PathsToSearch.Add($_.Root)\r\n            }\r\n        }\r\n    }\r\n\r\n    # Initialize a list for paths that need to be corrected (adding a trailing backslash if missing).\r\n    $ReplacementPaths = New-Object System.Collections.Generic.List[Object]\r\n\r\n    # Check each path and add a backslash if it's missing.\r\n    $PathsToSearch | ForEach-Object {\r\n        if ($_ -notmatch '^[A-Z]:\\\\$' -and $_ -match '^[A-Z]:$') {\r\n            $NewPath = \"$_\\\"\r\n            $ReplacementPaths.Add(\r\n                [PSCustomObject]@{\r\n                    Index   = $PathsToSearch.IndexOf(\"$_\")\r\n                    NewPath = $NewPath\r\n                }\r\n            )\r\n                \r\n            Write-Warning \"Backslash missing from the search path. Changing it to $NewPath.\"\r\n        }\r\n    }\r\n\r\n    # Apply the path corrections.\r\n    $ReplacementPaths | ForEach-Object {\r\n        $PathsToSearch[$_.index] = $_.NewPath \r\n    }\r\n\r\n    # Function to test if the script is running with elevated permissions.\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    # Handy function to set a custom field.\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        )\r\n\r\n        $Characters = $Value | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters\r\n        if ($Characters -ge 200000) {\r\n            throw [System.ArgumentOutOfRangeException]::New(\"Character limit exceeded, value is greater than or equal to 200,000 characters.\")\r\n        }\r\n    \r\n        # If we're requested to set the field value for a Ninja document we'll specify it here.\r\n        $DocumentationParams = @{}\r\n        if ($DocumentName) { $DocumentationParams[\"DocumentName\"] = $DocumentName }\r\n    \r\n        # This is a list of valid fields that can be set. If no type is given, it will be assumed that the input doesn't need to be changed.\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        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        # The field below requires additional information to be set\r\n        $NeedsOptions = \"Dropdown\"\r\n        if ($DocumentName) {\r\n            if ($NeedsOptions -contains $Type) {\r\n                # We'll redirect the error output to the success stream to make it easier to error out if nothing was found or something else went wrong.\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        # If an error is received it will have an exception property, the function will exit with that error information.\r\n        if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions }\r\n    \r\n        # The below type's require values not typically given in order to be set. The below code will convert whatever we're given into a format ninjarmm-cli supports.\r\n        switch ($Type) {\r\n            \"Checkbox\" {\r\n                # While it's highly likely we were given a value like \"True\" or a boolean datatype it's better to be safe than sorry.\r\n                $NinjaValue = [System.Convert]::ToBoolean($Value)\r\n            }\r\n            \"Date or Date Time\" {\r\n                # Ninjarmm-cli expects the GUID of the option to be selected. Therefore, the given value will be matched with a GUID.\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                # Ninjarmm-cli is expecting the guid of the option we're trying to select. So we'll match up the value we were given with a 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                if (-not $Selection) {\r\n                    throw [System.ArgumentOutOfRangeException]::New(\"Value is not present in dropdown\")\r\n                }\r\n    \r\n                $NinjaValue = $Selection\r\n            }\r\n            default {\r\n                # All the other types shouldn't require additional work on the input.\r\n                $NinjaValue = $Value\r\n            }\r\n        }\r\n    \r\n        # We'll need to set the field differently depending on if its a field in a Ninja Document or not.\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            $CustomField = $NinjaValue | Ninja-Property-Set-Piped -Name $Name 2&gt;&amp;1\r\n        }\r\n    \r\n        if ($CustomField.Exception) {\r\n            throw $CustomField\r\n        }\r\n    }\r\n\r\n    $ExitCode = 0\r\n}\r\nprocess {\r\n    # Check if the script is running with Administrator privileges. Exit with an error message if not.\r\n    if (!(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    # Remove illegal extensions\r\n    $ExtensionsToRemove = New-Object System.Collections.Generic.List[String]\r\n    $invalidExtensions = '[&lt;&gt;:\"\/\\\\|\\x00-\\x1F]|\\.$'\r\n    $ExtensionsToSearch | ForEach-Object {\r\n        if($_ -match $invalidExtensions){\r\n            Write-Host -Object \"[Error] Extension $_ contains one of the following invalid characters or ends in a period. '\\:&lt;&gt;`\"\/|'\"\r\n            $ExtensionsToRemove.Add($_)\r\n            $ExitCode = 1\r\n        }\r\n    }\r\n\r\n    # Actual removal\r\n    $ExtensionsToRemove | ForEach-Object {\r\n        $ExtensionsToSearch.Remove($_) | Out-Null\r\n    }\r\n\r\n    # Exit the script if there are no valid extensions left to search.\r\n    if ($ExtensionsToSearch.Count -eq 0) {\r\n        Write-Host \"[Error] No valid extensions to search!\"\r\n        exit 1\r\n    }\r\n\r\n    # Initialize lists to store information about paths and errors.\r\n    $CustomFieldErrorInfo = New-Object System.Collections.Generic.List[string]\r\n\r\n    # These characters are not valid for a search path.\r\n    $invalidSearchPathCharacters = '[&lt;&gt;\"\/|?\\x00-\\x1F]'\r\n\r\n    # Initialize a generic list to store paths that don't exist and should be removed from the search.\r\n    $PathsToRemove = New-Object System.Collections.Generic.List[String]\r\n    # Check each path in the search list to ensure it exists. Collect paths that don't exist for removal.\r\n    $PathsToSearch | ForEach-Object {\r\n        if($_ -match $invalidSearchPathCharacters){\r\n            Write-Host -Object \"[Error] Path $_ contains one of the following invalid characters. '&lt;&gt;`\"\/|'\"\r\n            $PathsToRemove.Add($_)\r\n            $ExitCode = 1\r\n            return\r\n        }\r\n\r\n        if (!(Test-Path $_)) {\r\n            Write-Host -Object \"[Error] $_ does not exist!\"\r\n            $PathsToRemove.Add($_)\r\n            $ExitCode = 1\r\n        }\r\n    }\r\n\r\n    # Remove non-existing paths from the search list.\r\n    $PathsToRemove | ForEach-Object {\r\n        $PathsToSearch.Remove($_) | Out-Null\r\n    }\r\n\r\n    # Exit the script if there are no valid paths left to search.\r\n    if ($PathsToSearch.Count -eq 0) {\r\n        Write-Host \"[Error] No valid paths to search!\"\r\n        exit 1\r\n    }\r\n\r\n    # Initialize a list to keep track of the search jobs created.\r\n    $SearchJobs = New-Object System.Collections.Generic.List[object]\r\n\r\n    # Create and start a PowerShell job for each path and extension combination.\r\n    foreach ($Path in $PathsToSearch) {\r\n        foreach ($Extension in $ExtensionsToSearch) {\r\n            Write-Host \"Searching '$Path' for files with extension '$Extension'...\"\r\n            $SearchJobs.Add(\r\n                (\r\n                    Start-Job -ScriptBlock {\r\n                        param($Path, $Extension)\r\n\r\n                        # Defines a function to convert file sizes to a human-readable format.\r\n                        function Get-FriendlySize {\r\n                            param($Bytes)\r\n                            # Converts Bytes to the highest matching unit\r\n                            $Sizes = 'Bytes,KB,MB,GB,TB,PB,EB,ZB' -split ','\r\n                            for ($i = 0; ($Bytes -ge 1kb) -and ($i -lt $Sizes.Count); $i++) { $Bytes \/= 1kb }\r\n                            $N = 2\r\n                            if ($i -eq 0) { $N = 0 }\r\n                            if ($Bytes) { \"$([System.Math]::Round($Bytes,$N)) $($Sizes[$i])\" }else { \"0 B\" }\r\n                        }\r\n\r\n                        # Search for files matching the extension and output their details in CSV format.\r\n                        Get-ChildItem -Path $Path -Filter \"*$Extension\" -Recurse -File -Force | Select-Object Name, FullName, CreationTime, LastWriteTime, Length, @{Name = \"Size\"; Expression = { Get-FriendlySize $_.Length } } | ConvertTo-Csv\r\n                    } -ArgumentList $Path, $Extension\r\n                )\r\n            )\r\n        }\r\n    }\r\n\r\n    # Wait for all search jobs to complete or timeout after 9000 seconds (2.5 hours).\r\n    $SearchJobs | Wait-Job -Timeout 9000 | Out-Null\r\n\r\n    # Check for incomplete jobs due to timeout and log an error.\r\n    $IncompleteJobs = $SearchJobs | Get-Job | Where-Object { $_.State -eq \"Running\" }\r\n    if ($IncompleteJobs) {\r\n        Write-Host \"[Error] The timeout period of 2.5 hours has been reached, but not all files or directories have been searched!\"\r\n        $CustomFieldErrorInfo.Add(\"[Error] The timeout period of 2.5 hours has been reached, but not all files or directories have been searched!\")\r\n        $ExitCode = 1\r\n    }\r\n\r\n    # Collect and process the output from each search job.\r\n    $MatchingItems = $SearchJobs | ForEach-Object {\r\n        $_ | Get-Job | Receive-Job -ErrorAction SilentlyContinue -ErrorVariable JobErrors | ConvertFrom-Csv\r\n    }\r\n\r\n    # Clear out duplicate entries\r\n    if ($MatchingItems) {\r\n        $MatchingItems = $MatchingItems | Sort-Object FullName -Unique\r\n    }\r\n\r\n    # Check for jobs that failed to complete successfully and log errors.\r\n    $FailedJobs = $SearchJobs | Get-Job | Where-Object { $_.State -ne \"Completed\" }\r\n    if ($JobErrors -or $FailedJobs) {\r\n        $CustomFieldErrorInfo.Add(\"[Error] Failed to search certain directories due to an error.\")\r\n\r\n        if ($JobErrors) {\r\n            $JobErrors | ForEach-Object { \r\n                $CustomFieldErrorInfo.Add(\"[Error] $($_.Exception.Message)\")\r\n            }\r\n        }\r\n        $ExitCode = 1\r\n    }\r\n\r\n    # Process and attempt to set custom field values based on search results and errors, with specific handling for multiline fields.\r\n    # Truncate data if it exceeds character limits for the fields.\r\n    if ($MultiLineField -and $MatchingItems) {\r\n        try {\r\n            Write-Host \"Attempting to set Custom Field '$MultiLineField'.\"\r\n\r\n            # Prepare the custom field output.\r\n            $CustomFieldValue = New-Object System.Collections.Generic.List[string]\r\n\r\n            # We don't want to edit the matching items array if we have to truncate later so we'll create a duplicate here.\r\n            $CustomFieldList = $MatchingItems | Select-Object -Property Name, FullName, CreationTime, LastWriteTime, Size\r\n\r\n            # Format the matching items into a nice list with the relevant properties.\r\n            $CustomFieldValue.Add(($CustomFieldList | Format-List -Property Name, FullName, CreationTime, LastWriteTime, Size | Out-String))\r\n            \r\n            # If any errors were encountered in the search add them to the bottom of the custom field output.\r\n            $CustomFieldErrorInfo | ForEach-Object {\r\n                $CustomFieldValue.Add($_)\r\n            }\r\n            \r\n            # Check that the output complies with the hard character limits.\r\n            $Characters = $CustomFieldValue | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters\r\n            if ($Characters -ge 9500) {\r\n                Write-Warning \"10,000 Character Limit has been reached! Trimming output until the character limit is satisified...\"\r\n                \r\n                # If it doesn't comply with the limits we'll need to recreate it with some adjustments.\r\n                $i = 0\r\n                do {\r\n                    # Recreate the custom field output starting with a warning that we truncated the output.\r\n                    $CustomFieldValue = New-Object System.Collections.Generic.List[string]\r\n                    $CustomFieldValue.Add(\"This info has been truncated to accommodate the 10,000 character limit.\")\r\n                    \r\n                    # The custom field information is sorted in alphabetical order. We'll flip the array upside down to sort it in reverse alphabetical.\r\n                    [array]::Reverse($CustomFieldList)\r\n\r\n                    # Remove the next item which in this case will be the smallest item.\r\n                    $CustomFieldList[$i] = $null\r\n                    $i++\r\n\r\n                    # We'll flip the array back to right side up.\r\n                    [array]::Reverse($CustomFieldList)\r\n\r\n                    # Add it back to the output.\r\n                    $CustomFieldValue.Add(($CustomFieldList | Format-List -Property Name, FullName, CreationTime, LastWriteTime, Size | Out-String))\r\n                    # Finish with adding any errors we encountered during the search.\r\n                    $CustomFieldErrorInfo | ForEach-Object {\r\n                        $CustomFieldValue.Add($_)\r\n                    }\r\n\r\n                    # Check that we now comply with the character limit. If not restart the do loop.\r\n                    $Characters = $CustomFieldValue | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters\r\n                }while ($Characters -ge 9500)\r\n            }\r\n\r\n            # Set the custom field.\r\n            Set-NinjaProperty -Name $MultiLineField -Value $CustomFieldValue\r\n            Write-Host \"Successfully set Custom Field '$MultiLineField'!\"\r\n        }\r\n        catch {\r\n            Write-Host \"[Error] $($_.Exception.Message)\"\r\n            $ExitCode = 1\r\n        }\r\n    }\r\n\r\n    # Process and attempt to set custom field values based on search results and errors, with specific handling for WYSIWYG fields.\r\n    # Truncate data if it exceeds character limits for the fields.\r\n    if ($WysiwygField -and $MatchingItems) {\r\n        try {\r\n            Write-Host \"Attempting to set Custom Field '$WysiwygField'.\"\r\n\r\n            # Prepare the custom field output.\r\n            $CustomFieldValue = New-Object System.Collections.Generic.List[string]\r\n\r\n            # Convert the matching items into an html report.\r\n            $htmlTable = $MatchingItems | Select-Object -Property Name, FullName, CreationTime, LastWriteTime, Size | ConvertTo-Html -Fragment\r\n            \r\n            # Add the newly created html into the custom field output.\r\n            $CustomFieldValue.Add($htmlTable)\r\n            # If any errors were encountered in the search add them to the bottom of the custom field output.\r\n            $CustomFieldErrorInfo | ForEach-Object {\r\n                $CustomFieldValue.Add($_)\r\n            }\r\n\r\n            # Check that the output complies with the hard character limits.\r\n            $Characters = $CustomFieldValue | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters\r\n            if ($Characters -ge 199500) {\r\n                Write-Warning \"200,000 Character Limit has been reached! Trimming output until the character limit is satisified...\"\r\n                \r\n                # If it doesn't comply with the limits we'll need to recreate it with some adjustments.\r\n                $i = 0\r\n                do {\r\n                    # Recreate the custom field output starting with a warning that we truncated the output.\r\n                    $CustomFieldValue = New-Object System.Collections.Generic.List[string]\r\n                    $CustomFieldValue.Add(\"&lt;h1&gt;This info has been truncated to accommodate the 200,000 character limit.&lt;\/h1&gt;\")\r\n\r\n                    # The custom field information is sorted in alphabetical order. We'll sort it into reverse alphabetical by flipping the array upside down.\r\n                    [array]::Reverse($htmlTable)\r\n                    # If the next entry is a row we'll delete it.\r\n                    if ($htmlTable[$i] -match '&lt;tr&gt;&lt;td&gt;') {\r\n                        $htmlTable[$i] = $null\r\n                    }\r\n                    $i++\r\n                    # We'll flip the array back to right side up.\r\n                    [array]::Reverse($htmlTable)\r\n\r\n                    # Add it back to the output.\r\n                    $CustomFieldValue.Add($htmlTable)\r\n                    # Finish with adding any errors we encountered during the search.\r\n                    $CustomFieldErrorInfo | ForEach-Object {\r\n                        $CustomFieldValue.Add($_)\r\n                    }\r\n                    # Check that we now comply with the character limit. If not restart the do loop.\r\n                    $Characters = $CustomFieldValue | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters\r\n                }while ($Characters -ge 199500)\r\n            }\r\n\r\n            # Set the custom field.\r\n            Set-NinjaProperty -Name $WysiwygField -Value $CustomFieldValue\r\n            Write-Host \"Successfully set Custom Field '$WysiwygField'!\"\r\n        }\r\n        catch {\r\n            Write-Host \"[Error] $($_.Exception.Message)\"\r\n            $ExitCode = 1\r\n        }\r\n    }\r\n\r\n    # Output the results of our search into the activity log.\r\n    if (!$MatchingItems) {\r\n        Write-Host \"No files found with extension $Extension!\"\r\n    }\r\n    else {\r\n        Write-Host \"Files found!\"\r\n        $MatchingItems | Format-List -Property Name, FullName, CreationTime, LastWriteTime, Size | Out-String | Write-Host\r\n    }\r\n\r\n    # If we encountered any errors during the search we'll output them here.\r\n    if ($JobErrors -or $FailedJobs) {\r\n        Write-Host \"\"\r\n        Write-Host \"[Error] Failed to search certain directories due to an error.\"\r\n\r\n        if ($JobErrors) {\r\n            Write-Host \"\"\r\n\r\n            $JobErrors | ForEach-Object {\r\n                Write-Host \"[Error] $($_.Exception.Message)\" \r\n            }\r\n        }\r\n        $ExitCode = 1\r\n    }\r\n\r\n    # Remove all jobs to clean up.\r\n    $SearchJobs | Get-Job | Remove-Job -Force\r\n\r\n    # Exit the script with the appropriate exit code\r\n    exit $ExitCode\r\n}\r\nend {\r\n    \r\n    \r\n     \r\n}<\/pre>\n<p>&nbsp;<\/p>\n\n<div class=\"blog-cta-new blog-cta-style-1\"><div class=\"cta-left\"><h2><\/h2><p><\/p><\/div><div class=\"cta-right\"><a class=\"button\" href=\"\"><\/a><\/div><\/div>\n<h2>An\u00e1lisis detallado<\/h2>\n<h3>Sinopsis del script para generar informes sobre extensiones de archivos<\/h3>\n<p>El script est\u00e1 dise\u00f1ado para buscar en directorios archivos con extensiones espec\u00edficas y crear un informe completo. Acepta varios par\u00e1metros, incluidas las extensiones que se van a buscar, los directorios en los que se va a buscar y campos personalizados opcionales para registrar los resultados.<\/p>\n<h3>Par\u00e1metros<\/h3>\n<ol>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><strong>Extensiones<\/strong>: una lista separada por comas de las extensiones de archivos a buscar. Se admiten comodines.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><strong>SearchPaths<\/strong>: directorios para iniciar la b\u00fasqueda, incluidos todos los subdirectorios.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><strong>MultiLineField<\/strong>: campo opcional para registrar los resultados de la b\u00fasqueda multil\u00ednea.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><strong>WysiwygField<\/strong>: un campo WYSIWYG opcional para registrar los resultados de la b\u00fasqueda.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><strong>ScanSystemDrive<\/strong>:un interruptor para establecer la unidad del sistema como punto de partida.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><strong>ScanAllDrives<\/strong>: un interruptor para establecer todas las unidades, incluidas las unidades flash, como punto de partida.<\/li>\n<\/ol>\n<h3>Ejecuci\u00f3n del script<\/h3>\n<ol>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"7\" data-aria-level=\"1\"><strong>Validaci\u00f3n de par\u00e1metros<\/strong>: el script para generar informes sobre extensiones de archivos comienza validando los par\u00e1metros de entrada. Garantiza que se proporcionan extensiones y rutas de b\u00fasqueda v\u00e1lidas. Tambi\u00e9n establece din\u00e1micamente los par\u00e1metros utilizando variables de entorno si est\u00e1n disponibles.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"7\" data-aria-level=\"1\"><strong>Extensi\u00f3n y preparaci\u00f3n de la ruta<\/strong>: normaliza las extensiones y las rutas de b\u00fasqueda a\u00f1adiendo los puntos y las barras invertidas que faltan. Se identifican y eliminan las extensiones y rutas no v\u00e1lidas.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"7\" data-aria-level=\"1\"><strong>Comprobaci\u00f3n de privilegios<\/strong>: el script para generar informes sobre extensiones de archivos comprueba si se est\u00e1 ejecutando con permisos elevados, saliendo en caso contrario.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"7\" data-aria-level=\"1\"><strong>Inicializaci\u00f3n de la b\u00fasqueda<\/strong>: crea una lista de trabajos de b\u00fasqueda para cada combinaci\u00f3n de rutas y extensiones. Cada tarea busca en la ruta especificada archivos con la extensi\u00f3n dada.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"7\" data-aria-level=\"1\"><strong>Recogida y tratamiento de resultados<\/strong>: el script recoge los resultados de la b\u00fasqueda, elimina los duplicados y los formatea para su salida. Tambi\u00e9n gestiona posibles errores y tiempos muertos durante la b\u00fasqueda.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"7\" data-aria-level=\"1\"><strong>Configuraci\u00f3n de campo personalizada<\/strong>: si se especifica, el script intenta establecer los resultados en los campos personalizados dados (MultiLineField y WysiwygField), asegur\u00e1ndose de que los datos cumplen con los l\u00edmites de caracteres.<\/li>\n<li data-leveltext=\"%1.\" data-font=\"Aptos\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"7\" data-aria-level=\"1\"><strong>Salida y limpieza<\/strong>: por \u00faltimo, el script para generar informes sobre extensiones de archivos env\u00eda los resultados de la b\u00fasqueda a la consola y limpia todos los trabajos antes de salir.<\/li>\n<\/ol>\n<h2>Posibles casos de uso<\/h2>\n<h3>Escenario hipot\u00e9tico<\/h3>\n<p>Imagina que un profesional de TI que trabaja para una empresa mediana necesita auditar todos los archivos ejecutables de la red. Ejecutando este script con el par\u00e1metro -Extensions \u00ab.exe\u00bb y especificando los directorios de red, puede generar r\u00e1pidamente un informe detallado de todos los archivos ejecutables, incluyendo sus rutas, tiempos de creaci\u00f3n y tama\u00f1os. Este informe puede utilizarse para garantizar el cumplimiento de las pol\u00edticas de la empresa e identificar instalaciones de software no autorizadas.<\/p>\n<h2>Comparaciones<\/h2>\n<h3>M\u00e9todos tradicionales vs. el script de PowerShell<\/h3>\n<p>Tradicionalmente, la b\u00fasqueda de archivos por extensi\u00f3n puede implicar la b\u00fasqueda manual a trav\u00e9s de directorios o el uso de herramientas b\u00e1sicas de l\u00ednea de comandos, lo que puede llevar mucho tiempo y ser propenso a errores. Este script de PowerShell automatiza el proceso, proporciona informes completos y gestiona los errores, por lo que es muy superior a los m\u00e9todos manuales o scripts m\u00e1s sencillos.<\/p>\n<h2>FAQ<\/h2>\n<h3>1) \u00bfQu\u00e9 ocurre si no se especifica ninguna extensi\u00f3n?<\/h3>\n<p>El script saldr\u00e1 con un error, indicando que se requiere una extensi\u00f3n para la b\u00fasqueda.<\/p>\n<h3>2) \u00bfPuedo buscar en varios directorios a la vez?<\/h3>\n<p>S\u00ed, proporcionando una lista de directorios separados por comas en el par\u00e1metro -SearchPaths, el script buscar\u00e1 en cada directorio especificado y en sus subdirectorios.<\/p>\n<h3>3) \u00bfY si necesito buscar en todas las unidades de mi sistema?<\/h3>\n<p>Utilizando el par\u00e1metro -ScanAllDrives, el script incluir\u00e1 todas las unidades disponibles en la b\u00fasqueda, incluidas las unidades flash.<\/p>\n<h3>4) \u00bfC\u00f3mo gestiona el script grandes cantidades de datos?<\/h3>\n<p>El script incluye mecanismos para truncar la salida si supera los l\u00edmites de caracteres de los campos personalizados, lo que garantiza que los resultados sigan siendo manejables y se ajusten a las limitaciones del sistema.<\/p>\n<h2>Implicaciones<\/h2>\n<h3>Seguridad y conformidad<\/h3>\n<p>Utilizando este script, los profesionales de TI pueden mejorar la seguridad identificando r\u00e1pidamente archivos potencialmente da\u00f1inos o no autorizados. Las auditor\u00edas peri\u00f3dicas con este script pueden ayudar a mantener el cumplimiento de las pol\u00edticas organizativas y los requisitos normativos.<\/p>\n<h2>Recomendaciones<\/h2>\n<ul>\n<li data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"2\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><strong>Realiza auditor\u00edas de forma peri\u00f3dica<\/strong>: ejecuta el script regularmente para mantener un inventario actualizado de tipos de archivo espec\u00edficos.<\/li>\n<\/ul>\n<ul>\n<li data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"2\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"2\" data-aria-level=\"1\"><strong>Revisa los registros<\/strong>: revisa siempre la salida y los registros del script en busca de errores o advertencias.<\/li>\n<\/ul>\n<ul>\n<li data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"2\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}\" data-aria-posinset=\"3\" data-aria-level=\"1\"><strong>Personaliza los campos<\/strong>: utiliza los campos personalizados para almacenar los resultados y mejorar la documentaci\u00f3n y el seguimiento.<\/li>\n<\/ul>\n<h2>Reflexiones finales<\/h2>\n<p>Este script de PowerShell es una potente herramienta para los profesionales de TI que buscan automatizar la tarea de localizar e informar de archivos por extensi\u00f3n. Su amplia funcionalidad, combinada con su facilidad de uso, lo convierten en un complemento esencial de cualquier kit de herramientas inform\u00e1ticas. Para las organizaciones que utilizan <a href=\"https:\/\/www.ninjaone.com\/\" target=\"_blank\" rel=\"noopener\">NinjaOne<\/a>, este script se integra a la perfecci\u00f3n, mejorando a\u00fan m\u00e1s las capacidades de la plataforma para la gesti\u00f3n y seguridad del sistema.<\/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":"","_lmt_disable":""},"operating_system":[4212],"use_cases":[4259],"class_list":["post-353766","script_hub","type-script_hub","status-publish","hentry","script_hub_category-windows","use_cases-configuracion-general"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/script_hub\/353766","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/script_hub"}],"about":[{"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/types\/script_hub"}],"author":[{"embeddable":true,"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/users\/35"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/comments?post=353766"}],"wp:attachment":[{"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/media?parent=353766"}],"wp:term":[{"taxonomy":"script_hub_category","embeddable":true,"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/operating_system?post=353766"},{"taxonomy":"use_cases","embeddable":true,"href":"https:\/\/www.ninjaone.com\/es\/wp-json\/wp\/v2\/use_cases?post=353766"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}