Create Directory Using Partial Path - powershell

Problem
In PowerShell, is it possible to create a new file directory using a partial path that has wildcards (Ex: C:\*\SomeFolder\MyBackup)? If so, how?
Details
I am working with PowerShell to create an application, and one part of that application has the user designate a backup directory. This directory could be an exact path, but I am also expecting that wildcards could be used. With that said, I know I can easily use MD C:\SomePath\ or New-Item "C:\SomePath\" -FileType Directory IF the path provided is absolute; however, it fails whenever I try to do this with wild cards.
Examples: These all fail when I attempt them
MD "C:\*\MyBackups\AppBackup"
New-Item "C:\*\MyApp\Backup" -FileType Directory
$fullPath = "C:\*\MyApp\Backup" | Resolve-Path
$fullPath = Resolve-Path "C:\*\MyApp\Backup"
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("C:\*\MyApp\Backup")
Now, I understand part of the failure is the wildcard itself as the command doesn't understand how to interpret it.
I have researched things like New-Item, Convert-Path, Resolve-Path, and Split-Path, but haven't been able to find anything that is related to what I'm trying to do.

md, which is an alias for a wrapper function around New-Item, expects an exact path. If you want to create a directory based on a wildcard path you need something like this:
Get-ChildItem 'C:\*\MyBackups' | ForEach-Object {
New-Item -Type Directory -Path $_.FullName -Name 'MyBackup' | Out-Null
}
Note that wildcards in a path usually cover just one level of hierarchy. If you want to find any subfolder MyBackups on the C: drive you need an approach like this:
Get-ChildItem 'C:\' -Filter 'MyBackups' -Directory -Recurse | ForEach-Object {
New-Item -Type Directory -Path $_.FullName -Name 'MyBackup' | Out-Null
}

If you'r attempting to take user input and make that represent the *, then you need to represent that in your code. The only area that confuses me is that you have wildcards. I don't think I fully understand what purpose the wildcards serve in your script.
$backupTemplate = 'MyBackups\AppBackup'
#This can be a lot of different methods of input, from a Read-Host or what I have below
$dirSelector = New-Object System.Windows.Forms.FolderBrowserDialog -Property #{ SelectedPath = Get-Location }
$dirSelector.ShowDialog() | Out-Null
$dirSelector.SelectedPath
$bckupDir = "$($dirSelector.SelectedPath)\$backupTemplate"
#EDIT: You were indicating below you needed to find the parent directory for the 'anchor'. Added this here for that.
$parentDir = (Get-Item $dirSelector.SelectedPath).Parent.FullName
if(-not (Test-Path -Path $bckupDir))
{
New-Item -Path $bckupDir
}
#Execute your code here on whatever is doing the backup.
EDIT/Addition:
If the problem you are running in to is that the User sets their backup directory once and then may not remember where it was, then you should look into storing this variable somewhere that both the user can recall in your cmdlets and your cmdlets can access to utilize.
A very common way to do this is to create a file that holds the settings that the user makes in regards to whatever you are attempting to accomplish.
A nice way to do this is to create a standard object and then export that as an CliXML (or JSON or whatever format you like to work with) like so:
$userSettings = New-Object -TypeName psobject
$userSettings | Add-Member -MemberType NoteProperty -Name User -Value $env:USERNAME
$userSettings | Add-Member -MemberType NoteProperty -Name LastUpdate -Value $(Get-Date -format u)
$userSettings | Add-Member -MemberType NoteProperty -Name BackupDirectory -Value $dirSelector.SelectedPath
$userSettings | Export-Clixml -Path "$env:USERPROFILE\psUserProf.xml"
#when you need to get the users backup folder in the future you just import the profile and work from there
$userSettings = Import-Clixml -Path "$env:USERPROFILE\psUserProf.xml"
$userSettings.BackupDirectory
Note though that if your users have more than one computer you'll need to facilitate a way for these settings to follow them.

Related

Create a powershell script to place a custom word template in the templates folder

I want to place a word template, template.dotm into the Word custom templates folder.
Using Office 365, latest version of Word. Windows 10. Apologies if my terminology is incorrect, still a powershell/programming novice.
This folder doesn't exist by default, and the directory Word looks for default templates in doesn't exist by default either. If a user has created a template, then it will create an expanding string named PersonalTemplates at the following registry key: HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Word\Options, with the value being the directory they've elected as their default custom templates directory.
I want to make a script which:
Checks for presence of PersonalTemplates. If present, and value is not null, store as $regvalue.
If not present, or value is null, create expanding string with the following value $newreg at HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Word\Options.
Then copy template.dotm into the $regvalue or $newreg. Powershell will be run from the same directory as the template.dotm is stored in.
I've got a bunch of snippets which do some of the principle operations, though I can't work out how to tie them together, and am missing some bits which I just can't work out:
Copy the template to the destination
ForEach ($user in (Get-ChildItem "C:\Users" -Exclude Public)) {
New-Item -ItemType Directory -Force -Path "C:\Users$($user.Name)\Documents\Custom Office Templates"
Copy-Item template.dotm -Destination "C:\Users$($user.Name)\Documents\Custom Office Templates"
Create registry key with value
Set-Location -Path
'HKCU:\SOFTWARE\Microsoft\Office\16.0\Word\Options'
New-ItemProperty -Path
'HKCU:\SOFTWARE\Microsoft\Office\16.0\Word\Options' -Name
'PersonalTemplates' -Value "C:\Users$($user.Name)\Documents\Custom
Office Templates" -PropertyType ExpandString -Force }
Get regvalue
$regvalue = (Get-ItemPropertyValue
'HKCU:\SOFTWARE\Microsoft\Office\16.0\Word\Options'
'PersonalTemplates')
I have put together your code snippets in order, also corrected the logic for checking if the Registry key is present or not.
ForEach ($user in (Get-ChildItem "C:\Users" -Exclude Public))
{
$location = "C:\Users\$($user.Name)\Documents\Custom Office Templates"
$IsPresent = Get-ItemProperty 'HKCU:\SOFTWARE\Microsoft\Office\16.0\Word\Options' | ForEach-Object {If($_ -like '*PersonalTemplates*'){ Return 'True' }}
if(-Not($IsPresent -eq 'True'))
{
New-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Office\16.0\Word\Options' -Name 'PersonalTemplates' -Value $location -PropertyType ExpandString -Force \\Not tested
New-Item -ItemType Directory -Force -Path $location
}
$existingValue= Get-ItemPropertyValue -Path 'HKCU:\SOFTWARE\Microsoft\Office\16.0\Word\Options' -Name 'PersonalTemplates'
if([string]::IsNullOrWhiteSpace($existingValue)){
Set-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Office\16.0\Word\Options' -Name 'PersonalTemplates' -Value $location
}
else{
$location=$existingValue
if(!(test-path $existingValue))
{
New-Item -ItemType Directory -Force -Path $existingValue
}
}
Copy-Item template.dotm -Destination $location
}
I have not tested the creation of registry key as I am on my work laptop, so assuming that line of code works.
Also, question for you: With this approach wouldn't the registry entry have the single value that of the first user folder, you may have to look into the logic? I feel you may have to run this script for each user after they login using $env:Username instead of looping through the user folder. But I could be wrong, there may be other who could suggest better.

Powershell Script Check if folder doesnt contain random file other than specific file, It will Create New File

Im quite new to powershell script. Currently I wan to find the files in the path, if the path doesnt contain .txt file, it will create a new text file. Is there anyway i can do that?
I've tried with script below but it came out with the error parameter eq not found
if (Test-Path $path -Exclude *.bak -eq false)
We can use Get-ChilItem as our base here and pass the properties we are searching for such as the the .txt extension and do something with it. Using the if conditional statement, we can accomplish this like so:
#assign the objects returned from Get-ChildItem to $loc
$Loc = Get-ChildItem C:\users\Abraham
#Check to see if the extension is found using a -notcontains conditional operator
#(against the property of .extension of each returned object (paths))
#See here https://www.computerperformance.co.uk/powershell/contains/
if($Loc.extension -notcontains ".txt"){
#this is our "DO", where the condition was met above
#so we will create the text file named "MyText"
#(by passing the "fullname" property which contains the full path of the objects in $Loc.)
New-Item -Path $Loc.fullname -Name MyText.txt}
What we're doing here is referencing the properties of the returned objects from Get-ChildItem by using whats called, "Dot Notation": $loc.Name, $loc.fullname, $loc.LastWriteTime, etc
You can get a list of the properties and methods(stuff you can do to the object(s)) by piping any cmdlet to Get-Member. Get-ChildItem | GM #GM is an alias for Get-Member.
Do you mean you want to use Test-Path to check if a certain file is present and if not create it?
Something like this then perhaps?
$path = 'D:\Test'
if (!(Test-Path -Path "$path\*" -Filter '*.txt' -PathType Leaf)) {
$null = New-Item -Path $path -Name ('Test_{0:ddMMyyyy}.txt' -f (Get-Date))
}

Trying to save specific file with his name folder

firstly excuse my self because i'm a newbie and it's realy hard for me to do simple script.
I trying to save some specific files with there directory parent
Exemple : Here i want to save name of "Folder2" "Folder4" and files2, 3, 6.
Folder1\Folder2\
|===> File1
|===> File2
|===> File3
Folder3\Folder4\
|===> File4
|===> File5
|===> File6
For create this tree in my save :
Save\Folder2\
|===> File2
|===> File3
Save\Folder4\
|===> File6
I trying with this :
$Src = "C:\Folder3\"
$Dst = "C:\Save\"
Get-ChildItem -Path $Src -Recurse -Name "File2.txt" | Where-Object {$_.PSIsContainer} | ForEach-Object {[System.IO.Path]::GetDirectoryName($_); New-Item -Path $Dst -Name $_}
I pretty sure it's really easy but i really don't understand how powershell work.
Thanks
If you are filtering so that only containers (directories) are returned You will never have a file object to copy.
If we know more about what you're trying to do we could probably polish this up nicely. So, this is just a start, but try something like:
$SrcRoot = "C:\temp\01-24-20"
$DstRoot = "C:\temp\Save\"
Get-ChildItem -Path $SrcRoot -Recurse -Directory |
ForEach-Object{
$Root = $_ # Makes it easier to reference the object in the outer ForEach loop.
$DstSub = New-Item -Path $DstRoot -Name $Root.Name -ItemType Directory
# > From your initial code it doesn't look like you need recursion here.
# > Also you may want to add the -Filter parameter to add a wildcard pattern
Get-ChildItem $Root.FullName -File |
ForEach-Object{
# > Add an If block below to make the copy conditional.
Copy-Item $_ -Destination $DstSub
}
}
Let us know how it goes.
By why are you setting these this way, when your diagram shows you are also using another folder...
Folder1\Folder2\
... that you are doing nothing within your code but you want files from it.
<#
$Src = "C:\Folder3\"
$Dst = "C:\Save\"
#>
So, then this. Unless that is a typo.
$src1 = 'Folder1\Folder2\'
$Src3 = 'Folder3\Folder4\'
$Dst = 'C:\Save\'
You are only asking for a single file here
# Get-ChildItem -Path $Src -Recurse -Name "File2.txt" |
$src1,$src3 |
ForEach ($File -in (Get-ChildItem -Path $PSItem.FullName) |
This also means, you have no idea where this file is in the tree, so, using the -Recurse to scan all of them. Don't scan if you don't have to. Also, why are you looking for a folder here, when you are only asking for a single file or file set?
Where-Object {
$_.PSIsContainer} |
You are doing a bunch of raw library stuff when there native cmdlets/properties for this sort of thing
ForEach-Object {
# [System.IO.Path]::GetDirectoryName($_)
$PSItem.DirectoryName
';' This is a terminator for string unrelated code on the same line. So, drop that and make this its own line. See why special characters need to be understood here and punctuation statement separators here.
You are also saying you want to save a file to some destination and that does not require the use of New-Item. New-Item is to create a new empty file, but your diagram appears to state you want to move a file or copy a file from the sources
*Folder1\Folder2*
|===> File1
|===> File2
|===> File3
... to
*Save\Folder2*
|===> File2
|===> File3
... and from
*Folder3\Folder4*
|===> File4
|===> File5
|===> File6
... to
*Save\Folder4*
|===> File6
If ($Filename -eq 'File2|File3')
{Copy-Item -Path "$Dst\$($Filename.DirectoryName)" -Force}
}
Your code appears to be something you copied and pasted from some legacy PowerShell level site and tweaked for what your use case. Nothing wrong with using someone else's code, but not if you do not understand it or are not skilled at using the language of the code use case. Don't cause yourself undue issues.
Again, get some training first. It's virtually all free on Youtube, free online books, etc. This will help get your mind and skills set to limit confusion, error, bad code/practices/habits, etc.
So, as you can see there is a good deal out of place in your code vs what you are after. Mostly due to what you say as not really knowing how PowerShell works.
So, along with the other suggested answer(s), try something like the below. Yet, because you've not spent any time studying up on PowerShell, this may not make sense even if it works for you. So, again, jump on Youtube to get some ramp-up first before trying anyone's code.
Just search for:
'Beginning PowerShell'
'Intermediate PowerShell'
'Advanced PowerShell'
'PowerShell file and folder management'
'PowerShell copying files'
'PowerShell moving files'
'PowerShell Loop'
'Learn PowerShell in a Month of Lunches'
Use the built-in help files and the examples there, practice with those and read the help files again:
# See all help topics
Select-Object -Property Name, Synopsis |
Out-GridView -Title 'Select Topic' -OutputMode Multiple |
ForEach-Object { Get-Help -Name $_.Name -ShowWindow }
explorer "$pshome\$($Host.CurrentCulture.Name)"
# get function / cmdlet details
Get-Command -Name Copy-Item -Syntax
(Get-Command -Name Copy-Item).Parameters.Keys
Get-help -Name Copy-Item -Full
Get-help -Name Copy-Item -Online
Get-help -Name Copy-Item -Examples
Get-Command -Name Move-Item -Syntax
(Get-Command -Name Move-Item).Parameters.Keys
Get-help -Name Move-Item -Full
Get-help -Name Move-Item -Online
Get-help -Name Move-Item -Examples
So, in PowerShell, there are always a number of ways to do X or Y thing, some solutions more elegant than others, but here is an example of what I feel you are after. Again, this is a very rough thing and can be tweaked to make it better, or go a different way, based on the use case.
# Clear the screen
Clear-Host
# Set source and destination resources
$src1 = 'D:\Temp\ParentFolder'
$src3 = 'D:\Test\Reference'
$Dst = 'D:\Save'
# Pipe in the source items
$src1,$src3 |
<#
Loop through the source items
#>
ForEach{
<#
Read the source items to locate specific files
#>
Get-ChildItem -Path $PSItem -Include Test_Audio.csv,hello.bat,ParentWELCOM98.WAV -Recurse |
ForEach {
<#
Target only specif cfile names
#>
If($PSitem -match 'Test_Audio.csv|hello.bat')
{
<#
If this is a match then copy to the destination root by adding the directory of the filename
Using teh -WhatIf to validate the action before deciding to remove the -WhatIf for actual taks completion.
#>
Copy-Item -Path $PSitem.FullName -Destination $Dst\$($PSitem.Directory.BaseName) -WhatIf
}
Else
{
<#
If this is a match then copy to the destination root by adding the directory of the filename
Using teh -WhatIf to validate the action before deciding to remove teh -WhatIf for actual taks completion.
#>
Copy-Item -Path $PSitem.FullName -Destination $Dst\$($PSitem.Directory.BaseName) -WhatIf
}
}
}
# Results
<#
What if: Performing the operation "Copy File" on target "Item: D:\Temp\ParentFolder\ParentWELCOM98.WAV Destination: D:\Save\ParentFolder".
What if: Performing the operation "Copy File" on target "Item: D:\Temp\ParentFolder\Test_Audio.csv Destination: D:\Save\ParentFolder".
What if: Performing the operation "Copy File" on target "Item: D:\Test\Reference\hello.bat Destination: D:\Save\Reference".
#>
When you'd remove the -Whatif and use -Force, things will complete and items will be created.

Powershell - Is it possible to load multiple FileSecurity objects from a single file?

I am using Windows 10 and Powershell. I am moving a large directory of files onto a new server. The files will be easily copied over however the permissions will not be copied over. I have been working on creating a series of scripts to save all of the permissions for each level of the directory, and then be able to give the files in the new location the same pemissions at every level. This is what I have come up with for saving the permissions to file. (T:\ is the directory that the files are being moved from)
Note: I was going to use get-acl and set-acl but apparently you cannot use set-acl without rewriting the owner of each file.
$output = #()
ForEach ($item in (Get-ChildItem -Path T:\ -Recurse -Directory)) {
ForEach ($acl in ($item.GetAccessControl().Access)){
$output += $acl |
Add-Member `
-MemberType NoteProperty `
-Name 'Folder' `
-Value $item.FullName `
-PassThru
}
}
$output | Export-Csv -Path .\outfile.csv -NoTypeInformation
This works to save the necessary information to file. My problem is in reloading all of the permissions once the files are in a new location. From what I have been able to find out I need to use the File.SetAccessControl(String, FileSecurity) Method. However everything I am trying is not working as desired.
Any help here? Maybe I am going about my goal all wrong.

Piping out to "New-Items" in PowerShell

I'm trying to output to a file that is created on the fly, but I can't seem to get either to work. Here's that portion of my code-
New-Item -Path $LogPath -Name $InfoLog -Type File -Force
New-Item -Path $LogPath -Name $ErrorLog -Type File -Force
"Script started at: $DateStamp_${TimeStamp}" | $InfoLog
I've also tried just ">>" instead of the pipe. The script runs fine, it just doesn't pipe the output into the file. Instead it pipes it out to a a files called "0" in the directory the script ran from.
Three things.
First, New-Item outputs the item it creates. So unless you do something with the output objects, New-Item will output the new file items to the pipeline. So I think you might want to say:
New-Item -Path $LogPath -Name $InfoLog -Type File -Force | Out-Null
Second, since you're not specifying the path to the file you want to write to, PowerShell will assume the current location.
Finally, if you want to write output to a log file, you probably want to use Out-File. Perhaps something like this:
$infoLogPath = Join-Path $LogPath $InfoLog
"Script started at: $DateStamp_${TimeStamp}" | Out-File $infoLogPath
Join-Path combines the directory and filename into a fully-qualified path name.