AutomatedLabInternals.psm1
#region Get-LabHyperVAvailableMemory function Get-LabHyperVAvailableMemory { # .ExternalHelp AutomatedLab.Help.xml if ($IsLinux -or $IsMacOS) { return [int]((Get-Content -Path /proc/meminfo) -replace ':', '=' -replace '\skB' | ConvertFrom-StringData).MemTotal } [int](((Get-CimInstance -Namespace Root\Cimv2 -Class win32_operatingsystem).TotalVisibleMemorySize) / 1kb) } #endregion Get-LabHyperVAvailableMemory #region Reset-AutomatedLab function Reset-AutomatedLab { Remove-Lab -Confirm:$false Remove-Module * } #endregion Reset-AutomatedLab #region Test-FileHashes function Test-FileHashes { [OutputType([System.Boolean])] [cmdletbinding()] param ( $Filename = 'C:\ALFiles.txt' ) $ModulePath = "$([environment]::getfolderpath('mydocuments'))\WindowsPowerShell\Modules" $StoredHashes = Import-Csv -Path $Filename $Issues = $False foreach ($File in $StoredHashes) { if (-not (Test-Path $File.path.replace('<MODULEPATH>', $ModulePath))) { Write-PSFMessage -Level Host -Message "'$File' is missing" $Issues = $True } else { if ((Get-FileHash -Path $File.path.replace('<MODULEPATH>', $ModulePath)).hash -ne $File.Hash) { Write-PSFMessage -Level Host -Message "'$File.Path' has wrong hash and is thereby not the file you think it is" $Issues = $True } } } $Issues } #endregion Test-FileHashes #region Save-FileList function Save-FileList { [cmdletbinding()] param ( $Filename = 'C:\ALfiles.txt' ) Get-ChildItem $ModulePath -Recurse -Directory -Include 'AutomatedLab', 'AutomatedLabDefinition', 'AutomatedLabUnattended', 'AutomatedLabWorker', 'HostsFile', 'PSFileTransfer', 'PSLog' | ForEach-Object { Get-ChildItem $_.FullName | Select-Object FullName } | Export-Csv -Path $Filename } #endregion Save-FileList #region Test-FileList function Test-FileList { [OutputType([System.Boolean])] [cmdletbinding()] param ( $Filename = 'C:\ALfiles.txt' ) $StoredFiles = Import-Csv -Path $Filename $Files = Get-ChildItem $ModulePath -Recurse -Directory -Include 'AutomatedLab', 'AutomatedLabDefinition', 'AutomatedLabUnattended', 'AutomatedLabWorker', 'HostsFile', 'PSFileTransfer', 'PSLog' | ForEach-Object { Get-ChildItem $_.FullName | Select-Object FullName } if (Compare-Object -ReferenceObject $StoredFiles -DifferenceObject $Files) { $true } else { $false } } #endregion Test-FileList #region Test-FolderExist function Test-FolderExist { [cmdletbinding()] param ( $FolderName ) if (-not (Test-Path -Path $FolderName)) { throw "The folder '$FolderName' is missing or is at the wrong level. This folder is required for setting up this lab" } } #endregion Test-FolderExist #region Test-FolderNotExist function Test-FolderNotExist { [cmdletbinding()] param ( $FolderName ) if (Test-Path -Path $FolderName) { throw "The folder '$FolderName' exist while it should NOT exist" } } #endregion Test-FolderNotExist #region Restart-ServiceResilient function Restart-ServiceResilient { [cmdletbinding()] param ( [string[]]$ComputerName, $ServiceName, [switch]$NoNewLine ) Write-LogFunctionEntry $jobs = Invoke-LabCommand -ComputerName $ComputerName -AsJob -PassThru -NoDisplay -ActivityName "Restart service '$ServiceName' on computers '$($ComputerName -join ', ')'" -ScriptBlock ` { param ( [string]$ServiceName ) function Get-ServiceRestartInfo { param ( [string]$ServiceName, [switch]$WasStopped, [switch]$WasStarted, [double]$Index ) $serviceDisplayName = (Get-Service $ServiceName).DisplayName $newestEvent = "($((Get-EventLog -LogName System -newest 1).Index)) " + (Get-EventLog -LogName System -newest 1).Message Write-Debug -Message "$(Get-Date -Format 'mm:dd:ss') - Get-ServiceRestartInfo - ServiceName: $ServiceName ($serviceDisplayName) - WasStopped: $WasStopped - WasStarted:$WasStarted - Index: $Index - Newest event: $newestEvent" $result = $true if ($WasStopped) { $events = @(Get-EventLog -LogName System -Index ($Index..($Index + 10000)) | Where-Object { $_.Message -like "*$serviceDisplayName*entered*stopped*" }) Write-Debug -Message "$(Get-Date -Format 'mm:dd:ss') - Events found: $($events.count)" $result = ($events.count -gt 0) } if ($WasStarted) { $events = @(Get-EventLog -LogName System -Index ($Index..($Index + 10000)) | Where-Object { $_.Message -like "*$serviceDisplayName*entered*running*" }) Write-Debug -Message "$(Get-Date -Format 'mm:dd:ss') - Events found: $($events.count)" $result = ($events.count -gt 0) } Write-Debug -Message "$(Get-Date -Format 'mm:dd:ss') - Result:$result" $result } $BackupVerbosePreference = $VerbosePreference $BackupDebugPreference = $DebugPreference $VerbosePreference = 'Continue' $DebugPreference = 'Continue' $ServiceName = 'nlasvc' $dependentServices = Get-Service -Name $ServiceName -DependentServices | Where-Object { $_.Status -eq 'Running' } | Select-Object -ExpandProperty Name Write-Verbose -Message "$(Get-Date -Format 'mm:dd:ss') - Dependent services: '$($dependentServices -join ',')'" $serviceDisplayName = (Get-Service $ServiceName).DisplayName if ((Get-Service -Name "$ServiceName").Status -eq 'Running') { $newestEventLogIndex = (Get-EventLog -LogName System -Newest 1).Index $retries = 5 do { Write-Verbose -Message "$(Get-Date -Format 'mm:dd:ss') - Trying to stop service '$ServiceName'" $EAPbackup = $ErrorActionPreference $WAPbackup = $WarningPreference $ErrorActionPreference = 'SilentlyContinue' $WarningPreference = 'SilentlyContinue' Stop-Service -Name $ServiceName -Force $ErrorActionPreference = $EAPbackup $WarningPreference = $WAPbackup $retries-- Start-Sleep -Seconds 1 } until ((Get-ServiceRestartInfo -ServiceName $ServiceName -WasStopped -Index $newestEventLogIndex) -or $retries -le 0) } if ($retries -gt 0) { Write-Verbose -Message "$(Get-Date -Format 'mm:dd:ss') - Service '$ServiceName' has been stopped" } else { Write-Verbose -Message "$(Get-Date -Format 'mm:dd:ss') - Service '$ServiceName' could NOT be stopped" return } if (-not (Get-ServiceRestartInfo -ServiceName $ServiceName -WasStarted -Index $newestEventLogIndex)) { #if service did not start by itself $newestEventLogIndex = (Get-EventLog -LogName System -Newest 1).Index $retries = 5 do { Write-Verbose -Message "$(Get-Date -Format 'mm:dd:ss') - Trying to start service '$ServiceName'" Start-Service -Name $ServiceName -ErrorAction SilentlyContinue $retries-- if (-not (Get-ServiceRestartInfo -ServiceName $ServiceName -WasStarted -Index $newestEventLogIndex)) { Start-Sleep -Seconds 1 } } until ((Get-ServiceRestartInfo -ServiceName $ServiceName -WasStarted -Index $newestEventLogIndex) -or $retries -le 0) } if ($retries -gt 0) { Write-Verbose -Message "$(Get-Date -Format 'mm:dd:ss') - Service '$ServiceName' was started" } else { Write-Verbose -Message "$(Get-Date -Format 'mm:dd:ss') - Service '$ServiceName' could NOT be started" return } foreach ($dependentService in $dependentServices) { if (Get-ServiceRestartInfo -ServiceName $dependentService -WasStarted -Index $newestEventLogIndex) { Write-Debug -Message "$(Get-Date -Format 'mm:dd:ss') - Dependent service '$dependentService' has already auto-started" } else { $newestEventLogIndex = (Get-EventLog -LogName System -Newest 1).Index $retries = 5 do { Write-Debug -Message "$(Get-Date -Format 'mm:dd:ss') - Trying to start depending service '$dependentService'" Start-Service $dependentService -ErrorAction SilentlyContinue $retries-- } until ((Get-ServiceRestartInfo -ServiceName $ServiceName -WasStarted -Index $newestEventLogIndex) -or $retries -le 0) if (Get-ServiceRestartInfo -ServiceName $ServiceName -WasStarted -Index $newestEventLogIndex) { Write-Debug -Message "$(Get-Date -Format 'mm:dd:ss') - Dependent service '$ServiceName' was started" } else { Write-Debug -Message "$(Get-Date -Format 'mm:dd:ss') - Dependent service '$ServiceName' could NOT be started" } } } $VerbosePreference = $BackupVerbosePreference $DebugPreference = $BackupDebugPreference } -ArgumentList $ServiceName Wait-LWLabJob -Job $jobs -NoDisplay -Timeout 30 -NoNewLine:$NoNewLine Write-LogFunctionExit } #endregion Restart-ServiceResilient #region Remove-LabDeploymentFiles function Remove-LabDeploymentFiles { Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName 'Remove deployment files (files used during deployment)' -AsJob -NoDisplay -ScriptBlock ` { $paths = 'C:\Unattend.xml', 'C:\WSManRegKey.reg', 'C:\AdditionalDisksOnline.ps1', 'C:\WinRmCustomization.ps1', 'C:\DeployDebug', 'C:\ALLibraries', 'C:\DeployDebug', "C:\$($env:COMPUTERNAME).cer" $paths | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue } } #endregion Remove-LabDeploymentFiles #region Enable-LabVMFirewallGroup function Enable-LabVMFirewallGroup { [cmdletbinding()] param ( [Parameter(Mandatory)] [string[]]$ComputerName, [Parameter(Mandatory)] [string[]]$FirewallGroup ) Write-LogFunctionEntry $machine = Get-LabVM -ComputerName $ComputerName Invoke-LabCommand -ComputerName $machine -ActivityName 'Enable firewall group' -NoDisplay -ScriptBlock ` { param ( [string]$FirewallGroup ) $FirewallGroups = $FirewallGroup.Split(';') foreach ($group in $FirewallGroups) { Write-Verbose -Message "Enable firewall group '$group' on '$(hostname)'" netsh.exe advfirewall firewall set rule group="$group" new enable=Yes } } -ArgumentList ($FirewallGroup -join ';') Write-LogFunctionExit } #endregion Enable-LabVMFirewallGroup #region Disable-LabVMFirewallGroup function Disable-LabVMFirewallGroup { [cmdletbinding()] param ( [Parameter(Mandatory)] [string[]]$ComputerName, [Parameter(Mandatory)] [string[]]$FirewallGroup ) Write-LogFunctionEntry $machine = Get-LabVM -ComputerName $ComputerName Invoke-LabCommand -ComputerName $machine -ActivityName 'Disable firewall group' -NoDisplay -ScriptBlock ` { param ( [string]$FirewallGroup ) $FirewallGroups = $FirewallGroup.Split(';') foreach ($group in $FirewallGroups) { Write-Verbose -Message "Disable firewall group '$group' on '$(hostname)'" netsh.exe advfirewall firewall set rule group="$group" new enable=No } } -ArgumentList ($FirewallGroup -join ';') Write-LogFunctionExit } #endregion Disable-LabVMFirewallGroup #region Get-LabInternetFile function Get-LabInternetFile { param( [Parameter(Mandatory = $true)] [string]$Uri, [Parameter(Mandatory = $true)] [string]$Path, [string]$FileName, [switch]$Force, [switch]$NoDisplay, [switch]$PassThru ) function Get-LabInternetFileInternal { param( [Parameter(Mandatory = $true)] [string]$Uri, [Parameter(Mandatory = $true)] [string]$Path, [string]$FileName, [bool]$NoDisplay, [bool]$Force ) if (Test-Path -Path $Path -PathType Container) { $Path = Join-Path -Path $Path -ChildPath $FileName } if ((Test-Path -Path $Path -PathType Leaf) -and -not $Force) { Write-Verbose -Message "The file '$Path' does already exist, skipping the download" } else { if (-not ($IsLinux -or $IsMacOS) -and -not (Get-NetConnectionProfile -ErrorAction SilentlyContinue | Where-Object { $_.IPv4Connectivity -eq 'Internet' -or $_.IPv6Connectivity -eq 'Internet' })) { #machine does not have internet connectivity if (-not $offlineNode) { Write-Error "Machine is not connected to the internet and cannot download the file '$Uri'" } return } Write-Verbose "Uri is '$Uri'" Write-Verbose "Path is '$Path'" try { try { #https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netcore-2.0#System_Net_SecurityProtocolType_SystemDefault if ($PSVersionTable.PSVersion.Major -lt 6 -and [Net.ServicePointManager]::SecurityProtocol -notmatch 'Tls12') { Write-Verbose -Message 'Adding support for TLS 1.2' [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12 } } catch { Write-Warning -Message 'Adding TLS 1.2 to supported security protocols was unsuccessful.' } $bytesProcessed = 0 $request = [System.Net.WebRequest]::Create($Uri) $request.AllowAutoRedirect = $true if ($request) { Write-Verbose 'WebRequest created' $response = $request.GetResponse() if ($response) { Write-Verbose 'Response received' $remoteStream = $response.GetResponseStream() if ([System.IO.Path]::GetPathRoot($Path) -ne $Path) { $parent = Split-Path -Path $Path } if (-not (Test-Path -Path $parent -PathType Container) -and -not ([System.IO.Path]::GetPathRoot($parent) -eq $parent)) { New-Item -Path $parent -ItemType Directory -Force | Out-Null } if ((Test-Path -Path $Path -PathType Container) -and -not $FileName) { $FileName = $response.ResponseUri.Segments[-1] $Path = Join-Path -Path $Path -ChildPath $FileName } if ([System.IO.Path]::GetPathRoot($Path) -eq $Path) { Write-Error "The path '$Path' is the drive root and the file name could not be retrived using the given url. Please provide a file name using the 'FileName' parameter." return } if (-not $FileName) { $FileName = Split-Path -Path $Path -Leaf } if ((Test-Path -Path $Path -PathType Leaf) -and -not $Force) { Write-Verbose -Message "The file '$Path' does already exist, skipping the download" } else { $localStream = [System.IO.File]::Create($Path) $buffer = New-Object System.Byte[] 10MB $bytesRead = 0 [int]$percentageCompletedPrev = 0 do { $bytesRead = $remoteStream.Read($buffer, 0, $buffer.Length) $localStream.Write($buffer, 0, $bytesRead) $bytesProcessed += $bytesRead [int]$percentageCompleted = $bytesProcessed / $response.ContentLength * 100 if ($percentageCompleted -gt 0) { if ($percentageCompletedPrev -ne $percentageCompleted) { $percentageCompletedPrev = $percentageCompleted Write-Progress -Activity "Downloading file '$FileName'" ` -Status ("{0:P} completed, {1:N2}MB of {2:N2}MB" -f ($percentageCompleted / 100), ($bytesProcessed / 1MB), ($response.ContentLength / 1MB)) ` -PercentComplete ($percentageCompleted) } } else { Write-Verbose -Message "Could not determine the ContentLength of '$Uri'" } } while ($bytesRead -gt 0) } } $response | Add-Member -Name FileName -MemberType NoteProperty -Value $FileName -PassThru } } catch { Write-Error -Exception $_.Exception } finally { if ($response) { $response.Close() } if ($remoteStream) { $remoteStream.Close() } if ($localStream) { $localStream.Close() } } } } $start = Get-Date #TODO: This needs to go into config $offlineNode = $true if (-not $FileName) { $internalUri = New-Object System.Uri($Uri) $tempFileName = $internalUri.Segments[$internalUri.Segments.Count - 1] if (Test-FileName -Path $tempFileName) { $FileName = $tempFileName $PSBoundParameters.FileName = $FileName } } $lab = Get-Lab -ErrorAction SilentlyContinue if (-not $lab) { $lab = Get-LabDefinition -ErrorAction SilentlyContinue $doNotGetVm = $true } if ($lab.DefaultVirtualizationEngine -eq 'Azure') { if (Test-LabPathIsOnLabAzureLabSourcesStorage -Path $Path) { # We need to test first, even if it takes a second longer. if (-not $doNotGetVm) { $machine = Invoke-LabCommand -PassThru -NoDisplay -ComputerName $(Get-LabVM -IsRunning) -ScriptBlock { if (Get-NetConnectionProfile -IPv4Connectivity Internet -ErrorAction SilentlyContinue) { hostname } } -ErrorAction SilentlyContinue | Select-Object -First 1 Write-PSFMessage "Target path is on AzureLabSources, invoking the copy job on the first available Azure machine." $argumentList = $Uri, $Path, $FileName $argumentList += if ($NoDisplay) { $true } else { $false } $argumentList += if ($Force) { $true } else { $false } } if ($machine) { $result = Invoke-LabCommand -ActivityName "Downloading file from '$Uri'" -NoDisplay:$NoDisplay.IsPresent -ComputerName $machine -ScriptBlock (Get-Command -Name Get-LabInternetFileInternal).ScriptBlock -ArgumentList $argumentList -PassThru } elseif (Get-LabAzureSubscription -ErrorAction SilentlyContinue) { $PSBoundParameters.Remove('PassThru') | Out-Null $param = Sync-Parameter -Command (Get-Command Get-LabInternetFileInternal) -Parameters $PSBoundParameters $param['Path'] = $Path.Replace((Get-LabSourcesLocation), (Get-LabSourcesLocation -Local)) $result = Get-LabInternetFileInternal @param $fullName = Join-Path -Path $param.Path.Replace($FileName, '') -ChildPath (?? { $FileName } { $FileName } { $result.FileName }) $pathFilter = $fullName.Replace("$(Get-LabSourcesLocation -Local)\", '') Sync-LabAzureLabSources -Filter $pathFilter -NoDisplay } else { Write-ScreenInfo -Type Erro -Message "Unable to upload file to Azure lab sources - No VM is available and no Azure subscription was added to the lab`r`n Please at least execute New-LabDefinition and Add-LabAzureSubscription before using Get-LabInternetFile" return } } else { Write-PSFMessage "Target path is local, invoking the copy job locally." $PSBoundParameters.Remove('PassThru') | Out-Null $result = Get-LabInternetFileInternal @PSBoundParameters } } else { Write-PSFMessage "Target path is local, invoking the copy job locally." $PSBoundParameters.Remove('PassThru') | Out-Null try { $result = Get-LabInternetFileInternal @PSBoundParameters $end = Get-Date Write-PSFMessage "Download has taken: $($end - $start)" } catch { Write-Error -ErrorRecord $_ } } if ($PassThru) { New-Object PSObject -Property @{ Uri = $Uri Path = $Path FileName = ?? { $FileName } { $FileName } { $result.FileName } FullName = Join-Path -Path $Path -ChildPath (?? { $FileName } { $FileName } { $result.FileName }) Length = $result.ContentLength } } } #endregion Get-LabInternetFile #region Unblock-LabSources function Unblock-LabSources { param( [string]$Path = $global:labSources ) Write-LogFunctionEntry $lab = Get-Lab -ErrorAction SilentlyContinue if (-not $lab) { $lab = Get-LabDefinition -ErrorAction SilentlyContinue } if ($lab.DefaultVirtualizationEngine -eq 'Azure' -and $Path.StartsWith("\\")) { Write-PSFMessage 'Skipping the unblocking of lab sources since we are on Azure and lab sources are unblocked during Sync-LabAzureLabSources' return } if (-not (Test-Path -Path $Path)) { Write-Error "The path '$Path' could not be found" return } $type = Get-Type -GenericType AutomatedLab.DictionaryXmlStore -T String, DateTime try { if ($IsLinux -or $IsMacOs) { $cache = $type::Import((Join-Path -Path (Get-LabConfigurationItem -Name LabAppDataRoot) -ChildPath 'Stores/Timestamps.xml')) } else { $cache = $type::ImportFromRegistry('Cache', 'Timestamps') } Write-PSFMessage 'Imported Cache\Timestamps from registry/file store' } catch { $cache = New-Object $type Write-PSFMessage 'No entry found in the registry at Cache\Timestamps' } if (-not $cache['LabSourcesLastUnblock'] -or $cache['LabSourcesLastUnblock'] -lt (Get-Date).AddDays(-1)) { Write-PSFMessage 'Last unblock more than 24 hours ago, unblocking files' if (-not ($IsLinux -or $IsMacOs)) { Get-ChildItem -Path $Path -Recurse | Unblock-File } $cache['LabSourcesLastUnblock'] = Get-Date if ($IsLinux -or $IsMacOs) { $cache.Export((Join-Path -Path (Get-LabConfigurationItem -Name LabAppDataRoot) -ChildPath 'Stores/Timestamps.xml')) } else { $cache.ExportToRegistry('Cache', 'Timestamps') } Write-PSFMessage 'LabSources folder unblocked and new timestamp written to Cache\Timestamps' } else { Write-PSFMessage 'Last unblock less than 24 hours ago, doing nothing' } Write-LogFunctionExit } #endregion Unblock-LabSources #region Set-LabVMDescription function Set-LabVMDescription { [CmdletBinding()] param ( [hashtable]$Hashtable, [string]$ComputerName ) Write-LogFunctionEntry $t = Get-Type -GenericType AutomatedLab.SerializableDictionary -T String, String $d = New-Object $t foreach ($kvp in $Hashtable.GetEnumerator()) { $d.Add($kvp.Key, $kvp.Value) } $sb = New-Object System.Text.StringBuilder $xmlWriterSettings = New-Object System.Xml.XmlWriterSettings $xmlWriterSettings.ConformanceLevel = 'Auto' $xmlWriter = [System.Xml.XmlWriter]::Create($sb, $xmlWriterSettings) $d.WriteXml($xmlWriter) Get-LWHypervVm -Name $ComputerName -ErrorAction SilentlyContinue | Set-VM -Notes $sb.ToString() Write-LogFunctionExit } #endregion Set-LabVMDescription #region Get-LabSourcesLocationInternal function Get-LabSourcesLocationInternal { param ( [switch]$Local ) $lab = $global:AL_CurrentLab $defaultEngine = 'HyperV' $defaultEngine = if ($lab) { $lab.DefaultVirtualizationEngine } if ($defaultEngine -eq 'kvm' -or ($IsLinux -and $Local.IsPresent)) { if (-not (Get-PSFConfigValue -FullName AutomatedLab.LabSourcesLocation)) { Set-PSFConfig -Module AutomatedLab -Name LabSourcesLocation -Description 'Location of lab sources folder' -Value $home/automatedlabsources -PassThru | Register-PSFConfig } Get-PSFConfigValue -FullName AutomatedLab.LabSourcesLocation } elseif (($defaultEngine -eq 'HyperV' -or $Local) -and (Get-PSFConfigValue AutomatedLab.LabSourcesLocation)) { Get-PSFConfigValue -FullName AutomatedLab.LabSourcesLocation } elseif ($defaultEngine -eq 'HyperV' -or $Local) { $hardDrives = (Get-CimInstance -NameSpace Root\CIMv2 -Class Win32_LogicalDisk | Where-Object DriveType -In 2, 3).DeviceID | Sort-Object -Descending $folders = foreach ($drive in $hardDrives) { if (Test-Path -Path "$drive\LabSources") { "$drive\LabSources" } } if ($folders.Count -gt 1) { Write-PSFMessage -Level Warning "The LabSources folder is available more than once ('$($folders -join "', '")'). The LabSources folder must exist only on one drive and in the root of the drive." } $folders } elseif ($defaultEngine -eq 'Azure') { try { (Get-LabAzureLabSourcesStorage -ErrorAction Stop).Path } catch { Get-LabSourcesLocationInternal -Local } } else { Get-LabSourcesLocationInternal -Local } } #endregion Get-LabSourcesLocationInternal #region Update-LabSysinternalsTools function Update-LabSysinternalsTools { if ($IsLinux -or $IsMacOs) { return } #Update SysInternals suite if needed $type = Get-Type -GenericType AutomatedLab.DictionaryXmlStore -T String, DateTime try { #https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netcore-2.0#System_Net_SecurityProtocolType_SystemDefault if ($PSVersionTable.PSVersion.Major -lt 6 -and [Net.ServicePointManager]::SecurityProtocol -notmatch 'Tls12') { Write-PSFMessage -Message 'Adding support for TLS 1.2' [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12 } } catch { Write-PSFMessage -Level Warning -Message 'Adding TLS 1.2 to supported security protocols was unsuccessful.' } try { Write-PSFMessage -Message 'Get last check time of SysInternals suite' if ($IsLinux -or $IsMacOs) { $timestamps = $type::Import((Join-Path -Path (Get-LabConfigurationItem -Name LabAppDataRoot) -ChildPath 'Stores/Timestamps.xml')) } else { $timestamps = $type::ImportFromRegistry('Cache', 'Timestamps') } $lastChecked = $timestamps.SysInternalsUpdateLastChecked Write-PSFMessage -Message "Last check was '$lastChecked'." } catch { Write-PSFMessage -Message 'Last check time could not be retrieved. SysInternals suite never updated' $lastChecked = Get-Date -Year 1601 $timestamps = New-Object $type } if ($lastChecked) { $lastChecked = $lastChecked.AddDays(7) } if ((Get-Date) -gt $lastChecked) { Write-PSFMessage -Message 'Last check time is more then a week ago. Check web site for update.' $sysInternalsUrl = Get-LabConfigurationItem -Name SysInternalsUrl $sysInternalsDownloadUrl = Get-LabConfigurationItem -Name SysInternalsDownloadUrl try { Write-PSFMessage -Message 'Web page downloaded' $webRequest = Invoke-WebRequest -Uri $sysInternalsURL -UseBasicParsing $pageDownloaded = $true } catch { Write-PSFMessage -Message 'Web page could not be downloaded' Write-ScreenInfo -Message "No connection to '$sysInternalsURL'. Skipping." -Type Error $pageDownloaded = $false } if ($pageDownloaded) { $updateStart = $webRequest.Content.IndexOf('Updated') + 'Updated:'.Length $updateFinish = $webRequest.Content.IndexOf('</p>', $updateStart) $updateStringFromWebPage = $webRequest.Content.Substring($updateStart, $updateFinish - $updateStart).Trim() Write-PSFMessage -Message "Update string from web page: '$updateStringFromWebPage'" $type = Get-Type -GenericType AutomatedLab.DictionaryXmlStore -T String, String try { if ($IsLinux -or $IsMacOs) { $versions = $type::Import((Join-Path -Path (Get-LabConfigurationItem -Name LabAppDataRoot) -ChildPath 'Stores/Versions.xml')) } else { $versions = $type::ImportFromRegistry('Cache', 'Versions') } } catch { $versions = New-Object $type } Write-PSFMessage -Message "Update string from registry: '$currentVersion'" if ($versions['SysInternals'] -ne $updateStringFromWebPage) { Write-ScreenInfo -Message 'Performing update of SysInternals suite and lab sources directory now' -Type Warning -TaskStart Start-Sleep -Seconds 1 # Download Lab Sources $null = New-LabSourcesFolder -Force -ErrorAction SilentlyContinue # Download SysInternals suite $tempFilePath = [System.IO.Path]::GetTempFileName() $tempFilePath = Rename-Item -Path $tempFilePath -NewName ([System.IO.Path]::ChangeExtension($tempFilePath, '.zip')) -PassThru Write-PSFMessage -Message "Temp file: '$tempFilePath'" try { Invoke-WebRequest -Uri $sysInternalsDownloadURL -UseBasicParsing -OutFile $tempFilePath $fileDownloaded = $true Write-PSFMessage -Message "File '$sysInternalsDownloadURL' downloaded" } catch { Write-ScreenInfo -Message "File '$sysInternalsDownloadURL' could not be downloaded. Skipping." -Type Error -TaskEnd $fileDownloaded = $false } if ($fileDownloaded) { if (-not ($IsLinux -or $IsMacOs)) { Unblock-File -Path $tempFilePath } #Extract files to Tools folder if (-not (Test-Path -Path "$labSources\Tools")) { Write-PSFMessage -Message "Folder '$labSources\Tools' does not exist. Creating now." New-Item -ItemType Directory -Path "$labSources\Tools" | Out-Null } if (-not (Test-Path -Path "$labSources\Tools\SysInternals")) { Write-PSFMessage -Message "Folder '$labSources\Tools\SysInternals' does not exist. Creating now." New-Item -ItemType Directory -Path "$labSources\Tools\SysInternals" | Out-Null } else { Write-PSFMessage -Message "Folder '$labSources\Tools\SysInternals' exist. Removing it now and recreating it." Remove-Item -Path "$labSources\Tools\SysInternals" -Recurse | Out-Null New-Item -ItemType Directory -Path "$labSources\Tools\SysInternals" | Out-Null } Write-PSFMessage -Message 'Extracting files' Microsoft.PowerShell.Archive\Expand-Archive -Path $tempFilePath -DestinationPath "$labSources\Tools\SysInternals" -Force Remove-Item -Path $tempFilePath #Update registry $versions['SysInternals'] = $updateStringFromWebPage if ($IsLinux -or $IsMacOs) { $versions.Export((Join-Path -Path (Get-LabConfigurationItem -Name LabAppDataRoot) -ChildPath 'Stores/Versions.xml')) } else { $versions.ExportToRegistry('Cache', 'Versions') } $timestamps['SysInternalsUpdateLastChecked'] = Get-Date if ($IsLinux -or $IsMacOs) { $timestamps.Export((Join-Path -Path (Get-LabConfigurationItem -Name LabAppDataRoot) -ChildPath 'Stores/Timestamps.xml')) } else { $timestamps.ExportToRegistry('Cache', 'Timestamps') } Write-ScreenInfo -Message "SysInternals Suite has been updated and placed in '$labSources\Tools\SysInternals'" -Type Warning -TaskEnd } } } } } #endregion Update-LabSysinternalsTools #region Register-LabArgumentCompleters function Register-LabArgumentCompleters { $commands = Get-Command -Module AutomatedLab*, PSFileTransfer | Where-Object { $_.Parameters -and $_.Parameters.ContainsKey('ComputerName') } Register-PSFTeppArgumentCompleter -Command $commands -Parameter ComputerName -Name 'AutomatedLab-ComputerName' } #endregion Register-LabArgumentCompleters #region Test-FileName function Test-FileName { param( [Parameter(Mandatory)] [string]$Path ) $fi = $null try { $fi = New-Object System.IO.FileInfo($Path) } catch [ArgumentException] { } catch [System.IO.PathTooLongException] { } catch [NotSupportedException] { } if ([object]::ReferenceEquals($fi, $null) -or $fi.Name -eq '') { return $false } else { return $true } } #endregion Test-FileName |