Public/Import-LinuxInventory.ps1
|
function Import-LinuxInventory { <# .SYNOPSIS Bulk imports Linux servers into Active Directory from a CSV file. .DESCRIPTION Reads a CSV file containing Linux server details and registers each one as a computer object in AD by calling Register-LinuxServer for each row. Expected CSV columns: Name, IPAddress, OperatingSystem, Description, ManagedBy. OperatingSystem, Description, and ManagedBy are optional columns -- blank values are handled gracefully. Returns a summary object with imported, skipped, and error counts. .PARAMETER CsvPath Path to the CSV file. The file must exist and have .csv extension. .PARAMETER OrganizationalUnit Target OU for all imported servers. Defaults to "OU=Linux Servers" under the domain root. .PARAMETER Force Overwrite existing computer objects if they already exist. .EXAMPLE Import-LinuxInventory -CsvPath .\linux-servers.csv Imports all servers from the CSV into the default OU. .EXAMPLE Import-LinuxInventory -CsvPath C:\data\servers.csv -Force Imports servers, overwriting any existing entries. .EXAMPLE Import-LinuxInventory -CsvPath .\servers.csv -OrganizationalUnit "OU=Linux Servers,OU=Servers,DC=contoso,DC=com" Imports into a specific OU. .NOTES Requires: ActiveDirectory module (RSAT). CSV template: Name,IPAddress,OperatingSystem,Description,ManagedBy web-prod-01,10.1.2.50,Ubuntu 22.04 LTS,Production web server,jsmith #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [ValidateScript({ if (-not (Test-Path $_)) { throw "CSV file not found: $_" } if ($_ -notmatch '\.csv$') { throw "File must have .csv extension: $_" } $true })] [string]$CsvPath, [string]$OrganizationalUnit, [switch]$Force ) begin { Import-Module ActiveDirectory -ErrorAction Stop $importedCount = 0 $skippedCount = 0 $errorCount = 0 $errors = [System.Collections.Generic.List[string]]::new() } process { $csvData = Import-Csv -Path $CsvPath -ErrorAction Stop $totalRows = @($csvData).Count Write-Verbose "Loaded $totalRows rows from $CsvPath" if ($totalRows -eq 0) { Write-Warning "CSV file is empty: $CsvPath" return } # Validate required columns $headers = ($csvData | Get-Member -MemberType NoteProperty).Name foreach ($required in @('Name', 'IPAddress')) { if ($required -notin $headers) { throw "CSV is missing required column '$required'. Found columns: $($headers -join ', ')" } } $rowIndex = 0 foreach ($row in $csvData) { $rowIndex++ Write-Verbose "Processing row $rowIndex of $totalRows : $($row.Name)" # Skip rows with missing mandatory fields if ([string]::IsNullOrWhiteSpace($row.Name) -or [string]::IsNullOrWhiteSpace($row.IPAddress)) { Write-Warning "Row $rowIndex skipped -- Name or IPAddress is blank." $skippedCount++ continue } # Build the parameter splat $regParams = @{ Name = $row.Name.Trim() IPAddress = $row.IPAddress.Trim() } if ($OrganizationalUnit) { $regParams['OrganizationalUnit'] = $OrganizationalUnit } if ($Force) { $regParams['Force'] = $true } # Optional columns if (-not [string]::IsNullOrWhiteSpace($row.OperatingSystem)) { $regParams['OperatingSystem'] = $row.OperatingSystem.Trim() } if ($headers -contains 'OperatingSystemVersion' -and -not [string]::IsNullOrWhiteSpace($row.OperatingSystemVersion)) { $regParams['OperatingSystemVersion'] = $row.OperatingSystemVersion.Trim() } if (-not [string]::IsNullOrWhiteSpace($row.Description)) { $regParams['Description'] = $row.Description.Trim() } if (-not [string]::IsNullOrWhiteSpace($row.ManagedBy)) { $regParams['ManagedBy'] = $row.ManagedBy.Trim() } try { $result = Register-LinuxServer @regParams if ($result) { $importedCount++ Write-Verbose "Registered: $($row.Name)" } else { $skippedCount++ } } catch { $errorCount++ $errorMsg = "Row $rowIndex ($($row.Name)): $_" $errors.Add($errorMsg) Write-Warning $errorMsg } } } end { $summary = [PSCustomObject]@{ CsvPath = $CsvPath Total = $totalRows Imported = $importedCount Skipped = $skippedCount Errors = $errorCount ErrorList = $errors } Write-Verbose "Import complete -- Imported: $importedCount, Skipped: $skippedCount, Errors: $errorCount" $summary } } |