Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
## [Current nightly]

### Added
- Added `Get-PnPUserAndContentMoveState` cmdlet to retrieve SharePoint Online user and OneDrive content move states.
- Added `Get-PnPMultiGeoCompanyAllowedDataLocation` cmdlet to retrieve SharePoint Online multi-geo allowed data locations. [#5336](https://github.com/pnp/powershell/pull/5336)
- Added `Get-PnPGeoMoveCrossCompatibilityStatus` cmdlet to retrieve SharePoint Online multi-geo move compatibility statuses.

Expand Down
193 changes: 193 additions & 0 deletions documentation/Get-PnPUserAndContentMoveState.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
Module Name: PnP.PowerShell
title: Get-PnPUserAndContentMoveState
schema: 2.0.0
applicable: SharePoint Online
external help file: PnP.PowerShell.dll-Help.xml
online version: https://pnp.github.io/powershell/cmdlets/Get-PnPUserAndContentMoveState.html
---

# Get-PnPUserAndContentMoveState

## SYNOPSIS
Returns the state of SharePoint Online user and OneDrive content move jobs.

## SYNTAX

### MoveReport

```powershell
Get-PnPUserAndContentMoveState [-Limit <UInt32>] [-MoveStartTime <DateTime>] [-MoveEndTime <DateTime>] [-MoveState <MoveState>] [-MoveDirection <MoveDirection>] [-Connection <PnPConnection>]
```

### UserPrincipalName

```powershell
Get-PnPUserAndContentMoveState -UserPrincipalName <String> [-Connection <PnPConnection>]
```

### OdbMoveId

```powershell
Get-PnPUserAndContentMoveState -OdbMoveId <Guid> [-Connection <PnPConnection>]
```

## DESCRIPTION
Returns status information for SharePoint Online multi-geo user and OneDrive content move jobs. You can retrieve one move job by user principal name or OneDrive move ID, or retrieve a move report filtered by state, direction, time window, and limit.

## EXAMPLES

### EXAMPLE 1

```powershell
Get-PnPUserAndContentMoveState -UserPrincipalName user@contoso.com
```

Returns the move state for the specified user.

### EXAMPLE 2

```powershell
Get-PnPUserAndContentMoveState -OdbMoveId 8f6f8e3a-2c1f-4d5b-9a7e-6b3c2a1f0e9d
```

Returns the move state for the specified OneDrive move job ID.

### EXAMPLE 3

```powershell
Get-PnPUserAndContentMoveState -MoveState All -MoveDirection All -Limit 100
```

Returns up to 100 user and content move jobs regardless of state or direction.

### EXAMPLE 4

```powershell
Get-PnPUserAndContentMoveState -MoveStartTime (Get-Date).AddDays(-7) -MoveEndTime (Get-Date) -MoveState Failed -MoveDirection MoveOut -Verbose
```

Returns failed move-out jobs from the last seven days and includes additional diagnostic properties.

## PARAMETERS

### -UserPrincipalName
The user principal name of the user whose move state should be retrieved.

```yaml
Type: String
Parameter Sets: UserPrincipalName

Required: True
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -OdbMoveId
The OneDrive move job ID whose state should be retrieved.

```yaml
Type: Guid
Parameter Sets: OdbMoveId

Required: True
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -Limit
Limits the number of move report entries returned. The value must be between 1 and 1000.

```yaml
Type: UInt32
Parameter Sets: MoveReport

Required: False
Position: Named
Default value: 0
Accept pipeline input: False
Accept wildcard characters: False
```

### -MoveStartTime
Filters move report entries to moves starting at or after the specified date and time. The value is converted to UTC before it is sent to SharePoint Online.

```yaml
Type: DateTime
Parameter Sets: MoveReport

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -MoveEndTime
Filters move report entries to moves ending at or before the specified date and time. The value is converted to UTC before it is sent to SharePoint Online.

```yaml
Type: DateTime
Parameter Sets: MoveReport

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -MoveState
Filters move report entries by move state. Valid values are `NotStarted`, `InProgress`, `Success`, `Failed`, `Stopped`, `Queued`, `NotSupported`, `Rescheduled`, and `All`.

```yaml
Type: MoveState
Parameter Sets: MoveReport

Required: False
Position: Named
Default value: NotStarted
Accept pipeline input: False
Accept wildcard characters: False
```

### -MoveDirection
Filters move report entries by move direction. Valid values are `MoveOut`, `MoveIn`, and `All`.

```yaml
Type: MoveDirection
Parameter Sets: MoveReport

Required: False
Position: Named
Default value: MoveOut
Accept pipeline input: False
Accept wildcard characters: False
```

### -Connection
Optional connection to be used by the cmdlet. Retrieve the value for this parameter by specifying `-ReturnConnection` on `Connect-PnPOnline` or by executing `Get-PnPConnection`.

```yaml
Type: PnPConnection
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

## OUTPUTS

### System.Management.Automation.PSObject
Returns objects with `UserPrincipalName`, `MoveJobId`, `SourceDataLocation`, `DestinationDataLocation`, `TimeStamp`, and `MoveState` properties. Validation-only move jobs return `ValidationState` instead of `TimeStamp` and `MoveState`. When `-Verbose` is specified, additional move job details are returned.

## RELATED LINKS

[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp)
163 changes: 163 additions & 0 deletions src/Commands/Admin/GetUserAndContentMoveState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
using PnP.PowerShell.Commands.Attributes;
using PnP.PowerShell.Commands.Base;
using PnP.PowerShell.Commands.Model;
using PnP.PowerShell.Commands.Utilities.MultiGeo;
using System;
using System.Globalization;
using System.Linq;
using System.Management.Automation;

namespace PnP.PowerShell.Commands.Admin
{
[Cmdlet(VerbsCommon.Get, "PnPUserAndContentMoveState", DefaultParameterSetName = ParameterSetMoveReport)]
[RequiredApiApplicationPermissions("sharepoint/Sites.FullControl.All")]
[RequiredApiDelegatedPermissions("sharepoint/AllSites.FullControl")]
[OutputType(typeof(PSObject))]
public class GetUserAndContentMoveState : PnPSharePointOnlineAdminCmdlet
{
private const string ParameterSetMoveReport = "MoveReport";
private const string ParameterSetUserPrincipalName = "UserPrincipalName";
private const string ParameterSetOdbMoveId = "OdbMoveId";
private static readonly DateTime MinSpecificDate = new(1900, 1, 1);
private static readonly DateTime MaxSpecificDate = new(9000, 1, 1);

[Parameter(Mandatory = true, ParameterSetName = ParameterSetUserPrincipalName)]
[ValidateNotNullOrEmpty]
public string UserPrincipalName { get; set; }

[Parameter(Mandatory = true, ParameterSetName = ParameterSetOdbMoveId)]
[ValidateNotNullOrEmpty]
public Guid OdbMoveId { get; set; }

[Parameter(Mandatory = false, ParameterSetName = ParameterSetMoveReport)]
[ValidateRange(1, 1000)]
public uint Limit { get; set; }

[Parameter(Mandatory = false, ParameterSetName = ParameterSetMoveReport)]
public DateTime MoveStartTime { get; set; }

[Parameter(Mandatory = false, ParameterSetName = ParameterSetMoveReport)]
public DateTime MoveEndTime { get; set; }

[Parameter(Mandatory = false, ParameterSetName = ParameterSetMoveReport)]
public MoveState MoveState { get; set; }

[Parameter(Mandatory = false, ParameterSetName = ParameterSetMoveReport)]
public MoveDirection MoveDirection { get; set; }

protected override void ExecuteCmdlet()
{
var multiGeoRestApiClient = new MultiGeoRestApiClient(AdminContext);
var includeVerboseProperties = IsVerboseMode();

if (ParameterSetName == ParameterSetUserPrincipalName)
{
WriteMoveState(multiGeoRestApiClient.GetUserAndContentMoveState(UserPrincipalName), includeVerboseProperties);
return;
}

if (ParameterSetName == ParameterSetOdbMoveId)
{
WriteMoveState(multiGeoRestApiClient.GetUserAndContentMoveState(OdbMoveId), includeVerboseProperties);
return;
}

var moveStartTimeInUtc = MoveStartTime == DateTime.MinValue ? DateTime.MinValue : MoveStartTime.ToUniversalTime();
var moveEndTimeInUtc = MoveEndTime == DateTime.MinValue ? DateTime.MinValue : MoveEndTime.ToUniversalTime();
var moveStates = multiGeoRestApiClient.GetUserAndContentMoveStates(MoveState, MoveDirection, moveStartTimeInUtc, moveEndTimeInUtc, Limit)
.Where(moveState => moveState != null)
.OrderByDescending(moveState => moveState.LastModified)
.Select(moveState => ConvertToPSObject(moveState, includeVerboseProperties));

WriteObject(moveStates, true);
}

private void WriteMoveState(UserAndContentMoveState moveState, bool includeVerboseProperties)
{
if (moveState != null)
{
WriteObject(ConvertToPSObject(moveState, includeVerboseProperties));
}
}

private bool IsVerboseMode()
{
return MyInvocation.BoundParameters.TryGetValue("Verbose", out var verboseValue) && verboseValue is SwitchParameter verbose && verbose.ToBool();
}

private static PSObject ConvertToPSObject(UserAndContentMoveState moveState, bool includeVerboseProperties)
{
var result = new PSObject();
AddProperty(result, "UserPrincipalName", moveState.UserPrincipalName);
AddProperty(result, "MoveJobId", moveState.Id);
AddProperty(result, "SourceDataLocation", moveState.SourceDataLocation);
AddProperty(result, "DestinationDataLocation", moveState.DestinationDataLocation);

if (moveState.Option.HasFlag(MoveOption.ValidationOnly))
{
AddProperty(result, "ValidationState", "Success");
}
else
{
AddProperty(result, "TimeStamp", moveState.LastModified.ToLocalTime());
AddProperty(result, "MoveState", GetMoveStateDisplayValue(moveState));
}

if (includeVerboseProperties)
{
AddVerboseProperties(result, moveState);
}

return result;
}

private static void AddVerboseProperties(PSObject result, UserAndContentMoveState moveState)
{
AddProperty(result, "IsValidPDL", moveState.ValidationResult == PreferredDataLocationValidationResult.Valid);
AddProperty(result, "HasODBInCurrentLocation", moveState.HasOdbInSourceDataLocation);

if (moveState.State == MoveState.Success)
{
AddProperty(result, "IsContentMoved", moveState.IsContentMoved);
}

AddProperty(result, "ErrorMessage", moveState.ErrorMessage);
AddProperty(result, "SiteId", moveState.SiteId);
AddProperty(result, "MoveDirection", moveState.Direction);
AddProperty(result, "MoveJobPhase", moveState.JobPhase);
AddProperty(result, "MoveJobType", moveState.Type);
AddProperty(result, "PreferredMoveBeginDate", FormatSpecificDate(moveState.PreferredMoveBeginDateInUtc));
AddProperty(result, "PreferredMoveEndDate", FormatSpecificDate(moveState.PreferredMoveEndDateInUtc));
AddProperty(result, "StartedDate", FormatSpecificDate(moveState.StartedDateInUtc));
AddProperty(result, "FinishedDate", FormatSpecificDate(moveState.FinishedDateInUtc));
AddProperty(result, "Option", moveState.Option);
AddProperty(result, "TriggeredBy", moveState.TriggeredBy);
}

private static string GetMoveStateDisplayValue(UserAndContentMoveState moveState)
{
if (!string.IsNullOrWhiteSpace(moveState.StateName))
{
return moveState.StateName;
}

return moveState.State switch
{
MoveState.NotStarted => "ReadyToTrigger",
MoveState.Queued => "Scheduled",
MoveState.InProgress => string.Format(CultureInfo.InvariantCulture, "{0}({1}/4)", moveState.State, (int)moveState.JobPhase),
_ => moveState.State.ToString()
};
}

private static DateTime? FormatSpecificDate(DateTime dateTime)
{
return dateTime > MinSpecificDate && dateTime < MaxSpecificDate ? dateTime.ToLocalTime() : null;
}

private static void AddProperty(PSObject result, string name, object value)
{
result.Properties.Add(new PSNoteProperty(name, value));
}
}
}
Loading
Loading