Environment:
System Center Configuration Manager 1810
Workstations = Windows 10 1709 / Windows 7 SP1
Application spécific with add-on script to accomplish the tasks
Way to accomplish it:
Deploy package without licence
Push licence file
Stop service
Read licence with SCCM tack and push it if necessary
Start service
Hundreds of computers are affected and the editor doesn't submit a guideline to accomplish it without rebooting computers without prompting end-users.
We are using SCCM to deploy and check packages (WMI query, registry, ...). We can use, powershell to query more objects, like reading the licence file to check if it is the good one.
Reading licence is done this way:
if ((Get-Content "C:\Program Files\XXX\X.LIC") -contains serial_no=XXXXX")) {
Write-Host "License OK"
}
If the licence, isn't the good one, a little is launched on the workstation (somewhere in a folder like C:\Windows\CCMCACHE\a)
like
If (Test-Path ("C:\Program Files (x86)\NetSupport\NetSupport School"))
{
If (Test-Path ("C:\Program Files (x86)\XXX\X.LIC"))
{
Rename-Item -Path "C:\Program Files (x86)\XXX\X.LIC" -NewName ("X.LIC." + (Get-Date).ToString("yyyyMMdd")) -Force
}
Copy-Item -Source $PSScriptRoot\X.LIC -Destination ("C:\Program Files (x86)\XXX") -Force
}
ElseIf(Test-Path ("C:\Program Files\XXX"))
{
If (Test-Path ("C:\Program Files\XXX\X.LIC"))
{
Rename-Item -Path "C:\Program Files\XXX\X.LIC" -NewName ("X.LIC." + (Get-Date).ToString("yyyyMMdd")) -Force
}
Copy-Item -Source $PSScriptRoot\X.LIC -Destination ("C:\Program Files\XXX") -Force
}
Do I need PowerShell v3? How can i do it with PSv2 ?
First of all, the Copy-Item was wrong -> -Path instead of -Source
And, in Windows PowerShell 2.0, $PSScriptroot is valid only in script modules (.psm1). Beginning in Windows PowerShell 3.0, it is valid in all scripts.
So, i do it like this :
$scriptpath = split-path -parent $MyInvocation.MyCommand.Definition
If (Test-Path ("C:\Program Files (x86)\XXX"))
{
If (Test-Path ("C:\Program Files (x86)\XXX\X.LIC"))
{
Rename-Item -Path "C:\Program Files (x86)\XXX\X.LIC" -NewName ("NSM.LIC." + (Get-Date).ToString("yyyyMMdd")) -Force
}
Copy-Item -Path ($scriptpath + "\X.LIC") -Destination ("C:\Program Files (x86)\XXX") -Force
}
ElseIf(Test-Path ("C:\Program Files\XXX"))
{
If (Test-Path ("C:\Program Files\XXX\X.LIC"))
{
Rename-Item -Path "C:\Program Files\XXX\X.LIC" -NewName ("X.LIC." + (Get-Date).ToString("yyyyMMdd")) -Force
}
Copy-Item -Path ($scriptpath + "\X.LIC") -Destination ("C:\Program Files\XXX") -Force
}
Thanks for your help ^^
Related
I am trying to write powershell Script which will create backupfolder on same Path where Application exist and need to copy the folders & files into backupfolder before deploying. Below are the command was using to perform but am getting error
$Source = "C:\XYZ"
$BackupFolder = New-Item -ItemType Directory -Force -Path $source_$(Get-Date)
Copy-Item -Path $Source\* $BackupFolder -Force
Error: Cannot copy item C:\XYZ\Backup_18-02-2017 on to itself
Try:
Copy-Item $Source\* $BackupFolder -Exclude $BackupFolder
That will eliminate the folder that you are copying into as a source that is being copied from.
Variables can contain underscores. The following works and displays the string "asdf"
$a_ = "adsf"; $a_
Your New-Item cmdlet call should have failed since $source_ is not a variable and would return null. This is default behavior for PowerShell. When I run your code as is I get the following:
New-Item : Cannot find drive. A drive with the name '02/18/2017 22' does not exist.At line:1 char:1
+ New-Item -ItemType Directory -Force -Path "$source_$(Get-Date)" -what ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (02/18/2017 22:String) [New-Item], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.NewItemCommand
So I would have expected your folder variable to be null. wOxxOm brings this up in comment as well
Several options to address what I am sure is the partial source of your issue.
$BackupFolder = New-Item -ItemType Directory -Force -Path "$source`_$(Get-Date)"
$BackupFolder = New-Item -ItemType Directory -Force -Path "$($source)_$(Get-Date)"
$BackupFolder = New-Item -ItemType Directory -Force -Path ("{0}_{1} -f "$source, Get-Date)
You will still have to try and exclude this folder from the copy as well like Keith Hill's answer is telling you
Copy-Item $Source\* $BackupFolder -Exclude $BackupFolder
try Something like this
$Source = "C:\XYZ"
$Destination="{0}{1:yyyyMMdd}" -f $source, (Get-Date)
New-Item -ItemType Directory -Force -Path $Destination
Copy-Item -Path $Source\* $Destination -Recurse -Force
If I understand the question correctly. You want to take "C:\XYZ" and backup into the same directory called "C:\XYZ\backup_$DATE". What you will actually do is create a loop that will break once it reaches the max 248 characters. If we use the -exclude option then we can exclude the backup directory "C:\XYZ\backup_$DATE".
This function will do the trick and also gives you error handling.
Function Get-CopyDirectory{
#####################
# Dynamic Variables #
#####################
$Date = Get-Date -format ddMM-yyyy
$Exclude="Backup*"
####################
# Static Variables #
####################
$AppPath = "F:\Test\"
$BackupPath = "$AppPath\BACKUP_$Date\"
if (Test-Path $BackupPath) {
Write-Host "Backup Exist" -f Cyan
}
else
{
Copy-Item "$AppPath\*" $BackupPath -Exclude $Exclude -recurse -verbose
}
}
CLS
Get-CopyDirectory
I have implemented a PS Script that deploys code on multiple servers at the same time. Here I need to copy some source file from one server to another. See the code below:
for ($i=1; $i -le 5; $i++) {
$serverName="iwflO" + $i
$sourceFile="\\iwdflO1\C$\Deploy\bin"
$destination="\\$serverName\C$\Program Files (X86)\Shian\MyService\bin\"
$Myblock = {
Param{$sourceFile,$destination)
Copy-Item -Force -Recurse $sourceFile -Destination $destination
}
$result = Invoke-Command -ComputerName $ServerName -Credential "shian" -ScriptBlock $Myblock -ArgumentList $sourceFile,$destination;
$result;
}
cd c:\
It's working fine for iwflO1 which is the root server from where I'm running the script but for other servers it's giving me an error like
Cannot find Path "\iwdflO1\C$\Deploy\bin" because it does not exist.
But if I logged in to iwflO2 or any other server and hit the path manually its working fine.
I can see the mistake is with the block :
Instead of this:
$Myblock={param{$sourceFile,$destination)
copy-Item -Force -Recurse $sourceFile -Destination $destination
}
Do this:
$Myblock={param($sourceFile,$destination)
copy-Item -Force -Recurse $sourceFile -Destination $destination
}
This is working fine if I am hardcoding the server names(tested in my local)
Since you are using admin share, directly try this:
Copy-Item -Path \\serverA\c$\programs\temp\test.txt -Destination \\serverB\c$\programs\temp\test.txt;
Note: You have to specify the file. Else you get-childitem -recurse inside the source folder and put it directly in the destination .
Hope it helps.
I run this PowerShell script, and it works fine on PowerShell 4.0. But I now have PowerShell 5.0 and the script does work but it throws an error:
The Script:
$path = "X"
$destination = "Y"
while (Test-Path -Path $path) {
Move-Item -Path "$path\*zip" -Destination "$destination"
}
The error I get is:
Move-Item : The process cannot access the file because it is being
used by another process.
The title of the question: "Test-Path Move-Item Problems" implies that one cmdlet might be impacting the other. That doesn't make sense to me as Test-Path is checking the folder's existence and Move-Item is working on child items within that folder.
Personally I would not use a while loop for this use case as, once you have determined that the path exists you don't need to keep testing it:
if(Test-Path -Path $path){
Move-Item -Path $path\*zip -Destination $destination
}
just do it
Move-Item -Path "$path\*zip" -Destination "$destination" -ErrorAction Ignore
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.
Using PowerShell, is it possible to remove some directory that contains files without prompting to confirm action?
Remove-Item -LiteralPath "foldertodelete" -Force -Recurse
or, with shorter version
rm /path -r -force
From PowerShell remove force answer:
help Remove-Item says:
The Recurse parameter in this cmdlet does not work properly
The command to workaround is
Get-ChildItem -Path $Destination -Recurse | Remove-Item -force -recurse
And then delete the folder itself
Remove-Item $Destination -Force
This worked for me:
Remove-Item $folderPath -Force -Recurse -ErrorAction SilentlyContinue
Thus the folder is removed with all files in there and it is not producing error if folder path doesn't exists.
2018 Update
In the current version of PowerShell (tested with v5.1 on Windows 10 and Windows 11 in 2023) one can use the simpler Unix syntax rm -R .\DirName to silently delete the directory .\DirName with all subdirectories and files it may contain. In fact many common Unix commands work in the same way in PowerShell as in a Linux command line.
One can also clean up a folder, but not the folder itself, using rm -R .\DirName\* (noted by Jeff in the comments).
in short, We can use rm -r -fo {folderName} to remove the folder recursively (remove all the files and folders inside) and force
To delete content without a folder you can use the following:
Remove-Item "foldertodelete\*" -Force -Recurse
rm -Force -Recurse -Confirm:$false $directory2Delete didn't work in the PowerShell ISE, but it worked through the regular PowerShell CLI.
I hope this helps. It was driving me bannanas.
This worked for me:
Remove-Item C:\folder_name -Force -Recurse
Powershell works with relative folders. The Remove-Item has couple of useful aliases which aligns with unix. Some examples:
rm -R -Force ./directory
del -R -Force ./directory/*
Below is a copy-pasteable implementation of Michael Freidgeim's answer
function Delete-FolderAndContents {
# http://stackoverflow.com/a/9012108
param(
[Parameter(Mandatory=$true, Position=1)] [string] $folder_path
)
process {
$child_items = ([array] (Get-ChildItem -Path $folder_path -Recurse -Force))
if ($child_items) {
$null = $child_items | Remove-Item -Force -Recurse
}
$null = Remove-Item $folder_path -Force
}
}
$LogPath = "E:\" # Your local of directories
$Folders = Get-Childitem $LogPath -dir -r | Where-Object {$_.name -like "*temp*"}
foreach ($Folder in $Folders)
{
$Item = $Folder.FullName
Write-Output $Item
Remove-Item $Item -Force -Recurse
}
Since my directory was in C:\users I had to run my powershell as administrator,
del ./[your Folder name] -Force -Recurse
this command worked for me.
If you have your folder as an object, let's say that you created it in the same script using next command:
$folder = New-Item -ItemType Directory -Force -Path "c:\tmp" -Name "myFolder"
Then you can just remove it like this in the same script
$folder.Delete($true)
$true - states for recursive removal
$LogPath = "E:\" # Your local of directories
$Folders = Get-Childitem $LogPath -dir -r | Where-Object {$_.name -like "*grav*"} # Your keyword name directories
foreach ($Folder in $Folders)
{
$Item = $Folder.FullName
Write-Output $Item
Remove-Item $Item -Force -Recurse -ErrorAction SilentlyContinue
}
Some multi-level directory folders need to be deleted twice, which has troubled me for a long time. Here is my final code, it works for me, and cleans up nicely, hope it helps.
function ForceDelete {
[CmdletBinding()]
param(
[string] $path
)
rm -r -fo $path
if (Test-Path -Path $path){
Start-Sleep -Seconds 1
Write-Host "Force delete retrying..." -ForegroundColor white -BackgroundColor red
rm -r -fo $path
}
}
ForceDelete('.\your-folder-name')
ForceDelete('.\your-file-name.php')
If you want to concatenate a variable with a fixed path and a string as the dynamic path into a whole path to remove the folder, you may need the following command:
$fixPath = "C:\Users\myUserName\Desktop"
Remove-Item ("$fixPath" + "\Folder\SubFolder") -Recurse
In the variable $newPath the concatenate path is now: "C:\Users\myUserName\Desktop\Folder\SubFolder"
So you can remove several directories from the starting point ("C:\Users\myUserName\Desktop"), which is already defined and fixed in the variable $fixPath.
$fixPath = "C:\Users\myUserName\Desktop"
Remove-Item ("$fixPath" + "\Folder\SubFolder") -Recurse
Remove-Item ("$fixPath" + "\Folder\SubFolder1") -Recurse
Remove-Item ("$fixPath" + "\Folder\SubFolder2") -Recurse