Remote Computer File Transfer PowerShell script does not do logging correctly - powershell

This is my remote computer file transfer PowerShell script:
#--------------------------------------------------------[Initialisations]-------------------------------------------------------
#Clears the contents of the DNS client cache
Clear-DnsClientCache
#Loading script configuration
$configuration = Get-Content '.\Resources\Remote Computer File Transfer Configuration.cfg' | Select-Object | ConvertFrom-StringData
#Initializing report file
New-Item -Path $configuration.ReportFile -ItemType File
$fileList = Get-Content -Path $configuration.FileList
$computerList = Get-Content -Path $configuration.ComputerList
#Initializing file counters
$successfulTransfers = 0
$failedTransfers = 0
#---------------------------------------------------------[Functions]----------------------------------------------------------
function Write-Log
{
param
(
[Parameter(Position = 0, Mandatory = $false)]
[String]
$OperationSuccessful,
[Parameter(Position = 1, Mandatory = $false)]
[String]
$Message,
[Parameter(Position = 2, Mandatory = $false)]
[String]
$LogSeparator
)
if($null -eq $LogSeparator)
{
$timestamp = Get-Date -Format "yyyy.MM.dd. HH:mm:ss:fff"
$logEntry = $timestamp + " - " + $Message
}
else
{
$logEntry = $LogSeparator
}
if($OperationSuccessful -eq "Successful")
{
Write-Host $logEntry -ForegroundColor Green -BackgroundColor Black
}
elseif($OperationSuccessful -eq "Failed")
{
Write-Host $logEntry -ForegroundColor Red -BackgroundColor Black
}
elseif($OperationSuccessful -eq "Partial")
{
Write-Host $logEntry -ForegroundColor Blue -BackgroundColor Black
}
else
{
Write-Host $logEntry -ForegroundColor Yellow -BackgroundColor Black
}
Add-content -Path $configuration.LogFile -Value $logEntry
Add-content -Path $configuration.ReportFile -Value $logEntry
}
function Send-Report
{
param
(
[Parameter(Position = 0, Mandatory = $true)]
[string]
$FinalMessage
)
if($configuration.SendReport -eq "true")
{
$body = $configuration.Body + "`n" + $FinalMessage
Send-MailMessage -SmtpServer $configuration.SmtpServer `
-Port $configuration.Port `
-To $configuration.To `
-From $configuration.From `
-Subject $configuration.Subject `
-Body $body `
-Attachments $configuration.ReportFile
Remove-Item -Path $configuration.ReportFile
}
}
#---------------------------------------------------------[Execution]----------------------------------------------------------
Write-Log -LogSeparator $configuration.LogTitle
Write-Log -LogSeparator $configuration.LogSeparator
#Get credential from user input
$credential = Get-Credential
$message = "User " + $credential.UserName + " entered credentials"
Write-Log -Message $message
foreach($file in $fileList)
{
if((Test-Path -Path $file) -eq $true)
{
$message = "Successfully checked " + $file + " file - ready for transfer."
Write-Log -OperationSuccessful "Successful" -Message $message
}
else
{
$message = "Failed to access " + $file + " file. It does not exist."
Write-Log -OperationSuccessful "Failed" -Message $message
$message = "Script stopped - MISSING FILE ERROR"
Write-Log -OperationSuccessful "Failed" -Message $message
Write-Log -Message $configuration.LogSeparator
Send-Report -FinalMessage $message
Exit
}
}
$message = "Successfully accessed all files - ready for transfer"
Write-Log -OperationSuccessful "Successful" -Message $message
#Start file transfer
Write-Log -Message "Started file transfer"
foreach($computer in $computerList)
{
#Mapping network drive
if((Test-Connection -TargetName $computer -Quiet -Count 1) -eq $true)
{
$message = "Successfully accessed " + $computer + " remote computer"
Write-Log -OperationSuccessful "Successful"-Message $message
#Network path creation to D partition on the remote computer
$partition = "\D$"
$networkPath = "\\" + $computer + $partition
#Try to create network drive to D partition on the remote computer
if(New-PSDrive -Name "T" -PSProvider "FileSystem" -Root $networkPath -Credential $credential)
{
$message = "Successfully mapped network drive to D partition on the " + $computer + " remote computer"
Write-Log -OperationSuccessful "Successful" -Message $message
$driveMappingSuccessful = $true
}
else
{
$message = "Failed to map network drive to D partition on the " + $computer + " remote computer"
Write-Log -OperationSuccessful "Failed" -Message $message
#Network path creation to C partition on the remote computer
$partition = "\C$"
$networkPath = "\\" + $computer + $partition
#Try to create network drive to C partition on the remote computer
if(New-PSDrive -Name "T" -PSProvider "FileSystem" -Root $networkPath -Credential $credential)
{
$message = "Successfully mapped network drive to C partition on the " + $computer + " remote computer"
Write-Log -OperationSuccessful "Successful" -Message $message
$driveMappingSuccessful = $true
}
else
{
$message = "Failed to map network drive to C partition on the " + $computer + " remote computer - Credential not valid"
Write-Log -OperationSuccessful "Failed" -Message $message
$driveMappingSuccessful = $false
}
}
}
else
{
$message = "Failed to access " + $computer + " remote computer - OFFLINE"
Write-Log -OperationSuccessful "Failed" -Message $message
$driveMappingSuccessful = $false
}
if($driveMappingSuccessful)
{
$path = "T:\" + $configuration.TransferFolder
if((Test-Path $path) -eq $true)
{
$message = "Successfully accessed " + $path + " folder"
Write-Log -OperationSuccessful "Successful" -Message $message
$deployingFolderSuccessful = $true
}
else
{
$message = "Failed to access " + $path + " folder - MISSING FOLDER ERROR"
Write-Log -OperationSuccessful "Failed" -Message $message
try
{
New-Item -Path $path -ItemType "Directory"
}
catch
{
Write-Log -OperationSuccessful "Failed" -LogSeparator $_.Exception
}
if((Test-Path $path) -eq $true)
{
$message = "Successfully created " + $path + " folder"
Write-Log -OperationSuccessful "Successful" -Message $message
$deployingFolderSuccessful = $true
}
else
{
$message = "Failed to create " + $path + " folder"
Write-Log -OperationSuccessful "Failed" -Message $message
$deployingFolderSuccessful = $false
}
}
if($deployingFolderSuccessful)
{
$fileList = Get-Content -Path $configuration.FileList
foreach($file in $fileList)
{
#File name extraction from file full path
$fileName = Split-Path $file -leaf
try
{
Copy-Item -Path $file -Destination $path -Force
}
catch
{
Write-Log -OperationSuccessful "Failed" -LogSeparator $_.Exception
}
$transferDestination = Join-Path -Path $path -ChildPath $file
if(Test-Path -Path $transferDestination)
{
$message = "Successfully transferred " + $fileName + " file to " + $transferDestination + " folder"
Write-Log -OperationSuccessful "Successful" -Message $message
$successfulTransfers ++
}
else
{
$message = "Failed to transfer " + $fileName + " file to " + $path + " folder"
Write-Log -OperationSuccessful "Failed" -Message $message
$failedTransfers ++
}
}
}
else
{
$message = "Canceld file transfer to " + $computer + " remote computer"
Write-Log -OperationSuccessful "Failed" -Message $message
}
}
else
{
$message = "Canceld file transfer to " + $computer + " remote computer"
Write-Log -OperationSuccessful "Failed" -Message $message
}
#Network drive removal
if($driveMappingSuccessful)
{
Remove-PSDrive -Name "T"
}
}
$message = "Completed Remote Computer File Transfer PowerShell Script"
Write-Log -Message $message
if($successfulTransfers -gt 0)
{
$message = "Successfully transferred " + $successfulTransfers + " files to " + $Destination + " folder"
Write-Log -OperationSuccessful "Successful" -Message $message
}
if($failedTransfers -gt 0)
{
$message = "Failed to transfer " + $failedTransfers + " files to " + $Destination + " folder"
Write-Log -OperationSuccessful "Failed" -Message $message
}
if(($successfulTransfers -gt 0 ) -and ($failedTransfers -eq 0))
{
$message = "Successfully transferred all files to " + $Destination + " folder"
Write-Log -OperationSuccessful "Successful" -Message $message
}
elseif(($successfulTransfers -gt 0 ) -and ($failedTransfers -gt 0))
{
$message = "Successfully transferred some files to " + $Destination + " folder with some failed"
Write-Log -OperationSuccessful "Partial" -Message $message
}
elseif(($successfulTransfers -eq 0 ) -and ($failedTransfers -gt 0))
{
$message = "Failed to transfer any file to " + $Destination + " folder"
Write-Log -OperationSuccessful "Failed" -Message $message
}
Write-Log -Message $configuration.LogSeparator
#Sends email with detailed report and deletes temporary report log file
Send-Report -FinalMessage $message
This is .cfg file:
LogTitle = *********************************************** Remote Computer File Transfer PowerShell Script Log ************************************************
LogSeparator = ******************************************************************************************************************************************************
SendReport = true
LogFile = .\\Resources\\Remote Computer File Transfer Log.log
ReportFile = .\\Resources\\Report.log
FileList = .\\Resources\\File-Paths.txt
ComputerList = .\\Resources\\Computer-List.txt
TransferFolder = _INSTALL
SmtpServer = smtp.mail.com
Port = 25
To = sistem.administrators#company.com
From = powershell#company.com
Subject = Remote Computer File Transfer Report
Body = This is an automated message sent from PowerShell script. Remote Computer File Transfer PowerShell Script has finished executing.
Something is wrong with logging. It must be a problem with Write-Log function. The script actually does the job but nothing is written on the console by Write-Log function, and it just writes this in the picture and I can't figure out why.
Grateful in advance!

As commenten, the if($null -eq $LogSeparator) test will never succeed, because if not given, the parameter $LogSeparator will be an empty string, not $null.
The function you have would work if you change that to if(!$LogSeparator) or if([string]::IsNullOrWhiteSpace($LogSeparator))
The function could be simplified by using a parameter that validates to a certain set of possible values. Extra advantage is that you get autocompletion in the editor aswell so you don't have to worry about sending a parameter value with a typo.
Something like this perhaps:
function Write-Log {
param (
[Parameter(Position = 0, Mandatory = $false)]
[ValidateSet('Success', 'Fail', 'Partial', 'None')]
[String]$OperationResult = 'None',
[Parameter(Position = 1, Mandatory = $false)]
[String]$Message
)
$timestamp = Get-Date -Format "yyyy.MM.dd. HH:mm:ss:fff"
$logEntry = $timestamp + " - " + $Message
switch ($OperationResult) {
'Success' { $fg = 'Green'; $bg = 'Black'; break }
'Fail' { $fg = 'Red'; $bg = 'Black'; break }
'Partial' { $fg = 'Blue'; $bg = 'Black'; break } # Cyan would be easier to read I think
default { $logEntry = $Message
$fg = 'Yellow'; $bg = 'Black' }
}
Write-Host $logEntry -ForegroundColor $fg -BackgroundColor $bg
Add-content -Path $configuration.LogFile -Value $logEntry
Add-content -Path $configuration.ReportFile -Value $logEntry
}
Testing:
Write-Log -Message $configuration.LogTitle
Write-Log -Message $configuration.LogSeparator
$message = "Successfully checked file - ready for transfer."
Write-Log -OperationResult Success -Message $message
$message = "Script stopped - MISSING FILE ERROR"
Write-Log -OperationResult Fail -Message $message
$message = "Script did not complete"
Write-Log -OperationResult Partial -Message $message
Write-Log -Message $configuration.LogSeparator
Result in console:
*********************************************** Remote Computer File Transfer PowerShell Script Log ************************************************
******************************************************************************************************************************************************
2020.07.30. 21:34:53:959 - Successfully checked file - ready for transfer.
2020.07.30. 21:34:53:959 - Script stopped - MISSING FILE ERROR
2020.07.30. 21:34:53:959 - Script did not complete
******************************************************************************************************************************************************

Related

Convert DOC with macro to DOTM

I am trying to convert DOC files with macros to DOTM with macros. My code changes the files but after the marco section is totally broken. When I convert it manually my macro code stays.
My code is:
function ReleaseRef ($ref) {
if($ref){
([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
[System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
}
function Convert-DOC{
Param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$filepath,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$filefilter
)
try {
$files = Get-ChildItem $filepath -Include "$filefilter" -recurse -ErrorAction SilentlyContinue | Where-Object { ($_.PSIsContainer -eq $false) -and ( $_.Extension -like "$filefilter") }
$totalfiles = $files.Count
[int]$currentfile = 0
Write-Host "converting files... [$totalfiles]"
#word object********************************
#load dotnet assembly
Add-Type -AssemblyName Microsoft.Office.Interop.Word
#create word object
$word = New-Object -ComObject Word.Application -Verbose:$false
$word.visible = $true
$word.DisplayAlerts = [Microsoft.Office.InterOp.Word.WdAlertLevel]::wdAlertsNone
foreach ($file in $files) {
#Current file number
[int]$currentfile = [int]$currentfile + 1
#Check for password
$catch = $false
try {
#open file
Write-Host $file
$worddoc = $word.Documents.Open($file.FullName, $null, $null, $null, "")
} catch {
#if error, file has password
Write-Host "$file is protected by Password, skipping..." -ForegroundColor Yellow
$catch = $true
continue
}
if ($catch -eq $false) {
try {
#**********convert file**********
write-host "converting " $file.fullname "file $currentfile of $totalfiles" -ForegroundColor DarkGreen
#check for links
if([IO.Path]::GetExtension($file) -eq ".doc"){
#check for macros in in file
if ($worddoc.HasVBProject) {
$doFixedFormat = [Microsoft.Office.Interop.Word.WdSaveFormat]::wdFormatXMLDocumentMacroEnabled
$newfile = ($file.fullname).substring(0, ($file.FullName).lastindexOf("."))
$newfile += ".docm"
} else {
$doFixedFormat = [Microsoft.Office.Interop.Word.XlFileFormat]::wdFormatXMLDocument
$newfile = ($file.fullname).substring(0, ($file.FullName).lastindexOf("."))
$newfile += ".docx"
}
}
#save file
if(Test-Path $newfile){
Write-host "$newfile already exists" -ForegroundColor Yellow
} else {
$worddoc.SaveAs($newfile, $doFixedFormat )
}
#close file
$worddoc.close()
Write-Host "done" -ForegroundColor DarkGreen
#**********Garbage Collector**********
$gc_int++
if([int]$gc_int -gt 5){
Write-Host 'Run Garbage Collector' -ForegroundColor DarkBlue -BackgroundColor White
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
[int]$gc_int = 0
}
} catch {
$formatstring = "{0} : {1}`n{2}`n" +
" + CategoryInfo : {3}`n" +
" + FullyQualifiedErrorId : {4}`n" +
" + Filename : {5}`n"
$fields = $_.InvocationInfo.MyCommand.Name,
$_.ErrorDetails.Message,
$_.InvocationInfo.PositionMessage,
$_.CategoryInfo.ToString(),
$_.FullyQualifiedErrorId,
$file.fullname
Write-Host -Foreground Red -Background Black ($formatstring -f $fields)
"$fields" | Out-File ($scriptpath + '\error_convert.log') -Append
}
}
}
} finally {
#**********clean-up************
Write-Host ""
Write-Host "Cleaning Up" -ForegroundColor DarkMagenta
Write-Host "Quiting Word"
$word.Quit()
Write-Host "Garbage Collector"
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Write-Host "Release Com Object Workbook"
$a = ReleaseRef($worddoc)
Write-Host "Release Com Object Word"
$a = ReleaseRef($word)
Write-Host "Finishing Clean-Up"
}
}
Convert-DOC -filepath "C:\_testmacro\" -filefilter "*.doc"
What I do is, checking if the file has a VB part and is so setting the extension:
[Microsoft.Office.Interop.Word.WdSaveFormat]::wdFormatXMLDocumentMacroEnabled
and then save it:
$worddoc.SaveAs($newfile, $doFixedFormat)
A similar function with XLS and XLAM works fine
Edit:
seems like I was missing a $worddoc.Convert() but now struggling with checkin/checkout
As my edit stated, I missed the convert command:
$worddoc.Convert()
$worddoc.SaveAs($newfile, $doFixedFormat)
before saving

Using PowerShell to detect and remove certain type of software

I wish to modify the below script that used to be working, but now it is not working.
The purpose of this script is to uninstall and remove all Microsoft Office 2010, 2013, 2016 any version (Standard or Professional) 32 and 64 bit.
Write it to the log file when failed.
This is the script below that I've tried to modify but not working:
$AppDisplayName = 'Office'
$TARGETDIR = 'C:\LOGS'
$global:uninstallLog = Join-Path $TARGETDIR uninst.log
$WhatIfPreference = $true
$global:ShouldProcess = $false
$savedVerbosePreference = $VerbosePreference
$VerbosePreference = 'Continue'
$VerbosePreference = $savedVerbosePreference
$WhatIfPreference = $false
function Write-Log {
Param (
[Parameter(Mandatory)]
[String]$Message,
$Color = 'Green',
[string]$logfile = $global:uninstallLog
)
Process {
$msg = '[{0:dd\MM\yyy HH:mm}{1}' -f [datetime]::Now, $Message
if($logfile){$msg | Out-File $logfile -Encoding ascii}
$msg | Write-Host -ForegroundColor $Color
}
}
function Get-UninstallInfo {
Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
Get-ItemProperty |
Where-Object { $_.DisplayName -match $AppDisplayName}
}
New-Item $TARGETDIR -ItemType Directory -Force
Get-UninstallInfo |
ForEach-Object{
Write-Verbose "Processing $($_.DisplayName)"
Try {
if ($_.DisplayName -match '365') {
Write-Log "$($ENV:COMPUTERNAME) is already using O365 , no need to uninstall"
} else {
if($_.UninstallString -match '{(.*)}'){
$productCode = $matches[1]
Write-Log "Non Office 365 Detected - $productcode - Querying Uninstall command"
$arglist = '/x', $productCode, '/qn', '/norestart', '/L*v', $uninstallLog
Write-Verbose "ProductCode is set to $productCode"
if($ShouldProcess){
Write-Log "Uninstall command detected as $($_.UninstallString) Attempting silent uninstall"
Start-Process msiexec.exe -ArgumentList $arglist -Wait -NoNewWindow -ErrorAction Stop
} else {
Write-Verbose '"$ShouldProcess" is set to "$false"'
}
} else {
Write-Log 'Product code not found' -Color Red
}
}
}
Catch {
Write-Log "Error: $($_.Exception.Message)" -Color Red
}
}
This is the error log:
The "=" operator is missing after a named argument.
At line:7 char:29
Missing function body in function declaration.
At line:17 char:2
Missing function body in function declaration.
At line:23 char:2

problems in script: Invoke-command "Error: There was an error in communication"

I created a script to copy a VM (Folder) from Host-1 to Host-2, the script is run from my laptop.
this is the Invoke-command part:
Invoke-Command -Computername $Thost -Credential $mycreds
-ScriptBlock {
#seting a temp drive on Host for simple approch
New-PSDrive -Name v -PSProvider FileSystem -Root $Using:VMPath -Credential $Using:mycreds
Write-Host "You Chose that $Using:OSVersion will be copyed to $Using:Thost"
Copy-Item -Path "v:\$Using:OSVersion" -Destination $using:VMXPath -Recurse
Get-ChildItem -Path $Using:VMXPath | Rename-Item -NewName { $_.name -Replace $Using:OSVersion,$Using:newVM }
}
there are 2 Problems in the script that I can't figure out:
even though the script runs successfully I still get this error: Error: There was an error in communication in a write-host format, not error format
after I run the script (again: successfully) and try to run it again I get an error: a specified logon session does not exist. it may already have been terminated
a specified logon session does not exist. it may already have been terminated
Error: There was an error in communication
please help.
Edit Problem #1 - I split the invoke-command to do the copy and a different invoke-command to change the file...
Edit Problem #2 - no change for the "re-run" problem, I still can't get the script to run Succesful without a timeout period of about 10 minutes
im Adding the Full script (hope for some help here)
##Functions -Start
function OS
{
param (
[string]$Title = 'OS Menu'
)
cls
Write-Host "Welcome to $Title"
Write-Host "1: Windows"
Write-Host "2: Linux"
Write-Host "Q: Press 'Q' to quit."
}
function WVersion
{
param (
[string]$Title1 = 'Windows Version Menu'
)
Write-Host "Welcome to $Title1"
Write-Host "1: Windows 10 Pro"
Write-Host "Q: Press 'Q' to quit."
}
function LVersion
{
param (
[string]$Title2 = 'Linux Menu'
)
Write-Host "Welcome to $Title2"
Write-Host "1: Ubuntu 18"
Write-Host "Q: Press 'Q' to quit."
}
function Host
{
param (
[string]$Title = 'Host`s Menu'
)
cls
Write-Host "Welcome to $Title"
Write-Host "1: Host-1"
Write-Host "Q: Press 'Q' to quit."
}
function Drive #Func to select the Drive to move the VM to (on remote Client / Target)
{
param (
[string]$Title = 'Driver Menu'
)
cls
Write-Host "Welcome to $Title"
Write-Host "1: C:\"
Write-Host "Q: Press 'Q' to quit."
}
#Username and password for COnnection
$Username = "<user>"
$Password = ConvertTo-SecureString "<password>" -AsPlainText -Force
#creating a secure User for connection to the remote Client
$mycreds = New-Object System.Management.Automation.PSCredential($Username, $Password)
##Global VAR -Start
#path to VM's folder
$Repo = "\\host-28\Templets"
##################
##Script --Start##
##################
OS
$input = Read-Host "Please select The OS"
switch ($input)
{
'1' {
WVersion
$OSFolder = "Windows"
$input2 = Read-Host "Please select The Version"
switch ($input2)
{
'1' {
Write-Host "Windows 10 Pro Selected"
$OSVersion = "TMP-w10x64-pro"
}'q' {
return
}
}
} '2' {
LVersion
$OSFolder = "Linux"
$input3 = Read-Host "Please select The Version"
switch ($input3)
{
'1' {
Write-Host "Ubuntu 18 Selected"
$OSVersion = "tmp-u18"
}'q' {
return
}
}
} 'q' {
return
}
}
Host
$input3 = Read-Host "Please select Target Host"
switch ($input3)
{
'1' {
Write-Host "Host-1"
$VMHost = "\\host-1\<Windows_VM_Folder>"
}'q' {
return
}
}
Drive
$input4 = Read-Host "Please select The Target Driver"
switch ($input4)
{
'1' {
Write-Host "C:\ Selected"
$cVMfolder = 'VMs'
$VMHost = "\\${Thost}" + "\${cVMfolder}"
$destination = 'C:\VMs'
}'q' {
return
}
}
#the Folder name (need to be the same as the VM hostname)
$newVM = Read-Host -Prompt 'Set New HostName to VM:'
# commbined path+folder name
$copyedFolder = "${VMHost}" + "\${newVM}"
#Full Path To selected VM Folder
$VMPath = "${Repo}" + "\${OSFolder}" # + "\${OSVersion}"
$VMXPath = "${destination}" + "\${newVM}"
# test for cahnging file name (after copy comand)
Invoke-Command -Computername $Thost -Credential $mycreds -ScriptBlock {
#seting a temp drive on Host for simple approch
new-PSDrive -Name w -PSProvider FileSystem -Root $Using:VMPath -Credential $Using:mycreds
Write-Host "You Chose that $Using:OSVersion will be copyed to $Using:Thost"
Copy-Item -Path "w:\$Using:OSVersion" -Destination $using:VMXPath -Recurse
}
Invoke-Command -Computername $Thost -Credential $mycreds -ScriptBlock {
#seting a temp drive on Host for simple approch
Get-ChildItem -Path $Using:VMXPath | Rename-Item -NewName { $_.name -Replace $Using:OSVersion,$Using:newVM }
Remove-PSDrive -Name w -PSProvider FileSystem
Restart-Service winrm
}
}
Drive
$input4 = Read-Host "Please select The Target Driver"
switch ($input4)
{
'1' {
Write-Host "C:\ Selected"
$cVMfolder = 'VMs'
$VMHost = "\\${Thost}" + "\${cVMfolder}"
$destination = 'C:\VMs'
}'2' {
Write-Host "D:\ Selected"
$cVMfolder = 'VMs2'
$VMHost = "\\${Thost}" + "\${cVMfolder}"
$destination = 'D:\VMs'
}'3' {
Write-Host "E:\ Selected"
$cVMfolder = 'VMs3'
$VMHost = "\\${Thost}" + "\${cVMfolder}"
$destination = 'E:\VMs'
}'4' {
Write-Host "F:\ Selected"
$cVMfolder = 'VMs4'
$VMHost = "\\${Thost}" + "\${cVMfolder}"
$destination = 'F:\VMs'
}'q' {
return
}
}
#the Folder name (need to be the same as the VM hostname)
$newVM = Read-Host -Prompt 'Set New HostName to VM:'
# commbined path+folder name
$copyedFolder = "${VMHost}" + "\${newVM}"
#Full Path To selected VM Folder
$VMPath = "${Repo}" + "\${OSFolder}" # + "\${OSVersion}"
$VMXPath = "${destination}" + "\${newVM}"
# test for cahnging file name (after copy comand)
Invoke-Command -Computername $Thost -Credential $mycreds -ScriptBlock {
#seting a temp drive on Host for simple approch
new-PSDrive -Name w -PSProvider FileSystem -Root $Using:VMPath -Credential $Using:mycreds
Write-Host "You Chose that $Using:OSVersion will be copyed to $Using:Thost"
Copy-Item -Path "w:\$Using:OSVersion" -Destination $using:VMXPath -Recurse
}
Invoke-Command -Computername $Thost -Credential $mycreds -ScriptBlock {
#seting a temp drive on Host for simple approch
Get-ChildItem -Path $Using:VMXPath | Rename-Item -NewName { $_.name -Replace $Using:OSVersion,$Using:newVM }
Remove-PSDrive -Name w -PSProvider FileSystem
Restart-Service winrm
}

Powershell - "Invalid assignment expression." error

I've got a couple things that i'm working on. One of them is sort of an import/export thing i found on here. but i'm getting the following error
PS C:\Users\joeblogs> C:\Users\joeblogs\Scripts\Copy user data.ps1 Invalid assignment expression. The left hand side of an assignment
operator needs to be something that can be assigned to like a variable
or a property. At C:\Users\delpillay\Documents\Scripts\Copy user
data.ps1:16 char:12
+ $username = <<<< gc env:userame
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : InvalidLeftHandSide
I don't know where to start and i'm not sure what to try...
Below is the code:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop",
#"Downloads",
"Favorites",
"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
######################################
$username = gc env:userame
$userprofile = gc env:userprofile
##$appData = gc env:localAPPDATA
###### Restore data section ######
if ([IO.Directory]::Exists($destination + "\" + $username + "\"))
{
$caption = "Choose Action";
$message = "A backup folder for $username already exists, would you like to restore the data to the local machine?";
$Yes = new-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Yes";
$No = new-Object System.Management.Automation.Host.ChoiceDescription "&No","No";
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($Yes,$No);
$answer = $host.ui.PromptForChoice($caption,$message,$choices,0)
if ($answer -eq 0)
{
write-host -ForegroundColor green "Restoring data to local machine for $username"
foreach ($f in $folder)
{
$currentLocalFolder = $userprofile + "\" + $f
$currentRemoteFolder = $destination + "\" + $username + "\" + $f
write-host -ForegroundColor cyan " $f..."
Copy-Item -ErrorAction silentlyContinue -recurse $currentRemoteFolder $userprofile
if ($f -eq "AppData\Local\Mozilla") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
if ($f -eq "AppData\Roaming\Mozilla") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
if ($f -eq "AppData\Local\Google") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
}
rename-item "$destination\$username" "$destination\$username.restored"
write-host -ForegroundColor green "Restore Complete!"
}
else
{
write-host -ForegroundColor yellow "Aborting process"
exit
}
}
###### Backup Data section ########
#else
{
Write-Host -ForegroundColor green "Outlook is about to close, save any unsaved emails then press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Get-Process | Where { $_.Name -Eq "OUTLOOK" } | Kill
write-host -ForegroundColor green "Backing up data from local machine for $username"
foreach ($f in $folder)
{
$currentLocalFolder = $userprofile + "\" + $f
$currentRemoteFolder = $destination + "\" + $username + "\" + $f
$currentFolderSize = (Get-ChildItem -ErrorAction silentlyContinue $currentLocalFolder -Recurse -Force | Measure-Object -ErrorAction silentlyContinue -Property Length -Sum ).Sum / 1MB
$currentFolderSizeRounded = [System.Math]::Round($currentFolderSize)
write-host -ForegroundColor cyan " $f... ($currentFolderSizeRounded MB)"
Copy-Item -ErrorAction silentlyContinue -recurse $currentLocalFolder $currentRemoteFolder
}
$oldStylePST = [IO.Directory]::GetFiles($appData + "\Microsoft\Outlook", "*.pst")
foreach($pst in $oldStylePST)
{
if ((test-path -path ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle")) -eq 0){new-item -type directory -path ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle") | out-null}
write-host -ForegroundColor yellow " $pst..."
Copy-Item $pst ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle")
}
write-host -ForegroundColor green "Backup complete!"
}
Few observations:
You are not commenting the Favourites and My Documents. If you want to use them then use comma separated directly.
Use this:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop","Favorites","My Documents"
#"Downloads",
#"Favorites",
#"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
You have missed the n in username:
$username = gc env:username
Donot use gc env:username. Instead directly access them like this below: Completely reframed:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop","Favorites","My Documents"
#"Downloads",
#"Favorites",
#"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
######################################
$username = $env:username
$userprofile = $env:userprofile
##$appData = gc env:localAPPDATA
These are the major things that have been fixed. Hope it helps you.

Logging actual error when script fails

I have a script here that reads a list of computers and changes the administrator password. When the script is ran, it'll have a log file that says whether the task succeeded or fail. But I also want to log the actual error to the log when it fails. How do I accomplish this?
[cmdletbinding()]
param (
[parameter(mandatory = $true)]
$InputFile,
$OutputDirectory
)
if(!$outputdirectory) {
$outputdirectory = (Get-Item $InputFile).directoryname
}
$failedcomputers = Join-Path $outputdirectory "output.txt"
$stream = [System.IO.StreamWriter] $failedcomputers
$stream.writeline("ComputerName `t IsOnline `t PasswordChangeStatus")
$stream.writeline("____________ `t ________ `t ____________________")
$password = Read-Host "Enter the password" -AsSecureString
$confirmpassword = Read-Host "Confirm the password" -AsSecureString
$pwd1_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
$pwd2_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($confirmpassword))
if($pwd1_text -ne $pwd2_text) {
Write-Error "Entered passwords are not same. Script is exiting"
exit
}
if(!(Test-Path $InputFile)) {
Write-Error "File ($InputFile) not found. Script is exiting"
exit
}
$Computers = Get-Content -Path $InputFile
foreach ($Computer in $Computers) {
$Computer = $Computer.toupper()
$Isonline = "OFFLINE"
$Status = "SUCCESS"
Write-Verbose "Working on $Computer"
if((Test-Connection -ComputerName $Computer -count 1 -ErrorAction 0)) {
$Isonline = "ONLINE"
Write-Verbose "`t$Computer is Online"
} else { Write-Verbose "`t$Computer is OFFLINE" }
try {
$account = [ADSI]("WinNT://$Computer/username,user")
$account.psbase.invoke("setpassword",$pwd1_text)
Write-Verbose "`tPassword Change completed successfully"
}
catch {
$status = "FAILED"
Write-Verbose "`tFailed to Change the administrator password. Error: $_"
}
$obj = New-Object -TypeName PSObject -Property #{
ComputerName = $Computer
IsOnline = $Isonline
PasswordChangeStatus = $Status
}
$obj | Select ComputerName, IsOnline, PasswordChangeStatus
if($Status -eq "FAILED" -or $Isonline -eq "OFFLINE") {
$stream.writeline("$Computer `t $isonline `t $status")
}
}
$stream.close()
Write-Host "`n`nFailed computers list is saved to $failedcomputers"
Change this:
catch {
$status = "FAILED"
Write-Verbose "`tFailed to Change the administrator password. Error: $_"
}
to this:
catch {
$status = "FAILED"
Write-Verbose "`tFailed to Change the administrator password. Error: $_"
$errmsg = $_.Exception.Message
}
to preserve the error message(s). And change this:
if($Status -eq "FAILED" -or $Isonline -eq "OFFLINE") {
$stream.writeline("$Computer `t $isonline `t $status")
}
to this:
if($Status -eq "FAILED" -or $Isonline -eq "OFFLINE") {
$stream.writeline("$Computer `t $isonline `t $status `t $errmsg")
}
to include the error message in the log.
If you want to unroll inner exceptions as well you can use this:
$e = $_.Exception
$errmsg = $e.Message
while ($e.InnerException) {
$e = $e.InnerException
$errmsg = "`n" + $e.Message
}
instead of just
$errmsg = $_.Exception.Message