PowerShell : Editing File Properties

Whilst writing a script which included a recycler section to ensure files older than 14 days were removed from a directory I needed a way to test the code. I then came across the set-itemproperty cmdlet which was just what I needed.

First i explored the cmdlet via:

get-help get-itemproperty


get-help get-itemproperty -examples

This then led me to run the commands

$folder = "C:\tmp\test"
get-itemproperty $folder | format-list -property *
PSPath : Microsoft.PowerShell.Core\FileSystem::C:\tmp\test
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\tmp
PSChildName : test
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
Mode : d-----
BaseName : test
Target : {}
LinkType :
Name : test
FullName : C:\tmp\test
Parent : tmp
Exists : True
Root : C:\
Extension :
CreationTime : 6/21/2018 11:30:13 AM
CreationTimeUtc : 6/21/2018 10:30:13 AM
LastAccessTime : 6/21/2018 11:30:13 AM
LastAccessTimeUtc : 6/21/2018 10:30:13 AM
LastWriteTime : 6/21/2018 11:30:13 AM
LastWriteTimeUtc : 6/21/2018 10:30:13 AM
Attributes : Directory

I wanted to set the creation and modified dates back in time to test my recyler one-liner, I acheieved this by first setting a variable to be a datetime object in the past (US date format so that’s the 31st January 2018):

$olddate = [datetime]"1/31/2018"

I then assigned this datetime value to the properties of the folder I wanted via the commands:

Set-ItemProperty $folder -Name CreationTime -Value $olddate
Set-ItemProperty $folder -Name LastWriteTime -Value $olddate

This worked a treat. I could then use this command to delete the folder and it’s contents if it was older than 14 days (credit to https://www.thomasmaurer.ch/2010/12/powershell-delete-files-older-than/ for this one):

$Daysback = "-14"
$CurrentDate = Get-Date
$DatetoDelete = $CurrentDate.AddDays($Daysback)
Get-ChildItem C:\tmp | Where-Object { $_.LastWriteTime -lt $DatetoDelete } | Remove-Item -recurse -force

SCCM DP Package Transfer Errors

The Problem

After a DNS change on one of our Distribution Points within SCCM 2012 R2 I decided to remove the old DP entry and recreate it. Removing the DP was easy enough, drop the roles and then delete the server from Administration > Servers and Site System Roles then wait 24 hours for synchronisation to remove the content from the DP.

I then created a new DP in SCCM under the new DNS name and monitored the package transfer via Monitoring > Distribution Point Group Status > All Site Distribution Points. The results looked bad, every package was failing to transfer.

The Investigation

I then reviewed the PkgXferMgr.log file at \\<<SCCM PRI SITE>\SMS_ES1\Logs\ which contained the errors:

Failed to get object class $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.828-60><thread=9112 (0x2398)>
~ExecStaticMethod failed (80041002) SMS_DistributionPoint, AddFile $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.828-60><thread=9112 (0x2398)>
CSendFileAction::AddFile failed; 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.828-60><thread=9112 (0x2398)>
Failed to add the file bcmnfcscr7-x64.cat in content 01C39B5C-4DA1-4A0E-A328-3FD2AC9F500F. Error 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.828-60><thread=9112 (0x2398)>
CSendFileAction::AddFileMetaData failed; 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.828-60><thread=9112 (0x2398)>
CSendFileAction::SendFiles failed; 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.828-60><thread=9112 (0x2398)>
CSendFileAction::SendContent failed; 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.828-60><thread=9112 (0x2398)>
~ Sending failed. Failure count = 24, Restart time = 14/06/2018 06:56:25 GMT Daylight Time $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.828-60><thread=9112 (0x2398)>
Failed to get object class $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.812-60><thread=5008 (0x1390)>
~ExecStaticMethod failed (80041002) SMS_DistributionPoint, AddFile $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.859-60><thread=5008 (0x1390)>
CSendFileAction::AddFile failed; 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.859-60><thread=5008 (0x1390)>
Failed to add the file dptf_cpu.cat in content 08D125AE-8841-4C08-90AA-0D6B091B5C39. Error 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.859-60><thread=5008 (0x1390)>
CSendFileAction::AddFileMetaData failed; 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.859-60><thread=5008 (0x1390)>
CSendFileAction::SendFiles failed; 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.859-60><thread=5008 (0x1390)>
CSendFileAction::SendContent failed; 0x80041002 $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.859-60><thread=5008 (0x1390)>
~ Sending failed. Failure count = 24, Restart time = 14/06/2018 06:56:25 GMT Daylight Time $$<SMS_PACKAGE_TRANSFER_MANAGER><06-14-2018 06:26:25.859-60><thread=5008 (0x1390)>

Inspecting the D:\ drive on the DP suggested the packages were actually being copied, there was plenty of capacity free on the taregt disk D:\ and it’s used storage was increasing through the synch process.

I did some searching online and came across a blog which described the error I received exactly:


Following this blog restored my DP’s synchronisation functionality! What follows is a copy and paste job for reference.

The Solution

On the affected distribution point:

  1. Backup the registry
  2. elevated command prompt:
    cd c:\windows\system32\wbem
    for %F in (*.dll) do regsvr32 /s %F
    mofcomp -check AuditRsop.mof
  3. Review the output of the mofcomp command, if it looks like it’ll compile continue else address any issues:
    mofcomp -AuditRsop.mof
  4. Locate the SMS_DP$ share directory
    cd SMS_DP$\sms\bin
    for %F in (*.dll) do regsvr32 /s %F
    mofcomp -check smsdpprov.mof
  5. Review the output of the mofcomp command, if it looks like it’ll compile continue else address any issues:
    mofcomp smsdpprov.mof
  6. Reboot the DP
  7. Monitor the distribution status from the SCCM console (Monitoring > Distribution Point Group Status > All Site Distribution Points)

Windows Commands : Safeboot

Safe Mode:

bcdedit /set {default} safeboot minimal

Safe Mode with Networking:

bcdedit /set {default} safeboot network

Safe Mode with Command Prompt:

bcdedit /set {default} safeboot minimal
bcdedit /set {default} safebootalternateshell yes

To Remove either use msconfig to return to a normal boot or:

bcdedit /deletevalue {default} safeboot

Powershell Get-ADComputer

The following Powershell commmand uses the Get-ADComputer cmdlet to query AD and return a CSV with the headers “OU”,”Name” & “OperatingSystem”.
It was designed to return this information to marry up with GPO controlled policies against the OUs so I could plan servicing schedules.

Note the “-Properties *” to ensure the OperatingSystem value is available and the “-NoTypeInformation” to drop the header row of the CSV file and leave the column headers as the first row.

Get-ADComputer -Filter * -property * -SearchBase "OU=XXX, DC=XXX, DC=XXX, DC=XXX, DC=XXX" | Select @{l='OU';e={$_.DistinguishedName.split(',')[1].split('=')[1]}},"Name", "OperatingSystem" | export-csv -Path C:\SomeDir\SomeFile.csv -NoTypeInformation

Cancelling a Scheduled chkdsk (Check Disk)

  1. Boot from the Windows 10 DVD / install media
  2. [SHIFT]+[F10] to reveal a command prompt
  3. REGEDT32 to open registry editor
  4. Highlight HKLM > File > Load Hive
  5. Navigate to the local system disk :\Windows\System32\config and open [SYSTEM]
  6. Navigate to HKEY_LOCAL_MACHINE\<LOADED HIVE NAME>\CurrentControlSet\Control\Session Manager\ and edit the BootExecute MULTI-SZ value to read only: autocheck autochk *
  7. Reboot
  8. tadaa!

WIndows Command For-loop

for /f "delims=*" %f in ('dir "C:\folder\of\files\*.efs" /b') DO C:\folder\of\executable\program.exe -f "C:%~pf%f"
  1. A standard debugging technique is to insert the echo command into scripts and even compound/complex commands.  If you do
    for /f "delims=*" %a in ('dir *.avi /b /s') do @echo md "%~na"

    you’ll get the output

    "file 1"
    "file 2"
    "file 3"
    "file 4"


    • The @ prevents the echo commands themselves from displaying, so you see only their output.
    • "delims=…" tells for how to parse the lines of output from the dir *.avi /b /s command.  I don’t know why the answer you linked to suggests "delims=*".  But the default behavior is to break lines apart at spaces, so, if your directory and/or file names contain spaces (as you indicated), you should use "delims=" (specifying that there are no delimiters) to get this to work.
  2. If you type for /? or help for, you’ll get documentation on the for command.  Down in the fifth page, you’ll see
    In addition, substitution of FOR variable references has been enhanced.
    You can now use the following optional syntax:
        %~I         - expands %I removing any surrounding quotes (")
        %~pI        - expands %I to a path only
        %~nI        - expands %I to a file name only
    The modifiers can be combined to get compound results …

    which explains why %~na is getting you just the file name of the *.avi files whose full names are in %a.  Now try

    for /f "delims=" %a in ('dir *.avi /b /s') do @echo md "%~pa"

    and you’ll get

    "the_current_directory\Folder A\"
    "the_current_directory\Folder A\"
    "the_current_directory\Folder B\"
    "the_current_directory\Folder B\"

    From which we can conclude that you want to do

    for /f "delims=" %a in ('dir *.avi /b /s') do md "%~pa%~na"

    to create the file 1 and file 2 directories under Folder A, and the file 3 and file 4 directories under Folder B.   And, as @dave_thompson_085 points out, you can combine %~pa%~na into %~pna.


Powershell Query – get-ADObject filter for Bitlocker

The following command returns all objects in the specified OU (replace XXX with your own values) which have Bitlocker recovery information and what the recovery key is.

Get-ADObject -Filter "objectClass -eq 'msFVE-RecoveryInformation'" -SearchBase "OU=XXX, OU=XXX, OU=XXX, DC=XXX, DC=XXX, DC=XXX, DC=XXX" -Properties msFVE-RecoveryPassword,whenCreated

I’m only really interested in the machine name for review so the following command both chops the Distinguished name field and returns just the second part and then chops that part removing the “CN=” section whilst also returning just that column name from the above query.

Get-ADObject -Filter "objectClass -eq 'msFVE-RecoveryInformation'" -SearchBase "OU=XXX, OU=XXX, OU=XXX, DC=XXX, DC=XXX, DC=XXX, DC=XXX" | Select @{l='ComputerName';e={$_.DistinguishedName.split(',')[1].split('=')[1]}}

The tricky part was the select statement, here’s a brief breakdown of what it’s doing.

@{} defines an array to be returned, which we’ll expect although it could end up being an array of 1 or 0 with no results.

“l” is shorthand for “label” which is the column header.
“e” is shorthand for “expression”.
“$_.” indicates a single item in the pipeline, the results of Get-ADObject are then piped and this catches them one at a time.
“DistinguishedName.split(‘,’)[1].split(‘=’)[1]” takes the value of the name DistinguishedName being piped and splits it first by “,”, selecting the second item in the array that forms “[1]” is second when counting from 0. It then splits that string again on the “=” sign, returning the second part (the actual computer name alone!).



Learned a little more, powershell syntax understanding on the up!

Powershell : Query for user’s last logon date

I needed to work out some AD accounts’ last logon dates to make a further assessment, in powershell I found this was fairly simple:

To get a list of all user attributes available for query:

$> get-aduser -identity <USERNAME_HERE> -Properties *

To query for last logon date:

$> get-aduser -identity <USERNAME_HERE> -Properties LastLogonDate