Skip to content

Commit 1faaacd

Browse files
authored
Merge pull request Azure#5721 from sergey-shandar/sergey-vmss-custom-image
New-AzureRmVm/Vmss: Custom image
2 parents 64fc5ba + 5a65380 commit 1faaacd

File tree

13 files changed

+203
-14
lines changed

13 files changed

+203
-14
lines changed

src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@
6060
<Compile Include="INestedResourceConfig.cs" />
6161
<Compile Include="INestedResourceConfigVisitor.cs" />
6262
<Compile Include="INestedResourceStrategy.cs" />
63+
<Compile Include="IResourceId.cs" />
6364
<Compile Include="Property.cs" />
65+
<Compile Include="ResourceId.cs" />
6466
<Compile Include="ResourceType.cs" />
6567
<Compile Include="SdkEngine.cs" />
6668
<Compile Include="IEngine.cs" />

src/ResourceManager/Common/Commands.Common.Strategies/EntityConfigExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ public static IEnumerable<string> GetIdFromSubscription(this IEntityConfig confi
3232
{
3333
var resourceGroupId = new[]
3434
{
35-
ResourceType.ResourceGroups, config.GetResourceGroupName()
35+
ResourceId.ResourceGroups, config.GetResourceGroupName()
3636
};
3737
return config.ResourceGroup == null
3838
? resourceGroupId
3939
: resourceGroupId.Concat(config.GetProvidersId());
4040
}
4141

4242
internal static IEnumerable<string> GetProvidersId(this IEntityConfig config)
43-
=> new[] { "providers" }.Concat(config.GetIdFromResourceGroup());
43+
=> new[] { ResourceId.Providers }.Concat(config.GetIdFromResourceGroup());
4444
}
4545
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
namespace Microsoft.Azure.Commands.Common.Strategies
16+
{
17+
public interface IResourceId
18+
{
19+
string SubscriptionId { get; }
20+
string ResourceGroupName { get; }
21+
ResourceType ResourceType { get; }
22+
string Name { get; }
23+
}
24+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
namespace Microsoft.Azure.Commands.Common.Strategies
16+
{
17+
public static class ResourceId
18+
{
19+
public const string Subscriptions = "subscriptions";
20+
public const string ResourceGroups = "resourceGroups";
21+
public const string Providers = "providers";
22+
23+
/// <summary>
24+
/// Returns 'null' if the given id is not parsable.
25+
/// </summary>
26+
/// <param name="id"></param>
27+
/// <returns></returns>
28+
public static IResourceId TryParse(string id)
29+
{
30+
const int EmptyI = 0;
31+
const int SubscriptionsI = 1;
32+
const int SubscriptionIdI = 2;
33+
const int ResourceGroupsI = 3;
34+
const int ResourceGroupNameI = 4;
35+
const int ProvidersI = 5;
36+
const int NamespaceI = 6;
37+
const int ProviderI = 7;
38+
const int NameI = 8;
39+
40+
var parts = id.Split('/');
41+
return parts.Length == 9
42+
&& parts[EmptyI] == string.Empty
43+
&& parts[SubscriptionsI] == Subscriptions
44+
&& parts[ResourceGroupsI] == ResourceGroups
45+
&& parts[ProvidersI] == Providers
46+
? new Implementation(
47+
subscriptionId: parts[SubscriptionIdI],
48+
resourceGroupName: parts[ResourceGroupNameI],
49+
resourceType: new ResourceType(
50+
namespace_: parts[NamespaceI],
51+
provider: parts[ProviderI]),
52+
name: parts[NameI])
53+
: null;
54+
}
55+
56+
sealed class Implementation : IResourceId
57+
{
58+
public string Name { get; }
59+
60+
public string ResourceGroupName { get; }
61+
62+
public ResourceType ResourceType { get; }
63+
64+
public string SubscriptionId { get; }
65+
66+
public Implementation(
67+
string subscriptionId,
68+
string resourceGroupName,
69+
ResourceType resourceType,
70+
string name)
71+
{
72+
SubscriptionId = subscriptionId;
73+
ResourceGroupName = resourceGroupName;
74+
ResourceType = resourceType;
75+
Name = name;
76+
}
77+
}
78+
}
79+
}

src/ResourceManager/Common/Commands.Common.Strategies/ResourceType.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@
1515
namespace Microsoft.Azure.Commands.Common.Strategies
1616
{
1717
public sealed class ResourceType
18-
{
19-
public const string ResourceGroups = "resourceGroups";
20-
18+
{
2119
public static ResourceType ResourceGroup { get; }
22-
= new ResourceType(null, ResourceGroups);
20+
= new ResourceType(null, ResourceId.ResourceGroups);
2321

2422
/// <summary>
2523
/// A resource type namespace, for example 'Microsoft.Network'.

src/ResourceManager/Common/Commands.Common.Strategies/SdkEngine.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Microsoft.Azure.Commands.Common.Strategies
2020
/// Engine for REST API calls using Azure SDK.
2121
/// </summary>
2222
public sealed class SdkEngine : IEngine
23-
{
23+
{
2424
string _SubscriptionId { get; }
2525

2626
public SdkEngine(string subscriptionId)
@@ -29,7 +29,7 @@ public SdkEngine(string subscriptionId)
2929
}
3030

3131
public string GetId(IEntityConfig config)
32-
=> new[] { "subscriptions", _SubscriptionId }
32+
=> new[] { ResourceId.Subscriptions, _SubscriptionId }
3333
.Concat(config.GetIdFromSubscription())
3434
.IdToString();
3535
}

src/ResourceManager/Compute/ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
- Additional information about change #1
1919
-->
2020
## Current Release
21+
* `New-AzureRmVM` and `New-AzureRmVMSS` support data disks.
22+
* `New-AzureRmVM` and `New-AzureRmVMSS` support custom image by name or by id.
2123
* Log analytic feature
2224
- Added `Export-AzureRmLogAnalyticRequestRateByInterval` cmdlet
2325
- Added `Export-AzureRmLogAnalyticThrottledRequests` cmdlet

src/ResourceManager/Compute/Commands.Compute/Manual/VirtualMachineScaleSetCreateOrUpdateMethod.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public string Location
139139
public async Task<ResourceConfig<VirtualMachineScaleSet>> CreateConfigAsync()
140140
{
141141
ImageAndOsType = await _client.UpdateImageAndOsTypeAsync(
142-
ImageAndOsType, _cmdlet.ImageName, Location);
142+
ImageAndOsType, _cmdlet.ResourceGroupName, _cmdlet.ImageName, Location);
143143

144144
// generate a domain name label if it's not specified.
145145
_cmdlet.DomainNameLabel = await PublicIPAddressStrategy.UpdateDomainNameLabelAsync(

src/ResourceManager/Compute/Commands.Compute/Properties/Resources.Designer.cs

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ResourceManager/Compute/Commands.Compute/Properties/Resources.resx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,4 +583,15 @@ The file needs to be a PowerShell script (.ps1 or .psm1) or a ZIP archive (.zip)
583583
<value>Use '{0}' to connect to the VMSS instances.</value>
584584
<comment>{0} = connection string</comment>
585585
</data>
586+
<data name="ComputeInvalidImageName" xml:space="preserve">
587+
<value>Invalid image resource id '{0}'.</value>
588+
<comment>{0} = image resource id</comment>
589+
</data>
590+
<data name="ComputeMismatchSubscription" xml:space="preserve">
591+
<value>The image subscription doesn't match the current subscription.</value>
592+
</data>
593+
<data name="ComputeNoImageFound" xml:space="preserve">
594+
<value>Can't find the image '{0}'.</value>
595+
<comment>{0} = image name</comment>
596+
</data>
586597
</root>

src/ResourceManager/Compute/Commands.Compute/Strategies/ComputeRp/ComputeStrategy.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ namespace Microsoft.Azure.Commands.Compute.Strategies.ComputeRp
2222
{
2323
static class ComputeStrategy
2424
{
25+
public const string Namespace = "Microsoft.Compute";
26+
2527
public static ResourceStrategy<TModel> Create<TModel, TOperations>(
2628
string provider,
2729
Func<ComputeManagementClient, TOperations> getOperations,
@@ -30,7 +32,7 @@ public static ResourceStrategy<TModel> Create<TModel, TOperations>(
3032
Func<TModel, int> createTime)
3133
where TModel : Resource
3234
=> ResourceStrategy.Create(
33-
type: new ResourceType("Microsoft.Compute", provider),
35+
type: new ResourceType(Namespace, provider),
3436
getOperations: getOperations,
3537
getAsync: getAsync,
3638
createOrUpdateAsync: createOrUpdateAsync,

src/ResourceManager/Compute/Commands.Compute/Strategies/ComputeRp/ImageEx.cs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ private static int[] CreatePorts(this OperatingSystemTypes osType)
3636
public static async Task<ImageAndOsType> UpdateImageAndOsTypeAsync(
3737
this IClient client,
3838
ImageAndOsType imageAndOsType,
39+
string resourceGroupName,
3940
string imageName,
4041
string location)
4142
{
@@ -44,6 +45,8 @@ public static async Task<ImageAndOsType> UpdateImageAndOsTypeAsync(
4445
return imageAndOsType;
4546
}
4647

48+
var compute = client.GetClient<ComputeManagementClient>();
49+
4750
if (imageName.Contains(':'))
4851
{
4952
if (location == null)
@@ -64,7 +67,7 @@ public static async Task<ImageAndOsType> UpdateImageAndOsTypeAsync(
6467
Sku = imageArray[2],
6568
Version = imageArray[3],
6669
};
67-
var compute = client.GetClient<ComputeManagementClient>();
70+
6871
if (image.Version.ToLower() == "latest")
6972
{
7073
var images = await compute.VirtualMachineImages.ListAsync(
@@ -81,10 +84,44 @@ public static async Task<ImageAndOsType> UpdateImageAndOsTypeAsync(
8184
location, image.Publisher, image.Offer, image.Sku, image.Version);
8285
return new ImageAndOsType(imageModel.OsDiskImage.OperatingSystem, image);
8386
}
87+
else if (imageName.Contains("/"))
88+
{
89+
var resourceId = ResourceId.TryParse(imageName);
90+
if (resourceId == null
91+
|| resourceId.ResourceType.Namespace != ComputeStrategy.Namespace
92+
|| resourceId.ResourceType.Provider != "images")
93+
{
94+
throw new ArgumentException(string.Format(Resources.ComputeInvalidImageName, imageName));
95+
}
96+
97+
if (compute.SubscriptionId != resourceId.SubscriptionId)
98+
{
99+
throw new ArgumentException(Resources.ComputeMismatchSubscription);
100+
}
101+
102+
var localImage = await compute.Images.GetAsync(
103+
resourceGroupName: resourceId.ResourceGroupName,
104+
imageName: resourceId.Name);
105+
106+
return new ImageAndOsType(
107+
localImage.StorageProfile.OsDisk.OsType,
108+
new ImageReference { Id = localImage.Id });
109+
}
84110
else
85111
{
86-
// get image
87-
return Images
112+
try
113+
{
114+
var localImage = await compute.Images.GetAsync(resourceGroupName, imageName);
115+
return new ImageAndOsType(
116+
localImage.StorageProfile.OsDisk.OsType,
117+
new ImageReference { Id = localImage.Id });
118+
}
119+
catch
120+
{
121+
}
122+
123+
// get generic image
124+
var result = Images
88125
.Instance
89126
.SelectMany(osAndMap => osAndMap
90127
.Value
@@ -95,6 +132,13 @@ public static async Task<ImageAndOsType> UpdateImageAndOsTypeAsync(
95132
: OperatingSystemTypes.Linux,
96133
nameAndImage.Value)))
97134
.FirstOrDefault();
135+
136+
if (result == null)
137+
{
138+
throw new ArgumentException(string.Format(Resources.ComputeNoImageFound, imageName));
139+
}
140+
141+
return result;
98142
}
99143
}
100144
}

src/ResourceManager/Compute/Commands.Compute/VirtualMachine/Operation/NewAzureVMCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ public async Task<ResourceConfig<VirtualMachine>> CreateConfigAsync()
254254
if (_cmdlet.DiskFile == null)
255255
{
256256
ImageAndOsType = await _client.UpdateImageAndOsTypeAsync(
257-
ImageAndOsType, _cmdlet.ImageName, Location);
257+
ImageAndOsType, _cmdlet.ResourceGroupName, _cmdlet.ImageName, Location);
258258
}
259259

260260
_cmdlet.DomainNameLabel = await PublicIPAddressStrategy.UpdateDomainNameLabelAsync(

0 commit comments

Comments
 (0)