Skip to content

Commit b7b80e4

Browse files
yifanz7yifanz0
andauthored
[Storage] Storage account planned failover GA (#28728)
Co-authored-by: yifanz0 <[email protected]>
1 parent 6f929ac commit b7b80e4

File tree

8 files changed

+385
-293
lines changed

8 files changed

+385
-293
lines changed

src/Storage/Storage.Management.Test/ScenarioTests/StorageAccountTests.ps1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,9 @@ function Test-GetAzureStorageAccountGeoReplicationStats
911911
Assert-AreEqual $kind $sto.Kind;
912912
Assert-NotNull $sto.GeoReplicationStats.Status
913913
Assert-NotNull $sto.GeoReplicationStats.LastSyncTime
914+
Assert-AreEqual "false" $sto.GeoReplicationStats.CanPlannedFailover
915+
Assert-AreEqual "Standard_LRS" $sto.GeoReplicationStats.PostFailoverRedundancy
916+
Assert-AreEqual "Standard_RAGRS" $sto.GeoReplicationStats.PostPlannedFailoverRedundancy
914917

915918
Retry-IfException { Remove-AzStorageAccount -Force -ResourceGroupName $rgname -Name $stoname; }
916919
}

src/Storage/Storage.Management.Test/SessionRecords/Microsoft.Azure.Commands.Management.Storage.Test.ScenarioTests.StorageAccountTests/TestGetAzureStorageAccountGeoReplicationStats.json

Lines changed: 234 additions & 261 deletions
Large diffs are not rendered by default.

src/Storage/Storage.Management/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
-->
2020
## Upcoming Release
2121
* Updated Azure.Core from 1.45.0 to 1.47.3
22+
* Supported Storage account planned failover: `Invoke-AzStorageAccountFailover`, `Get-AzStorageAccount`
2223

2324
## Version 9.3.0
2425
* Supported Blob Geo Replication SLA on Storage accounts

src/Storage/Storage.Management/Models/PSGeoReplicationStats.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,24 @@ public static PSGeoReplicationStats ParsePSGeoReplicationStats(GeoReplicationSta
2727
return null;
2828
}
2929

30-
PSGeoReplicationStats pSGeoReplicationStats = new PSGeoReplicationStats();
31-
32-
pSGeoReplicationStats.Status = geoReplicationStats.Status;
33-
pSGeoReplicationStats.LastSyncTime = geoReplicationStats.LastSyncTime;
34-
pSGeoReplicationStats.CanFailover = geoReplicationStats.CanFailover;
30+
PSGeoReplicationStats pSGeoReplicationStats = new PSGeoReplicationStats
31+
{
32+
Status = geoReplicationStats.Status,
33+
LastSyncTime = geoReplicationStats.LastSyncTime,
34+
CanFailover = geoReplicationStats.CanFailover,
35+
CanPlannedFailover = geoReplicationStats.CanPlannedFailover,
36+
PostFailoverRedundancy = geoReplicationStats.PostFailoverRedundancy,
37+
PostPlannedFailoverRedundancy = geoReplicationStats.PostPlannedFailoverRedundancy
38+
};
3539

3640
return pSGeoReplicationStats;
3741
}
3842

3943
public string Status { get; set; }
4044
public DateTime? LastSyncTime { get; set; }
4145
public bool? CanFailover { get; set; }
46+
public bool? CanPlannedFailover { get; set; }
47+
public string PostFailoverRedundancy { get; set; }
48+
public string PostPlannedFailoverRedundancy { get; set; }
4249
}
4350
}

src/Storage/Storage.Management/StorageAccount/InvokeAzureStorageAccountFailover.cs

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ public class InvokeAzureStorageAccountFailoverCommand : StorageAccountBaseCmdlet
5959
[ValidateNotNullOrEmpty]
6060
public string Name { get; set; }
6161

62+
[Parameter(
63+
Mandatory = false,
64+
HelpMessage = "Specify the failover type. Possible values are: Unplanned, Planned. If not specified, the default failover type is Unplanned.")]
65+
[PSArgumentCompleter(AccountFailoverType.Planned,
66+
AccountFailoverType.Unplanned)]
67+
public string FailoverType { get; set; }
68+
6269
[Parameter(Mandatory = true,
6370
HelpMessage = "Storage account object",
6471
ValueFromPipeline = true,
@@ -83,30 +90,53 @@ public override void ExecuteCmdlet()
8390

8491
if (ShouldProcess(this.Name, "Invoke Failover of Storage Account"))
8592
{
86-
StringBuilder shouldContinuePrompt = new StringBuilder();
87-
shouldContinuePrompt.AppendLine("Failover the storage account, the secondary cluster will become primary after failover. Please understand the following impact to your storage account before you initiate the failover:");
88-
shouldContinuePrompt.AppendLine(" 1. Please check the Last Sync Time using Get-"+ ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "StorageAccount cmdlet with -IncludeGeoReplicationStats parameter, and check GeoReplicationStats property of your account. This is the data you may lose if you initiate the failover.");
89-
shouldContinuePrompt.AppendLine(" 2. After the failover, your storage account type will be converted to locally redundant storage (LRS). You can convert your account to use geo-redundant storage (GRS).");
90-
shouldContinuePrompt.AppendLine(" 3. Once you re-enable GRS for your storage account, Microsoft will replicate data to your new secondary region. Replication time is dependent on the amount of data to replicate. Please note that there are bandwidth charges for the bootstrap. Please refer to doc: https://azure.microsoft.com/en-us/pricing/details/bandwidth/");
91-
92-
93-
if (this.force || ShouldContinue(shouldContinuePrompt.ToString(), ""))
93+
if (ParameterSetName == AccountObjectParameterSet)
9494
{
95-
if (ParameterSetName == AccountObjectParameterSet)
95+
this.ResourceGroupName = InputObject.ResourceGroupName;
96+
this.Name = InputObject.StorageAccountName;
97+
}
98+
99+
StorageModels.FailoverType? type = null;
100+
if (!String.IsNullOrEmpty(this.FailoverType)) {
101+
if (this.FailoverType.ToLower() == AccountFailoverType.Planned.ToLower())
96102
{
97-
this.ResourceGroupName = InputObject.ResourceGroupName;
98-
this.Name = InputObject.StorageAccountName;
103+
type = StorageModels.FailoverType.Planned;
104+
}
105+
else if (this.FailoverType.ToLower() != AccountFailoverType.Unplanned.ToLower())
106+
{
107+
throw new ArgumentException(string.Format("The Failover Type {0} is invalid.", this.FailoverType), "FailoverType");
99108
}
109+
}
100110

101-
this.StorageClient.StorageAccounts.Failover(
102-
this.ResourceGroupName,
103-
this.Name);
104-
105-
var storageAccount = this.StorageClient.StorageAccounts.GetProperties(this.ResourceGroupName, this.Name);
111+
if (type == null) {
112+
StringBuilder shouldContinuePrompt = new StringBuilder();
113+
shouldContinuePrompt.AppendLine("Failover the storage account, the secondary cluster will become primary after failover. Please understand the following impact to your storage account before you initiate the failover:");
114+
shouldContinuePrompt.AppendLine(" 1. Please check the Last Sync Time using Get-" + ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "StorageAccount cmdlet with -IncludeGeoReplicationStats parameter, and check GeoReplicationStats property of your account. This is the data you may lose if you initiate the failover.");
115+
shouldContinuePrompt.AppendLine(" 2. After the failover, your storage account type will be converted to locally redundant storage (LRS). You can convert your account to use geo-redundant storage (GRS).");
116+
shouldContinuePrompt.AppendLine(" 3. Once you re-enable GRS for your storage account, Microsoft will replicate data to your new secondary region. Replication time is dependent on the amount of data to replicate. Please note that there are bandwidth charges for the bootstrap. Please refer to doc: https://azure.microsoft.com/en-us/pricing/details/bandwidth/");
117+
118+
if (this.force || ShouldContinue(shouldContinuePrompt.ToString(), ""))
119+
{
120+
ExecuteFailover(type);
121+
}
106122

107-
WriteStorageAccount(storageAccount, DefaultContext);
123+
} else
124+
{
125+
ExecuteFailover(type);
108126
}
109127
}
110128
}
129+
130+
private void ExecuteFailover(StorageModels.FailoverType? type = null)
131+
{
132+
this.StorageClient.StorageAccounts.Failover(
133+
this.ResourceGroupName,
134+
this.Name,
135+
type);
136+
137+
var storageAccount = this.StorageClient.StorageAccounts.GetProperties(this.ResourceGroupName, this.Name);
138+
139+
WriteStorageAccount(storageAccount, DefaultContext);
140+
}
111141
}
112142
}

src/Storage/Storage.Management/StorageAccount/StorageAccountBaseCmdlet.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ protected struct DefaultSharePermissionType
100100
internal const string StorageFileDataSmbShareOwner = "StorageFileDataSmbShareOwner";
101101
}
102102

103+
protected struct AccountFailoverType
104+
{
105+
internal const string Planned = "Planned";
106+
internal const string Unplanned = "Unplanned";
107+
}
108+
103109
public IStorageManagementClient StorageClient
104110
{
105111
get

src/Storage/Storage.Management/help/Get-AzStorageAccount.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Get-AzStorageAccount
5757

5858
This command gets all of the Storage accounts in the subscription.
5959

60-
### Example 4: Get a Storage accounts with its blob restore status
60+
### Example 4: Get a Storage account with its blob restore status
6161
```powershell
6262
$account = Get-AzStorageAccount -ResourceGroupName "myresourcegoup" -Name "mystorageaccount" -IncludeBlobRestoreStatus
6363
@@ -70,7 +70,24 @@ Status RestoreId FailureReason Parameters.TimeToR
7070
InProgress a70cd4a1-f223-4c86-959f-cc13eb4795a8 2020-02-10T13:45:04.7155962Z [container1/blob1 -> container2/blob2]
7171
```
7272

73-
This command gets a Storage accounts with its blob restore status, and show the blob restore status.
73+
This command gets a Storage account with its blob restore status, and show the blob restore status.
74+
75+
### Example 5: Get a Storage account with its geo-replication stats
76+
```powershell
77+
$account = Get-AzStorageAccount -ResourceGroupName myresourcegroup -Name myaccount -IncludeGeoReplicationStats
78+
$account.GeoReplicationStats
79+
```
80+
81+
```output
82+
Status : Live
83+
LastSyncTime : 10/21/2025 3:42:38 AM
84+
CanFailover : True
85+
CanPlannedFailover : True
86+
PostFailoverRedundancy : Standard_LRS
87+
PostPlannedFailoverRedundancy : Standard_GRS
88+
```
89+
90+
This command gets a Storage account with its geo-replication stats, and shows the geo-replication stats.
7491

7592
## PARAMETERS
7693

src/Storage/Storage.Management/help/Invoke-AzStorageAccountFailover.md

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ Invokes failover of a Storage account.
1414

1515
### AccountName (Default)
1616
```
17-
Invoke-AzStorageAccountFailover [-ResourceGroupName] <String> [-Name] <String> [-Force] [-AsJob]
18-
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm]
17+
Invoke-AzStorageAccountFailover [-ResourceGroupName] <String> [-Name] <String> [-FailoverType <String>]
18+
[-Force] [-AsJob] [-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm] [<CommonParameters>]
1919
[<CommonParameters>]
2020
```
2121

2222
### AccountObject
2323
```
24-
Invoke-AzStorageAccountFailover -InputObject <PSStorageAccount> [-Force] [-AsJob]
24+
Invoke-AzStorageAccountFailover [-FailoverType <String>] -InputObject <PSStorageAccount> [-Force] [-AsJob]
2525
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm]
2626
[<CommonParameters>]
2727
```
@@ -36,24 +36,64 @@ Please understand the following impact to your storage account before you initia
3636

3737
## EXAMPLES
3838

39-
### Example 1: Invoke failover of a Storage account
39+
### Example 1: Invoke an unplanned failover of a Storage account
4040
<!-- Skip: Output cannot be splitted from code -->
4141

4242

4343
```
4444
$account = Get-AzStorageAccount -ResourceGroupName "MyResourceGroup" -Name "mystorageaccount" -IncludeGeoReplicationStats
4545
$account.GeoReplicationStats
4646
47-
Status LastSyncTime
48-
------ ------------
49-
Live 11/13/2018 2:44:22 AM
47+
Status : Live
48+
LastSyncTime : 10/21/2025 3:42:38 AM
49+
CanFailover : True
50+
CanPlannedFailover : True
51+
PostFailoverRedundancy : Standard_LRS
52+
PostPlannedFailoverRedundancy : Standard_GRS
5053
5154
$job = Invoke-AzStorageAccountFailover -ResourceGroupName "MyResourceGroup" -Name "mystorageaccount" -Force -AsJob
5255
$job | Wait-Job
5356
```
5457

5558
This command check the last sync time of a Storage account then invokes failover of it, the secondary cluster will become primary after failover. Since failover takes a long time, suggest to run it in the backend with -Asjob parameter, and then wait for the job complete.
5659

60+
### Example 2: Invoke a planned failover of a Storage account
61+
<!-- Skip: Output cannot be splitted from code -->
62+
```
63+
PS C:\>$account = Get-AzStorageAccount -ResourceGroupName "MyResourceGroup" -Name "mystorageaccount" -IncludeGeoReplicationStats
64+
PS C:\>$account.GeoReplicationStats
65+
66+
Status : Live
67+
LastSyncTime : 10/21/2025 3:42:38 AM
68+
CanFailover : True
69+
CanPlannedFailover : True
70+
PostFailoverRedundancy : Standard_LRS
71+
PostPlannedFailoverRedundancy : Standard_GRS
72+
73+
PS C:\>$job = Invoke-AzStorageAccountFailover -ResourceGroupName "MyResourceGroup" -Name "mystorageaccount" -FailoverType Planned -Force -AsJob
74+
PS C:\>$job | Wait-Job
75+
```
76+
This command check the last sync time and canFailover status of a Storage account and then invokes a planned failover of it.
77+
78+
### Example 3: Invoke an unplanned failover of a Storage account with FailoverType set to Unplanned
79+
<!-- Skip: Output cannot be splitted from code -->
80+
```
81+
PS C:\>$account = Get-AzStorageAccount -ResourceGroupName "MyResourceGroup" -Name "mystorageaccount" -IncludeGeoReplicationStats
82+
PS C:\>$account.GeoReplicationStats
83+
84+
Status : Live
85+
LastSyncTime : 10/21/2025 3:42:38 AM
86+
CanFailover : True
87+
CanPlannedFailover : True
88+
PostFailoverRedundancy : Standard_LRS
89+
PostPlannedFailoverRedundancy : Standard_GRS
90+
91+
PS C:\>$job = Invoke-AzStorageAccountFailover -ResourceGroupName "MyResourceGroup" -Name "mystorageaccount" -FailoverType Unplanned -Force -AsJob
92+
PS C:\>$job | Wait-Job
93+
```
94+
This command check the last sync time and canFailover status of a Storage account and then invokes an unplanned failover of it.
95+
96+
5797
## PARAMETERS
5898

5999
### -AsJob
@@ -86,6 +126,21 @@ Accept pipeline input: False
86126
Accept wildcard characters: False
87127
```
88128
129+
### -FailoverType
130+
Specify the failover type. Possible values are: Unplanned, Planned. If not specified, the default failover type is Unplanned.
131+
132+
```yaml
133+
Type: System.String
134+
Parameter Sets: (All)
135+
Aliases:
136+
137+
Required: False
138+
Position: Named
139+
Default value: None
140+
Accept pipeline input: False
141+
Accept wildcard characters: False
142+
```
143+
89144
### -Force
90145
Force to Failover the Account
91146
@@ -182,7 +237,7 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
182237
183238
## INPUTS
184239
185-
### System.String
240+
### Microsoft.Azure.Commands.Management.Storage.Models.PSStorageAccount
186241
187242
## OUTPUTS
188243

0 commit comments

Comments
 (0)