Uplift.Core.ps1
$ErrorActionPreference = "Stop" # appinsight helpers $hereFolder = $PSScriptRoot . "$hereFolder/Uplift.AppInsights.ps1" function Write-UpliftMessage { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Scope="Function")] param( $message, $level = "INFO" ) $stamp = $(get-date -f "MM-dd-yyyy HH:mm:ss.fff") $messageColor = "White" if($level -eq "INFO") { $messageColor = "Green" } if($level -eq "VERBOSE") { $messageColor = "Blue" } if($level -eq "DEBUG") { $messageColor = "DarkGray" } if($level -eq "ERROR") { $messageColor = "Red" } if($level -eq "WARN") { $messageColor = "Yellow" } if($ENV:UPLF_LOG_LEVEL -ne "DEBUG") { if($level -eq "DEBUG" -or $level -eq "VERBOSE") { return; } } $level = $level.PadRight(7) # use [environment]::UserDomainName / [environment]::UserName # $env:USERDOMAIN won't work on non-windows platforms $logMessage = "UPLIFT : $stamp : $level : $([environment]::UserDomainName)/$([environment]::UserName) : $message" Write-Host $logMessage ` -ForegroundColor $messageColor } function Write-UpliftInfoMessage($message) { Write-UpliftMessage "$message" "INFO" } function Write-UpliftDebugMessage($message) { Write-UpliftMessage "$message" "DEBUG" } function Write-UpliftVerboseMessage($message) { Write-UpliftMessage "$message" "VERBOSE" } function Write-UpliftErrorMessage($message) { Write-UpliftMessage "$message" "ERROR" } function Write-UpliftWarnMessage($message) { Write-UpliftMessage "$message" "WARN" } function Confirm-UpliftExitCode { Param( [Parameter(Mandatory=$True)] $code, [Parameter(Mandatory=$True)] $message, [Parameter(Mandatory=$False)] $allowedCodes = @( 0 ) ) $valid = $false Write-UpliftMessage "Checking exit code: $code with allowed values: $allowedCodes" foreach ($allowedCode in $allowedCodes) { if($code -eq $allowedCode) { $valid = $true break } } if( $valid -eq $false) { $error_message = "[!] $message - exit code is: $code but allowed values were: $allowedCodes" Write-UpliftMessage $error_message throw $error_message } else { Write-UpliftMessage "[+] exit code is: $code within allowed values: $allowedCodes" } } function New-UpliftFolder { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Scope="Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope="Function")] param( $path ) New-Item -ItemType Directory -Force -Path $path | out-null } function New-UpliftDSCConfigurationData { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Scope="Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope="Function")] param( ) return @{ AllNodes = @( @{ NodeName = 'localhost' PSDscAllowPlainTextPassword = $true RetryCount = 10 RetryIntervalSec = 30 } ) } } function Start-UpliftDSCConfiguration { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Scope="Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope="Function")] Param( [Parameter(Mandatory=$True)] [System.Management.Automation.ConfigurationInfo] $Name, [Parameter(Mandatory=$False)] $Config = $null, [Parameter(Mandatory=$False)] $ExpectInDesiredState = $null ) $skipDscCheck = $false if($null -eq $Config) { $Config = New-UpliftDSCConfigurationData } # should check? if( ( $null -ne $ENV:UPLF_DSC_CHECK ) -and ($null -eq $ExpectInDesiredState) ) { $ExpectInDesiredState = $true } if($null -ne $ENV:UPLF_DSC_CHECK_SKIP ) { $skipDscCheck = $true } $dscFolder = Get-UpliftEnvVariable 'UPLF_DSC_CONFIG_PATH' 'default value' 'C:/_uplift_dsc' $dscConfigFolder = [System.IO.Path]::Combine($dscFolder, $Name) Write-UpliftMessage "[~] ensuring folder: $dscFolder for config: $Name" New-UpliftFolder $dscFolder if(Test-Path $dscConfigFolder) { Write-UpliftMessage "[~] clearing previous configuration: $dscConfigFolder" Remove-Item $dscConfigFolder -Recurse -Force } else { Write-UpliftMessage "[~] previous configuration dose not exist: $dscConfigFolder" } Write-UpliftMessage "Compiling new configuration: $Name" & $Name -ConfigurationData $Config -OutputPath $dscConfigFolder | Out-Null Write-UpliftMessage "Starting configuration: $Name" $result = $null $inDesiredState = $null $elapsedMilliseconds = $null $dscStopwatch = [System.Diagnostics.Stopwatch]::StartNew() $dscException = $null $dscConfigId = (New-Guid).ToString() try { if($null -ne $ENV:UPLF_DSC_VERBOSE) { Start-DscConfiguration -Path $dscConfigFolder -Force -Wait -Verbose } else { Start-DscConfiguration -Path $dscConfigFolder -Force -Wait } $dscStopwatch.Stop() $elapsedMilliseconds = $dscStopwatch.ElapsedMilliseconds Write-UpliftMessage "Completed in: $elapsedMilliseconds" if($skipDscCheck -eq $false) { Write-UpliftMessage "Testing configuration: $Name" $result = Test-DscConfiguration -Path $dscConfigFolder $inDesiredState = $result.InDesiredState if($ExpectInDesiredState -eq $true) { Write-UpliftMessage "Expecting DSC [$Name] in a desired state" if($result.InDesiredState -ne $true) { $message = "[~] DSC: $Name - is NOT in a desired state: $result" Write-UpliftMessage $message if ($null -ne $result.ResourcesNotInDesiredState) { foreach($resource in $result.ResourcesNotInDesiredState) { Write-UpliftMessage $resource } } throw $message } else { $message = "[+] DSC: $Name is in a desired state" Write-UpliftMessage $message } } else { Write-UpliftMessage "[+] No check for DSC [$Name] is done. Skipping." } } else { Write-UpliftMessage "[+] Skipping testing configuration: $Name" } } catch { Write-UpliftErrorMessage $_ $dscException = $_ } finally { if($dscStopwatch.IsRunning -eq $true) { $dscStopwatch.Stop() $elapsedMilliseconds = $dscStopwatch.ElapsedMilliseconds } try { Confirm-UpliftUpliftAppInsightClient $success = $false if($ExpectInDesiredState -eq $True) { $success = ($inDesiredState -eq $True) } else { $success = ($null -eq $dscException) } $eventProps = New-UpliftAppInsighsProperties @{ "dsc_name" = $Name.Name "dsc_expect" = $ExpectInDesiredState "dsc_state" = $inDesiredState "dsc_elapsed" = $elapsedMilliseconds "dsc_success" = $success "dsc_config_id" = $dscConfigId } if($null -ne $dscException) { $eventProps.Add("dsc_error", $dscException.ToString()) } New-UpliftTrackEvent "uplift-core.dsc" $eventProps if($null -ne $dscException) { New-UpliftTrackException $dscException.Exception $eventProps $null } } catch { Write-UpliftWarnMessage "[!] Cannot use AppInsight, please report this error or use UPLF_NO_APPINSIGHT env variable to disable it." Write-UpliftWarnMessage "[!] $_" } } return $result } function Write-UpliftVariableValue($name, $value, $indentation) { $isSecterVariable = Test-UpliftSecretVariableName $name if([String]::IsNullOrEmpty($indentation) -eq $true) { $indentation = "" } if($isSecterVariable -eq $true) { Write-UpliftMessage "$indentation[ENV:$name]: ******" } else { Write-UpliftMessage "$indentation[ENV:$name]: $value" } } function Write-UpliftEnv($showAll = $false) { Write-UpliftMessage "Running as: $($env:UserDomain)\$($env:UserName)" Write-UpliftMessage "Uplift environmanet variables:" $props = $null if($showAll -eq $false) { $props = Get-ChildItem Env: ` | Where-Object { ( $_.Name.ToUpper().StartsWith("UPLIFT_") -eq $True) -or ( $_.Name.ToUpper().StartsWith("UPLF_") -eq $True) } } else { $props = Get-ChildItem Env: ` } foreach($prop in $props) { $name = $prop.Name $value = $prop.Value Write-UpliftVariableValue $name $value "`t" } } function Test-UpliftSecretVariableName($name) { return $name.ToUpper().Contains("_KEY") -or $name.ToUpper().Contains("_PASSWORD") -or $name.ToUpper().Contains("_TOKEN") } function Get-UpliftEnvVariable($name, $message, $defaultValue) { $x = $name $value = $null try { $value = (get-item env:$x -ErrorAction SilentlyContinue).Value } catch { $value = $null } if([String]::IsNullOrEmpty($value) -eq $true) { $errorMessage = "[~] cannot find env variable by name: $name - $message, will try default value if provided" Write-UpliftMessage $errorMessage if($null -ne $defaultValue) { Write-UpliftMessage " - using default value" Write-UpliftVariableValue $name $defaultValue return $defaultValue } else { throw "Cannot find env variable by name: $name - $message, and no default value wer provided" } throw $errorMessage } else { Write-UpliftVariableValue $name $value } return $value } function Install-UpliftInstallPackage { Param( [Parameter(Mandatory=$True)] $filePath, [Parameter(Mandatory=$True)] $packageName, [Parameter(Mandatory=$True)] $silentArgs, [Parameter(Mandatory=$True)] $validExitCodes, [Parameter(Mandatory=$False)] $fileType, [Parameter(Mandatory=$False)] $chocolateyInstallerPath ) # this is a wrap up of boxstarter and chocolatey # idea is to get KBs installed in "offline" mode out of uplift file resources # https://github.com/riezebosch/BoxstarterPackages/blob/master/KB2919355/Tools/ChocolateyInstall.ps1 # https://github.com/chocolatey/choco/blob/e96fb159e0957d9e2fee1e738d42dcc414957c91/src/chocolatey.resources/helpers/functions/Install-ChocolateyPackage.ps1 if($null -eq $chocolateyInstallerPath) { $chocolateyInstallerPath = "C:\ProgramData\chocolatey\helpers\chocolateyInstaller.psm1" } if($null -eq $fileType) { $fileType = "msu" } if (Get-HotFix -id $packageName -ea SilentlyContinue) { Write-UpliftMessage "Skipping installation, package is already installed: $packageName" return 0 } Write-UpliftMessage "Importing Chocolatey install helper: $chocolateyInstallerPath" Import-Module $chocolateyInstallerPath Write-UpliftMessage "Installing package:" Write-UpliftMessage "`t - PackageName: $packageName" Write-UpliftMessage "`t - SilentArgs: $silentArgs" Write-UpliftMessage "`t - File: $filePath" Write-UpliftMessage "`t - FileType: $fileType" Write-UpliftMessage "`t - ValidExitCodes: $validExitCodes" if( [System.IO.File]::Exists($filePath) -eq $false) { $errroMessage = "File does not exist: $filePath" Write-UpliftMessage $errroMessage $fileDirPath = [System.IO.Path]::GetDirectoryName($filePath) if((Test-Path -Path $fileDirPath )){ Write-UpliftMessage "Showing folder content: $fileDirPath" Get-ChildItem -Path $fileDirPath } else { Write-UpliftMessage "Folder does not exist: $fileDirPath" } throw $errroMessage } $result = Install-ChocolateyInstallPackage -PackageName $packageName ` -SilentArgs $silentArgs ` -File $filePath ` -FileType $fileType ` -ValidExitCodes $validExitCodes Write-UpliftMessage "Finished installation, result: $result" return $result } function Wait-UpliftProcess() { Param( [Parameter(Mandatory=$True)] $processName ) while( $null -ne ( get-process | Where-Object { $_.ProcessName.ToLower() -eq $processName } ) ) { Write-UpliftMessage "$processName is still running... sleeping 5 sec.." Start-Sleep -Seconds 5 } } function Invoke-UpliftIISReset { Write-UpliftMessage "Restarting IIS..." iisreset Write-UpliftMessage "Completed restarting IIS!" Invoke-UpliftIISPoolStart } function Invoke-UpliftIISPoolStart { Import-Module WebAdministration Write-UpliftMessage "Bringing up IIS pools..." $pools = Get-ChildItem -Path 'IIS:\AppPools' foreach($pool in $pools) { $name = $pool.Name Write-UpliftMessage "Bringing up IIS pool: $name" Start-WebAppPool -Name $name } } function Find-UpliftFileInPath { Param( [Parameter(Mandatory=$True)] $path, [Parameter(Mandatory=$False)] $ext = "exe" ) $folder = $path # file or folder? if($path.ToUpper().EndsWith($ext.ToUpper()) -eq $true) { $folder = Split-Path $path } else { $folder = $path } Write-UpliftMessage "Looking for '$ext' file in folder: $path" $exeFile = Get-ChildItem $folder -Filter "*.$ext" | Select-Object -First 1 Write-UpliftMessage " - found: $($exeFile.FullName)" if( ($null -eq $exeFile) -or ($null -eq $exeFile.Name) ) { throw "Cannot find any '$ext' files in folder: $path" } return $exeFile.FullName } function Install-UpliftPSModules { Param( [Parameter(Mandatory=$True)] $packages ) foreach($package in $packages ) { $maxAttempt = 5 $attempt = 0 $success = $false $name = $package["Id"] $version = $package["Version"] while ( ($attempt -le $maxAttempt) -and (-not $success) ) { try { Write-UpliftMessage "`t[$attempt/$maxAttempt] installing package: $name $version" Write-UpliftMessage "`tchecking is package exists: $name $version" $existingModule = Get-Module -ListAvailable -Name $name if($existingModule) { Write-UpliftMessage "`t`tpackage exists, nothing to do: $name $version" } else { Write-UpliftMessage "`t`tpackage does not exist, installing: $name $version" if ([System.String]::IsNullOrEmpty($version) -eq $true) { Install-Module -Name $name -Force; } else { Install-Module -Name $name -RequiredVersion $version -Force; } } Write-UpliftMessage "`t[$attempt/$maxAttempt] finished installing package: $name $version" $success = $true } catch { $exception = $_.Exception Write-UpliftMessage "`t[$attempt/$maxAttempt] coudn't install package: $name $version" Write-UpliftMessage "`t[$attempt/$maxAttempt] error was: $exception" $attempt = $attempt + 1 } } if($success -eq $false) { $errorMessage = "`t[$attempt/$maxAttempt] coudn't install package: $name $version" Write-UpliftMessage $errorMessage throw $errorMessage } } } function New-UpliftPSRepository { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Scope="Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope="Function")] param( $name, $source, $publish = $null, $installPolicy = "Trusted" ) $repo = Get-PSRepository ` -Name $name ` -ErrorAction SilentlyContinue if($null -eq $repo) { Write-UpliftInfoMessage " [~] Regestering repo: $name" Write-UpliftInfoMessage " - path: $source" Write-UpliftInfoMessage " - installPolicy: $installPolicy" if($null -eq $publish) { Write-UpliftInfoMessage " - path: $source" Register-PSRepository -Name $name ` -SourceLocation $source ` -PublishLocation $source ` -InstallationPolicy $installPolicy } else { Write-UpliftInfoMessage " - path: $publish" Register-PSRepository -Name $name ` -SourceLocation $source ` -PublishLocation $publish ` -InstallationPolicy $installPolicy } } else { Write-UpliftInfoMessage "Repo exists: $name" } } function Set-UpliftDCPromoSettings { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Scope="Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope="Function")] param( $domainAdminPass ) # https://aryannava.com/2012/01/05/administrator-password-required-error-in-dcpromo-exe/ $message = "Executing 'net user Administrator /passwordreq:yes' to bypass dcpromo errors" Write-UpliftMessage $message net user Administrator $domainAdminPass /passwordreq:yes Confirm-UpliftExitCode $LASTEXITCODE "Failed to execute: $message" } function Disable-UpliftIP6Interface { Disable-NetAdapterBinding -InterfaceAlias "Ethernet" ` -ComponentID ms_tcpip6 ` -ErrorAction SilentlyContinue Disable-NetAdapterBinding -InterfaceAlias "Ethernet 2" ` -ComponentID ms_tcpip6 ` -ErrorAction SilentlyContinue } function Install-UpliftPS6Module() { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Scope = "Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope = "Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Scope = "Function")] param( $moduleName, $version, $repository ) Install-UpliftPSModule $moduleName $version $repository $True } function Install-UpliftPSModule() { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Scope = "Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope = "Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Scope = "Function")] param( $moduleName, $version, $repository, $usePS6 = $false ) if( [String]::IsNullOrEmpty($version) -eq $True) { $version = $null } Write-UpliftMessage "Installing module: $moduleName version: $version, repository: $repository" Write-UpliftMessage "Looking for the latest module $moduleName" $moduleDefinition = Find-Module -Name $moduleName ` | Select-Object Version, Repository ` | Sort-Object Version -Descending ` | Select-Object -First 1 Write-UpliftMessage "Found latest module" Write-UpliftMessage $moduleDefinition Write-UpliftMessage " - version : $($moduleDefinition.Version)" Write-UpliftMessage " - repository: $($moduleDefinition.Repository)" if([String]::IsNullOrEmpty($version) -eq $True) { if($null -eq $moduleDefinition) { throw "Failed to install module $moduleName - repo/version were not provided, and cannot find latest in any repo" } $version = $moduleDefinition.Version if([String]::IsNullOrEmpty($repository) -eq $True) { $repository = $moduleDefinition.Repository } Write-UpliftMessage "Installing latest ($version) $moduleName version: $version, repository: $repository" if($usePS6 -eq $True) { pwsh -c "Install-Package $moduleName -Source $repository -RequiredVersion $version -Force" Confirm-UpliftExitCode $LASTEXITCODE "Cannot install PS6 module: $moduleName, version: $version repository: $repository" } else { Install-Package $moduleName -Source $repository -RequiredVersion $version -Force } } else { if([String]::IsNullOrEmpty($repository) -eq $True) { $repository = $moduleDefinition.Repository } Write-UpliftMessage "Installing specified version $moduleName version: $version, repository: $repository" if($usePS6 -eq $True) { pwsh -c "Install-Package $moduleName -Source $repository -Force -RequiredVersion $version" Confirm-UpliftExitCode $LASTEXITCODE "Cannot install PS6 module: $moduleName, version: $version repository: $repository" } else { Install-Package $moduleName -Source $repository -Force -RequiredVersion $version } } Write-UpliftMessage "Checking installed module: $moduleName" if($usePS6 -eq $True) { pwsh -c "Get-InstalledModule $moduleName" Confirm-UpliftExitCode $LASTEXITCODE "Cannot find installed PS6 module: $moduleName" } else { # TODO } } function Repair-UpliftIISApplicationHostFile { # https://forums.iis.net/t/1160389.aspx # You may be able to get into a working state by deleting # the existing keys inside the configProtectedData section in applicationhost.config # and then running "%windir%\system32\inetsrv\iissetup.exe /install SharedLibraries" # - note that any existing encrypted properties in the cofig file is lost at this point, # this should however setup up the encryption keys correctly to be able # to write new encrypted properties. $filePath = "C:\Windows\System32\inetsrv\config\applicationHost.config" $filePathFlagFile = "C:\Windows\System32\inetsrv\config\applicationHost.config.metabox-patch-flag" $shouldUpdate = ((Test-Path $filePathFlagFile) -eq $false) if($shouldUpdate) { Write-UpliftMessage "Fixing web server feature install..." Write-UpliftMessage "Running: Install-WindowsFeature web-server -IncludeAllSubFeature" Install-WindowsFeature Web-Server -IncludeAllSubFeature | Out-Null Write-UpliftMessage "Fixing up machine keys..." # fix machine keys for IIS after sysprep # http://rcampi.blogspot.com.au/2012/02/iis-75-cloning-machine-keys.html Repair-UpliftMachineKeys Write-UpliftMessage "Cleaning up old machine keys..." Remove-UpliftOldMachineKeys Write-UpliftMessage "Patching IISApplicationHostFile: $filePath" $xml = [xml](Get-Content $filePath) $configProtectedDataNode = $xml.configuration.configProtectedData Write-UpliftMessage " - configProtectedData nodes count: $($configProtectedDataNode.providers.ChildNodes.Count)" if ($configProtectedDataNode.ChildNodes.Count -gt 0) { Write-UpliftMessage " - cleaning up section: configuration.configProtectedData.providers" $configProtectedDataNode.RemoveChild($configProtectedDataNode.ChildNodes[0]) Write-UpliftMessage " - saving file: $filePath" $xml.Save($filePath) } else { Write-UpliftMessage " - can't find sections in configuration.configProtectedData" } Write-UpliftMessage "Running c:\windows\system32\inetsrv\iissetup.exe /install SharedLibraries, expecting '0' or 'Failed = 0x80070005'" c:\windows\system32\inetsrv\iissetup.exe /install SharedLibraries Write-UpliftMessage " - adding flag file: $filePathFlagFile" "yes" > $filePathFlagFile } else { Write-UpliftMessage "IISApplicationHostFile has already been patched..." } } function Remove-UpliftOldMachineKeys { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Scope = "Function")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Scope = "Function")] param( ) $path = "C:\ProgramData\Microsoft\Crypto\RSA\S-1-5-18" if(Test-Path $path) { Write-UpliftMessage "Deleting path: $path" Remove-Item $path -Recurse -Force } else { Write-UpliftMessage "Path was already deleted: $path" } } function Repair-UpliftMachineKeys { # http://rcampi.blogspot.com.au/2012/02/iis-75-cloning-machine-keys.html #Variables $regGUIDPath = "HKLM:\SOFTWARE\Microsoft\Cryptography" $regGuidName = "MachineGuid" $machineKeyFolder = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys" $key1 = "c2319c42033a5ca7f44e731bfd3fa2b5_" #$key2 = "7a436fe806e483969f48a894af2fe9a1_" $key2 = "76944fb33636aeddb9590521c2e8815a_" #Get CurRename-Itemt GUID $machGUID = (Get-ItemProperty -Path $regGUIDPath -Name $regGuidName).MachineGuid #Rename-Itemame new one if it was created. If IIS starts and there is no key, it will create a new one with new GUID if(test-path $machineKeyFolder\$key1$machGUID){ Rename-Item "$machineKeyFolder\$key1$machGUID" "$key1$machGUID.OLD" } if(test-path $machineKeyFolder\$key2$machGUID){ Rename-Item "$machineKeyFolder\$key2$machGUID" "$key2$machGUID.OLD" } #Now find the oldest key and Rename-Itemame it using the new machine GUID $files = Get-ChildItem ("$machineKeyFolder\*.*") -include ("$key1*") | sort-object -property ($_.CreationTime) foreach ($file in $files) { $fileName = $file.Name if (!$fileName.EndsWith($machGUID)) { Copy-Item "$machineKeyFolder\$fileName" "$machineKeyFolder\$fileName.OLD" Rename-Item "$machineKeyFolder\$fileName" "$key1$machGUID" break } } $files = Get-ChildItem ("$machineKeyFolder\*.*") -include ("$key2*") | sort-object -property ($_.CreationTime) foreach ($file in $files) { $fileName = $file.Name if (!$fileName.EndsWith($machGUID)) { Copy-Item "$machineKeyFolder\$fileName" "$machineKeyFolder\$fileName.OLD" Rename-Item "$machineKeyFolder\$fileName" "$key2$machGUID" break } } } |