public PetDoctorStack()
        {
            var config = new Config();

            var azureResources = CreateBaseAzureInfrastructure(config);

            var clusterOptions = new PetDoctorClusterOptions
            {
                Domain    = config.Require("domain"),
                Namespace = config.Require("kubernetes-namespace"),
                CertificateIssuerAcmeEmail = config.Require("certmanager-acme-email"),
                AppointmentApi             = new ReplicaSetConfiguration
                {
                    AadPodIdentityBindingName = "appointments-api-pod-identity-binding",
                    AadPodIdentityName        = "appointments-api-pod-identity",
                    AadPodIdentitySelector    = "appointments-api",
                    DeploymentName            = "appointments-api",
                    IngressName  = "appointments-api-ingress",
                    ServiceName  = "appointments-api-svc",
                    Image        = azureResources.Registry.LoginServer.Apply(loginServer => $"{loginServer}/pet-doctor/appointments/api:{config.Require("versions-appointments-api")}"),
                    Port         = 80,
                    ReplicaCount = 2,
                    Cpu          = new ResourceLimit
                    {
                        Request = "25m",
                        Limit   = "50m"
                    },
                    Memory = new ResourceLimit
                    {
                        Request = "250Mi",
                        Limit   = "400Mi"
                    },
                    SecretName = "appointments-api-secrets"
                }
            };

            ConfigureKubernetesCluster(azureResources, clusterOptions);

            var appointmentApiAzureResources = CreateAppointmentApiAzureResources(azureResources, config, clusterOptions.AppointmentApi.Image);

            SetupAppointmentApiInKubernetes(azureResources, appointmentApiAzureResources, clusterOptions);
        }
        private static void SetupAppointmentApiInKubernetes(AzureResourceBag azureResources, AppointmentApiAzureResourceBag appointmentApiAzureResources, PetDoctorClusterOptions clusterOptions)
        {
            var customOpts = new CustomResourceOptions
            {
                DependsOn = azureResources.Cluster,
                Provider  = azureResources.ClusterProvider
            };

            var secret = new Secret(clusterOptions.AppointmentApi.SecretName, new SecretArgs
            {
                Metadata = new ObjectMetaArgs
                {
                    Namespace = clusterOptions.Namespace,
                    Name      = clusterOptions.AppointmentApi.SecretName
                },
                Kind       = "Secret",
                ApiVersion = "v1",
                Type       = "Opaque",
                Data       = new InputMap <string>
                {
                    { "keyvault-url", appointmentApiAzureResources.KeyVault.VaultUri.Apply(kvUrl => Convert.ToBase64String(Encoding.UTF8.GetBytes(kvUrl))) },
                    { "appinsights-instrumentationkey", azureResources.AppInsights.InstrumentationKey.Apply(key => Convert.ToBase64String(Encoding.UTF8.GetBytes(key))) }
                }
            }, customOpts);

            var appointmentApiPodIdentity = new CustomResource(clusterOptions.AppointmentApi.AadPodIdentityName,
                                                               new AzureIdentityResourceArgs
            {
                Metadata = new ObjectMetaArgs
                {
                    Name = clusterOptions.AppointmentApi.AadPodIdentityName
                },
                Spec = new AzureIdentitySpecArgs
                {
                    Type       = 0,
                    ResourceId = appointmentApiAzureResources.Identity.Id,
                    ClientId   = appointmentApiAzureResources.Identity.ClientId
                }
            }, customOpts);

            var appointmentApiPodIdentityBinding = new CustomResource(clusterOptions.AppointmentApi.AadPodIdentityBindingName,
                                                                      new AzureIdentityBindingResourceArgs
            {
                Metadata = new ObjectMetaArgs
                {
                    Name = clusterOptions.AppointmentApi.AadPodIdentityBindingName
                },
                Spec = new AzureIdentityBindingSpecArgs
                {
                    AzureIdentity = clusterOptions.AppointmentApi.AadPodIdentityName,
                    Selector      = clusterOptions.AppointmentApi.AadPodIdentitySelector
                }
            }, new CustomResourceOptions
            {
                DependsOn = new InputList <Resource> {
                    azureResources.Cluster, appointmentApiAzureResources.Identity
                },
                Provider = azureResources.ClusterProvider
            });

            var appointmentApiDeployment = new Deployment("appointment-api-deployment", new DeploymentArgs
            {
                ApiVersion = "apps/v1beta1",
                Kind       = "Deployment",
                Metadata   = new ObjectMetaArgs
                {
                    Name      = clusterOptions.AppointmentApi.DeploymentName,
                    Namespace = clusterOptions.Namespace,
                    Labels    = new InputMap <string>
                    {
                        { "app", clusterOptions.AppointmentApi.DeploymentName },
                        { "aadpodidbinding", clusterOptions.AppointmentApi.AadPodIdentitySelector }
                    }
                },
                Spec = new DeploymentSpecArgs
                {
                    Replicas = clusterOptions.AppointmentApi.ReplicaCount,
                    Selector = new LabelSelectorArgs
                    {
                        MatchLabels = new InputMap <string>
                        {
                            { "app", clusterOptions.AppointmentApi.DeploymentName }
                        }
                    },
                    Template = new PodTemplateSpecArgs
                    {
                        Metadata = new ObjectMetaArgs
                        {
                            Labels = new InputMap <string>
                            {
                                { "app", clusterOptions.AppointmentApi.DeploymentName },
                                { "aadpodidbinding", clusterOptions.AppointmentApi.AadPodIdentitySelector }
                            }
                        },
                        Spec = new PodSpecArgs
                        {
                            Containers = new InputList <ContainerArgs>
                            {
                                new ContainerArgs
                                {
                                    Name  = clusterOptions.AppointmentApi.DeploymentName,
                                    Image = clusterOptions.AppointmentApi.Image,
                                    Ports = new InputList <ContainerPortArgs>
                                    {
                                        new ContainerPortArgs
                                        {
                                            ContainerPortValue = clusterOptions.AppointmentApi.Port,
                                            Protocol           = "TCP"
                                        }
                                    },
                                    ReadinessProbe = new ProbeArgs
                                    {
                                        HttpGet = new HTTPGetActionArgs
                                        {
                                            Port   = clusterOptions.AppointmentApi.Port,
                                            Path   = "/ready",
                                            Scheme = "HTTP"
                                        },
                                        InitialDelaySeconds = 15,
                                        TimeoutSeconds      = 2,
                                        PeriodSeconds       = 10,
                                        FailureThreshold    = 3
                                    },
                                    LivenessProbe = new ProbeArgs
                                    {
                                        HttpGet = new HTTPGetActionArgs
                                        {
                                            Port   = clusterOptions.AppointmentApi.Port,
                                            Path   = "/live",
                                            Scheme = "HTTP"
                                        },
                                        InitialDelaySeconds = 30,
                                        TimeoutSeconds      = 2,
                                        PeriodSeconds       = 5,
                                        FailureThreshold    = 3
                                    },
                                    Env = new InputList <EnvVarArgs>
                                    {
                                        new EnvVarArgs
                                        {
                                            Name  = "ASPNETCORE_ENVIRONMENT",
                                            Value = "Production"
                                        },
                                        new EnvVarArgs
                                        {
                                            Name      = "KEYVAULT__URL",
                                            ValueFrom = new EnvVarSourceArgs
                                            {
                                                SecretKeyRef = new SecretKeySelectorArgs
                                                {
                                                    Name = clusterOptions.AppointmentApi.SecretName,
                                                    Key  = "keyvault-url"
                                                }
                                            }
                                        },
                                        new EnvVarArgs
                                        {
                                            Name      = "APPLICATIONINSIGHTS__INSTRUMENTATIONKEY",
                                            ValueFrom = new EnvVarSourceArgs
                                            {
                                                SecretKeyRef = new SecretKeySelectorArgs
                                                {
                                                    Name = clusterOptions.AppointmentApi.SecretName,
                                                    Key  = "appinsights-instrumentationkey"
                                                }
                                            }
                                        },
                                        new EnvVarArgs
                                        {
                                            Name  = "HOSTENV",
                                            Value = "K8S"
                                        },
                                        new EnvVarArgs
                                        {
                                            Name  = "PATH_BASE",
                                            Value = "api"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }, new CustomResourceOptions
            {
                DependsOn = new InputList <Resource> {
                    azureResources.Cluster, appointmentApiAzureResources.Identity, appointmentApiPodIdentityBinding
                },
                Provider = azureResources.ClusterProvider
            });

            var appointmentApiService = new Service(clusterOptions.AppointmentApi.ServiceName, new ServiceArgs
            {
                ApiVersion = "v1",
                Kind       = "Service",
                Metadata   = new ObjectMetaArgs
                {
                    Name      = clusterOptions.AppointmentApi.ServiceName,
                    Namespace = clusterOptions.Namespace
                },
                Spec = new ServiceSpecArgs
                {
                    Selector = new InputMap <string>
                    {
                        { "app", clusterOptions.AppointmentApi.DeploymentName }
                    },
                    Type      = "ClusterIP",
                    ClusterIP = "None",
                    Ports     = new InputList <ServicePortArgs>
                    {
                        new ServicePortArgs
                        {
                            Name       = "http",
                            Protocol   = "TCP",
                            Port       = 80,
                            TargetPort = clusterOptions.AppointmentApi.Port
                        }
                    }
                }
            }, customOpts);

            var appointmentApiIngress = new Ingress(clusterOptions.AppointmentApi.IngressName, new IngressArgs
            {
                ApiVersion = "extensions/v1beta1",
                Kind       = "Ingress",
                Metadata   = new ObjectMetaArgs
                {
                    Name        = clusterOptions.AppointmentApi.IngressName,
                    Namespace   = clusterOptions.Namespace,
                    Annotations = new InputMap <string>
                    {
                        { "kubernetes.io/ingress.class", "nginx" },
                        { "certmanager.k8s.io/cluster-issuer", "letsencrypt-prod" }
                    }
                },
                Spec = new IngressSpecArgs
                {
                    Tls = new InputList <IngressTLSArgs>
                    {
                        new IngressTLSArgs
                        {
                            Hosts = new InputList <string>
                            {
                                clusterOptions.Domain
                            },
                            SecretName = "tls-secret"
                        }
                    },
                    Rules = new InputList <IngressRuleArgs>
                    {
                        new IngressRuleArgs
                        {
                            Host = clusterOptions.Domain,
                            Http = new HTTPIngressRuleValueArgs
                            {
                                Paths = new InputList <HTTPIngressPathArgs>
                                {
                                    new HTTPIngressPathArgs
                                    {
                                        Path    = "/api",
                                        Backend = new IngressBackendArgs
                                        {
                                            ServiceName = clusterOptions.AppointmentApi.ServiceName,
                                            ServicePort = clusterOptions.AppointmentApi.Port
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }, customOpts);
        }
        private static void ConfigureKubernetesCluster(AzureResourceBag azureResources, PetDoctorClusterOptions clusterOptions)
        {
            var componentOpts = new ComponentResourceOptions
            {
                DependsOn = azureResources.Cluster,
                Provider  = azureResources.ClusterProvider
            };

            var customOpts = new CustomResourceOptions
            {
                DependsOn = azureResources.Cluster,
                Provider  = azureResources.ClusterProvider
            };

            var aadPodIdentityDeployment = new ConfigFile("k8s-aad-pod-identity", new ConfigFileArgs
            {
                File = "https://raw.githubusercontent.com/Azure/aad-pod-identity/master/deploy/infra/deployment-rbac.yaml"
            }, componentOpts);

            var certManagerDeployment = new ConfigFile("k8s-cert-manager", new ConfigFileArgs
            {
                File = "https://raw.githubusercontent.com/jetstack/cert-manager/release-0.8/deploy/manifests/00-crds.yaml"
            }, componentOpts);

            var nginxDeployment = new ConfigFile("k8s-nginx-ingress", new ConfigFileArgs
            {
                File =
                    "https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/cloud/deploy.yaml"
            }, componentOpts);

            var clusterNamespace = new Namespace("k8s-namespace", new NamespaceArgs
            {
                Metadata = new ObjectMetaArgs
                {
                    Name = clusterOptions.Namespace
                }
            }, customOpts);

            var clusterIssuer = new CustomResource("k8s-cert-manager-cluster-issuer", new CertManagerClusterIssuerResourceArgs
            {
                Metadata = new ObjectMetaArgs
                {
                    Name = "letsencrypt-prod"
                },
                Spec = new CertManagerClusterIssuerSpecArgs
                {
                    Acme = new CertManagerClusterIssuerAcmeArgs
                    {
                        Email  = clusterOptions.CertificateIssuerAcmeEmail,
                        Server = "https://acme-v02.api.letsencrypt.org/directory",
                        PrivateKeySecretRef = new CertManagerClusterIssuerAcmeSecretArgs
                        {
                            Name = "letsencrypt-prod"
                        }
                    }
                }
            }, customOpts);

            var certs = new CustomResource("k8s-cert-manager-domain-cert", new CertManagerCertificateResourceArgs
            {
                Metadata = new ObjectMetaArgs
                {
                    Name = "tls-secret"
                },
                Spec = new CertManagerCertificateSpecArgs
                {
                    SecretName = "tls-secret",
                    DnsNames   = clusterOptions.Domain,
                    Acme       = new CertManagerCertificateAcmeArgs
                    {
                        Config = new CertManagerCertificateAcmeConfigArgs
                        {
                            Http = new CertManagerCertificateAcmeConfigHttpArgs
                            {
                                IngressClass = "nginx"
                            },
                            Domains = clusterOptions.Domain
                        }
                    },
                    IssuerRef = new CertManagerCertificateIssuerRefArgs
                    {
                        Name = "letsencrypt-prod",
                        Kind = "ClusterIssuer"
                    }
                }
            }, customOpts);
        }