Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Add New-TemporaryDirectory cmdlet (Fixes #25754) - #27086

#27086
Open
Thatgfsj wants to merge 27 commits into
PowerShell:masterPowerShell/PowerShell:masterfrom
Thatgfsj:feature/issue-25754-v2Thatgfsj/PowerShell:feature/issue-25754-v2Copy head branch name to clipboard
Open

Add New-TemporaryDirectory cmdlet (Fixes #25754)#27086
Thatgfsj wants to merge 27 commits into
PowerShell:masterPowerShell/PowerShell:masterfrom
Thatgfsj:feature/issue-25754-v2Thatgfsj/PowerShell:feature/issue-25754-v2Copy head branch name to clipboard

Conversation

@Thatgfsj

@Thatgfsj Thatgfsj commented Mar 22, 2026

Copy link
Copy Markdown

PR Summary

Adds a New-TemporaryDirectory cmdlet that creates a temporary directory in the system's temp folder and returns a DirectoryInfo object pointing to it. This mirrors the existing New-TemporaryFile cmdlet pattern.

PR Context

Fixes #25754. PowerShell currently has New-TemporaryFile but no equivalent for directories. Users often need a throwaway directory for intermediate file processing, build artifacts, or isolated test environments. This cmdlet fills that gap by providing a simple, consistent, and ShouldProcess-aware way to create temporary directories.

PR Checklist

@Thatgfsj
Thatgfsj requested a review from a team as a code owner March 22, 2026 15:09
@microsoft-github-policy-service microsoft-github-policy-service Bot added the Review - Needed The PR is being reviewed label Mar 30, 2026
- Initialize tempDirPath to null for definite assignment
- Move variable declarations to method level (matching New-TemporaryFile pattern)
- Add null/empty check before creating DirectoryInfo (matching New-TemporaryFile pattern)
@Thatgfsj

Copy link
Copy Markdown
Author

Friendly ping @SteveL-MSFT @daxian-dbw — this adds a cmdlet (mirrors the existing pattern). The latest commit fixes a CodeFactor code-quality flag by matching the reference implementation's null-initialization and null-check pattern. CI re-running, ready for review.

Thatgfsj added 3 commits April 29, 2026 01:24
StyleCop SA1117 requires parameters to be all on one line or each on
its own line. The [Cmdlet] attribute had mixed formatting with two
positional parameters on the first line and named parameters on
separate lines.
Put all parameters on the first line except HelpUri, matching the
existing reference implementation in the same directory.
StyleCop SA1117 requires parameters to be either all on the same
line or each on its own line. The reference file's mixed format is
grandfathered but new files must comply.
@kilasuit

Copy link
Copy Markdown
Collaborator

@Thatgfsj Kindly please fill out the summary, context and checklist as part of the PR Template as we cannot/should not merge without these being filled out correctly.

@Thatgfsj

Thatgfsj commented May 6, 2026

Copy link
Copy Markdown
Author

Gentle ping for review. This PR has been open for 6 weeks. Anything needed from my side?

@kilasuit kilasuit added WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module WG-NeedsReview Needs a review by the labeled Working Group labels May 16, 2026
@kilasuit kilasuit added the CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log label May 16, 2026
@kilasuit

kilasuit commented May 16, 2026

Copy link
Copy Markdown
Collaborator

@Thatgfsj - Thank you for updating the PR Context, Summary and Checklist.
This has been added to the cmdlets wg queue to review.
Please also open an issue for this new cmdlet in the docs repo.
Please also add some tests for this new cmdlet.
Other than that I don't think there is more that is needed from you & once all that has been completed we can look to get this merged.

@SteveL-MSFT SteveL-MSFT moved this from Queue to In-Progress-PullRequests in Cmdlets Working Group May 20, 2026
@SteveL-MSFT SteveL-MSFT added WG-Reviewed A Working Group has reviewed this and made a recommendation and removed WG-NeedsReview Needs a review by the labeled Working Group labels May 20, 2026
@SteveL-MSFT SteveL-MSFT moved this from In-Progress-PullRequests to Reviewed in Cmdlets Working Group May 20, 2026
@microsoft-github-policy-service microsoft-github-policy-service Bot removed the Review - Needed The PR is being reviewed label May 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new New-TemporaryDirectory cmdlet to Microsoft.PowerShell.Utility to mirror the existing New-TemporaryFile cmdlet by creating a temporary directory under the system temp path and returning a DirectoryInfo.

Changes:

  • Export New-TemporaryDirectory from Microsoft.PowerShell.Utility on both Windows and Unix module manifests.
  • Add NewTemporaryDirectoryCommand cmdlet implementation returning System.IO.DirectoryInfo.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 Exports the new New-TemporaryDirectory cmdlet on Windows.
src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 Exports the new New-TemporaryDirectory cmdlet on Unix.
src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTemporaryDirectoryCommand.cs Implements the New-TemporaryDirectory cmdlet logic.

/// <summary>
/// The implementation of the "New-TemporaryDirectory" cmdlet.
/// </summary>
[Cmdlet(VerbsCommon.New, "TemporaryDirectory", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=xxxxx")]
Comment on lines +27 to +28
tempDirPath = Path.Combine(tempPath, Path.GetRandomFileName());
Directory.CreateDirectory(tempDirPath);
Comment on lines +19 to +24
protected override void EndProcessing()
{
string tempDirPath = null;
string tempPath = Path.GetTempPath();
if (ShouldProcess(tempPath))
{
@kilasuit

Copy link
Copy Markdown
Collaborator

@SteveL-MSFT we missed this PR in the Cmdlets WG are we wanting to take this one or the other one?

@Thatgfsj

Copy link
Copy Markdown
Author

CodeFactor green ✅ on bad391b. All checks passing.

@jshigetomi

Copy link
Copy Markdown
Collaborator

@Thatgfsj Because this is older I am going to try to take this one instead of #27549.


Context "OutputType" {
It "has an OutputType of System.IO.DirectoryInfo" {
(Get-Command New-TemporaryDirectory).OutputType | Should -BeExactly "System.IO.DirectoryInfo"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OutputType property returns a collection of PSTypeName objects rather than a plain string, so -BeExactly against a string is fragile and can fail even when the cmdlet is correctly annotated. Asserting against .OutputType.Name with -Contain is more robust. This mirrors the form used on the parallel PR #27549 (and what Copilot recommended there).

Suggested change
(Get-Command New-TemporaryDirectory).OutputType | Should -BeExactly "System.IO.DirectoryInfo"
(Get-Command New-TemporaryDirectory).OutputType.Name | Should -Contain "System.IO.DirectoryInfo"

@KirtiRamchandani

Copy link
Copy Markdown

@jshigetomi Can we have .OutputType.Name | Should -Contain "System.IO.DirectoryInfo" which you pointed in this? Though it is #27549 implements this test and also it keeps command ordering cleaner by inserting New-TemporaryDirectory before New-TemporaryFile in the export/default-command lists. Helpuri etc was also added in it so we may be it is merge ready?

@jshigetomi

jshigetomi commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator
  • Changed base class CmdletPSCmdlet (consistent with NewTemporaryFileCommand)

@Thatgfsj This might be a hallucination, NewTemporaryFileCommand uses base class Cmdlet

@Thatgfsj

Copy link
Copy Markdown
Author

@jshigetomi You're absolutely right — my mistake. NewTemporaryFileCommand does use Cmdlet, not PSCmdlet. Fixed in 7e71dd1: reverted back to Cmdlet.

To summarize the current state of this PR:

Features:

  • New-TemporaryDirectory cmdlet returning DirectoryInfo
  • Optional -Prefix parameter for custom name prefix
  • ShouldProcess support (-WhatIf / -Confirm)
  • Collision-safe directory creation (do/while loop matching PathUtils pattern)
  • UnauthorizedAccessException + IOException error handling

Tests:

  • Basic creation, uniqueness, prefix, WhatIf, OutputType
  • Proper cleanup with BeforeEach/AfterEach and try/finally

Other:

…mprove validation

- Remove HelpUri (was shared with New-TemporaryFile, needs its own fwlink)
- Add ValidatePattern on Prefix to reject path-illegal characters
- Fix ShouldProcess targetDescription to be more descriptive
- Restore do/while collision guard in CreateWithoutPrefix (race condition fix)
- Use Directory.CreateDirectory() return value instead of redundant new DirectoryInfo()
@Thatgfsj

Copy link
Copy Markdown
Author

Pushed 712fbdc — addressed code review feedback:

  1. Removed shared HelpUri — was identical to New-TemporaryFile's; a cmdlet-specific fwlink should be assigned before release
  2. Added [ValidatePattern] on -Prefix — rejects path-illegal characters (, /, :, *, ?, ", <, >, |)
  3. Fixed ShouldProcess targetDescription — now uses descriptive string instead of incomplete path
  4. Restored race condition guardCreateWithoutPrefix now uses do/while loop to ensure unique name before CreateDirectory, matching PathUtils.CreateTemporaryDirectory()
  5. Removed redundant new DirectoryInfo() — uses Directory.CreateDirectory() return value directly

CodeFactor green ✅.

@Thatgfsj

Thatgfsj commented Jun 29, 2026

Copy link
Copy Markdown
Author

Here's a summary of where this PR stands:

What was wrong initially:

  • HelpUri was a placeholder (LinkId=xxxxx), Copilot flagged it as breaking Get-Help -Online
  • Directory.CreateDirectory doesn't check for name collisions, so it could return an existing directory — not what you'd expect from "New"
  • No tests at all, Copilot pointed out a new cmdlet needs Pester coverage

What was done to fix it:

  • Removed the HelpUri for now — it shouldn't share the same fwlink as New-TemporaryFile, the docs team can assign a dedicated one later
  • For the no-prefix path: added a do/while loop that checks if the directory already exists before creating, same approach as PathUtils.CreateTemporaryDirectory()
  • For the prefix path: uses Directory.CreateTempSubdirectory(Prefix) which handles uniqueness internally
  • Added a -Prefix parameter with [ValidatePattern] to reject path-illegal characters
  • Added full test coverage: basic creation, with prefix, uniqueness across calls, -WhatIf, output type
  • Extracted error handling into a CreateErrorRecord helper, consistent with New-TemporaryFile

Current status:

  • SteveL-MSFT approved earlier
  • All three Copilot comments addressed
  • jshigetomi flagged that the OutputType test assertion style needs fixing (.OutputType returns an object collection, not a string) — will fix that next
  • Base class confirmed as Cmdlet (matching NewTemporaryFileCommand)

Still need to fix the OutputType test assertion and double-check the base class. Should be close to done after that.

@Thatgfsj

Copy link
Copy Markdown
Author

@jshigetomi Thanks for the review. Fixed the OutputType test assertion to use .Name | Should -Contain as suggested. Let me know if anything else is needed.

@jshigetomi

Copy link
Copy Markdown
Collaborator

@Thatgfsj Could you add the help uri back in? @sdwheeler will make it go live once this is merged in and released.

@Thatgfsj

Thatgfsj commented Jul 3, 2026

Copy link
Copy Markdown
Author

Okay, I'll finish it later.

Thatgfsj added 2 commits July 3, 2026 14:15
…comma

- Windows manifest: add missing comma after Get-SecureRandom
- Both manifests: New-TemporaryDirectory now correctly before New-TemporaryFile
- DefaultCommands.Tests.ps1: swap entries to correct alphabetical order
@Thatgfsj

Thatgfsj commented Jul 3, 2026

Copy link
Copy Markdown
Author

@jshigetomi Done! Added the HelpUri back in d26e5a3. Currently pointing to the same fwlink as New-TemporaryFile@sdwheeler can update it once this is merged and the docs page goes live.

@KirtiRamchandani

Copy link
Copy Markdown

@jshigetomi this pr seems weird, don't know why. Can you please ask @Thatgfsj to handle ShouldProcess even more better? In the #27549 it generates the actual target directory path first and passes that exact path to ShouldProcess while this one passes descriptive text like Temp directory under ..., so -WhatIf/-Confirm is less precise.

If you also see the #27549 actually uses the shared PathUtils.GetTemporaryDirectory() split it introduces, this one currently introduces that helper too, but its cmdlet no longer uses it in the no-prefix path, so it has extra shared-code change without clear benefit, imo.

@Thatgfsj Can you please make this little better using the above and looking at the #27549 ? But wait for the confirmation for @jshigetomi and @sdwheeler .

Thanks!

@Thatgfsj

Thatgfsj commented Jul 4, 2026

Copy link
Copy Markdown
Author

Okay, I will respond in two days.

@Thatgfsj

Thatgfsj commented Jul 5, 2026

Copy link
Copy Markdown
Author

@jshigetomi @Thatgfsj Thanks for the review! Updated based on the feedback:

  1. ShouldProcess now receives the actual target directory path instead of descriptive text. Both with-prefix and without-prefix branches generate the candidate DirectoryInfo first, then call ShouldProcess(targetDirectory.FullName) so -WhatIf/-Confirm show the exact path (mirrors Add New-TemporaryDirectory cmdlet #27549).

  2. Reused PathUtils.GetTemporaryDirectory() in the no-prefix path, so the directory-name generation lives in a single place. The prefix branch keeps its own GetTemporaryDirectoryWithPrefix helper since it needs to prepend the user-supplied prefix.

  3. While at it, also adopted Add New-TemporaryDirectory cmdlet #27549's CreateTemporaryDirectory helper that sets UserRead|UserWrite|UserExecute on Unix so newly created directories are accessible by the owner (matches existing New-TemporaryFile behavior).

The descriptive text ('Temp directory under ...' etc.) is gone, and the CreateTemporaryDirectory shared helper in PathUtils.cs introduced by this PR is now actually used by the cmdlet — so the split has clear benefit. Pushed as d21d9bb on feature/issue-25754-v2. Ready for another look.

@kilasuit kilasuit added WG-Cmdlets general cmdlet issues and removed WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module labels Jul 17, 2026
@jshigetomi

jshigetomi commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

@Thatgfsj Could you please make the following updates before we proceed with this PR?

  1. Fix the current CodeFactor failure (SA1204) by placing the private static methods before the private instance methods.
  2. Make -Prefix validation platform-aware. The current hard-coded pattern applies Windows filename restrictions on every platform and does not cover every invalid character. Please validate against the platform's Path.GetInvalidFileNameChars() behavior and add focused tests for rejected prefixes.
  3. Consolidate the duplicated creation path. CreateWithPrefix() and CreateWithoutPrefix() repeat the ShouldProcess, directory creation, output, and exception-handling logic. Please select/generate the target first and use one common creation path.
  4. Make directory creation atomic. The current existence check followed later by Directory.CreateDirectory() has a check-to-create race and can still return a directory created by another process. Please use Directory.CreateTempSubdirectory(Prefix) or another mechanism that guarantees exclusive creation. If that means ShouldProcess must describe the temp root/prefix rather than an exact final path, preserving the fresh-directory guarantee is more important.

@Thatgfsj

Copy link
Copy Markdown
Author

Ok,let me take a look

… platform-aware validation

- Fix SA1204: move static CreateErrorRecord before instance members
- Remove hardcoded [ValidatePattern] regex, use Path.GetInvalidFileNameChars() for platform-aware validation
- Consolidate CreateWithPrefix / CreateWithoutPrefix into a single code path
- Use Directory.CreateTempSubdirectory() for both prefix and no-prefix cases, guaranteeing atomic creation without race conditions
- Add test for invalid prefix characters
@Thatgfsj

Copy link
Copy Markdown
Author

@jshigetomi Addressed all four points in a523546:

  1. SA1204 ✅ — CreateErrorRecord moved before instance members.
  2. Platform-aware prefix validation ✅ — Removed the hard-coded [ValidatePattern] regex; now uses Path.GetInvalidFileNameChars() at runtime so it adapts to the platform. Added a focused test for rejected prefixes.
  3. Consolidated creation path ✅ — Merged CreateWithPrefix / CreateWithoutPrefix into a single code path in EndProcessing(). The prefix vs. no-prefix decision handles only the Directory.CreateTempSubdirectory() argument.
  4. Atomic creation ✅ — Both paths now use Directory.CreateTempSubdirectory() (no prefix: the parameterless overload; with prefix: CreateTempSubdirectory(Prefix)). No check-to-create race.

@jshigetomi

Copy link
Copy Markdown
Collaborator

@Thatgfsj The current CI failures are compile-time failures, so the Pester/xUnit tests have not run yet.

Directory.CreateTempSubdirectory has a single overload with an optional string parameter. The XML documentation currently references a nonexistent zero-parameter overload, and documentation warnings are treated as errors:

- /// Uses <see cref="Directory.CreateTempSubdirectory()"/> for atomic creation,
+ /// Uses <see cref="Directory.CreateTempSubdirectory(string)"/> for atomic creation,

This same diagnostic is causing the Windows, Linux, macOS, xUnit, and CodeQL jobs to fail at NewTemporaryDirectoryCommand.cs:45.

Also, the latest implementation no longer calls PathUtils.GetTemporaryDirectory(), so please revert the now-unused PathUtils.cs refactor from this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log WG-Cmdlets general cmdlet issues WG-Reviewed A Working Group has reviewed this and made a recommendation

Projects

Status: Reviewed

Development

Successfully merging this pull request may close these issues.

New-TemporaryDirectory or New-TemporaryItem or New-Item -Temporary

6 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.