C# で ARM テンプレート関数を使う

C# のプログラムで Azure Resource Manager のテンプレート関数が使いたかった。具体的には guid を。Azure.Deployments.Expression パッケージを使って実現できた。

www.nuget.org

guid 関数を呼び出して、deterministic な GUID を生成するサンプルは次のとおり。

    public static Guid SeededGuid(string src1, string src2, string src3)
    {
        var parameters = new FunctionArgument[]
        {
            new FunctionArgument(new JValue(src1)),
            new FunctionArgument(new JValue(src2)),
            new FunctionArgument(new JValue(src3)),

        };
        var result = ExpressionBuiltInFunctions.Functions
            .EvaluateFunction(
                functionName: "guid",
                parameters: parameters,
                context: new ExpressionEvaluationContext())
            .ToString();
        return Guid.Parse(result);
    }

C# で Azure SQL Databaseの長期バックアップ保有期間を変更する

Azure.ResourceManager.Sql を使って、SQL Database の長期バックアップも設定できる。

www.nuget.org

毎月 1 回目のフルバックアップを 2 ヶ月保有するように変更するサンプルは次の通り。

using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Sql;
using Azure.ResourceManager.Sql.Models;

const string subscriptionId = "サブスクリプション ID";
const string resourceGroupName = "リソースグループ名";
const string serverName = "Azure SQL Server リソース名";
const string databaseName = "データベース名";
const string monthlyRetention = "P2M"; // 2 か月保管

var armClient = new ArmClient(new DefaultAzureCredential());

var id = SqlDatabaseResource.CreateResourceIdentifier(
    subscriptionId: subscriptionId,
    resourceGroupName: resourceGroupName,
    serverName: serverName,
    databaseName: databaseName);
SqlDatabaseResource sqlDatabase = await armClient.GetSqlDatabaseResource(id).GetAsync();
Console.WriteLine(sqlDatabase.Data.Name);

LongTermRetentionPolicyResource policy = await sqlDatabase.GetLongTermRetentionPolicyAsync(
    new LongTermRetentionPolicyName("default"));
Console.WriteLine($"Name: {policy.Data.Name}");
Console.WriteLine($"WeeklyRetention: {policy.Data.WeeklyRetention}");
Console.WriteLine($"MonthlyRetention: {policy.Data.MonthlyRetention}");
Console.WriteLine($"WeekOfYear: {policy.Data.WeekOfYear}");
Console.WriteLine($"YearlyRetention: {policy.Data.YearlyRetention}");

if (policy.Data.MonthlyRetention != monthlyRetention)
{
    policy.Data.MonthlyRetention = monthlyRetention;
    // WeekOfYear は 1 以上 52 以下でないといけない
    if (policy.Data.WeekOfYear == 0)
    {
        policy.Data.WeekOfYear = 1;
    }

    var operation = await policy.UpdateAsync(
        Azure.WaitUntil.Completed,
        data: policy.Data);
    var response = await operation.WaitForCompletionAsync();
    policy = response.Value;
    Console.WriteLine($"Name: {policy.Data.Name}");
    Console.WriteLine($"WeeklyRetention: {policy.Data.WeeklyRetention}");
    Console.WriteLine($"MonthlyRetention: {policy.Data.MonthlyRetention}");
    Console.WriteLine($"WeekOfYear: {policy.Data.WeekOfYear}");
    Console.WriteLine($"YearlyRetention: {policy.Data.YearlyRetention}");
}

Console.ReadLine();

C# で Azure SQL Databaseの自動バックアップ保有期間を変更する

Azure.ResourceManager.Sql を使って、SQL Database の PITR を設定できる。

www.nuget.org

PITR の保有期間を 14 日間に変更するサンプルは次の通り。

using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Sql;
using Azure.ResourceManager.Sql.Models;

const string subscriptionId = "サブスクリプションID";
const string resourceGroupName = "リソースグループ名";
const string serverName = "Azure SQL Server リソース名";
const string databaseName = "データベース名";
const int retentionDays = 14;

var armClient = new ArmClient(new DefaultAzureCredential());

var id = SqlDatabaseResource.CreateResourceIdentifier(
    subscriptionId: subscriptionId,
    resourceGroupName: resourceGroupName,
    serverName: serverName,
    databaseName: databaseName);
SqlDatabaseResource sqlDatabase = await armClient.GetSqlDatabaseResource(id).GetAsync();
Console.WriteLine(sqlDatabase.Data.Name);

BackupShortTermRetentionPolicyResource policy = await sqlDatabase.GetBackupShortTermRetentionPolicyAsync(
    new ShortTermRetentionPolicyName("default"));
Console.WriteLine($"Name:{policy.Data.Name}");
Console.WriteLine($"RetentionDays:{policy.Data.RetentionDays}");
Console.WriteLine($"DiffBackupIntervalInHours:{policy.Data.DiffBackupIntervalInHours}");
Console.WriteLine();

if (policy.Data.RetentionDays != retentionDays)
{
    policy.Data.RetentionDays = retentionDays;
    var operation = await policy.UpdateAsync(
        Azure.WaitUntil.Completed,
        data: policy.Data);
    var response = await operation.WaitForCompletionAsync();
    policy = response.Value;
    Console.WriteLine($"Name:{policy.Data.Name}");
    Console.WriteLine($"RetentionDays:{policy.Data.RetentionDays}");
    Console.WriteLine($"DiffBackupIntervalInHours:{policy.Data.DiffBackupIntervalInHours}");
}

Console.ReadLine();

C# で Azure Entra ID に登録されているアプリにロールを割り当てる

Azure.ResourceManager.Authorization を使えばできた。

www.nuget.org

リソースの共同作成者を割り当ててみたサンプル。

using Azure;
using Azure.Core;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Authorization;
using Azure.ResourceManager.Authorization.Models;
using Azure.ResourceManager.Sql;

var armClient = new ArmClient(new DefaultAzureCredential());

var sqlServerResourceId = SqlServerResource.CreateResourceIdentifier(
    subscriptionId: "your subscription id",
    resourceGroupName: "your resource group name",
    serverName: "your server name");

var servicePrincipalId = new Guid("your service principal id");

await AssignContributorRoleAsync(
    armClient: armClient,
    resourceIdentifier: sqlServerResourceId,
    principalId: servicePrincipalId);

async static ValueTask AssignContributorRoleAsync(ArmClient armClient, ResourceIdentifier resourceIdentifier, Guid principalId)
{
    // 「共同作成者」のロール ID
    const string ContributorRoleId = "b24988ac-6180-42a0-ab88-20f7382dd24c";
    await AssignRoleAsync(
        armClient: armClient,
        principalId: principalId,
        roleAssignmentName: ContributorRoleId,
        scope: resourceIdentifier.ToString());
}

async static ValueTask<RoleAssignmentResource> AssignRoleAsync(ArmClient armClient, Guid principalId, string roleAssignmentName, string scope)
{
    var roleAssignmentResourceId = RoleAssignmentResource.CreateResourceIdentifier(
        scope: scope,
        roleAssignmentName: roleAssignmentName);

    var roleAssignmentResource = armClient.GetRoleAssignmentResource(roleAssignmentResourceId);

    var content = new RoleAssignmentCreateOrUpdateContent(
        roleDefinitionId: roleAssignmentResource.Id,
        principalId: principalId)
    {
        PrincipalType = RoleManagementPrincipalType.ServicePrincipal,
    };

    var armOperation = await roleAssignmentResource.UpdateAsync(
        waitUntil: WaitUntil.Completed,
        content: content);

    var response = await armOperation.WaitForCompletionAsync();

    return response.Value;
}

C# で Microsoft Azure の DNS ゾーンに A レコードを登録する

Azure.ResourceManager.Dns パッケージを使って DNS ゾーンを操作できる。

www.nuget.org

A レコードを追加するサンプルコードは次の通り。既存のパブリックIPアドレスリソースのエイリアスにしている。

using Azure;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Dns;
using Azure.ResourceManager.Network;

const string dnsZoneSubscriptionId = "DNS ゾーンリソースがあるサブスクリプションの ID";
const string dnsZoneResourceGroupName = "DNS ゾーンリソースがあるリソースグループの名前";
const string zoneName = "DNS ゾーンりそーずの名前";
const string aRecordName = "A レコードの名前";
const string publicIPAddressSubscriptionId = "IP アドレスリソースがあるサブスクリプションの ID";
const string publicIPAddressResourceGroupName = "IP アドレスリソースがあるリソースグループの名前";
const string publicIPAddressName = "IP アドレスリソースの名前";

var armClient = new ArmClient(new DefaultAzureCredential());

var dnsZoneId = DnsZoneResource.CreateResourceIdentifier(
    subscriptionId: dnsZoneSubscriptionId,
    resourceGroupName: dnsZoneResourceGroupName,
    zoneName: zoneName);
var dnsZoneResponse = await armClient.GetDnsZoneResource(dnsZoneId).GetAsync();
var dnsZone = dnsZoneResponse.Value;
Console.WriteLine(dnsZone.Data.Name);

var dnsARecords = dnsZone.GetDnsARecords();
DnsARecordResource dnsARecord;
if (await dnsARecords.ExistsAsync(aRecordName))
{
    var dnsARecordResponse = await dnsARecords.GetAsync(aRecordName);
    dnsARecord = dnsARecordResponse.Value;
}
else
{
    var publicIPAddressId = PublicIPAddressResource.CreateResourceIdentifier(
        subscriptionId: publicIPAddressSubscriptionId,
        resourceGroupName: publicIPAddressResourceGroupName,
        publicIPAddressName: publicIPAddressName);

    var dnsARecordOperation = await dnsARecords.CreateOrUpdateAsync(
        waitUntil: WaitUntil.Completed,
        aRecordName: aRecordName,
        data: new DnsARecordData
        {
            TtlInSeconds = 60 * 60,
            TargetResourceId = publicIPAddressId,
        });
    var dnsARecordResponse = await dnsARecordOperation.WaitForCompletionAsync();
    dnsARecord = dnsARecordResponse.Value;
}
Console.WriteLine($"Name: {dnsARecord.Data.Name}");
Console.WriteLine($"Fqdn: {dnsARecord.Data.Fqdn}");
Console.WriteLine($"TtlInSeconds: {dnsARecord.Data.TtlInSeconds}");
Console.WriteLine($"TargetResourceId: {dnsARecord.Data.TargetResourceId}");
Console.ReadLine();

C# で Microsoft Azure のパブリックIPアドレスを作成する

Azure.ResourceManager.Network パッケージを使う。

www.nuget.org

パブリック IP アドレスが無ければ作成するサンプル。

using Azure;
using Azure.Core;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Network;
using Azure.ResourceManager.Network.Models;
using Azure.ResourceManager.Resources;

var armClient = new ArmClient(new DefaultAzureCredential());
var subscription = await armClient.GetDefaultSubscriptionAsync();

// リソースグループが無ければ作成
const string resourceGroupName = "rg-ipaddress-test";
var resourceGroups = subscription.GetResourceGroups();
ResourceGroupResource resourceGroup;
if (await resourceGroups.ExistsAsync(resourceGroupName))
{
    resourceGroup = await resourceGroups.GetAsync(resourceGroupName);
}
else
{
    var resourceGroupOperation = await resourceGroups.CreateOrUpdateAsync(
        waitUntil: WaitUntil.Completed,
        resourceGroupName: resourceGroupName,
        data: new ResourceGroupData(AzureLocation.JapanEast));
    resourceGroup = await resourceGroupOperation.WaitForCompletionAsync();
}


// IP アドレスが無ければ作成
const string publicIPAddressName = "pip-ipaddress-test";
var publicIPAddresses = resourceGroup.GetPublicIPAddresses();
PublicIPAddressResource publicIPAddress;
if (await publicIPAddresses.ExistsAsync(publicIPAddressName))
{
    publicIPAddress = await publicIPAddresses.GetAsync(publicIPAddressName);
}
else
{
    var publicIPAddressOperation = await publicIPAddresses.CreateOrUpdateAsync(
        waitUntil: WaitUntil.Completed,
        publicIPAddressName: publicIPAddressName,
        data: new PublicIPAddressData
        {
            Location = AzureLocation.JapanEast,
            Sku = new PublicIPAddressSku
            {
                Name = PublicIPAddressSkuName.Standard,
                Tier = PublicIPAddressSkuTier.Regional,
            },
            PublicIPAddressVersion = NetworkIPVersion.IPv4,
            PublicIPAllocationMethod = NetworkIPAllocationMethod.Static,
        });
    publicIPAddress = await publicIPAddressOperation.WaitForCompletionAsync();
}

Console.WriteLine($"{publicIPAddress.Data.Name}:{publicIPAddress.Data.IPAddress}");
Console.ReadLine();

A10 - Resort Hotel

atcoder.jp

左からと右からの累積MAX(?)を計算しておく。

var N = int.Parse(Console.ReadLine()!);
var A = Console.ReadLine()!.Split(' ').Select(x => int.Parse(x)).ToList();
var D = int.Parse(Console.ReadLine()!);
var Q = new List<(int Left, int Right)>();
for (var i = 0; i < D; i++)
{
    var input = Console.ReadLine()!.Split(' ').Select(x => int.Parse(x)).ToList();
    Q.Add((input[0], input[1]));

}

// 左からの累積 MAX を計算
var L = new int[N];
L[0] = A[0];
for (var i = 1; i < N; i++)
{
    L[i] = Math.Max(L[i - 1], A[i]);
}

// 右からの累積 MAX を計算
var R = new int[N];
R[N - 1] = A[N - 1];
for (var i = N - 2; i >= 0; i--)
{
    R[i] = Math.Max(R[i + 1], A[i]);
}

for (var i = 0; i < D; i++)
{
    var left = Q[i].Left <= 1 ? 0 : L[Q[i].Left - 1 - 1];
    var right = Q[i].Right >= N ? 0 : R[Q[i].Right - 1 + 1];
    var answer = Math.Max(left, right);
    Console.WriteLine(answer);
}