Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bastion] Add support for Bastion shareable links #25623

Merged
merged 13 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 10 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
8 changes: 8 additions & 0 deletions src/Network/Network.Test/ScenarioTests/BastionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,13 @@ public void TestBastionCreateWithFeatures()
{
TestRunner.RunTestScript("Test-BastionCreateWithFeatures");
}

[Fact]
[Trait(Category.AcceptanceType, Category.CheckIn)]
[Trait(Category.Owner, NrpTeamAlias.bastion)]
public void TestBastionShareableLink()
{
TestRunner.RunTestScript("Test-BastionShareableLink");
}
}
}
100 changes: 100 additions & 0 deletions src/Network/Network.Test/ScenarioTests/BastionTests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -462,3 +462,103 @@ function Test-BastionIpObjectParam {
Clean-ResourceGroup $rgname
}
}

<#
Test Bastion Shareable Link
#>
function Test-BastionShareableLink {
# Setup
$rgname = Get-ResourceGroupName
$resourceTypeParent = "Microsoft.Network/bastionHosts"
$location = Get-ProviderLocation $resourceTypeParent
$subnetName = "AzureBastionSubnet"
$vnetName = "$(Get-ResourceName)-Vnet"
abhi7860 marked this conversation as resolved.
Show resolved Hide resolved
$vnetName2 = "$(Get-ResourceName)-Vnet"
$publicIpName = "$(Get-ResourceName)-Pip"
$publicIpName2 = "$(Get-ResourceName)-Pip"
$bastionName = "$(Get-ResourceName)-Bastion"
$bastionName2 = "$(Get-ResourceName)-Bastion2"
$vmUsername = "azureuser"
$vmPassword = ConvertTo-SecureString "$vmUsername@123" -AsPlainText -Force
$vmName = "$(Get-ResourceName)-Vm"
$vmSize = "Standard_D2ds_v4"
$nicName = "$(Get-ResourceName)-Nic"

try {
# Create the resource group
$resourceGroup = New-AzResourceGroup -Name $rgName -Location $location

# Create the Virtual Network
$subnet = New-AzVirtualNetworkSubnetConfig -Name $subnetName -AddressPrefix 10.0.0.0/24
$vnet = New-AzVirtualNetwork -Name $vnetName -ResourceGroupName $rgname -Location $location -AddressPrefix 10.0.0.0/16 -Subnet $subnet
$vnet2 = New-AzVirtualNetwork -Name $vnetName2 -ResourceGroupName $rgname -Location $location -AddressPrefix 10.0.0.0/16 -Subnet $subnet

# Create Public IP
$publicIp = New-AzPublicIpAddress -ResourceGroupName $rgname -name $publicIpName -location $location -AllocationMethod Static -Sku Standard
$publicIp2 = New-AzPublicIpAddress -ResourceGroupName $rgname -name $publicIpName2 -location $location -AllocationMethod Static -Sku Standard

# Create Bastion
$createBastionJob = New-AzBastion -Name $bastionName -ResourceGroupName $rgname -VirtualNetwork $vnet -PublicIpAddress $publicIp -EnableShareableLink $true -AsJob
$createBastionJob2 = New-AzBastion -Name $bastionName2 -ResourceGroupName $rgname -VirtualNetwork $vnet2 -PublicIpAddress $publicIp2 -Sku Basic -AsJob

# Create VM
$nic = New-AzNetworkInterface -Name $NICName -ResourceGroupName $RgName -Location $location -SubnetId $vnet.Subnets[0].Id
$Credential = New-Object System.Management.Automation.PSCredential ($vmUsername, $vmPassword);
$vm = New-AzVMConfig -VMName $vmName -VMSize $vmSize
$vm = Set-AzVMOperatingSystem -VM $vm -Windows -ComputerName $vmName -Credential $Credential -ProvisionVMAgent -EnableAutoUpdate
$vm = Add-AzVMNetworkInterface -VM $vm -Id $NIC.Id
$vm = Set-AzVMSourceImage -VM $vm -PublisherName "MicrosoftWindowsServer" -Offer "WindowsServer" -Skus "2022-datacenter-azure-edition-core" -Version latest
$vm = Set-AzVMBootDiagnostic -VM $vm -Disable
$createVmJob = New-AzVM -ResourceGroupName $RgName -Location $location -VM $vm -Verbose -AsJob

# Wait for create Basic Bastion completion
$createBastionJob2 | Wait-Job
$bastion2 = $createBastionJob2 | Receive-Job
Assert-NotNull $bastion2

# Receive error message
$randomName = Get-ResourceName
# New BSL
Assert-Throws { New-AzBastionShareableLink -ResourceGroupName $rgname -Name $randomName -TargetVmId $bastionName2 } "Resource '$randomName' not found"
Assert-Throws { New-AzBastionShareableLink -ResourceGroupName $rgname -Name $bastionName2 -TargetVmId $bastionName2 } "Shareable link feature is not enabled"
# Get BSL
Assert-Throws { Get-AzBastionShareableLink -ResourceGroupName $rgname -Name $randomName -TargetVmId $bastionName2 } "Resource '$randomName' not found"
Assert-Throws { Get-AzBastionShareableLink -ResourceGroupName $rgname -Name $bastionName2 -TargetVmId $bastionName2 } "Shareable link feature is not enabled"
# Remove BSL
Assert-Throws { Remove-AzBastionShareableLink -ResourceGroupName $rgname -Name $randomName -TargetVmId $bastionName2 -Force } "Resource '$randomName' not found"
Assert-Throws { Remove-AzBastionShareableLink -ResourceGroupName $rgname -Name $bastionName2 -TargetVmId $bastionName2 -Force } "Shareable link feature is not enabled"

# Wait for create Bastion completion
$createBastionJob | Wait-Job
$bastion = $createBastionJob | Receive-Job
Assert-NotNull $bastion
Assert-AreEqual $true $bastion.EnableShareableLink

# Wait for create VM completion
$createVmJob | Wait-Job
$vm = Get-AzVM -ResourceGroupName $RgName -Name $vmName
Assert-NotNull $vm
Assert-NotNull $vm.Id

# Create BSL
New-AzBastionShareableLink -InputObject $bastion -TargetVmId $vm.Id

# Get BSL
$getBsl = Get-AzBastionShareableLink -InputObject $bastion
Assert-NotNull $getBsl
Assert-AreEqual $vm.Id $getBsl.VmId
Assert-NotNull $getBsl.Bsl
Assert-NotNull $getBsl.CreatedAt

# Delete BSL
Remove-AzBastionShareableLink -InputObject $bastion -TargetVmId $vm.Id -Force

# Get BSL
$getBsl = Get-AzBastionShareableLink -InputObject $bastion
Assert-AreEqual 0 $getBsl.Count
}
finally {
# Clean up
Clean-ResourceGroup $rgname
}
}

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/Network/Network/Az.Network.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ CmdletsToExport = 'Add-AzApplicationGatewayAuthenticationCertificate',
'Get-AzApplicationSecurityGroup',
'Get-AzAutoApprovedPrivateLinkService',
'Get-AzAvailablePrivateEndpointType', 'Get-AzAvailableServiceAlias',
'Get-AzAvailableServiceDelegation', 'Get-AzBastion',
'Get-AzAvailableServiceDelegation', 'Get-AzBastion', 'Get-AzBastionShareableLink',
'Get-AzBgpServiceCommunity', 'Get-AzCustomIpPrefix',
'Get-AzDdosProtectionPlan', 'Get-AzDelegation',
'Get-AzEffectiveNetworkSecurityGroup', 'Get-AzEffectiveRouteTable',
Expand Down Expand Up @@ -333,7 +333,7 @@ CmdletsToExport = 'Add-AzApplicationGatewayAuthenticationCertificate',
'New-AzApplicationGatewayTrustedRootCertificate',
'New-AzApplicationGatewayUrlPathMapConfig',
'New-AzApplicationGatewayWebApplicationFirewallConfiguration',
'New-AzApplicationSecurityGroup', 'New-AzBastion',
'New-AzApplicationSecurityGroup', 'New-AzBastion', 'New-AzBastionShareableLink',
'New-AzContainerNicConfig', 'New-AzContainerNicConfigIpConfig',
'New-AzCustomIpPrefix', 'New-AzDdosProtectionPlan',
'New-AzDelegation', 'New-AzExpressRouteCircuit',
Expand Down Expand Up @@ -476,7 +476,7 @@ CmdletsToExport = 'Add-AzApplicationGatewayAuthenticationCertificate',
'Remove-AzApplicationGatewayTrustedClientCertificate',
'Remove-AzApplicationGatewayTrustedRootCertificate',
'Remove-AzApplicationGatewayUrlPathMapConfig',
'Remove-AzApplicationSecurityGroup', 'Remove-AzBastion',
'Remove-AzApplicationSecurityGroup', 'Remove-AzBastion', 'Remove-AzBastionShareableLink',
'Remove-AzCustomIpPrefix', 'Remove-AzDdosProtectionPlan',
'Remove-AzDelegation', 'Remove-AzExpressRouteCircuit',
'Remove-AzExpressRouteCircuitAuthorization',
Expand Down
3 changes: 3 additions & 0 deletions src/Network/Network/Bastion/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ internal class Constants
// Scale Units
internal const int MinimumScaleUnits = 2;
internal const int MaximumScaleUnits = 50;

// Shareable Link
internal const string ShareableLink = "ShareableLink";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

namespace Microsoft.Azure.Commands.Network.Bastion
{
using System;
using System.Collections.Generic;
using System.Management.Automation;

using Microsoft.Azure.Commands.Network.Models;
using Microsoft.Azure.Commands.Network.Models.Bastion;
using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
using Microsoft.Azure.Management.Internal.Resources.Utilities.Models;
using Microsoft.Azure.Management.Network;

[Cmdlet(VerbsCommon.Get,
ResourceManager.Common.AzureRMConstants.AzureRMPrefix + Constants.BastionResourceName + Constants.ShareableLink,
DefaultParameterSetName = BastionParameterSetNames.ByResourceGroupName + BastionParameterSetNames.ByName,
SupportsShouldProcess = true),
OutputType(typeof(List<PSBastionShareableLink>))]
public class GetAzBastionShareableLinkCommand : BastionBaseCmdlet
{
[Parameter(
Mandatory = true,
ParameterSetName = BastionParameterSetNames.ByResourceGroupName + BastionParameterSetNames.ByName,
HelpMessage = "The resource group name where Bastion resource exists")]
[ResourceGroupCompleter]
[ValidateNotNullOrEmpty]
public string ResourceGroupName { get; set; }

[Alias("ResourceName", "BastionName")]
[Parameter(
Mandatory = true,
ParameterSetName = BastionParameterSetNames.ByResourceGroupName + BastionParameterSetNames.ByName,
HelpMessage = "The Bastion resource name.")]
[ValidateNotNullOrEmpty]
[ResourceNameCompleter("Bastion", "ResourceGroupName")]
public string Name { get; set; }

[Parameter(
Mandatory = true,
ParameterSetName = BastionParameterSetNames.ByResourceId,
HelpMessage = "The Bastion Resource ID")]
[ValidateNotNullOrEmpty]
public string ResourceId { get; set; }

[Parameter(
Mandatory = true,
ParameterSetName = BastionParameterSetNames.ByBastionObject,
HelpMessage = "Bastion Object")]
[ValidateNotNullOrEmpty]
public PSBastion InputObject { get; set; }

[Parameter(
Mandatory = false,
ValueFromPipeline = true,
HelpMessage = "ID of the VMs to get Bastion shareable links")]
public List<string> TargetVmId { get; set; }

[Parameter(
Mandatory = false,
ValueFromPipeline = true,
HelpMessage = "Run cmdlet in the background")]
public SwitchParameter AsJob { get; set; }

public override void Execute()
{
base.Execute();
if (ParameterSetName.Equals(BastionParameterSetNames.ByResourceId, StringComparison.OrdinalIgnoreCase))
{
var parsedResourceId = new ResourceIdentifier(this.ResourceId);
this.Name = parsedResourceId.ResourceName;
this.ResourceGroupName = parsedResourceId.ResourceGroupName;
}
else if (ParameterSetName.Equals(BastionParameterSetNames.ByBastionObject, StringComparison.OrdinalIgnoreCase))
{
this.Name = this.InputObject.Name;
this.ResourceGroupName = this.InputObject.ResourceGroupName;
}

if (!this.TryGetBastion(this.ResourceGroupName, this.Name, out PSBastion bastion))
{
throw new ItemNotFoundException(string.Format(Properties.Resources.ResourceNotFound, this.Name));
}

if (!bastion.EnableShareableLink.Value)
{
throw new PropertyNotFoundException(Properties.Resources.BastionShareableLinkNotEnabled);
}

var psBslRequest = new PSBastionShareableLinkRequest(this.TargetVmId);
abhi7860 marked this conversation as resolved.
Show resolved Hide resolved
var getBslResultIter = this.NetworkClient.NetworkManagementClient.GetBastionShareableLink(this.ResourceGroupName, this.Name, psBslRequest.ToSdkObject());

List<PSBastionShareableLink> psBslResult = new List<PSBastionShareableLink>();
foreach (var bsl in getBslResultIter)
{
psBslResult.Add(new PSBastionShareableLink(bsl));
}

WriteVerbose($"Found {psBslResult.Count} Bastion shareable links");
WriteObject(psBslResult);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

namespace Microsoft.Azure.Commands.Network.Bastion
{
using System;
using System.Collections.Generic;
using System.Management.Automation;

using Microsoft.Azure.Commands.Network.Models;
using Microsoft.Azure.Commands.Network.Models.Bastion;
using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
using Microsoft.Azure.Management.Internal.Resources.Utilities.Models;
using Microsoft.Azure.Management.Network;

[Cmdlet(VerbsCommon.New,
ResourceManager.Common.AzureRMConstants.AzureRMPrefix + Constants.BastionResourceName + Constants.ShareableLink,
DefaultParameterSetName = BastionParameterSetNames.ByResourceGroupName + BastionParameterSetNames.ByName,
SupportsShouldProcess = true),
OutputType(typeof(List<PSBastionShareableLink>))]
public class NewAzBastionShareableLinkCommand : BastionBaseCmdlet
{
[Parameter(
Mandatory = true,
ParameterSetName = BastionParameterSetNames.ByResourceGroupName + BastionParameterSetNames.ByName,
HelpMessage = "The resource group name where Bastion resource exists")]
[ResourceGroupCompleter]
[ValidateNotNullOrEmpty]
public string ResourceGroupName { get; set; }

[Alias("ResourceName", "BastionName")]
[Parameter(
Mandatory = true,
ParameterSetName = BastionParameterSetNames.ByResourceGroupName + BastionParameterSetNames.ByName,
HelpMessage = "The Bastion resource name")]
[ValidateNotNullOrEmpty]
[ResourceNameCompleter("Bastion", "ResourceGroupName")]
public string Name { get; set; }

[Parameter(
Mandatory = true,
ParameterSetName = BastionParameterSetNames.ByResourceId,
HelpMessage = "The Bastion resource ID")]
[ValidateNotNullOrEmpty]
public string ResourceId { get; set; }

[Parameter(
Mandatory = true,
ParameterSetName = BastionParameterSetNames.ByBastionObject,
HelpMessage = "Bastion object")]
[ValidateNotNullOrEmpty]
public PSBastion InputObject { get; set; }

[Parameter(
Mandatory = true,
ValueFromPipeline = true,
HelpMessage = "ID of the VMs that require generation of Bastion shareable links")]
[ValidateNotNullOrEmpty]
public List<string> TargetVmId { get; set; }

[Parameter(
Mandatory = false,
ValueFromPipeline = true,
HelpMessage = "Run cmdlet in the background")]
public SwitchParameter AsJob { get; set; }

public override void Execute()
{
base.Execute();
if (ParameterSetName.Equals(BastionParameterSetNames.ByResourceId, StringComparison.OrdinalIgnoreCase))
{
var parsedResourceId = new ResourceIdentifier(this.ResourceId);
this.Name = parsedResourceId.ResourceName;
this.ResourceGroupName = parsedResourceId.ResourceGroupName;
}
else if (ParameterSetName.Equals(BastionParameterSetNames.ByBastionObject, StringComparison.OrdinalIgnoreCase))
{
this.Name = this.InputObject.Name;
this.ResourceGroupName = this.InputObject.ResourceGroupName;
}

if (!this.TryGetBastion(this.ResourceGroupName, this.Name, out PSBastion bastion))
{
throw new ItemNotFoundException(string.Format(Properties.Resources.ResourceNotFound, this.Name));
}

if (!bastion.EnableShareableLink.Value)
{
throw new PropertyNotFoundException(Properties.Resources.BastionShareableLinkNotEnabled);
}

var psBslRequest = new PSBastionShareableLinkRequest(this.TargetVmId);
this.NetworkClient.NetworkManagementClient.PutBastionShareableLink(this.ResourceGroupName, this.Name, psBslRequest.ToSdkObject());

var getBslResultIter = this.NetworkClient.NetworkManagementClient.GetBastionShareableLink(this.ResourceGroupName, this.Name, psBslRequest.ToSdkObject());

List<PSBastionShareableLink> psBslResult = new List<PSBastionShareableLink>();
foreach (var bsl in getBslResultIter)
{
psBslResult.Add(new PSBastionShareableLink(bsl));
}

WriteVerbose($"Generated {psBslResult.Count} Bastion shareable links");
WriteObject(psBslResult);
}
}
}
Loading
Loading