Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Managing Any Cloud with .NET

Managing Any Cloud with .NET

All Pulumi capabilities for defining cloud resources are now available for .NET developers. Pulumi engineer Mikhail Shilkov will show you how you can use C#, F#, and VB.NET to define the infrastructure for Azure, AWS, or even on-prem Kubernetes clusters.

Mikhail Shilkov

April 13, 2020
Tweet

More Decks by Mikhail Shilkov

Other Decks in Technology

Transcript

  1. About me • Mikhail Shilkov • Software engineer at Pulumi

    .NET SDK, Azure, Core platform • Serverless • Functional programming, F# • Microsoft Azure MVP @MikhailShilkov [email protected]
  2. • Pulumi: Modern Infrastructure as Code • Use .NET languages

    to manage cloud resources • Desired state configuration • Tools that you love • Reusable abstractions • Automated testing • Multi-cloud Agenda
  3. Providers • AWS • Azure • GCP • Digital Ocean

    • Cloudflare … and more • Docker • Kubernetes • OpenStack • PostgreSQL • New Relic
  4. Unified Management API Management API (e.g., Azure Resource Manager) Cloud

    Service Cloud Service Cloud Service Web Portal CLI SDK 3rd Party
  5. var launchRequest = new RunInstancesRequest { ImageId = amiID, InstanceType

    = "t1.micro", MinCount = 1, MaxCount = 1, KeyName = keyPairName, SecurityGroupIds = groups }; var launchResponse = ec2Client.RunInstances(launchRequest); var instances = launchResponse.Reservation.Instances; SDKs to call those REST endpoints
  6. aws ec2 run-instances \ --image-id ami-5ec1673e \ --key-name MyKey \

    --security-groups EC2SecurityGroup \ --instance-type t2.micro \ --placement AvailabilityZone=us-west-2b Command-line Tools
  7. SDK and Scripting: Challenges 12 Imperative Step-by-step provisioning process Complex

    Depend on both the current and the target state Error-prone How to handle failures?
  8. var resourceGroup = new ResourceGroup("rg"); var storageAccount = new Account("storage",

    new AccountArgs { ResourceGroupName = resourceGroup.Name, AccountReplicationType = "LRS", AccountTier = "Standard", }); C# Example
  9. Desired State! var resourceGroup = new ResourceGroup("rg"); var storageAccount =

    new Account("storage", new AccountArgs { ResourceGroupName = resourceGroup.Name, AccountReplicationType = "LRS", AccountTier = "Standard", });
  10. How Pulumi Works CLI & engine Last deployed state Program.cs

    Language host AWS Azure GCP Kubernetes new Resource() CRUD
  11. Markup-based Languages 18 AWS CloudFormation YAML Azure RM Templates JSON

    Terraform HCL "resources": [ { "apiVersion": "2016-01-01", "type": "Microsoft.Storage/storageAccounts", "name": "mystorageaccount", "location": "westus", "sku": { "name": "Standard_LRS" }, "kind": "Storage", "properties": { } } ] Resources: S3BucketForURLs: Type: "AWS::S3::Bucket" DeletionPolicy: Delete Properties: BucketName: !If [ "CreateNewBucket", !Ref WebsiteConfiguration: IndexDocument: "index.html" LifecycleConfiguration: Rules: - Id: DisposeShortUrls ExpirationInDays: !Ref URLExpiration Prefix: "u" Status: Enabled resource "azurerm_storage_account" "sa" { name = "mysa" location = azurerm_resource_group .rg.location resource_group_name = azurerm_resource_group .rg.name account_tier = "Standard" account_replication_type = "LRS" } Every alternative has its own language, own tools, own conventions
  12. Markup Authoring Experience 19 Tooling Lack of tooling on day

    1. No fast feedback from editors. Discovery Which properties can be set, and which values are valid? Size Thousands of lines of markup are hard to write, read, and maintain
  13. Example App: Serverless URL Shortener KV Store Table “URLs” Function

    “Add URL” Function “Open URL” Tool Lines of code Function Code ~20 CloudFormation ~240 ARM Templates ~160 Terraform ~220 Estimates based on public examples
  14. Pulumi ❤ .NET • .NET Core 3.1 • OS: Windows,

    macOS, Linux • Language: C#, F#, VB.NET • Editor and IDE: Visual Studio, Code, Rider • IntelliSense, ReSharper, StyleCop, DocFX • Package Manager: NuGet, MyGet, Paket • NUnit, xUnit.net, Moq
  15. var storageAccount = new Account("sa", new AccountArgs { ResourceGroupName =

    resourceGroup.Name, AccountReplicationType = "LRS", AccountTier = "Standard" }); var appServicePlan = new Plan("asp", new PlanArgs { ResourceGroupName = resourceGroup.Name, Kind = "App", Sku = new PlanSkuArgs { Tier = "Basic", Size = "B1” } }); Cloud Resources with C#
  16. let storageAccount = Account("sa", AccountArgs (ResourceGroupName = io resourceGroup.Name, AccountReplicationType

    = input "LRS", AccountTier = input "Standard")) let sku = PlanSkuArgs(Tier = input "Basic", Size = input "B1") let appServicePlan = Plan("asp", PlanArgs (ResourceGroupName = io resourceGroup.Name, Kind = input "App", Sku = input sku)) Cloud Resources with F#
  17. ' Create an Azure Resource Group Dim resourceGroup = New

    ResourceGroup("resourceGroup") ' Create an Azure Storage Account Dim storageAccount = New Account("storage", New AccountArgs With { .ResourceGroupName = resourceGroup.Name, .AccountReplicationType = "LRS", .AccountTier = "Standard" }) Cloud Resources with VB.NET
  18. // A resource group to contain our Azure Functions var

    resourceGroup = new ResourceGroup("functions-rg"); // A .NET Azure Function var dotnet = new ArchiveFunctionApp("http", new ArchiveFunctionAppArgs { ResourceGroup = resourceGroup, Archive = new FileArchive("./dotnet/bin/Debug/netcoreapp3.1/publish") }); .NET Azure Function
  19. Let’s run pulumi up … Type Name Plan + pulumi:pulumi:Stack

    functions-dev create + ├─ azure:appservice:ArchiveFunctionApp http create + │ ├─ azure:storage:Account http create + │ ├─ azure:appservice:Plan http create + │ ├─ azure:storage:Container http create + │ ├─ azure:storage:ZipBlob http create + │ └─ azure:appservice:FunctionApp http create + └─ azure:core:ResourceGroup rg create
  20. var app = new CosmosApp("functions", new CosmosAppArgs { ResourceGroup =

    resourceGroup, Locations = ["WestUS", "EastUS", "WestEurope"], Factory = global => region => { var func = new ArchiveFunctionApp("app", new ArchiveFunctionAppArgs { ResourceGroupName = resourceGroup.Name, Location = region.Location, Archive = new FileArchive("./app/bin/Debug/netcoreapp2.2/publish"), AppSettings = { { "CosmosDBConnection", global.ConnectionString } } }); return new AzureEndpoint(func.AppId); } }); Global Applications
  21. var app = new CosmosApp("functions", new CosmosAppArgs { ResourceGroup =

    resourceGroup, Locations = ["WestUS", "EastUS", "WestEurope"], Factory = global => region => { var func = new ArchiveFunctionApp("app", new ArchiveFunctionAppArgs { ResourceGroupName = resourceGroup.Name, Location = region.Location, Archive = new FileArchive("./app/bin/Debug/netcoreapp2.2/publish"), AppSettings = { { "CosmosDBConnection", global.ConnectionString } } }); return new AzureEndpoint(func.AppId); } }); Global Applications
  22. [Fact] public async Task InstancesMustNotHaveSshPortOpenToInternet() { var resources = await

    Deployment.TestAsync<MyStack>(); var resource = resources.OfType<SecurityGroup>().Single(); var ingress = await resource.GetAsync(sg => sg.Ingress); foreach (var rule in ingress) foreach (var cidrBlock in rule.CidrBlocks) { Assert.False(rule.FromPort == 22 && cidrBlock == "0.0.0.0/0", "Illegal SSH port 22 open to the Internet (CIDR 0.0.0.0/0)"); } } Unit Testing
  23. [Fact] public async Task InstancesMustNotHaveSshPortOpenToInternet() { var resources = await

    Deployment.TestAsync<MyStack>(); var resource = resources.OfType<SecurityGroup>().Single(); var ingress = await resource.GetAsync(sg => sg.Ingress); foreach (var rule in ingress) foreach (var cidrBlock in rule.CidrBlocks) { Assert.False(rule.FromPort == 22 && cidrBlock == "0.0.0.0/0", "Illegal SSH port 22 open to the Internet (CIDR 0.0.0.0/0)"); } } Unit Testing
  24. [Fact] public async Task InstancesMustNotHaveSshPortOpenToInternet() { var resources = await

    Deployment.TestAsync<MyStack>(); var resource = resources.OfType<SecurityGroup>().Single(); var ingress = await resource.GetAsync(sg => sg.Ingress); foreach (var rule in ingress) foreach (var cidrBlock in rule.CidrBlocks) { Assert.False(rule.FromPort == 22 && cidrBlock == "0.0.0.0/0", "Illegal SSH port 22 open to the Internet (CIDR 0.0.0.0/0)"); } } Unit Testing
  25. Policy as Code (.NET coming later) const policies = new

    PolicyPack("aws", { policies: [ { name: "prohibited-public-internet", description: "Ingress rules with public internet access are prohibited.", enforcementLevel: "mandatory", validateResource: validateTypedResource(aws.ec2.SecurityGroup, (sg, args, report) => { const publicInternetRules = (sg.ingress || []).find(ingressRule => (ingressRule.cidrBlocks || []).find(cidr => cidr === "0.0.0.0/0")); if (publicInternetRules) { report("Ingress rules with public internet access are prohibited."); } }), }, ], });
  26. Cloud Transition 44 Kubernetes Azure Functions AWS S3 Azure Analytics

    Google ML MySQL DataDog App Docker DataDog New Relic App MySQL EARLY CLOUD Mostly Static N-Tier Apps VMs Private Cloud CURRENT CLOUD Partly Dynamic Less Monolithic VMs and Containers* Hybrid - Public/Private *Experimentation FUTURE CLOUD Fully Dynamic Hyper-Connected Services Containers and Serverless Mostly Public Cloud
  27. Infrastructure Landscape 45 Founda3on Security IAM KMS Networking VPC Subnets

    Firewalls Load Balancing DNS Compute VMs Containers Clusters Registries APM Monitoring Logging AlerKng Serverless Functions API Gateways Data Object Stores Databases SQL NoSQL MQ Queues Pub/Sub Applications Images Container Images Code Packaging CI/CD
  28. Kubernetes layers Managed Kubernetes cluster Infrastructure Resources (networking, storage, identity)

    Managed Service Managed Service Application Application Application
  29. Stack References Org: acme-corp vpc Stack: dev env: dev region:

    us-east-1 k8s-cluster Stack: dev env: dev region: us-east-1 svc-userprofile Stack: dev env: dev region: us-east-1 svc-email Stack: dev env: dev region: us-east-1
  30. PROVISIONING Developer-friendly Familiar language experience, toolchain, packages – applied to

    cloud infrastructure. Developers and operators working in a team. Cloud Engineering Transformed TESTING Confidence and quality Unit testing and TDD with battle-tested tools to ensure correctness. Policy as Code for compliance, cost control, and company-wide best practices. ARCHITECTURE Logic and abstractions Conditionals, loops, functions, classes, and packages out of the box. Reusable components that encapsulate complex logic and provider the right level of abstraction. Modern Infrastructure as Code Capabilities to ship faster and with confidence
  31. Thank you! Join slack.pulumi.com for help after this session Submit

    requests and bugs on github.com/pulumi/pulumi Subscribe for weekly tutorials on youtube.com/pulumiTV Follow @PulumiCorp for updates, or hit me up directly @MikhailShilkov Here are some more ways to learn about Pulumi and join the community Upcoming talks and workshops at pulumi.com/webinars