{"id":20648121,"url":"https://github.com/unosd/pulumi.fsharp.extensions","last_synced_at":"2025-04-06T14:11:21.544Z","repository":{"id":42450370,"uuid":"274506480","full_name":"UnoSD/Pulumi.FSharp.Extensions","owner":"UnoSD","description":"F# computational expressions to reduce boilerplate in Pulumi code","archived":false,"fork":false,"pushed_at":"2024-04-01T21:33:39.000Z","size":1576,"stargazers_count":90,"open_issues_count":5,"forks_count":6,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-04-28T01:07:50.257Z","etag":null,"topics":["aws","azure","fsharp","iac","kubernetes","myriad","pulumi","pulumi-aws","pulumi-azure","pulumi-kubernetes"],"latest_commit_sha":null,"homepage":"","language":"F#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/UnoSD.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-23T20:51:37.000Z","updated_at":"2024-04-08T23:53:43.000Z","dependencies_parsed_at":"2024-11-15T01:15:04.950Z","dependency_job_id":"8f6bdbc3-a715-44b1-bce5-898eadb24f0b","html_url":"https://github.com/UnoSD/Pulumi.FSharp.Extensions","commit_stats":{"total_commits":423,"total_committers":4,"mean_commits":105.75,"dds":0.1749408983451537,"last_synced_commit":"159630b6d0e095621423358f6a5a63af889cfa73"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UnoSD%2FPulumi.FSharp.Extensions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UnoSD%2FPulumi.FSharp.Extensions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UnoSD%2FPulumi.FSharp.Extensions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UnoSD%2FPulumi.FSharp.Extensions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/UnoSD","download_url":"https://codeload.github.com/UnoSD/Pulumi.FSharp.Extensions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247492515,"owners_count":20947545,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","azure","fsharp","iac","kubernetes","myriad","pulumi","pulumi-aws","pulumi-azure","pulumi-kubernetes"],"created_at":"2024-11-16T17:06:21.662Z","updated_at":"2025-04-06T14:11:21.516Z","avatar_url":"https://github.com/UnoSD.png","language":"F#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pulumi.FSharp.Extensions\nF# computational expressions to reduce boilerplate in Pulumi code\n\n[![NuGet Version and Downloads count](https://buildstats.info/nuget/Pulumi.FSharp.Azure)](https://www.nuget.org/packages/Pulumi.FSharp.Azure)\n[![NuGet Version and Downloads count](https://buildstats.info/nuget/Pulumi.FSharp.Aws)](https://www.nuget.org/packages/Pulumi.FSharp.Aws)\n[![NuGet Version and Downloads count](https://buildstats.info/nuget/Pulumi.FSharp.Kubernetes)](https://www.nuget.org/packages/Pulumi.FSharp.Kubernetes)\n[![NuGet Version and Downloads count](https://buildstats.info/nuget/Pulumi.FSharp.AzureAD)](https://www.nuget.org/packages/Pulumi.FSharp.AzureAD)\n[![NuGet Version and Downloads count](https://buildstats.info/nuget/Pulumi.FSharp.Core)](https://www.nuget.org/packages/Pulumi.FSharp.Core)\n[![NuGet Version and Downloads count](https://buildstats.info/nuget/Pulumi.FSharp.AzureNative)](https://www.nuget.org/packages/Pulumi.FSharp.AzureNative)\n[![NuGet Version and Downloads count](https://buildstats.info/nuget/Pulumi.FSharp.Gcp)](https://www.nuget.org/packages/Pulumi.FSharp.Gcp)\n[![NuGet Version and Downloads count](https://buildstats.info/nuget/Pulumi.FSharp.Tls)](https://www.nuget.org/packages/Pulumi.FSharp.Tls)\n\n# Readability difference\n\n## **Pulumi.Tls** vs **Pulumi.FSharp.Tls** for a self signed certificate\n\n![Pulumi.Tls](readability.png)\n\nThe computational expression syntax removes redundant parenthesis, `input`/`io`/`inputList` helpers, equal signs, lists for single input, subitems boilerplate.\n\n# Packages examples\n\n## Pulumi.FSharp.Aws\n\n```f#\nbucket {\n    name \"bucket-example\"\n    acl  \"private\"\n\n    bucketWebsite { \n        indexDocument \"index.html\"\n    }\n}\n```\n\n## Pulumi.FSharp.Azure\n\n```f#\nlet rg =\n    resourceGroup {\n        name                   \"ResourceGroupName\"\n    }\n\nlet sa =\n    storageAccount {\n        name                   \"StorageAccountName\"\n        resourceGroup          rg.Name\n        accountReplicationType \"LRS\"\n        accountTier            \"Standard\"\n        enableHttpsTrafficOnly true\n    }\n    \nlet container =\n    storageContainer {\n        name                   \"StorageContainer\"\n        account                sa.Name\n    }\n    \nlet contentBlob =\n    blob {\n        name                   \"StorageBlob\"\n        storageAccountName     storage\n        storageContainerName   buildContainer\n        source                 { Text = \"Blob content\" }.ToPulumiType\n    }\n    \nlet sasToken =\n    sasToken {\n        storage                sa\n        blob                   contentBlob\n    }\n    \nlet appServicePlan =\n    plan {\n        name                   \"FunctionAppServiceName\"\n        resourceGroup          rg.Name\n        kind                   \"FunctionApp\"\n        \n        planSku {\n            size \"Y1\"\n            tier \"Dynamic\"\n        }\n    }\n```\n\n## Pulumi.FSharp.AzureAD\n\n```f#\napplication {\n    name                    \"AzureADApplicationName\"\n    displayName             \"AzureAD application name\"\n    oauth2AllowImplicitFlow true\n    \n    replyUrls               [\n        config.[\"WebEndpoint\"]\n        \"https://jwt.ms\"\n        \"http://localhost:8080\"\n    ]            \n    \n    applicationOptionalClaims {\n        idTokens [\n            applicationOptionalClaimsIdToken {\n                name                 \"upn\"\n                additionalProperties \"include_externally_authenticated_upn\"\n                essential            true\n            }\n        ]\n    }\n}\n```\n\n## Pulumi.FSharp.AzureNative\n\n```f#\nlet storage =\n    storageAccount {\n        resourceGroup rg.Name\n        location      rg.Location\n        name          \"StorageAccount\"            \n        sku           { name \"LRS\" }            \n        kind          Kind.StorageV2\n    }\n\nblobContainer { \n    accountName       storage.Name\n    resourceGroup     rg.Name\n    name              \"StorageContainer\"\n    \n    PublicAccess.None\n}\n```\n\n## Pulumi.FSharp.Core\n\n```f#\n// Output computational expressions\nlet deploymentCountBars =\n    output {\n        let! previousOutputs =\n            StackReference(Deployment.Instance.StackName).Outputs\n        \n        return previousOutputs.[\"CountBars\"] + \"I\"\n    }\n    \n// Output as secret\nlet someSecret =\n    secretOutput {\n        let! key1 = sa.PrimaryConnectionString\n        let! key2 = sa.SecondaryConnectionString\n        \n        return $\"Secret connection strings: {key1} {key2}\"\n    }\n    \n// Mixing Output\u003c\u003e and Task\u003c\u003e\nlet sas =\n    output {\n        let! connectionString = sa.PrimaryConnectionString\n        let! containerName = container.Name\n        let! url = blob.Url\n\n        let start =\n            DateTime.Now.ToString(\"u\").Replace(' ', 'T')\n        \n        let expiry =\n            DateTime.Now.AddHours(1.).ToString(\"u\").Replace(' ', 'T')\n        \n        // Task to Output\n        let! tokenResult =\n            GetAccountBlobContainerSASArgs(\n                ConnectionString = connectionString,\n                ContainerName = containerName,\n                Start = start,\n                Expiry = expiry,\n                Permissions = (GetAccountBlobContainerSASPermissionsArgs(Read = true))\n            ) |\u003e\n            // This returns Task\u003c\u003e\n            GetAccountBlobContainerSAS.InvokeAsync\n\n        return url + tokenResult.Sas\n    }\n```\n\n## Pulumi.FSharp.Kubernetes\n\n```f#\ndeployment {\n    name \"application\"\n\n    deploymentSpec {\n        replicas 1\n\n        labelSelector { \n            matchLabels [ \"app\", input \"nginx\" ]\n        }\n\n        podTemplateSpec {\n            objectMeta {\n                labels [ \"app\", input \"nginx\" ]\n            }\n\n            podSpec {\n                containers [\n                    container {\n                        name  \"nginx\"\n                        image \"nginx\"\n                        ports [ containerPort { containerPortValue 80 } ]\n                    }\n                ]\n            }\n        }\n    }\n}\n```\n\n# Full stack file example\n\n- Create a Pulumi F# project using `pulumi new fsharp`\n- Upgrade the project to .NET 5 \n- Add the NuGet package `Pulumi.FSharp.Azure`\n- Edit the `Program.fs` and paste the example below\n- Run `pulumi up` and create the infrastructure using a readable strongly-typed DSL\n- Log in your new Visual Studio VM using the IP from the outputs and credentials in code\n\nTo discover available properties for each resource, examine the code documentation of the builders (E.G. hover over a `Pulumi.FSharp.Azure.Compute.windowsVirtualMachine` computational expression to find all available properties and on each property to discover their description)\n\n```f#\nmodule Program\n\nopen Pulumi.FSharp.Azure.Compute.Inputs\nopen Pulumi.FSharp.Azure.Network.Inputs\nopen Pulumi.FSharp.Azure.Compute\nopen Pulumi.FSharp.Azure.Network\nopen Pulumi.FSharp.Azure.Core\nopen Pulumi.FSharp\n\nlet infra () =\n    let rg =\n        resourceGroup {\n            name     \"rg-example\"\n            location \"West Europe\"\n        }\n\n    let pip =\n        publicIp {\n            name             \"pip-example\"\n            resourceGroup    rg.Name\n            location         rg.Location\n            allocationMethod \"Dynamic\"\n        }\n    \n    let vnet =\n        virtualNetwork {\n            name          \"vnet-example\"\n            addressSpaces \"10.0.0.0/16\"\n            location      rg.Location\n            resourceGroup rg.Name\n        }\n\n    let subnet =\n        subnet {\n            name               \"snet-example\"\n            resourceGroup      rg.Name\n            virtualNetworkName vnet.Name\n            addressPrefixes    \"10.0.2.0/24\"\n        }\n\n    let nic =\n        networkInterface {\n            name          \"nic-example\"\n            location      rg.Location\n            resourceGroup rg.Name\n\n            ipConfigurations [ \n                networkInterfaceIpConfiguration {\n                    name                       \"internal\"\n                    subnetId                   subnet.Id\n                    privateIpAddressAllocation \"Dynamic\"\n                    publicIpAddressId          pip.Id\n                }\n            ]\n        }\n    \n    let vm =\n        windowsVirtualMachine {\n            name                \"vm-example\"\n            resourceName        \"vm-example\"\n            resourceGroup       rg.Name\n            size                \"Standard_A1_v2\"\n            networkInterfaceIds nic.Id\n            \n            windowsVirtualMachineOsDisk {\n                caching            \"ReadWrite\"\n                storageAccountType \"Standard_LRS\"\n            }\n            \n            adminUsername \"unosdpulumi\"\n            adminPassword \"ReplaceThisWithAProperPassword%%55\"\n            \n            windowsVirtualMachineSourceImageReference {\n                offer     \"visualstudio2019latest\"\n                publisher \"microsoftvisualstudio\"\n                sku       \"vs-2019-comm-latest-win10-n\"\n                version   \"latest\"\n            }\n        }\n        \n    dict [ \"PublicIP\", vm.PublicIpAddress :\u003e obj ]\n           \n[\u003cEntryPoint\u003e]\nlet main _ =\n  Deployment.run infra\n```\n\n# Examples library\n\n[https://github.com/UnoSD/Pulumi.FSharp.Extensions.Examples](https://github.com/UnoSD/Pulumi.FSharp.Extensions.Examples)\n\n# Example project using the library\n\n[https://github.com/UnoSD/UnoCash](https://github.com/UnoSD/UnoCash/blob/master/UnoCash.Pulumi/Program.fs)\n\n# One language to rule them all (F# eXchange 2020)\n\nI had the pleasure of being invited to speak at the F# eXchange 2020 on the 21st of October and, as promised, I have created a repo to share the slides of the talk: https://github.com/UnoSD/FSharp-eXChange2020\n\nThe talk goes through infrastructure as code in F# with Pulumi in general and explains the rationale and some of the technical details behind this repository.\n\nLink to the video: https://skillsmatter.com/skillscasts/14888-lightning-talk-one-language-to-rule-them-all-iac-in-f-sharp\n\n# Development notes\n\nGiven that changes to the `Pulumi.FSharp.Myriad` project do not trigger re-generation unless the Myriad.fs file is modified, a `PreBuildEvent` in each `fsproj` file uses `sed` to change a dummy variable to a random integer on each build (this only works on GNU/Linux machines with `sed`). To avoid `git` finding changes in the `Myriad.fs` file every time, use the following:\n\n```bash\ngit update-index --skip-worktree Pulumi.FSharp.Aws/Myriad.fs\ngit update-index --skip-worktree Pulumi.FSharp.Gcp/Myriad.fs\ngit update-index --skip-worktree Pulumi.FSharp.Tls/Myriad.fs\ngit update-index --skip-worktree Pulumi.FSharp.Azure/Myriad.fs\ngit update-index --skip-worktree Pulumi.FSharp.AzureAD/Myriad.fs\ngit update-index --skip-worktree Pulumi.FSharp.Kubernetes/Myriad.fs\ngit update-index --skip-worktree Pulumi.FSharp.AzureNative/Myriad.fs\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funosd%2Fpulumi.fsharp.extensions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funosd%2Fpulumi.fsharp.extensions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funosd%2Fpulumi.fsharp.extensions/lists"}