I'm trying to copy a license file to all domain joined computers,
and i am only able to use PowerShell.
GPO or even (GPO) scheduled task is a no-go, because these don't seem to be possible yet within a Server 2003 environment.
The application this is regarding is already installed, and only the license file needs to be overwritten.
i am hoping to achieve:
- check if online, else skip
- check for 32b folder location, if exist, copy, else skip
- check for 64b folder location, if exist copy, else skip
- write every computers' result to a log file so i can prune successful
1st attempt;
Code i have currently:
$computers = gc "c:\temp\computers.txt"
$source = "c:\temp\almmodule.lic"
$dest32b = 'c$\program files (x86)\ALM - Automatic Login Module'
$dest64b = 'c$\program files\ALM - Automatic Login Module'
$TestPath32 = Test-Path -path "\\$computer\$dest32b\*"
$TestPath64 = Test-Path -path "\\$computer\$dest64b\*"
foreach ($computer in $computers) {
if (test-Connection -Cn $computer -quiet) {
IF (!$TestPath32) {
Copy-Item $source -Destination "\\$computer\$dest32b" -recurse -force -verbose}
ELSE {
"$computer' 32b folder not found. Skipping"}
IF (!$TestPath64) {
Copy-Item $source -Destination "\\$computer\$dest64b" -recurse -force -verbose}
ELSE
{"$computer 64b folder not found. Skipping"}
ELSE {
"$computer is not online"
}
}
}
I've tried some minor variations, but i can't seem to get anywhere.
Also the logging needs yet to be created.
Using myself as a test target, having run above variables, and using single commands;
$TestPath32 = Test-Path -path "\$computer\$dest32b*"
Returns: True
Copy-Item $source -Destination "\$computer\$dest32b" -recurse -force -verbose
is successful, and copies the file
Running the PowerShell at the moment complains about the last ELSE statement.
But most of the time it failed not recognizing i don't have a 64b folder, and it either gives an error, or places a file, with the directory as the filename.
I'm at a loss and have now tried so many things i'm afraid of braking the little that i've got.
2nd attempt;
I have edited the code and commented out some parts, just to get a working > model to progress from there.
foreach ($computer in $computers) {
$TestPath32 = Test-Path "\\$computer\$dest32b\*"
$TestPath64 = Test-Path "\\$computer\$dest64b\*"
# if (test-Connection -Cn $computer -quiet) {
IF (!$TestPath32) {
Copy-Item $source -Destination "\\$computer\$dest32b\" -recurse -force -verbose}
ELSE
{"$computer' 32b folder not found. Skipping"}
IF (!$TestPath64) {
Copy-Item $source -Destination "\\$computer\$dest64b\" -recurse -force -verbose}
ELSE
{"$computer 64b folder not found. Skipping"}
# ELSE {
# "$computer is not online"
# }
#}
}
Now returns :
PS C:\windows\system32> C:\Temp\ALM 2016 LIC copy.ps1
L2016010' 32b folder not found. Skipping
VERBOSE: Performing operation "Copy File" on Target "Item: C:\temp\almmodule.lic Destination: \\L2016010\c$\program files\ALM - Automatic Login Module\".
Copy-Item : De syntaxis van de bestandsnaam, mapnaam of volumenaam is onjuist.
At C:\Temp\ALM 2016 LIC copy.ps1:14 char:12
+ Copy-Item <<<< $source -Destination "\\$computer\$dest64b\" -force -verbose}
+ CategoryInfo : NotSpecified: (:) [Copy-Item], IOException
+ FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.CopyItemCommand
i can assure you i DO have the 32b path. The copy-item doesn't place the file.
i DO NOT have the 64b path obviously, and i feel it breaks on this instead of just neatly returning $false like it should.
When i mess arround with he Path (because i thought that was the reason for the failure), it sometimes places a file in Program Files named "ALM - Automatic Login Module" which is the size of the license file.
Again, if i run the line Copy-Item $source -Destination "\\$computer\$dest32b\" as stand-alone, it DOES copy the file.
3rd attempt, now working;
$computers = gc "c:\temp\computers.txt"
$source = "c:\temp\almmodule.lic"
$dest32b = 'c$\program files (x86)\ALM - Automatic Login Module'
$dest64b = 'c$\program files\ALM - Automatic Login Module'
foreach ($computer in $computers) {
$TestUp = test-Connection -Cn $computer -quiet
$TestPath32 = Test-Path -pathType container "\\$computer\$dest32b"
$TestPath64 = Test-Path -pathType container "\\$computer\$dest64b"
IF (!$TestUp) {
write-host "$computer is not online"
} ELSE {
IF (!$TestPath32){
write-host "$computer' 32b folder not found. Skipping"
} ELSE {
Copy-Item $source -Destination "\\$computer\$dest32b\" -force -verbose
}
IF (!$TestPath64){
write-host "$computer 64b folder not found. Skipping"
} ELSE {
Copy-Item $source -Destination "\\$computer\$dest64b\" -force -verbose
}
}
}
The use of !$ totally went over my head, and i was supposed to work from a point of:
IF NOT, THEN, ELSE
Now the script skips folders not present, havn't been able to test a down computer yet, but i assume this now works also.
Now all i need to figure out, is how to log the output into 1 logfile, in a readable format
While using a GPO or a scheduled task would be better, or possibly deploying with SCCM if you've got it, there is an answer within your constraints. You've got the right basic idea, but move your assignments of $TestPath32 and $TestPath64 into the foreach loop:
...
foreach ($computer in $computers) {
if (test-Connection -Cn $computer -quiet) {
$TestPath32 = Test-Path -path "\\$computer\$dest32b\*"
$TestPath64 = Test-Path -path "\\$computer\$dest64b\*"
IF (!$TestPath32) {
...
I vote for GPO as well, as mentioned in the comments, you should definitely consider that.
However, if it doesn't help you,
Your $TestPath32 & $TestPath64 variables are evaluated before you assign your individual computer values. I would simply be null, and hence both will be $false. You can simply move them inside your for-loop,
foreach ($computer in $computers) {
if (test-Connection -Cn $computer -quiet) {
$TestPath32 = Test-Path -path "\\$computer\$dest32b\*"
$TestPath64 = Test-Path -path "\\$computer\$dest64b\*"
......
}
Also in the other hand I hope you have formatted the content of your file to return an array. If not you simply delimit it with "," and read
File Content: Computer1,Computer2,Computer3
and read the file as an array directly
$computers = Get-Content 'c:\temp\computers.txt' # It will read it as an array only. YOu do not need any additional formatting in this case.
Final version.
Couldn't get out-file to work like i wanted to.
Resorted to Start-Transcript.
Had to put Get-Content at the end to format the output to something that was readable.
#set-executionpolicy RemoteSigned
$computers = gc "c:\temp\computers.txt"
$source = "c:\temp\almmodule.lic"
$dest32b = 'c$\program files (x86)\ALM - Automatic Login Module'
$dest64b = 'c$\program files\ALM - Automatic Login Module'
Start-Transcript -path C:\temp\ALM-Log.txt -append
foreach ($computer in $computers) {
$TestUp = test-Connection -Cn $computer -quiet
$TestPath32 = Test-Path -pathType container "\\$computer\$dest32b"
$TestPath64 = Test-Path -pathType container "\\$computer\$dest64b"
IF (!$TestUp) {
write-host "$computer is not online`r"
} ELSE {
IF (!$TestPath32){
write-host "$computer' 32b folder not found. Skipping`r"
} ELSE {
Copy-Item $source -Destination "\\$computer\$dest32b\" -force -verbose
}
IF (!$TestPath64){
write-host "$computer 64b folder not found. Skipping`r"
} ELSE {
Copy-Item $source -Destination "\\$computer\$dest64b\" -force -verbose
}
}
}
Stop-Transcript
$log = Get-Content C:\temp\ALM-Log.txt
$log > c:\temp\ALM-Log.log
The actual solution as to why this works = Test-Path -pathtype container
Related
I was wondering if another set of eyes can spot where I'm going wrong.
I'm trying to add am exceptions.sites file to a users Java folder via GPO so as to allow for Edge Compatibility mode to work without user intervention. We have a lot of legacy applications and a lot of users so automating this would be great.
The script seems to run without error.
$Source = '\\thepath\Policies\{1FA921CE-231E-4982-8EBA-29E3AD4A44EF}\Machine\Scripts\Startup\exception.sites'
$users = (Get-ChildItem 'c:\Users\').name
$JavaFolder = "C:\Program Files\Java\"
$isJava = $false
$SEL = "Select string"
$DAFMComputer = "ControlPC"
#Do PC check to apply to only one PC
if ($DAFMComputer -like "ControlPC")
{
if (JavaPath -Path $JavaFolder) #if Java is installed
{
Foreach ($user in $users)
{
if (FileExists -Path "c:\Users\$user\AppData\LocalLow\Sun\Java\Deployment\security\exception.sites") #check for the file
{
$SEL = Select-String -Path "c:\Users\$user\AppData\LocalLow\Sun\Java\Deployment\security\exception.sites" -Pattern https://agapps.agriculture.gov.ie #Check for the string pattern
if ($SEL -ne $null)
{
#do nothing as the exception file exists with the URL we need do nothing
Copy-Item -Path \\thepath\Policies\{1FA921CE-231E-4982-8EBA-29E3AD4A44EF}\Machine\Scripts\Startup\exception.sites -Destination "c:\Temp\AlreadyThere\"
}
else
{
#add the following lines.
Add-Content "c:\Users\$user\AppData\LocalLow\Sun\Java\Deployment\security\exception.sites" "`nhttps://app.company.com/"
Add-Content "c:\Users\$user\AppData\LocalLow\Sun\Java\Deployment\security\exception.sites" "`nhttps://app.company.com/"
Add-Content "c:\Users\$user\AppData\LocalLow\Sun\Java\Deployment\security\exception.sites" "`We are adding lines"
Copy-Item -Path \\thepath\Policies\{1FA921CE-231E-4982-8EBA-29E3AD4A44EF}\Machine\Scripts\Startup\exception.sites -Destination "c:\Temp\FileExistsAddlines\"
}
}
else #file does not exist
{
Copy-Item -Path $Source -Destination "c:\Users\$user\AppData\LocalLow\Sun\Java\Deployment\security\exception.sites" -force
Copy-Item -Path \\thepath\Policies\{1FA921CE-231E-4982-8EBA-29E3AD4A44EF}\Machine\Scripts\Startup\exception.sites -Destination "c:\Temp\Filecopy\"
}
}
} # end file copy.
else
{
#No Java Do nothing
Copy-Item -Path \\thepath\Policies\{1FA921CE-231E-4982-8EBA-29E3AD4A44EF}\Machine\Scripts\Startup\exception.sites -Destination "c:\Temp\NoJavaDoNothing\"
}
else
{
Copy-Item -Path \\thepath\Policies\{1FA921CE-231E-4982-8EBA-29E3AD4A44EF}\Machine\Scripts\Startup\exception.sites -Destination "c:\Temp\NoComputerMatch\"
#do nothing as the PC name does not match
}
}
#Control line showing the script runs.
Copy-Item -Path \\thepath\Policies\{1FA921CE-231E-4982-8EBA-29E3AD4A44EF}\Machine\Scripts\Startup\exception.sites -Destination "c:\Temp\Running\"
The only line that reliably runs is the last control line on line 55 which copies a file to a folder. I've used similar copy file indicators in the if else loops to track and see where the loop is. But none of these are ever populated.
It's all the if elses are evaluating to false and no matter what i can't get it to enter the loop.
Any suggestions / pointers would be welcome
Thanks
John
I'm trying to copy files from a source folder to a destination folder, and rename the files in the process.
$Source = "C:\Source"
$File01 = Get-ChildItem $Source | Where-Object {$_.name -like "File*"}
$Destination = "\\Server01\Destination"
Copy-Item "$Source\$File01" "$Destination\File01.test" -Force -
Confirm:$False -ErrorAction silentlyContinue
if(-not $?) {write-warning "Copy Failed"}
else {write-host "Successfully moved $Source\$File01 to
$Destination\File01.test"}
The problem is that since Get-ChildItem doesn't throw an error message if the file is not found, but rather just gives you a blank, I end up with a folder called File01.test in destination if no file named File* exists in $Source.
If it does exist, the copy operation carries out just fine. But I don't want a folder to be created if no matching files exist in $Source, rather I just want an error message logged in a log file, and no file operation to occur.
This shouldn't matter what the file name is, but it won't account for files that already exist in the destination. So if there is already File01.txt and you're trying to copy File01.txt again you'll have problems.
param
(
$Source = "C:\Source",
$Destination = "\\Server01\Destination",
$Filter = "File*"
)
$Files = `
Get-ChildItem -Path $Source `
| Where-Object -Property Name -Like -Value $Filter
for ($i=0;$i -lt $Files.Count;$i++ )
{
$NewName = '{0}{1:D2}{3}' -f $Files[$i].BaseName,$i,$Files[$i].Extension
$NewPath = Join-Path -Path $Destination -ChildPath $NewName
try
{
Write-Host "Moving file from '$($Files[$i].FullName)' to '$NewPath'"
Copy-Item -Path $Files[$i] -Destination
}
catch
{
throw "Error moving file from '$($Files[$i].FullName)' to '$NewPath'"
}
}
You can add an "if" statement to ensure that the code to copy the files only runs when the file exists.
$Source = "C:\Source"
$Destination = "\\Server01\Destination"
$File01 = Get-ChildItem $Source | Where-Object {$_.name -like "File*"}
if ($File01) {
Copy-Item "$Source\$File01" "$Destination\File01.test" -Force -Confirm:$False -ErrorAction silentlyContinue
if(-not $?) {write-warning "Copy Failed"}
else {write-host "Successfully moved $Source\$File01 to
$Destination\File01.test"}
} else {
Write-Output "File did not exist in $source" | Out-File log.log
}
In the "if" block, it will check to see if $File01 has anything in it, and if so, then it'll run the subsequent code. In the "else" block, if the previous code did not run, it'll send the output to the log file "log.log".
I have a script that moves files around in a production environment and currently performs a copy-item, then test-path, followed by remove-item if the test-path worked ok, similar to the below:
if ($copySuccess -eq $true) {
$files = Get-ChildItem $fileDir -Filter $filePrefix*.*
$files | ForEach-Object {
if ($copySuccess -eq $true) {
Copy-Item $fileDir\$_ -Destination $destDir
if (!(Test-Path $destDir\$_)) {
$copySuccess = $false
}
}
}
}
This method makes me feel comfortable as the test-path guarantees that the file is where it needs to be, before removing it.
I'm planning to rewrite parts of the script and I'm wondering if by using a copy-item with a catch on error, I can be sure that if no error is seen that the file has definitely been copied to the destination (without the need to use test-path as I assume this would make it quicker). As in below:
Get-ChildItem $fileDir -Filter $filePrefix*.* | ForEach {
if ($copySuccess -eq $true) {
try {
Copy-Item $fileDir\$_ -Destination $destDir -ErrorAction Stop
}
catch {
$copySuccess = $false
}
}
}
}
Of course, if there is a better way, please let me know (Powershell v5). The reason for this level of checking is that there are often network issues on the infrastructure, hence the test-path currently in use.
ErrorAction won't work in this case since:
The ErrorAction parameter has no effect on terminating errors (such as
missing data, parameters that are not valid, or insufficient
permissions) that prevent a command from completing successfully.
[Source]
If you want to check whether Copy-Item worked you can do this in a couple of ways to make sure.
The first one is to use $? variable:
Errors and Debugging: The success or failure status of the last
command can be determined by checking $?
Copy-Item $fileDir\$_ -Destination $destDir
if(-not $?) {
Write-Warning "Copy Failed"
}
Another method is by using the -Passthru parameter, we can capture the results to a variable. Note, this variable will only be populated if the operation was successful:
if(-not Copy-Item $fileDir\$_ -Destination $destDir -PassThru) {
Write-Warning "Copy Failed"
}
This identical code has been used in 3 servers, and only one of them does it silently fail to move the items (it still REMOVES them, but they do not appear in the share).
Azure-MapShare.ps1
param (
[string]$DriveLetter,
[string]$StorageLocation,
[string]$StorageKey,
[string]$StorageUser
)
if (!(Test-Path "${DriveLetter}:"))
{
cmd.exe /c "net use ${DriveLetter}: ${StorageLocation} /u:${StorageUser} ""${StorageKey}"""
}
Get-Exclusion-Days.ps1
param (
[datetime]$startDate,
[int]$daysBack
)
$date = $startDate
$endDate = (Get-Date).AddDays(-$daysBack)
$allDays =
do {
"*"+$date.ToString("yyyyMMdd")+"*"
$date = $date.AddDays(-1)
} until ($date -lt $endDate)
return $allDays
Migrate-Files.ps1
param(
[string]$Source,
[string]$Filter,
[string]$Destination,
[switch]$Remove=$False
)
#Test if source path exist
if((Test-Path -Path $Source.trim()) -ne $True) {
throw 'Source did not exist'
}
#Test if destination path exist
if ((Test-Path -Path $Destination.trim()) -ne $True) {
throw 'Destination did not exist'
}
#Test if no files in source
if((Get-ChildItem -Path $Source).Length -eq 0) {
throw 'No files at source'
}
if($Remove)
{
#Move-Item removes the source files
Move-Item -Path $Source -Filter $Filter -Destination $Destination -Force
} else {
#Copy-Item keeps a local copy
Copy-Item -Path $Source -Filter $Filter -Destination $Destination -Force
}
return $True
The job step is type "PowerShell" on all 3 servers and contains this identical code:
#Create mapping if missing
D:\Scripts\Azure-MapShare.ps1 -DriveLetter 'M' -StorageKey "[AzureStorageKey]" -StorageLocation "[AzureStorageAccountLocation]\backup" -StorageUser "[AzureStorageUser]"
#Copy files to Archive
D:\Scripts\Migrate-Files.ps1 -Source "D:\Databases\Backup\*.bak" -Destination "D:\Databases\BackupArchive"
#Get date range to exclude
$exclusion = D:\Scripts\Get-Exclusion-Days.ps1 -startDate Get-Date -DaysBack 7
#Remove items that are not included in exclusion range
Remove-Item -Path "D:\Databases\BackupArchive\*.bak" -exclude $exclusion
#Move files to storage account. They will be destroyed
D:\Scripts\Migrate-Files.ps1 -Source "D:\Databases\Backup\*.bak" -Destination "M:\" -Remove
#Remove remote backups that are not from todays backup
Remove-Item -Path "M:\*.bak" -exclude $exclusion
If I run the job step using SQL then the files get removed but do not appear in the storage account. If I run this code block manually, they get moved.
When I start up PowerShell on the server, I get an error message: "Attempting to perform the InitializeDefaultDrives operation on the 'FileSystem' provider failed." However, this does not really impact the rest of the operations (copying the backup files to BackupArchive folder, for instance).
I should mention that copy-item also fails to copy across to the share, but succeeds in copying to the /BackupArchive folder
Note sure if this will help you but you could try to use the New-PSDrive cmdlet instead of net use to map your shares:
param (
[string]$DriveLetter,
[string]$StorageLocation,
[string]$StorageKey,
[string]$StorageUser
)
if (!(Test-Path $DriveLetter))
{
$securedKey = $StorageKey | ConvertTo-SecureString -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential ($StorageUser, $securedKey)
New-PSDrive -Name $DriveLetter -PSProvider FileSystem -Root $StorageLocation -Credential $credentials -Persist
}
Apparently I tricked myself on this one. During testing I must have run the net use command in an elevated command prompt. This apparently hid the mapped drive from non-elevated OS features such as the Windows Explorer and attempts to view its existence via non-elevated command prompt sessions. I suppose it also was automatically reconnecting during reboots because that did not fix it.
The solution was as easy as running the net use m: /delete command from an elevated command prompt.
I am looking to log the output of my script to a log file. But not able to get the output to a file.
Set-ExecutionPolicy RemoteSigned
$server_names = Get-Content "E:\Bibin\Copy\complist.txt"
$Folder=$((Get-Date).ToString('yyyy-MM-dd'))
$Logfile = "E:\Bibin\Copy\copy.log"
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
Foreach ($server in $server_names)
{
$FileExists = Test-Path "\\$server\C$\temp\TEST\*"
If ($FileExists -eq $True)
{
New-Item "\\$server\C$\temp\TEST\$Folder" -type directory
Move-Item "\\$server\C$\temp\TEST\*" -Destination "\\$server\C$\temp\TEST\$Folder" -force
Copy-Item "\\DC1NAS02P00\data\IT\CPS\Projects\NGNet\CpsServerUpgradeFiles\Upgrade Version 2.0\2003_Files\*.*" -Destination "\\$server\C$\temp\TEST" -Recurse
}
Else
{
New-Item "\\$server\C$\temp\TEST" -type directory
Copy-Item "\\DC1NAS02P00\PDSdata\IT\CPS\Projects\NGNet\CpsServerUpgradeFiles\Upgrade Version 2.0\2003_Files\*.*" -Destination "\\$server\C$\temp\TEST" -Recurse
}
}
Also I want some time gap between New_item and Move-item, since it is saying file is already in use ..
Thanks
Bibin
instead of using Add-Content use Out-File.
Also try robocopy instead of copy, this will wait for the locks to release before copying.