private async Task CreateContainerGroup()

in src/ccf/caci-ccf-provider/CAciRecoveryServiceInstanceProvider.cs [262:581]


    private async Task<ContainerGroupData> CreateContainerGroup(
        string instanceName,
        string serviceName,
        string akvEndpoint,
        string maaEndpoint,
        string managedIdentityId,
        NetworkJoinPolicy networkJoinPolicy,
        SecurityPolicyConfiguration policyOption,
        JsonObject providerConfig,
        string dnsNameLabel)
    {
        var client = new ArmClient(new DefaultAzureCredential());
        string location = providerConfig["location"]!.ToString();
        string subscriptionId = providerConfig["subscriptionId"]!.ToString();
        string resourceGroupName = providerConfig["resourceGroupName"]!.ToString();
        ResourceIdentifier resourceGroupResourceId =
            ResourceGroupResource.CreateResourceIdentifier(subscriptionId, resourceGroupName);
        ResourceGroupResource resourceGroupResource =
            client.GetResourceGroupResource(resourceGroupResourceId);

        ContainerGroupCollection collection = resourceGroupResource.GetContainerGroups();

        string base64EncodedJoinPolicy = Convert.ToBase64String(
            Encoding.UTF8.GetBytes(JsonSerializer.Serialize(networkJoinPolicy)));

        var containerGroupSecurityPolicy = await GetContainerGroupSecurityPolicy();

        ContainerGroupData data = CreateContainerGroupData(
            location,
            instanceName,
            serviceName,
            akvEndpoint,
            maaEndpoint,
            managedIdentityId,
            networkJoinPolicy,
            dnsNameLabel,
            containerGroupSecurityPolicy);

        this.logger.LogInformation(
            $"Starting container group creation for recovery service: {instanceName}");

        ArmOperation<ContainerGroupResource> lro = await collection.CreateOrUpdateAsync(
            WaitUntil.Completed,
            instanceName,
            data);
        ContainerGroupResource result = lro.Value;

        // The variable result is a resource, you could call other operations on this instance as
        // well.
        ContainerGroupData resourceData = result.Data;

        this.logger.LogInformation(
            $"container group creation succeeded. " +
            $"id: {resourceData.Id}, IP address: {resourceData.IPAddress.IP}, " +
            $"fqdn: {resourceData.IPAddress.Fqdn}");
        return resourceData;

        async Task<ContainerGroupSecurityPolicy> GetContainerGroupSecurityPolicy()
        {
            this.logger.LogInformation($"policyCreationOption: {policyOption.PolicyCreationOption}");
            if (policyOption.PolicyCreationOption == SecurityPolicyCreationOption.allowAll ||
                policyOption.PolicyCreationOption == SecurityPolicyCreationOption.userSupplied)
            {
                var ccePolicyInput = policyOption.PolicyCreationOption ==
                    SecurityPolicyCreationOption.allowAll ?
                    AciConstants.AllowAllRegoBase64 : policyOption.Policy!;
                return new ContainerGroupSecurityPolicy
                {
                    ConfidentialComputeCcePolicy = ccePolicyInput,
                    Images = new()
                {
                    {
                        AciConstants.ContainerName.CcfRecoveryService,
                        $"{ImageUtils.CcfRecoveryServiceImage()}:" +
                        $"{ImageUtils.CcfRecoveryServiceTag()}"
                    },
                    {
                        AciConstants.ContainerName.CcrAttestation,
                        $"{ImageUtils.CcrAttestationImage()}:{ImageUtils.CcrAttestationTag()}"
                    },
                    {
                        AciConstants.ContainerName.Skr,
                        $"{ImageUtils.SkrImage()}:{ImageUtils.SkrTag()}"
                    },
                    {
                        AciConstants.ContainerName.CcrProxy,
                        $"{ImageUtils.CcrProxyImage()}:{ImageUtils.CcrProxyTag()}"
                    }
                }
                };
            }

            (var policyRego, var policyDocument) = await this.DownloadAndExpandPolicy(
                policyOption.PolicyCreationOption,
                base64EncodedJoinPolicy);

            var ccePolicy = Convert.ToBase64String(Encoding.UTF8.GetBytes(policyRego));

            var policyContainers = policyDocument.Containers.ToDictionary(x => x.Name, x => x);
            List<string> requiredContainers =
                [
                    AciConstants.ContainerName.CcfRecoveryService,
                    AciConstants.ContainerName.CcrAttestation,
                    AciConstants.ContainerName.Skr,
                    AciConstants.ContainerName.CcrProxy
                ];
            var missingContainers = requiredContainers.Where(r => !policyContainers.ContainsKey(r));
            if (missingContainers.Any())
            {
                throw new Exception(
                    $"Policy document is missing the following required containers: " +
                    $"{JsonSerializer.Serialize(missingContainers)}");
            }

            var securityPolicy = new ContainerGroupSecurityPolicy
            {
                ConfidentialComputeCcePolicy = ccePolicy,
                Images = []
            };

            foreach (var containerName in requiredContainers)
            {
                var pc = policyContainers[containerName];
                securityPolicy.Images.Add(containerName, $"{pc.Image}@{pc.Digest}");
            }

            return securityPolicy;
        }

        ContainerGroupData CreateContainerGroupData(
            string location,
            string instanceName,
            string serviceName,
            string akvEndpoint,
            string maaEndpoint,
            string managedIdentityId,
            NetworkJoinPolicy? networkJoinPolicy,
            string dnsNameLabel,
            ContainerGroupSecurityPolicy containerGroupSecurityPolicy)
        {
#pragma warning disable MEN002 // Line is too long
            return new ContainerGroupData(
                new AzureLocation(location),
                new ContainerInstanceContainer[]
                {
                new(
                    AciConstants.ContainerName.CcfRecoveryService,
                    containerGroupSecurityPolicy.Images[AciConstants.ContainerName.CcfRecoveryService],
                    new ContainerResourceRequirements(new ContainerResourceRequestsContent(1.5, 1)))
                    {
                        EnvironmentVariables =
                        {
                            new ContainerEnvironmentVariable("ASPNETCORE_URLS")
                            {
                                Value = $"http://+:{Ports.RecoveryServicePort}"
                            },
                            new ContainerEnvironmentVariable("AKV_ENDPOINT")
                            {
                                Value = akvEndpoint
                            },
                            new ContainerEnvironmentVariable("MAA_ENDPOINT")
                            {
                                Value = maaEndpoint
                            },
                            new ContainerEnvironmentVariable("SKR_ENDPOINT")
                            {
                                Value = $"http://localhost:{Ports.SkrPort}"
                            },
                            new ContainerEnvironmentVariable("SERVICE_CERT_LOCATION")
                            {
                                Value = ServiceCertPemFilePath
                            },
                            new ContainerEnvironmentVariable("CCF_NETWORK_INITIAL_JOIN_POLICY")
                            {
                                Value = base64EncodedJoinPolicy
                            }
                        },
                        VolumeMounts =
                        {
                            new ContainerVolumeMount("uds", "/mnt/uds"),
                            new ContainerVolumeMount("shared", "/app/service")
                        }
                    },
                new(
                    AciConstants.ContainerName.CcrAttestation,
                    containerGroupSecurityPolicy.Images[AciConstants.ContainerName.CcrAttestation],
                    new ContainerResourceRequirements(
                        new ContainerResourceRequestsContent(0.5, 0.2)))
                    {
                        Command =
                        {
                            "app",
                            "-socket-address",
                            "/mnt/uds/sock"
                        },
                        VolumeMounts =
                        {
                            new ContainerVolumeMount("uds", "/mnt/uds")
                        }
                    },
                new(
                    AciConstants.ContainerName.Skr,
                    containerGroupSecurityPolicy.Images[AciConstants.ContainerName.Skr],
                    new ContainerResourceRequirements(
                        new ContainerResourceRequestsContent(0.5, 0.2)))
                    {
                        Command =
                        {
                            "/skr.sh"
                        },
                        EnvironmentVariables =
                        {
                            new ContainerEnvironmentVariable("SkrSideCarArgs")
                            {
                                Value = "ewogICAiY2VydGNhY2hlIjogewogICAgICAiZW5kcG9pbnQiOiAiYW1lcmljYXMuYWNjY2FjaGUuYXp1cmUubmV0IiwKICAgICAgInRlZV90eXBlIjogIlNldlNucFZNIiwKICAgICAgImFwaV92ZXJzaW9uIjogImFwaS12ZXJzaW9uPTIwMjAtMTAtMTUtcHJldmlldyIKICAgfQp9"
                            },
                            new ContainerEnvironmentVariable("Port")
                            {
                                Value = $"{Ports.SkrPort}"
                            },
                            new ContainerEnvironmentVariable("LogLevel")
                            {
                                Value = "Info"
                            },
                            new ContainerEnvironmentVariable("LogFile")
                            {
                                Value = "skr.log"
                            }
                        }
                    },
                new(
                    AciConstants.ContainerName.CcrProxy,
                    containerGroupSecurityPolicy.Images[AciConstants.ContainerName.CcrProxy],
                    new ContainerResourceRequirements(
                        new ContainerResourceRequestsContent(0.5, 0.2)))
                {
                    Ports =
                    {
                        new ContainerPort(Ports.EnvoyPort)
                    },
                    Command =
                    {
                        "/bin/sh",
                        "https-http/bootstrap.sh"
                    },
                    EnvironmentVariables =
                    {
                        new ContainerEnvironmentVariable("CCR_ENVOY_DESTINATION_PORT")
                        {
                            Value = Ports.RecoveryServicePort.ToString()
                        },
                        new ContainerEnvironmentVariable("CCR_ENVOY_SERVICE_CERT_OUTPUT_FILE")
                        {
                            Value = ServiceCertPemFilePath
                        }
                    },
                    VolumeMounts =
                    {
                        new ContainerVolumeMount("shared", ServiceFolderMountPath)
                    }
                },
                },
                ContainerInstanceOperatingSystemType.Linux)
            {
                Sku = ContainerGroupSku.Confidential,
                ConfidentialComputeCcePolicy =
                    containerGroupSecurityPolicy.ConfidentialComputeCcePolicy,
                Identity = new ManagedServiceIdentity(ManagedServiceIdentityType.UserAssigned)
                {
                    UserAssignedIdentities =
                    {
                        {
                            new ResourceIdentifier(managedIdentityId),
                            new UserAssignedIdentity()
                        }
                    }
                },
                Tags =
                {
                    {
                        AciConstants.CcfRecoveryServiceNameTag,
                        serviceName
                    },
                    {
                        AciConstants.CcfRecoveryServiceTypeTag,
                        "recovery-service"
                    },
                    {
                        AciConstants.CcfRecoveryServiceResourceNameTag,
                        instanceName
                    }
                },
                IPAddress = new ContainerGroupIPAddress(
                    new ContainerGroupPort[]
                    {
                        new(Ports.EnvoyPort)
                        {
                            Protocol = ContainerGroupNetworkProtocol.Tcp,
                        }
                    },
                    ContainerGroupIPAddressType.Public)
                {
                    DnsNameLabel = dnsNameLabel,
                    AutoGeneratedDomainNameLabelScope = DnsNameLabelReusePolicy.Unsecure
                },
                Volumes =
                {
                    new ContainerVolume("uds")
                    {
                        EmptyDir = BinaryData.FromObjectAsJson(new Dictionary<string, object>())
                    },
                    new ContainerVolume("shared")
                    {
                        EmptyDir = BinaryData.FromObjectAsJson(new Dictionary<string, object>())
                    }
                }
            };
#pragma warning restore MEN002 // Line is too long
        }
    }