🎓 A1Learner
🔍
0 of 47 lessons completed

PowerShell Project: Backup Tool

A backup tool is a script that copies files to a backup folder and checks the copy. It proves the copy worked by comparing the file counts.

Copy files with an advanced function

The function takes a source folder and a backup folder. It copies the files and prints both counts.

function Backup-Files {
    [CmdletBinding()]
    param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Source, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Backup)
    if (-not (Test-Path $Backup)) { $null = New-Item -ItemType Directory -Path $Backup }
    Copy-Item -Path (Join-Path $Source "*.txt") -Destination $Backup
    $sourceCount = (Get-ChildItem -Path $Source -File).Count
    $backupCount = (Get-ChildItem -Path $Backup -File).Count
    "Source: $sourceCount"
    "Backup: $backupCount"
}
$source = Join-Path $PSScriptRoot "source"
$backup = Join-Path $PSScriptRoot "backup"
$null = New-Item -ItemType Directory -Path $source
Set-Content -Path (Join-Path $source "a.txt") -Value "alpha"
Set-Content -Path (Join-Path $source "b.txt") -Value "beta"
Set-Content -Path (Join-Path $source "c.txt") -Value "gamma"
Backup-Files -Source $source -Backup $backup

Preview with -WhatIf instead of copying

With -WhatIf, PowerShell prints a What if line for each file and ShouldProcess returns false. The function then prints a skipped line for each file and copies nothing.

function Backup-Files {
    [CmdletBinding(SupportsShouldProcess)]
    param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Source, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Backup)
    if (-not (Test-Path $Backup)) { $null = New-Item -ItemType Directory -Path $Backup }
    foreach ($file in (Get-ChildItem -Path $Source -File | Sort-Object Name)) {
        if ($PSCmdlet.ShouldProcess($file.Name, "Copy")) { Copy-Item -Path $file.FullName -Destination $Backup } else { "Skipped: $($file.Name)" }
    }
}
$source = Join-Path $PSScriptRoot "source"
$backup = Join-Path $PSScriptRoot "backup"
$null = New-Item -ItemType Directory -Path $source
Set-Content -Path (Join-Path $source "a.txt") -Value "alpha"
Set-Content -Path (Join-Path $source "b.txt") -Value "beta"
Set-Content -Path (Join-Path $source "c.txt") -Value "gamma"
$null = New-Item -ItemType Directory -Path $backup
Backup-Files -Source $source -Backup $backup -WhatIf

Verify the copy with a fixed-format report

After the copy, the script compares the two counts. Equal counts mean the copy is verified.

function Backup-Files {
    [CmdletBinding()]
    param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Source, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Backup)
    if (-not (Test-Path $Backup)) { $null = New-Item -ItemType Directory -Path $Backup }
    Copy-Item -Path (Join-Path $Source "*.txt") -Destination $Backup
    $sourceCount = (Get-ChildItem -Path $Source -File).Count
    $backupCount = (Get-ChildItem -Path $Backup -File).Count
    "Backup Report"
    "Source files: $sourceCount"
    "Backup files: $backupCount"
    if ($sourceCount -eq $backupCount) { "Verified: Yes" } else { "Verified: No" }
    "Copied: " + ((Get-ChildItem -Path $Backup -File | Sort-Object Name | Select-Object -ExpandProperty Name) -join ", ")
}
$source = Join-Path $PSScriptRoot "source"
$backup = Join-Path $PSScriptRoot "backup"
$null = New-Item -ItemType Directory -Path $source
Set-Content -Path (Join-Path $source "a.txt") -Value "alpha"
Set-Content -Path (Join-Path $source "b.txt") -Value "beta"
Set-Content -Path (Join-Path $source "c.txt") -Value "gamma"
Backup-Files -Source $source -Backup $backup

Example

function Backup-Files {
    [CmdletBinding(SupportsShouldProcess)]
    param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Source, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Backup)
    if (-not (Test-Path $Backup)) { $null = New-Item -ItemType Directory -Path $Backup }
    foreach ($file in (Get-ChildItem -Path $Source -File | Sort-Object Name)) {
        if ($PSCmdlet.ShouldProcess($file.Name, "Copy")) { Copy-Item -Path $file.FullName -Destination $Backup } else { "Skipped: $($file.Name)" }
    }
}
$source = Join-Path $PSScriptRoot "source"
$backup = Join-Path $PSScriptRoot "backup"
$null = New-Item -ItemType Directory -Path $source
Set-Content -Path (Join-Path $source "a.txt") -Value "alpha"
Set-Content -Path (Join-Path $source "b.txt") -Value "beta"
Set-Content -Path (Join-Path $source "c.txt") -Value "gamma"
$null = New-Item -ItemType Directory -Path $backup
Backup-Files -Source $source -Backup $backup -WhatIf
Backup-Files -Source $source -Backup $backup
$sourceCount = (Get-ChildItem -Path $source -File).Count
$backupCount = (Get-ChildItem -Path $backup -File).Count
"Backup Report"
"Source files: $sourceCount"
"Backup files: $backupCount"
if ($sourceCount -eq $backupCount) { "Verified: Yes" } else { "Verified: No" }
"Copied: " + ((Get-ChildItem -Path $backup -File | Sort-Object Name | Select-Object -ExpandProperty Name) -join ", ")

Key points

  • Advanced function: a function with CmdletBinding that gains common parameters such as -WhatIf.
  • Use SupportsShouldProcess to let a function ask whether an action should run.
  • Use ShouldProcess inside the function to run the action or print a skipped line.
  • Use ValidateNotNullOrEmpty to reject an empty path before the function runs.
  • Use Get-ChildItem -File to list only files and read their count.
  • Verification: a check that compares the source count with the backup count.

In this example, the backup function first runs with -WhatIf, so PowerShell prints a What if line for each file and the function prints a skipped line for each file and copies nothing. It then runs for real and copies the files. The driver then prints a fixed-format report that compares the source count with the backup count and lists the copied file names in order.

Note

-WhatIf never copies a file, so a WhatIf run must leave the backup folder empty.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Which attribute lets an advanced function support -WhatIf?