# Helper functions function GetCurLocalTimeStr { $dt = [DateTime]::Now.ToLocalTime() return [String]::Format("{0:yyyy-MM-dd HH:mm:ss} {1}", $dt, $dt.ToString("%K").Replace(":", "")) } function GetDaysSince { param([Parameter(Mandatory)][DateTimeOffset]$dt) return [DateTimeOffset]::UtcNow.Subtract($dt).TotalDays } # If there's an error in a call such as $cfg.settings.Lock(), it is important that the script stops. # Continuing while another application is also potentially modifying settings may corrupt them. $ErrorActionPreference = "Stop" $virtAcctInactivityDays = 60 # The PowerShell instance executing this script needs to run elevated, as administrator, to access SSH Server settings. $cfg = new-object -com "Bitvise.BssCfg" foreach ($instance in $cfg.instances.entries) { if (-not $instance.canManage) { Write-Host "SSH Server instance '$($instance.name)' cannot be managed using the available COM object" } else { Write-Host "Checking for inactive virtual accounts in SSH Server instance '$($instance.name)'" $cfg.SetInstance($instance.name) # Check if any statistics files are available $statsDir = Join-Path $instance.installDir "Stats" if (-not (Test-Path -Path $statsDir -PathType Container)) { Write-Host "SSH Server instance '$($instance.name)' does not contain a Stats directory" continue } # Check that the SSH Server instance is not younger than $virtAcctInactivityDays days $statsDirCreationTime = [DateTimeOffset](Get-Item $statsDir).CreationTimeUtc if ((GetDaysSince $statsDirCreationTime) -lt $virtAcctInactivityDays) { Write-Host "SSH Server instance '$($instance.name)' contains a Stats directory created less than $virtAcctInactivityDays days ago" continue } # Find active virtual accounts in the SSH Server's statistics files $statsFiles = Get-ChildItem $statsDir -Filter "BssStats-*.xml" $activeAccts = @() foreach ($statsFile in $statsFiles) { $stats = Select-Xml -Path $statsFile.FullName -XPath "/stats" if (($stats.Node.type -eq "VirtAccount") -and $stats.Node.info.lastLogin) { $lastLogin = [DateTimeOffset]::Parse($stats.Node.info.lastLogin) if ((GetDaysSince $lastLogin) -lt $virtAcctInactivityDays) { $activeAccts += $stats.Node.account } } } # Settings must be locked while undergoing change to preserve their integrity. # If you lock settings and do not unlock them, no other process will be able to edit settings until the $cfg object is released. $cfg.settings.Lock() try { $cfg.settings.Load() # Find SSH Server settings entries for inactive accounts and disable them $anyDisabled = $false foreach ($acct in $cfg.settings.access.virtAccounts.entries) { if ($activeAccts -contains $acct.virtAccount -or $acct.loginAllowed -eq $cfg.enums.DefaultGroupYesNo.no) { continue } $acctCreationTime = [DateTimeOffset]::MaxValue if ([DateTimeOffset]::TryParse($acct.creationTime, [ref]$acctCreationTime) -and (GetDaysSince $acctCreationTime) -lt $virtAcctInactivityDays) { continue } Write-Host "Disabling virtual account $($acct.virtAccount)" $acct.loginAllowed = $cfg.enums.DefaultGroupYesNo.no $commentPostfix = "Disabled for inactivity at $(GetCurLocalTimeStr)" $acct.comment = (@($acct.comment, $commentPostfix) | Where-Object { $_ }) -join "; " $anyDisabled = $true } # Save and unlock settings if ($anyDisabled) { $cfg.settings.Save() } } finally { $cfg.settings.Unlock() } } }