├── .config └── dotnet-tools.json ├── .editorconfig ├── .github └── workflows │ └── gh-pages.yml ├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── Farmer.sln ├── Icon.jpg ├── LICENSE ├── Logo.png ├── MAINTAINERS.md ├── RELEASE_NOTES.md ├── azure-pipelines.yml ├── docs ├── -f │ └── images │ │ ├── arm-graph.jpg │ │ ├── comparison.png │ │ ├── deploy.jpg │ │ ├── farmer-favicon.png │ │ ├── farmer-flow.jpg │ │ ├── logo.png │ │ └── quickstarts │ │ ├── multiple-web-apps.png │ │ ├── serverless-etl.png │ │ └── webapp-cosmos.png ├── README.md ├── archetypes │ └── default.md ├── config.toml └── content │ ├── _index.md │ ├── about │ └── _index.md │ ├── api-overview │ ├── _index.md │ ├── basic-types.md │ ├── dependencies.md │ ├── expressions.md │ ├── outputs.md │ ├── parameters.md │ ├── resources │ │ ├── _index.md │ │ ├── action-group.md │ │ ├── aks-cluster.md │ │ ├── alert.md │ │ ├── app-insights.md │ │ ├── application-gateway.md │ │ ├── autoscale-settings.md │ │ ├── availability-tests.md │ │ ├── azure-firewall.md │ │ ├── b2c-tenant.md │ │ ├── bastion-host.md │ │ ├── bing-search.md │ │ ├── cdn.md │ │ ├── cognitive-services.md │ │ ├── communication-services.md │ │ ├── container-apps.md │ │ ├── container-group.md │ │ ├── container-registry.md │ │ ├── cosmos-db.md │ │ ├── dashboard.md │ │ ├── data-lake.md │ │ ├── databricks-workspace.md │ │ ├── dedicated-hosts.md │ │ ├── deployment-script.md │ │ ├── diagnosticsetting.md │ │ ├── disk.md │ │ ├── dns-resolver.md │ │ ├── dns.md │ │ ├── event-grid.md │ │ ├── event-hub.md │ │ ├── express-route.md │ │ ├── functions.md │ │ ├── gallery.md │ │ ├── image-template.md │ │ ├── iot-hub.md │ │ ├── keyvault.md │ │ ├── load-balancer.md │ │ ├── loganalytics.md │ │ ├── logic-apps.md │ │ ├── managed-identity.md │ │ ├── maps.md │ │ ├── nat-gateway.md │ │ ├── network-interface.md │ │ ├── nsg.md │ │ ├── operations-management.md │ │ ├── postgresql.md │ │ ├── private-endpoint.md │ │ ├── private-link-service.md │ │ ├── redis.md │ │ ├── resource-group.md │ │ ├── route-server.md │ │ ├── route-tables.md │ │ ├── search.md │ │ ├── service-bus.md │ │ ├── signalr.md │ │ ├── sql.md │ │ ├── static-web-app.md │ │ ├── storage-account.md │ │ ├── traffic-manager.md │ │ ├── virtual-hub.md │ │ ├── virtual-machine.md │ │ ├── virtual-wan.md │ │ ├── vm-scale-set.md │ │ ├── vnet-gateway.md │ │ ├── vnet.md │ │ └── web-app.md │ └── template-generation.md │ ├── arm-vs-farmer │ └── _index.md │ ├── contributing │ ├── _index.md │ ├── adding-resources │ │ ├── 1-the-farmer-pipline.md │ │ ├── 2-iarm-resource.md │ │ ├── 3-ibuilder.md │ │ ├── 4-creating-builder-syntax.md │ │ ├── 5-unit-testing.md │ │ └── _index.md │ ├── arm-basics.md │ ├── create-pull-requests.md │ └── outputs-and-expressions.md │ ├── deployment-guidance │ └── _index.md │ ├── faq │ └── _index.md │ ├── images │ ├── arm-graph.jpg │ ├── cit.png │ ├── comparison.png │ ├── deploy.jpg │ ├── farmer-favicon.png │ ├── farmer-flow.jpg │ ├── logo.png │ └── tutorials │ │ ├── enterprise1.png │ │ ├── enterprise2.png │ │ ├── imperative-resource.png │ │ ├── multiple-web-apps.png │ │ ├── serverless-etl.png │ │ ├── webapp-cosmos.png │ │ ├── webapp-keyvault-connection.png │ │ ├── webapp-keyvault.png │ │ └── webapp.png │ ├── links │ └── _index.md │ ├── quickstarts │ ├── _index.md │ ├── quickstart-1.md │ ├── quickstart-2.md │ ├── quickstart-3.md │ └── template.md │ ├── support │ └── _index.md │ ├── testimonials │ └── _index.md │ └── tutorials │ ├── _index.md │ ├── aci-fsx.md │ ├── cosmos-backed-webapp.md │ ├── custom-output.md │ ├── keyvault-certs.md │ ├── minecraft-server-aci.md │ ├── multiple-web-apps.md │ ├── serverless-etl.md │ ├── traditional-ea.md │ ├── web-storage-keyvault.md │ └── webapp-deploy.md ├── global.json ├── pull_request_template.md ├── readme.md ├── samples ├── .gitignore ├── SampleApp │ ├── Program.fs │ └── SampleApp.fsproj └── scripts │ ├── acr-containerapps-workflow.fsx │ ├── aks.fsx │ ├── appinsights-loganalytics.fsx │ ├── appinsights.fsx │ ├── applicationGateway.fsx │ ├── bastion.fsx │ ├── cdn.fsx │ ├── container-app.fsx │ ├── container-group.fsx │ ├── container-instance-gpu.fsx │ ├── container-instance.fsx │ ├── container-registry.fsx │ ├── cosmos.fsx │ ├── data-lake.fsx │ ├── databricks.fsx │ ├── deployment-script.fsx │ ├── diagnosticsetting.fsx │ ├── dns.fsx │ ├── eventgrid-fn.fsx │ ├── eventgrid.fsx │ ├── eventhubs.fsx │ ├── functions.fsx │ ├── iot.fsx │ ├── keyvault-keys.fsx │ ├── keyvault.fsx │ ├── loadbalancer.fsx │ ├── loganalytics.fsx │ ├── logicapp.fsx │ ├── nested-resourcegroups.fsx │ ├── operations-management.fsx │ ├── postgresql.fsx │ ├── redis.fsx │ ├── safe-template.fsx │ ├── search.fsx │ ├── service-bus.fsx │ ├── sqlserver.fsx │ ├── storage.fsx │ ├── template.json │ ├── tutorials │ ├── aci-fsharp.fsx │ ├── cosmos-backed-webapp.fsx │ ├── custom-output.fsx │ ├── keyvault-certs-app.fsx │ ├── keyvault-certs.fsx │ ├── minecraft-server-aci.fsx │ ├── multiple-web-apps.fsx │ ├── serverless-etl.fsx │ ├── webapp-deploy.fsx │ └── webapp-keyvault.fsx │ ├── vm-multinic-ultradisk.fsx │ ├── vm-spot-instance.fsx │ ├── vm.fsx │ ├── vnet-gateway.fsx │ ├── vnet-hub-and-spoke.fsx │ ├── vnet.fsx │ ├── webapp-appinsights.fsx │ └── webapp-storage.fsx └── src ├── Farmer ├── Aliases.fs ├── Arm │ ├── AVS.fs │ ├── ActionGroup.fs │ ├── Alert.fs │ ├── App.fs │ ├── ApplicationGateway.fs │ ├── AutomationAccount.fs │ ├── AutoscaleSettings.fs │ ├── AvailabilityTest.fs │ ├── AzureFirewall.fs │ ├── B2cTenant.fs │ ├── Bastion.fs │ ├── BingSearch.fs │ ├── Cache.fs │ ├── Cdn.fs │ ├── CognitiveServices.fs │ ├── CommunicationServices.fs │ ├── Compute.fs │ ├── ContainerInstance.fs │ ├── ContainerRegistry.fs │ ├── ContainerService.fs │ ├── DBforPostgreSQL.fs │ ├── Dashboard.fs │ ├── DataLakeStore.fs │ ├── Databricks.fs │ ├── DeploymentScript.fs │ ├── Devices.fs │ ├── DiagnosticSetting.fs │ ├── Disk.fs │ ├── Dns.fs │ ├── DocumentDb.fs │ ├── EventGrid.fs │ ├── EventHub.fs │ ├── Gallery.fs │ ├── ImageTemplate.fs │ ├── Insights.fs │ ├── KeyVault.fs │ ├── LoadBalancer.fs │ ├── LogAnalytics.fs │ ├── LogicApps.fs │ ├── ManagedIdentity.fs │ ├── Maps.fs │ ├── Network.fs │ ├── NetworkSecurityGroup.fs │ ├── OperationsManagement.fs │ ├── PrivateLinkService.fs │ ├── ResourceGroup.fs │ ├── RoleAssignment.fs │ ├── Search.fs │ ├── ServiceBus.fs │ ├── SignalR.fs │ ├── Sql.fs │ ├── Storage.fs │ ├── TrafficManager.fs │ ├── VirtualHub.fs │ ├── VirtualWan.fs │ ├── Web.fs │ └── Webhook.fs ├── Builders │ ├── Builders.ActionGroup.fs │ ├── Builders.Alert.fs │ ├── Builders.AppInsights.fs │ ├── Builders.ApplicationGateway.fs │ ├── Builders.AutoscaleSettings.fs │ ├── Builders.AvailabilityTest.fs │ ├── Builders.AzureFirewall.fs │ ├── Builders.B2cTenant.fs │ ├── Builders.Bastion.fs │ ├── Builders.BingSearch.fs │ ├── Builders.Cdn.fs │ ├── Builders.CognitiveServices.fs │ ├── Builders.CommunicationServices.fs │ ├── Builders.ContainerApps.fs │ ├── Builders.ContainerGroups.fs │ ├── Builders.ContainerRegistry.fs │ ├── Builders.ContainerService.fs │ ├── Builders.Cosmos.fs │ ├── Builders.Dashboard.fs │ ├── Builders.DataLake.fs │ ├── Builders.Databricks.fs │ ├── Builders.DeploymentScript.fs │ ├── Builders.DiagnosticSetting.fs │ ├── Builders.Disk.fs │ ├── Builders.Dns.fs │ ├── Builders.DnsResolver.fs │ ├── Builders.EventGrid.fs │ ├── Builders.EventHub.fs │ ├── Builders.ExpressRoute.fs │ ├── Builders.Functions.fs │ ├── Builders.Gallery.fs │ ├── Builders.Helpers.fs │ ├── Builders.HostGroup.fs │ ├── Builders.ImageTemplate.fs │ ├── Builders.IotHub.fs │ ├── Builders.KeyVault.fs │ ├── Builders.LoadBalancer.fs │ ├── Builders.LogAnalytics.fs │ ├── Builders.LogicApps.fs │ ├── Builders.Maps.fs │ ├── Builders.NatGateway.fs │ ├── Builders.NetworkInterface.fs │ ├── Builders.NetworkSecurityGroup.fs │ ├── Builders.OperationsManagement.fs │ ├── Builders.PostgreSQL.fs │ ├── Builders.PrivateEndpoint.fs │ ├── Builders.PrivateLink.fs │ ├── Builders.Redis.fs │ ├── Builders.ResourceGroup.fs │ ├── Builders.RouteServer.fs │ ├── Builders.RouteTable.fs │ ├── Builders.Search.fs │ ├── Builders.ServiceBus.fs │ ├── Builders.ServicePlan.fs │ ├── Builders.SignalR.fs │ ├── Builders.Sql.fs │ ├── Builders.StaticWebApp.fs │ ├── Builders.Storage.fs │ ├── Builders.TrafficManager.fs │ ├── Builders.UserAssignedIdentity.fs │ ├── Builders.VirtualHub.fs │ ├── Builders.VirtualNetwork.fs │ ├── Builders.VirtualNetworkGateway.fs │ ├── Builders.VirtualWan.fs │ ├── Builders.Vm.fs │ ├── Builders.VmScaleSet.fs │ ├── Builders.WebApp.fs │ └── Extensions.fs ├── Common.fs ├── Deploy.fs ├── DiagnosticLogCategories.fs ├── Farmer.fsproj ├── IdentityExtensions.fs ├── Result.fs ├── Types.fs └── Writer.fs └── Tests ├── ActionGroup.fs ├── Alerts.fs ├── AllTests.fs ├── AppGateway.fs ├── AppInsights.fs ├── AutoscaleSettings.fs ├── AvailabilityTests.fs ├── AzCli.fs ├── AzureFirewall.fs ├── B2cTenant.fs ├── Bastion.fs ├── BingSearch.fs ├── Cdn.fs ├── CognitiveServices.fs ├── Common.fs ├── CommunicationServices.fs ├── ContainerApps.fs ├── ContainerGroup.fs ├── ContainerRegistry.fs ├── ContainerService.fs ├── Cosmos.fs ├── Dashboards.fs ├── Databricks.fs ├── DedicatedHosts.fs ├── DeploymentScript.fs ├── DiagnosticSettings.fs ├── Disk.fs ├── Dns.fs ├── DnsResolver.fs ├── EventGrid.fs ├── EventHub.fs ├── ExpressRoute.fs ├── Functions.fs ├── Gallery.fs ├── Helpers.fs ├── Identity.fs ├── ImageTemplate.fs ├── IotHub.fs ├── JsonRegression.fs ├── KeyVault.fs ├── LoadBalancer.fs ├── LogAnalytics.fs ├── LogicApps.fs ├── Maps.fs ├── Network.fs ├── NetworkSecurityGroup.fs ├── OperationsManagement.fs ├── PostgreSQL.fs ├── PrivateLink.fs ├── ResourceGroup.fs ├── RoleAssignment.fs ├── ServiceBus.fs ├── ServicePlan.fs ├── SignalR.fs ├── Sql.fs ├── StaticWebApp.fs ├── Storage.fs ├── Template.fs ├── Tests.fsproj ├── TrafficManager.fs ├── Types.fs ├── VirtualHub.fs ├── VirtualMachine.fs ├── VirtualNetworkGateway.fs ├── VirtualWan.fs ├── VmScaleSet.fs ├── WebApp.fs ├── foo.txt └── test-data ├── aks-with-acr.json ├── azure-firewall.json ├── blank-logic-app.json ├── diagnostics.json ├── event-grid.json ├── load-balancer.json ├── lots-of-resources.json ├── service-bus.json ├── virtual-wan.json └── vm.json /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "fantomas": { 6 | "version": "6.3.9", 7 | "commands": [ 8 | "fantomas" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = false 9 | 10 | [*.{fs,fsx}] 11 | fsharp_multiline_bracket_style = stroustrup 12 | fsharp_newline_before_multiline_computation_expression = false -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: gh-pages 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - 'docs/**' 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout master branch 14 | uses: actions/checkout@v2 15 | with: 16 | ref: master # Pull the master branch 17 | submodules: true # Fetch Hugo themes as well (they're in sub-modules) 18 | fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod 19 | 20 | - name: Setup Hugo 21 | uses: peaceiris/actions-hugo@v2 22 | with: 23 | hugo-version: "0.68.3" 24 | 25 | # Builds using hugo; outputs to ./public by default, but ./ is ./docs here, 26 | # because of the working-directory setting, so the output will be in ./docs/public 27 | # this is then used for `publish_dir` in the Deploy step below 28 | - name: Build 29 | working-directory: ./docs 30 | run: hugo --minify 31 | 32 | - name: Deploy # Pushes output of the build to the GH Pages branch 33 | uses: peaceiris/actions-gh-pages@v3 34 | with: 35 | commit_message: ${{ github.event.head_commit.message }} 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | publish_dir: ./docs/public 38 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs/themes/hugo-theme-learn"] 2 | path = docs/themes/hugo-theme-learn 3 | url = https://github.com/compositionalit/hugo-theme-learn.git 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | > This article is not a detailed guide on how to create a pull request (PR). See [here](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) to learn more about how to work with pull requests on GitHub. 2 | 3 | The purpose of this article is to illustrate the main checklists you must go through before a PR will be considered for inclusion in Farmer. If you are new to Farmer, F# or GitHub - **don't worry**. The team will be happy to support you getting your feature over the line. 4 | 5 | These are the following checks we'll normally put in place: 6 | 7 | #### 1. Create an issue first! 8 | Except for small pull requests, create an issue to discuss the feature. The last thing we want is for someone to spend hours of their time on a feature only for someone else to have started work on something similar, or for the admins of the project to reject it for whatever reason e.g. does not fit with the project etc. Creating an issue does not take long and will help save time for everyone. 9 | #### 2. Create Documentation 10 | Every PR to Farmer **must** have some documentation with it. If you modify a resource and add a new keyword, it **must** be added to the appropriate docs page. 11 | #### 3. Write Unit Tests 12 | Every PR to Farmer **should** have at least one test associated with it. If no tests are added, you can expect at least a request for one or explanation as to why one is not necessary. 13 | #### 4. Write Release Notes 14 | Every PR to Farmer **must** include an entry to the `RELEASE_NOTES.md` file under the next release. Briefly explain the feature and ideally link to the PR number e.g. 15 | #### 5. Adhere to Coding Standards 16 | Here are some (very basic!) standards for the project: 17 | 18 | * Do not use `yield` - it is no longer necessary in F#. 19 | * Prefer `[ for x in y do ... ]` to `[ for x in y -> ... ]` 20 | * **Never** use `.Value` on `Option` types. 21 | 22 | ##### 5.1 Using Fantomas 23 | We use Fantomas to consistently format F#. 24 | 25 | 1. Install it via: `dotnet tool restore` (only required once). 26 | 2. Run it either: 27 | * Through IDE tooling (e.g. VS Code, Rider or Visual Studio) 28 | * Via the command line `dotnet fantomas src -r` 29 | 30 | If you do not apply Fantomas formatting, your PR will be rejected as this is automatically checked by the build system. -------------------------------------------------------------------------------- /Icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/Icon.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Compositional IT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/Logo.png -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | Maintainers Guide 2 | ================= 3 | 4 | This guide is for _maintainers_ who have commit access to the repository and can merge PR's and create release tags. 5 | 6 | Release Process 7 | --------------- 8 | 9 | The release process starts with drafting a new release in GitHub and results in a nuget package published to the public feed. 10 | 11 | 1. Update the `` tag in the [Farmer.fsproj](src/Farmer/Farmer.fsproj) file with the version number and commit it to master. 12 | 2. Go to [releases](https://github.com/CompositionalIT/farmer/releases) and click "Draft a new Release". 13 | 3. Under "Choose a tag", enter the version number for the new package to release to create a tag for the version when it is published. Creating this tag starts the Azure DevOps Pipeline. 14 | 4. In "Release Title", name it for the version number as well. This makes it easier to correlate a particular release with the nuget package when viewing the list of releases. 15 | 5. In "Describe this release", include the bullet points from the release notes for all the features added in this release. Viewing the "raw" [RELEASE_NOTES.md](https://raw.githubusercontent.com/CompositionalIT/farmer/master/RELEASE_NOTES.md) makes it easier to copy these for the description. 16 | 6. Click "Publish Release". 17 | 7. Go to the Azure DevOps pipeline and wait for the approval stage. If everything looks good, approve to publish the package to nuget. 18 | -------------------------------------------------------------------------------- /docs/-f/images/arm-graph.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/-f/images/arm-graph.jpg -------------------------------------------------------------------------------- /docs/-f/images/comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/-f/images/comparison.png -------------------------------------------------------------------------------- /docs/-f/images/deploy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/-f/images/deploy.jpg -------------------------------------------------------------------------------- /docs/-f/images/farmer-favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/-f/images/farmer-favicon.png -------------------------------------------------------------------------------- /docs/-f/images/farmer-flow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/-f/images/farmer-flow.jpg -------------------------------------------------------------------------------- /docs/-f/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/-f/images/logo.png -------------------------------------------------------------------------------- /docs/-f/images/quickstarts/multiple-web-apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/-f/images/quickstarts/multiple-web-apps.png -------------------------------------------------------------------------------- /docs/-f/images/quickstarts/serverless-etl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/-f/images/quickstarts/serverless-etl.png -------------------------------------------------------------------------------- /docs/-f/images/quickstarts/webapp-cosmos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/-f/images/quickstarts/webapp-cosmos.png -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Farmer Docs 2 | 3 | ## About the Documentation Platform 4 | 5 | Farmer's docs use [Hugo](https://gohugo.io/) and the 6 | [hugo-theme-learn theme](https://github.com/compositionalit/hugo-theme-learn). 7 | 8 | ## To build these docs locally 9 | 10 | * Install [Go language](https://golang.org/) support and 11 | [Hugo](https://gohugo.io/) (if on Windows, we recommend using 12 | [Chocolatey](https://chocolatey.org/) and running `choco install golang hugo`) 13 | 14 | *Note*: there is currently a problem with the newest version of Hugo and the 15 | theme, so use version 0.68.3 of Hugo, otherwise you'll get a compilation error 16 | * The theme is in a sub-module, so you'll also want to run 17 | `git submodule update --init` and then `cd docs/themes` followed by 18 | `git clone https://github.com/compositionalit/hugo-theme-learn.git` 19 | * To build, run `hugo --minify` from the `docs` folder. 20 | To serve a local copy, run `hugo server`. 21 | 22 | ## Publishing these Docs 23 | 24 | These docs use [GitHub Actions](https://github.com/features/actions) and the 25 | [Actions-hugo](https://github.com/peaceiris/actions-hugo) tooling to publish 26 | the contents to [GitHub pages](https://pages.github.com/) 27 | 28 | How it works: 29 | 30 | * A change is committed to the `master` branch (say, when a PR is merged). 31 | * The GitHub Actions workflow begins. 32 | * The action runs hugo against the `docs` folder, and then publishes the 33 | `public` folder output to the `gh-pages` branch. 34 | * The `gh-pages` branch is served by GitHub Pages at 35 | https://compositionalit.github.io/farmer. 36 | -------------------------------------------------------------------------------- /docs/archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "{{ replace .Name "-" " " | title }}" 3 | date: {{ .Date }} 4 | draft: true 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /docs/config.toml: -------------------------------------------------------------------------------- 1 | baseURL = "https://compositionalit.github.io/farmer/" 2 | languageCode = "en-gb" 3 | title = "Farmer" 4 | author = "Compositional IT" 5 | theme = "hugo-theme-learn" 6 | [outputs] 7 | relativeUrls = true 8 | home = [ "HTML", "RSS", "JSON"] 9 | [params] 10 | themeVariant="green" 11 | editURL = "https://github.com/CompositionalIT/farmer/tree/master/docs/content/" 12 | description = "Farmer - Making repeatable Azure deployments easy!" 13 | 14 | [[Languages.en.menu.shortcuts]] 15 | name = " GitHub repo" 16 | identifier = "ds" 17 | url = "https://github.com/compositionalit/farmer" 18 | weight = 10 19 | 20 | [[Languages.en.menu.shortcuts]] 21 | name = " Nuget" 22 | identifier = "nuget" 23 | url = "https://www.nuget.org/packages/Farmer/" 24 | weight = 10 25 | 26 | [[Languages.en.menu.shortcuts]] 27 | name = " Compositional IT" 28 | identifier = "cit" 29 | url = "https://compositional-it.com" 30 | weight = 11 31 | -------------------------------------------------------------------------------- /docs/content/about/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "About" 3 | date: 2020-02-04T22:44:07+01:00 4 | weight: 1 5 | --- 6 | 7 | #### About Farmer 8 | Farmer is a .NET domain-specific-language (DSL) for rapidly generating Azure Resource Manager (ARM) templates. Farmer is [commercially supported](../support/), open source and free-to-use. 9 | 10 | For those of you working with Azure today, you may already be aware that one of the most useful features is the ability to generate entire infrastructure architectures as code via ARM Template files. These templates contain a declarative model that allows repeatable deployments and idempotent releases (among other things). 11 | 12 | #### What's wrong with ARM? 13 | Unfortunately, ARM templates have some limitations caused by the fact that they must be authored in a verbose JSON dialect: 14 | * They provide very limited type checking and support, which makes creating discovery and creation of template features difficult. 15 | * Templates need a lot of boilerplate to be created for even relatively simple and common resources. 16 | * It requires "embedded", difficult-to-maintain stringly-typed code in order to achieve what might be trivial in a "proper" programming language, such as references, variables and parameters - or writing elements such as loops. 17 | * The documentation for ARM templates is not always kept up-to-date, so understanding and learning how to properly use them can involve a lot of searching and trial-and-error. 18 | 19 | In other words, whilst working with ARM templates that have already been created is relatively straightforward, the *authoring* of the templates themselves is time-consuming and error-prone. 20 | 21 | Whilst there have been some recent improvements to ARM - including tooling improvements in VS Code through an extension - we think that we can do much better than relying on tooling for a specific IDE, which means using something different than JSON when directly authoring ARM templates. 22 | 23 | #### What does Farmer do to fix this? 24 | Farmer templates are simple .NET Core applications which reference the [Farmer NuGet package](https://www.nuget.org/packages/Farmer/). This package contains a set of *types* that model Azure resources in a strongly-typed and succinct fashion, as well as functionality to create ARM templates from this model - and even deploy directly to Azure. 25 | 26 | #### What can I use Farmer for? 27 | Farmer currently has support for a [large number of common resources](../api-overview/resources) including web apps, sql and storage, with more being added over time. -------------------------------------------------------------------------------- /docs/content/api-overview/basic-types.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Basic Types" 3 | date: 2022-05-25T13:32:12+01:00 4 | draft: false 5 | weight: 3 6 | --- 7 | Farmer often uses references to link resouces, when this is not possible, Farmer uses certain types to define links. 8 | 9 | #### ResourceName 10 | 11 | A ResourceName represents a name of an ARM resource. 12 | 13 | 14 | ```fsharp 15 | ResourceName "myapp" 16 | ``` 17 | 18 | #### ResourceId 19 | 20 | A ResourceId identifies an ARM resource. 21 | 22 | This is used when you want to link unmanaged resources to a deployment or have multiple deployment parts. 23 | 24 | A ResourceId can be created with the resourceId member of a ResourceType: 25 | 26 | ```fsharp 27 | let subnet = Arm.Network.virtualNetworks.resourceId(ResourceName "") 28 | ``` 29 | 30 | or directly as a ResourceId: 31 | 32 | ```fsharp 33 | let mySubnet = { 34 | Type = Arm.Network.subnets 35 | Name = ResourceName "my-vnet" 36 | ResourceGroup = Some("myResourceGroup") 37 | Subscription = None 38 | Segments = [ResourceName "mySubnet"] 39 | } 40 | ``` 41 | 42 | These resources can be used i.e. in referencing a `web app { }` to a subnet. 43 | 44 | ```fsharp 45 | let webApp = webApp { 46 | name "myWebApp" 47 | service_plan_name "myServicePlan" 48 | sku WebApp.Sku.B1 49 | link_to_unmanaged_vnet mySubnet 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/content/api-overview/outputs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Outputs" 3 | date: 2020-08-22T09:13:36+01:00 4 | draft: false 5 | weight: 3 6 | --- 7 | ARM templates also support the notion of *[outputs](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-outputs)*. Outputs can be used to provide your Farmer applications with values which were generated during the deployment process, to be used further downstream. 8 | 9 | For example, you may wish to prime an Azure storage account with data post-creation. In this case, one way is to return back out the connection string of the storage account and use that to connect and upload your data. 10 | 11 | #### Creating and Consuming outputs 12 | Outputs are applied onto the `arm { }` builder using the [output keyword](../resources/arm/#builder-keywords). 13 | 14 | ```fsharp 15 | let myStorage = storageAccount { 16 | name "sampleaccount" 17 | } 18 | 19 | let template = arm { 20 | add_resource myStorage 21 | output "storage_key" myStorage.Key 22 | } 23 | 24 | let outputs = template |> Deploy.execute "my-resource-group" [] 25 | 26 | let connectionString = outputs.["storage_key"] 27 | ``` 28 | 29 | Outputs are returned back from the deployment as a simple `Map`. 30 | 31 | Any [ARM expression](../expressions) can be returned as an output, and you can create as many outputs as you wish. 32 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Resources" 3 | date: 2020-02-04T22:37:39+01:00 4 | weight: 10 5 | chapter: true 6 | --- 7 | 8 | # Resources 9 | 10 | Farmer currently has support for the most popular resources in Azure, such as Storage, App Service and Functions. You can find out more about each resource in detail in the menu. -------------------------------------------------------------------------------- /docs/content/api-overview/resources/app-insights.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "App Insights" 3 | date: 2020-02-05T08:53:46+01:00 4 | weight: 1 5 | chapter: false 6 | --- 7 | 8 | #### Overview 9 | The App Insights builder is used to create Application Insights accounts. Use this if you need a standalone AI instance; if you need one for a web app, the web app will create one by default and configure the application settings automatically. 10 | 11 | * Application Insights (`Microsoft.Insights/components`) 12 | 13 | > This builder supports both "Classic" (standalone) and "Workspace Enabled" (Log Analytics-backed) instances of App Insights. See the `log_analytics_workspace` keyword to see how to create the latter type of instance. 14 | 15 | #### Builder Keywords 16 | 17 | | Keyword | Purpose | 18 | |-|-| 19 | | name | Sets the name of the App Insights instance. | 20 | | disable_ip_masking | Disable IP masking. | 21 | | sampling_percentage | Define sampling percentage (0-100) | 22 | | log_analytics_workspace | Use a Log Analytics workspace as the backing store for this AI instance. You can supply either a Farmer-generate Log Analytics`WorkspaceConfig` instance that exists in the same resource group, or a fully-qualified Resource ID path to that instance. This will also switch the AI instance over to create a "workspace enabled" AI instance. | 23 | 24 | #### Configuration Members 25 | 26 | | Member | Purpose | 27 | |-|-| 28 | | InstrumentationKey | Gets the ARM expression path to the instrumentation key of this App Insights instance. | 29 | | ConnectionString | Gets the ARM expression path to the connection string of this App Insights instance. | 30 | 31 | #### Example 32 | 33 | ```fsharp 34 | open Farmer 35 | open Farmer.Builders 36 | 37 | let ai = appInsights { 38 | name "myAI" 39 | log_analytics_workspace myWorkspace // use to activate workspace-enabled AI instances. 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/availability-tests.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "App Insights - Availability Tests" 3 | date: 2021-08-06T07:00:00+01:00 4 | weight: 1 5 | chapter: false 6 | --- 7 | 8 | #### Overview 9 | The App Insights - Availability Tests builder is used to create Application Insights Availability Tests. You will need an Application Insights instance to run the tests. 10 | The tests can be just pinging the website and expecting a response code of 200, or they can be recorded Visual Studio WebTests as custom XML strings. 11 | 12 | * Application Insights (`Microsoft.Insights/webtests`) 13 | 14 | #### Builder Keywords 15 | 16 | | Keyword | Purpose | 17 | |-|-| 18 | | name | Sets the name of this Webtest instance. | 19 | | link_to_app_insights | Name or resource of the App Insight instance. | 20 | | web_test | AvailabilityTest.WebsiteUrl Uri to website, or AvailabilityTest.CustomWebtestXml string | 21 | | locations | List of locations where the site is pinged. These are not format of Farmer.Location but AvailabilityTest.TestSiteLocation. | 22 | | timeout | Timeout if the test is not responding. Default: 120 seconds. | 23 | | frequency | Frequency how often the test is run. Default: 900 seconds. | 24 | 25 | #### Example 26 | 27 | ```fsharp 28 | open Farmer 29 | open Farmer.Builders 30 | 31 | let ai = appInsights { name "ai" } 32 | let myAvailabilityTest = 33 | availabilityTest { 34 | name "avTest" 35 | link_to_app_insights ai 36 | timeout 60 37 | frequency 800 38 | locations [ 39 | AvailabilityTest.TestSiteLocation.NorthEurope 40 | AvailabilityTest.TestSiteLocation.WestEurope 41 | AvailabilityTest.TestSiteLocation.CentralUS 42 | AvailabilityTest.TestSiteLocation.UKSouth 43 | ] 44 | web_test ( 45 | "https://mywebsite.com" 46 | |> System.Uri 47 | |> AvailabilityTest.WebsiteUrl) 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/azure-firewall.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Azure Firewall" 3 | date: 2021-07-07T11:22:17-05:00 4 | chapter: false 5 | weight: 1 6 | --- 7 | 8 | #### Overview 9 | 10 | The Azure Firewall builder (`azureFirewall`) is used to create Azure Firewall instances. 11 | 12 | - Azure Firewall (`Microsoft.Network/azureFirewalls`) 13 | 14 | #### Builder Keywords 15 | 16 | | Resource | Keyword | Purpose | 17 | |---------------|-----------------------------------|--------------------------------------------------------------------------------| 18 | | azureFirewall | name | Sets the name of the azure firewall | 19 | | azureFirewall | sku | Sets the name and tier of the Azure firewall sku | 20 | | azureFirewall | link_to_unmanaged_firewall_policy | Configure the azure firewall to use an existing firewall policy | 21 | | azureFirewall | link_to_firewall_policy | Configure the Azure firewall to use a firewall policy deployed by Farmer | 22 | | azureFirewall | link_to_unmanaged_vhub | Specify the existing virtual hub to which the azure firewall belongs | 23 | | azureFirewall | link_to_vhub | Specify the virtual hub deployed by Farmer to which the azure firewall belongs | 24 | | azureFirewall | public_ip_reservation_count | Specify the number of Public IP addresses associated with the azure firewall | 25 | | azureFirewall | availablity_zones | Specify the availability zones. | 26 | | azureFirewall | pick_zones | Picks availability zones within a region. | 27 | | azureFirewall | depends_on | Specify resources deployed by Farmer the azure firewall depends on | 28 | 29 | ### Example 30 | 31 | ```fsharp 32 | open Farmer 33 | open Farmer.Builders 34 | 35 | let firewall = azureFirewall { 36 | name "farmer_firewall" 37 | sku SkuName.AZFW_Hub SkuTier.Standard 38 | public_ip_reservation_count 2 39 | link_to_unmanaged_vhub (virtualHubs.resourceId "unmanaged-vhub") 40 | } 41 | 42 | let deployment = arm { 43 | location Location.NorthEurope 44 | add_resource firewall 45 | } 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/bing-search.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Bing Search" 3 | date: 2021-01-29T07:33:46+01:00 4 | chapter: false 5 | weight: 2 6 | --- 7 | 8 | #### Overview 9 | The Bing Search builder is used to create Azure Bing Search instances. 10 | 11 | * Bing Search (`Microsoft.Bing/accounts`, kind: `Bing.Search.v7`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the Bing Search instance. | 17 | | sku | Sets the SKU of the instance. Defaults to `F1` (free). | 18 | | statistics | Sets the `statisticsEnabled` property of the instance. Defaults to `false` | 19 | 20 | #### Configuration Members 21 | 22 | | Member | Purpose | 23 | |-|-| 24 | | Key | Gets the ARM expression path to the Key of this Bing Search instance. | 25 | 26 | #### Example 27 | ```fsharp 28 | open Farmer 29 | open Farmer.Builders 30 | 31 | let tags = [ "a", "1"; "b", "2" ] 32 | let translator = bingSearch { 33 | name "test" 34 | sku S0 35 | add_tags tags 36 | statistics Enabled 37 | } 38 | 39 | let key : ArmExpression = translator.Key 40 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/resources/cognitive-services.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Cognitive Services" 3 | date: 2020-04-10T08:53:46+01:00 4 | chapter: false 5 | weight: 3 6 | --- 7 | 8 | #### Overview 9 | The Cognitive Services builder is used to create Azure Cognitive Services instances. 10 | 11 | * Cognitive Services (`Microsoft.CognitiveServices/accounts`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the Cognitive Services instance. | 17 | | sku | Sets the SKU of the instance. Defaults to F0 (free). | 18 | | api | Specifies the Kind of api to use for the service instance. Defaults to `AllInOne`. | 19 | 20 | #### Configuration Members 21 | 22 | | Member | Purpose | 23 | |-|-| 24 | | Key | Gets the ARM expression path to the Key of this Cognitive Services instance. | 25 | 26 | #### Example 27 | ```fsharp 28 | open Farmer 29 | open Farmer.Builders 30 | 31 | let translator = cognitiveServices { 32 | name "mytranslator" 33 | sku CognitiveServices.F0 34 | api CognitiveServices.AnomalyDetector 35 | } 36 | 37 | let key : ArmExpression = translator.Key 38 | ``` 39 | 40 | ### BingSearch (obsolete) 41 | 42 | Starting from 1.4.0 BingSearch api is available as a part of `bingSearch` builder instead of `cognitiveServices`. -------------------------------------------------------------------------------- /docs/content/api-overview/resources/communication-services.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Communication Services" 3 | date: 2021-04-28T23:33:46+01:00 4 | chapter: false 5 | weight: 3 6 | --- 7 | 8 | #### Overview 9 | The Communication Services builder is used to create Azure Communication Services instances. 10 | 11 | * Communication Services (`Microsoft.Communication/communicationServices`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the Communication Services instance. | 17 | | data_location | Sets the `dataLocation` property of the instance. Defaults to `United States` | 18 | 19 | #### Configuration Members 20 | 21 | | Member | Purpose | 22 | |-|-| 23 | | Key | Gets the ARM expression path to the Key of this Communication Services instance. | 24 | | Key | Gets the ARM expression path to the Connection String of this Communication Services instance. | 25 | 26 | #### Example 27 | ```fsharp 28 | open Farmer 29 | open Farmer.Builders 30 | 31 | let tags = [ "a", "1"; "b", "2" ] 32 | let cs = communicationService { 33 | name "test" 34 | add_tags tags 35 | data_location DataLocation.Australia 36 | } 37 | 38 | let key : ArmExpression = cs.Key 39 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/resources/container-registry.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Container Registry" 3 | date: 2020-04-30T19:10:46+02:00 4 | chapter: false 5 | weight: 3 6 | --- 7 | 8 | #### Overview 9 | The Container Registry builder is used to create Azure Container Registry (ACR) instances. 10 | 11 | * Container Registry (`Microsoft.ContainerRegistry/registries`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the Container Registry instance. | 17 | | sku | Sets the SKU of the instance. Defaults to Basic. | 18 | | enable_admin_user | The value that indicates whether the admin user is enabled. | 19 | 20 | #### Configuration Members 21 | 22 | | Member | Purpose | 23 | |-|-| 24 | | Password | Gets the ARM expression path to the first admin password of this container registry if the admin user was enabled. | 25 | | Password2 | Gets the ARM expression path to the second admin password of this container registry if the admin user was enabled. | 26 | | Username | Gets the ARM expression path to the admin username of this container registry if the admin user was enabled. | 27 | 28 | #### Example 29 | ```fsharp 30 | open Farmer 31 | open Farmer.Builders 32 | 33 | let myRegistry = containerRegistry { 34 | name "myRegistry" 35 | sku ContainerRegistry.Basic 36 | enable_admin_user 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/data-lake.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Data Lake" 3 | date: 2020-06-11T00:55:30+02:00 4 | chapter: false 5 | weight: 4 6 | --- 7 | 8 | #### Overview 9 | The Data Lake builder is used to create Azure Data Lake instances. 10 | 11 | * Data Lake (`Microsoft.DataLakeStore/accounts`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the Cognitive Services instance. | 17 | | sku | Sets the SKU of the instance. Defaults to Consumption. | 18 | | enable_encryption | Turns on data lake encryption. | 19 | 20 | #### Example 21 | ```fsharp 22 | open Farmer 23 | open Farmer.Builders 24 | 25 | let myLake = dataLake { 26 | name "myDataLake" 27 | sku DataLake.Commitment_100TB 28 | enable_encryption 29 | } 30 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/resources/databricks-workspace.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Databricks Workspace" 3 | date: 2021-01-31T15:43:30+00:00 4 | chapter: false 5 | weight: 4 6 | --- 7 | 8 | #### Overview 9 | The Databricks Workspace builder is used to create Azure Databricks Workspaces. 10 | 11 | * Workspace (`Microsoft.Databricks/workspaces`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the workspace. | 17 | | sku | Sets the pricing tier of the workspace. Defaults to Standard Tier. | 18 | | encrypt_with_key_vault | Given a key vault builder / resourceid / vault name, and the name of a key, activates the use of Key Vault for the key store. | 19 | | encrypt_with_databricks | Specifies to use DataBricks itself for key encryption. | 20 | | encrypt_with | Allows you to programmatically specify whether to use key vault or data bricks encryption. | 21 | | key_vault_key_version | Specifies the version of the key vault key to use; if this is not specified, the latest version of the key is used. | 22 | | allow_public_ip | Whether to use public IP addresses for cluster virtual machines. Defaults to Enabled. | 23 | | attach_to_vnet | Given a Resource Id / Name / VNet Config, and Public & Private Subnets, attaches the workspace to the VNet specified. | 24 | | managed_resource_group_id | Sets the name of the resource group that will be created by the workspace. Optional. | 25 | 26 | #### Example 27 | ```fsharp 28 | open Farmer 29 | open Farmer.Builders 30 | 31 | let myVault = keyVault { name "my-vault" } 32 | 33 | let myWorkspace = databricksWorkspace { 34 | name "my-databricks-workspace" 35 | sku Databricks.Sku.Standard 36 | encrypt_with_key_vault myVault "workspace-encryption-key" 37 | attach_to_vnet "databricks-vnet" "databricks-pub-snet" "databricks-priv-snet" 38 | } 39 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/resources/dedicated-hosts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Dedicated Hosts" 3 | date: 2022-10-10T22:26:00-04:00 4 | chapter: false 5 | weight: 5 6 | --- 7 | 8 | #### Overview 9 | The `hostGroup` and `host` builders create dedicated hosts in azure and their parent resource, a host group, to efficiently manage a physical host resource in Azure. Dedicated hosts are the same physical servers used in our data centers, provided as a resource. To learn more about dedicated hosts, reference the [Azure Docs](https://learn.microsoft.com/en-us/azure/virtual-machines/dedicated-hosts) 10 | 11 | * Host Group (`Microsoft.Compute/hostGroups`) 12 | * Host (`Microsoft.Compute/hostGroups/hosts`) 13 | 14 | #### Builder Keywords 15 | 16 | | Applies To | Keyword | Purpose | 17 | |-|-|-| 18 | | hostGroup | name | Name of the host group resource | 19 | | hostGroup | add_availability_zone | Assign a zone to the host group. | 20 | | hostGroup| support_automatic_placement | Feature flag for automatic placement of the VMs | 21 | | hostGroup| platform_fault_domain_count | How many fault domains to support, depends on the region. | 22 | | host | name | Name of the host resource | 23 | | host | license_type | The licenses to bring the hosts, i.e. WindowsHybrid, WindowsPerpetual | 24 | | host | auto_replace_on_failure| Feature flag whether to auto replace the host on failure | 25 | | host | sku | name of the sku for the dedicated hosts. Valid sku's vary by subscription, consult the [Dedicated Host documentation](https://learn.microsoft.com/en-us/azure/virtual-machines/dedicated-host-compute-optimized-skus) | 26 | | host | platform_fault_domain | Fault domain to assign the host | 27 | | host | parent_host_group | Name of the host group to assign the hosts to | 28 | 29 | #### Example 30 | 31 | ```fsharp 32 | #r "nuget:Farmer" 33 | 34 | open Farmer 35 | open Farmer.Builders 36 | 37 | arm { 38 | location Location.EastUS 39 | 40 | add_resources 41 | [ 42 | hostGroup { 43 | name "myhostgroup" 44 | support_automatic_placement true 45 | add_availability_zone "1" 46 | platform_fault_domain_count 2 47 | } 48 | host { 49 | name "myhost" 50 | parent_host_group (ResourceName "myHostGroup") 51 | platform_fault_domain 2 52 | sku "Fsv2-Type2" 53 | } 54 | ] 55 | } 56 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/resources/diagnosticsetting.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: " Diagnostic Settings " 3 | date: 2020-12-02T12:10:03 +00:00 4 | chapter: false 5 | weight: 4 6 | --- 7 | 8 | #### Overview 9 | 10 | The Diagnostic Settings builder is used to create diagnostic settings instances to send platform logs and metrics to different destinations (storage, event hub and log analytics). Support for Farmer builders and external resources is supported. 11 | 12 | - Diagnostic Settings (`providers/diagnosticSettings`) 13 | 14 | #### Builder Keywords 15 | 16 | | Keyword | Purpose| 17 | |-|-| 18 | | name | Sets the name of the Diagnostic Settings resource. | 19 | | metrics_source | The resource that will be used for the source of logging and metrics information. Can be any Builder, or you can supply a ResourceId for an external resource. | 20 | | capture_metrics | Specifies the list of Metrics to capture from the source resource. | 21 | | capture_logs | Specifies the list of Log Categories to capture from the source resource. | 22 | | add_destination | Adds a destination for all logs and metrics, either a storage account, log analytics workspace, event hub or a Resource ID pointing to any valid Resource for those three resource types. | 23 | | event_hub_destination_name | Allows you to override the event hub name to use. | 24 | | loganalytics_output_type | If a Log Analytics Workspace is specified as output, specify whether to use the default Azure Diagnostics grouping or a dedicated grouping for logging and metrics. | 25 | 26 | #### Example 27 | The example below illustrates how to create a web application and set up a diagnostics setting against it, 28 | whilst setting up three destinations for the diagnostics (storage, event hub and log analytics). Also notice 29 | the use of the `Logging.` namespace, which contains all documented Logging categories. 30 | 31 | ```fsharp 32 | open Farmer 33 | open Farmer.Builders 34 | open Farmer.DiagnosticSettings 35 | 36 | let data = storageAccount { name "isaacsuperdata" } 37 | let hub = eventHub { name "isaacsuperhub" } 38 | let logs = logAnalytics { name "isaacsuperlogs" } 39 | let web = webApp { name "isaacdiagsuperweb"; app_insights_off } 40 | 41 | let mydiagnosticSetting = diagnosticSettings { 42 | name "myDiagnosticSetting" 43 | metrics_source web 44 | 45 | add_destination data 46 | add_destination logs 47 | add_destination hub 48 | loganalytics_output_type Dedicated 49 | capture_metrics [ "AllMetrics" ] 50 | capture_logs [ 51 | Logging.Web.Sites.AppServicePlatformLogs 52 | Logging.Web.Sites.AppServiceAntivirusScanAuditLogs 53 | ] 54 | } 55 | 56 | let deployment = arm { 57 | add_resources [ data; web; hub; logs; mydiagnosticSetting ] 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/iot-hub.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "IOT Hub" 3 | date: 2020-05-19T23:14:14+02:00 4 | chapter: false 5 | weight: 9 6 | --- 7 | 8 | #### Overview 9 | The IOT Hub builder creates IOT Hub and linked Provision Services. 10 | 11 | * IOT Hubs (`Microsoft.Devices/IotHubs`) 12 | * Provisioning Services (`Microsoft.Devices/provisioningServices`) 13 | 14 | #### Builder Keywords 15 | 16 | | Keyword | Purpose | 17 | |-|-| 18 | | name | Specifies the name of the IOT Hub | 19 | | sku | Sets the SKU of the IOT Hub | 20 | | capacity | Sets the name of the capacity for the IOT Hub instance | 21 | | partition_count | Sets the name of the SKU/Tier for the IOT Hub instance | 22 | | retention_days | Sets the name of the SKU/Tier for the IOT Hub instance | 23 | | enable_device_provisioning | Sets the name of the SKU/Tier for the IOT Hub instance | 24 | 25 | #### Configuration Members 26 | 27 | | Member | Purpose | 28 | |-|-| 29 | | GetKey | Returns an ARM expression to retrieve the IOT Hub key for a specific policy e.g IotHubOwner or RegistryReadWrite. Useful for e.g. supplying the key to another resource e.g. KeyVault or an app setting in the App Service. | 30 | | GetConnectionString | Returns an ARM expression to generate an IOT Hub connection string for a specific policy e.g IotHubOwner or RegistryReadWrite. Useful for e.g. supplying the key to another resource e.g. KeyVault or an app setting in the App Service. | 31 | 32 | #### Example 33 | 34 | ```fsharp 35 | open Farmer 36 | open Farmer.Builders 37 | 38 | let hub = iotHub { 39 | name "yourhubname" 40 | sku IotHub.B1 41 | capacity 2 42 | partition_count 2 43 | retention_days 3 44 | enable_device_provisioning 45 | } 46 | 47 | let deployment = arm { 48 | location Location.NorthEurope 49 | add_resource hub 50 | output "iot_key" (hub.GetKey IotHub.IotHubOwner) 51 | output "iot_connection" (hub.GetConnectionString IotHub.RegistryReadWrite) 52 | } 53 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/resources/loganalytics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Log Analytics" 3 | date: 2020-10-7T19:10:46+02:00 4 | chapter: false 5 | weight: 12 6 | --- 7 | 8 | #### Overview 9 | 10 | The Log Analytics builder is used to create Work space instances. 11 | 12 | - Log Analytics (`Microsoft.OperationalInsights/workspaces`) 13 | 14 | #### Builder Keywords 15 | 16 | | Keyword | Purpose | 17 | | ---------------- | --------------------------------------------------------------- | 18 | | name | Sets the name of the log analytics instance. | 19 | | retention_period | Sets the retention period for logs in days. | 20 | | enable_ingestion | Enables ingestion network traffic. | 21 | | enable_query | Enables query network traffic. | 22 | | daily_cap | Specifies an upper limit on the amount of data to ingest daily. | 23 | | add_tags | Adds a set of tags to the resource | 24 | | add_tag | Adds a tag to the resource | 25 | 26 | #### Configuration Members 27 | 28 | | Member | Purpose | 29 | |-|-| 30 | | CustomerID | Gets the ARM expression path to the customer ID of this LogAnalytics instance. | 31 | | CustomerID | Gets the ARM expression path to the primary shared key of this LogAnalytics instance. | 32 | 33 | #### Example 34 | 35 | ```fsharp 36 | open Farmer 37 | open Farmer.Builders 38 | 39 | let myAnalytics = logAnalytics { 40 | name "myloganalytics" 41 | retention_period 30 42 | enable_ingestion 43 | enable_query 44 | daily_cap 5 45 | add_tag "tag1" "myTestResourceFarmer" 46 | } 47 | 48 | let deployment = arm { 49 | location Location.WestEurope 50 | add_resource myAnalytics 51 | } 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/logic-apps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Logic Apps" 3 | date: 2022-04-27T00:55:30+02:00 4 | chapter: false 5 | weight: 12 6 | --- 7 | 8 | #### Overview 9 | The Logic App builder is used to create Azure Logic App Workflows. 10 | 11 | * Workflows (`Microsoft.Logic/workflows`) 12 | 13 | #### Builder keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the workflow. | 17 | | definition | Sets the file path (via `FileDefinition path`) or the definition directly (via `ValueDefinition value`) | 18 | | add_tags | Adds tags to the script runtime resource. | 19 | | add_tag | Adds a tag to the script runtime resource. | 20 | 21 | #### Example 22 | 23 | ```fsharp 24 | open Farmer 25 | open Farmer.Builders 26 | 27 | let emptyLogicApp = 28 | """ 29 | { 30 | "definition": { 31 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 32 | "actions": {}, 33 | "contentVersion": "1.0.0.0", 34 | "outputs": {}, 35 | "parameters": {}, 36 | "triggers": {} 37 | }, 38 | "parameters": {} 39 | } 40 | """ 41 | 42 | let myValueLogicApp = logicApp { 43 | name "value-test-logic-app" 44 | definition (ValueDefinition emptyLogicApp) 45 | add_tags [("created-by", "farmer")] 46 | } 47 | 48 | let filepath = "./logicAppDefinition.json" 49 | 50 | let myFileLogicApp = logicApp { 51 | name "file-test-logic-app" 52 | definition (FileDefinition filepath) 53 | add_tags [("created-by", "farmer")] 54 | } 55 | 56 | let deployment = arm { 57 | location Location.NorthCentralUS 58 | add_resource myValueLogicApp 59 | add_resource myFileLogicApp 60 | } 61 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/resources/maps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Maps" 3 | date: 2020-05-26T11:24:00+01:00 4 | chapter: false 5 | weight: 13 6 | --- 7 | 8 | #### Overview 9 | The Maps builder creates Azure Maps accounts. 10 | 11 | * Maps (`Microsoft.Maps/accounts`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the Azure Maps account. | 17 | | sku | Sets the sku of the Azure Maps account. | 18 | 19 | #### Example 20 | 21 | ```fsharp 22 | open Farmer 23 | open Farmer.Builders 24 | 25 | let myMaps = maps { 26 | name "mymaps" 27 | sku Maps.S0 28 | } 29 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/resources/nat-gateway.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "NAT Gateway" 3 | date: 2022-08-02T22:26:00-04:00 4 | chapter: false 5 | weight: 5 6 | --- 7 | 8 | #### Overview 9 | The `natGateway` builder creates a NAT Gateway to efficiently manage the SNAT traffic used by resources 10 | in a virtual network. By default, it creates a single static public IP for the NAT Gateway, but more IP 11 | addresses or prefixes of groups of addresses can be specified. 12 | 13 | * NatGateway (`Microsoft.Network/natGateways`) 14 | 15 | #### Builder Keywords 16 | 17 | | Applies To | Keyword | Purpose | 18 | |-|-|-| 19 | | natGateway | name | Name of the NAT Gateway resource | 20 | | natGateway | idle_timeout | Timeout after which connections that have seen no traffic will be disconnected to free SNAT ports. | 21 | 22 | #### Example 23 | 24 | ```fsharp 25 | #r "nuget:Farmer" 26 | 27 | open Farmer 28 | open Farmer.Builders 29 | 30 | arm { 31 | location Location.EastUS 32 | add_resources [ 33 | natGateway { 34 | name "my-nat-gateway" 35 | } 36 | vnet { 37 | name "my-net" 38 | add_address_spaces [ "10.100.0.0/16" ] 39 | add_subnets [ 40 | subnet { 41 | name "my-services" 42 | prefix "10.100.12.0/24" 43 | nat_gateway (Farmer.Arm.Network.natGateways.resourceId "my-nat-gateway") 44 | } 45 | ] 46 | } 47 | ] 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/operations-management.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Operations Management" 3 | date: 2022-04-29T09:40:00-04:00 4 | weight: 15 5 | chapter: false 6 | --- 7 | 8 | #### Overview 9 | The Operations Management builder is used to create [Solutions](https://docs.microsoft.com/en-us/azure/templates/microsoft.operationsmanagement/solutions?tabs=bicep) for a [Log Analytics Workspace](https://docs.microsoft.com/en-us/azure/azure-monitor/logs/log-analytics-workspace-overview). 10 | 11 | #### Builder Keywords 12 | 13 | | Builder | Keyword | Purpose | 14 | |-|-|-| 15 | | omsPlan | name | The name of the plan, which can match the name of the overall Solution. | 16 | | omsPlan | publisher | The publisher of the solution, usually "Microsoft" (the default value). | 17 | | omsPlan | product | The specific solution being created, such as `OMGSGallery/SecurityInsights`. | 18 | | omsProperties | workspace | The Log Analytics workspace this solution uses. | 19 | | omsProperties | add_contained_resource | Adds a resource contained by this solution. | 20 | | omsProperties | add_contained_resources | Adds multiple resources contained by this solution. | 21 | | omsProperties | add_referenced_resource | Adds a resource referenced by this solution. | 22 | | omsProperties | add_referenced_resources | Adds multiple resources referenced by this solution. | 23 | | oms | name | The name of the solution. | 24 | | oms | plan | The `OMSPlan` for the solution. | 25 | | oms | properties | The `OMSProperties` for the solution. | 26 | | oms | add_tag | Add a tag to the solution. | 27 | | oms | add_tags | Add one or more tags to the solution. | 28 | 29 | #### Example 30 | 31 | This example creates an Azure Sentinel solution on a Log Analytics Workspace. 32 | 33 | ```fsharp 34 | open Farmer 35 | open Farmer.Builders 36 | 37 | let sentinelWorkspace = logAnalytics { 38 | name "my-sentinel-workspace" 39 | retention_period 30 40 | enable_query 41 | daily_cap 5 42 | } 43 | 44 | let solutionName = $"SecurityInsights({sentinelWorkspace.Name.Value})" 45 | 46 | let sentinelSolution = oms { 47 | name solutionName 48 | plan (omsPlan { 49 | name solutionName 50 | publisher "Microsoft" 51 | product "OMSGallery/SecurityInsights" 52 | }) 53 | properties(omsProperties { 54 | workspace sentinelWorkspace 55 | }) 56 | } 57 | 58 | let deployment = arm { 59 | location Location.NorthCentralUS 60 | add_resource sentinelWorkspace 61 | add_resource sentinelSolution 62 | } 63 | ``` 64 | 65 | 66 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/private-endpoint.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Private Endpoint" 3 | date: 2022-08-05T16:13:00-04:00 4 | chapter: false 5 | weight: 12 6 | --- 7 | 8 | #### Overview 9 | The Private Endpoint builder (`privateEndpoint`) creates a private endpoint for accessing Azure resources or a private link service without traversing the Internet. 10 | 11 | * Private Endpoint (`Microsoft.Network/privateEndpoints`) 12 | 13 | #### Builder Keywords 14 | 15 | | Applies To | Keyword | Purpose | 16 | |-|-|-| 17 | | privateEndpoint | name | Specifies the name of the private endpoint. | 18 | | privateEndpoint | subnet_reference | Attaches the private endpoint to a referenced subnet. | 19 | | privateEndpoint | link_to_subnet | Attaches the private endpoint to a subnet deployed in the same deployment. | 20 | | privateEndpoint | link_to_unmanaged_subnet | Attaches the private endpoint to an existing subnet. | 21 | | privateEndpoint | resource | Specifies the ARM resource ID of the service it is connecting to. | 22 | | privateEndpoint | custom_nic_name | Optionally specify the name for the NIC generated for the private endpoint. | 23 | | privateEndpoint | add_group_ids | Specify one or more group IDs the private link service provides. | 24 | 25 | #### Configuration Members 26 | 27 | | Member | Purpose | 28 | |-|-| 29 | | CustomNicEndpointIP | If the `custom_nic_name` is set, this gets an ARM Expression to get the private endpoint IP address by 0-based index. | 30 | | CustomNicFirstEndpointIP | If the `custom_nic_name` is set, this gets an ARM Expression to get the first private endpoint IP address. | 31 | 32 | #### Example 33 | 34 | ```fsharp 35 | open Farmer 36 | open Farmer.Builders 37 | 38 | let myPrivateEndpoint = privateEndpoint { 39 | name "private-endpoint" 40 | custom_nic_name "private-endpoint-nic" 41 | link_to_subnet (subnets.resourceId (ResourceName "my-net", ResourceName "priv-endpoints" )) 42 | resource (Unmanaged existingPrivateLinkId) 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/redis.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Redis Cache" 3 | date: 2020-02-23T20:00:00+01:00 4 | chapter: false 5 | weight: 17 6 | --- 7 | 8 | #### Overview 9 | The Redis builder creates managed Redis Cache accounts. 10 | 11 | * Redis (`Microsoft.Cache/redis`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the Redis cache instance. | 17 | | sku | Sets the sku of the Redis cache instance. | 18 | | capacity | Sets the capacity level of the Redis cache instance, which should be between 1-6 - see [here](https://azure.microsoft.com/en-gb/pricing/details/cache/). | 19 | | enable_non_ssl_port | Enabled access to the cache over the non-SSL port. | 20 | | setting | Allows you to set a Redis-cache specific setting at deployment-time | 21 | 22 | #### Configuration Members 23 | | Member | Purpose | 24 | |-|-| 25 | | Key | Gets an ARM expression for the primary key of the Redis cache instance. | 26 | 27 | #### Example 28 | 29 | ```fsharp 30 | open Farmer 31 | open Farmer.Builders.Redis 32 | 33 | let myCache = redis { 34 | name "myredis" 35 | sku Redis.Standard 36 | capacity 1 37 | enable_non_ssl_port 38 | setting "maxclients" 256 39 | setting "maxmemory-reserved" 2 40 | setting "maxfragmentationmemory-reserved" 12 41 | setting "maxmemory-delta" 2 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/search.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Search" 3 | date: 2020-02-05T08:53:46+01:00 4 | chapter: false 5 | weight: 18 6 | --- 7 | 8 | #### Overview 9 | The Search builder creates storage accounts and their associated containers. 10 | 11 | * Search (`Microsoft.Search/searchServices`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the Azure Search instance. | 17 | | sku | Sets the sku of the Azure Search instance. | 18 | | replicas | Sets the replica count of the Azure Search instance. | 19 | | partitions | Sets the number of partitions of the Azure Search instance. | 20 | 21 | #### Configuration Members 22 | 23 | | Member | Purpose | 24 | |-|-| 25 | | AdminKey | Gets an ARM expression for the admin key of the search instance. | 26 | | QueryKey | Gets an ARM expression for the query key of the search instance. | 27 | 28 | #### Example 29 | 30 | ```fsharp 31 | open Farmer 32 | open Farmer.Builders 33 | 34 | let mySearch = search { 35 | name "isaacsSearch" 36 | sku Search.Basic 37 | } 38 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/resources/signalr.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "SignalR" 3 | date: 2020-06-01T11:13:00+01:00 4 | chapter: false 5 | weight: 18 6 | --- 7 | 8 | #### Overview 9 | The SignalR builder creates SignalR services. 10 | 11 | * SignalR Service (`Microsoft.SignalRService/signalR`) 12 | 13 | #### Builder Keywords 14 | | Keyword | Purpose | 15 | |-|-| 16 | | name | Sets the name of the SignalR service. | 17 | | sku | Sets the sku of the SignalR service. | 18 | | capacity | Sets the capacity of the SignalR service. (optional) | 19 | | service_mode | Sets the service mode of the SignalR service. (optional) | 20 | | allowed_origins | Sets the allowed origins of the SignalR service. (optional) | 21 | 22 | #### Configuration Members 23 | 24 | | Member | Purpose | 25 | |-|-| 26 | | Key | Returns an ARM expression to retrieve the primary Key of the service. Useful for e.g. supplying the connection string to another resource e.g. KeyVault or an app setting in the App Service. | 27 | | ConnectionString | Returns an ARM expression to retrieve the primary Connection String of the service. Useful for e.g. supplying the connection string to another resource e.g. KeyVault or an app setting in the App Service. | 28 | 29 | #### Example 30 | 31 | ```fsharp 32 | open Farmer 33 | open Farmer.Builders 34 | 35 | let mySignalR = signalR { 36 | name "mysignalr" 37 | sku SignalR.Standard 38 | capacity 10 39 | service_mode ServiceMode.Default 40 | allowed_origins [ "https://github.com" ] 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/static-web-app.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Static Web Apps" 3 | date: 2020-02-05T08:53:46+01:00 4 | chapter: false 5 | weight: 18 6 | --- 7 | 8 | #### Overview 9 | The Static Web App builder is used to create [Static Web Apps](https://azure.microsoft.com/en-us/services/app-service/static/). The Static Web App service is a modern web app service that offers streamlined full-stack development from source code to global high availability. You can use it to host static web applications and Azure Functions in a single resource, using GitHub native workflows to build and deploy your application. 10 | 11 | * Static Site (`Microsoft.Web/staticSites`) 12 | 13 | > At the time of writing, Static Web Apps are in public preview. Not all Azure locations support them. 14 | 15 | #### Static Web App Builder Keywords 16 | | Keyword | Purpose | 17 | |-|-| 18 | | name | Sets the name of the static web app. | 19 | | repository | The URI of the GitHub repository containing your static web app. | 20 | | artifact_location | The folder where the built web app is copied to e.g. `build` (optional) | 21 | | api_location | The path containing your Azure Functions (optional) | 22 | | app_location | The path containing your application code (optional) | 23 | | branch | The branch that you which to use for the static web app (optional, defaults to 'master') | 24 | | app_settings | Accepts a list of tuple strings representing key/value pairs for the app setting of the static web app | 25 | 26 | #### Configuration Members 27 | | Name | Purpose | 28 | |-|-| 29 | | RepositoryParameter | Provides the generated name for the repository token parameter name. 30 | 31 | #### Parameters 32 | | Name | Purpose | 33 | |-|-| 34 | | repositorytoken-for-`name` | Provides the Github Personal Access Token (PAT) required to authenticate and create the appropriate Github Action. | 35 | 36 | #### Example 37 | 38 | ```fsharp 39 | open Farmer 40 | open Farmer.Builders 41 | 42 | let myApp = staticWebApp { 43 | name "isaacsstatic" 44 | repository "https://github.com/isaacabraham/staticwebreact" 45 | artifact_location "build" 46 | api_location "api" 47 | app_settings [ 48 | "key1", "value1" 49 | "key2", "value2" 50 | ] 51 | } 52 | 53 | let deployment = arm { 54 | location Location.WestEurope 55 | add_resource myApp 56 | } 57 | 58 | deployment 59 | |> Deploy.execute "my-resource-group" [ myApp.RepositoryParameter, "Github personal access token goes here..." ] 60 | 61 | ``` 62 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/virtual-hub.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Virtual Hub" 3 | date: 2021-07-07T08:53:46+01:00 4 | chapter: false 5 | weight: 21 6 | --- 7 | 8 | #### Overview 9 | The Virtual WAN builder (`vhub`) is used to create Azure Virtual Hub instances. 10 | 11 | - Virtual Hub (`Microsoft.Network/virtualHubs`) 12 | 13 | #### Builder Keywords 14 | 15 | | Resource | Keyword | Purpose | 16 | | -------------- | -------------------- | -----------------------------------------------------------------------| 17 | | vhub | name | Sets the name of the virtual hub | 18 | | vhub | sku | Sets the sku of the virtual hub | 19 | | vhub | address_prefix | Sets the address prefix of the virtual hub | 20 | | vhub | link_to_vwan | Sets the virtual wan deployed by Farmer to which the virtual hub belongs | 21 | | vhub | link_to_unmanaged_vwan | Sets the existing virtual wan to which the virtual hub belongs | 22 | 23 | ### Example 24 | 25 | ```fsharp 26 | open Farmer 27 | open Farmer.Builders 28 | 29 | let vhub = vhub { 30 | name "my-vhub" 31 | address_prefix (IPAddressCidr.parse "10.0.0.0/24") 32 | } 33 | 34 | let deployment = arm { 35 | location Location.NorthEurope 36 | add_resource vhub 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/content/api-overview/resources/virtual-wan.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Virtual WAN" 3 | date: 2021-05-03T11:22:17-05:00 4 | chapter: false 5 | weight: 21 6 | --- 7 | 8 | #### Overview 9 | 10 | The Virtual WAN builder (`vwan`) is used to create Azure Virtual WAN instances. 11 | 12 | - Virtual WAN (`Microsoft.Network/virtualWans`) 13 | 14 | #### Builder Keywords 15 | 16 | | Resource | Keyword | Purpose | 17 | | -------------- | -------------------- | -----------------------------------------------------------------------| 18 | | vwan | name | Sets the name of the virtual wan | 19 | | vwan | standard_vwan | Sets the virtual wan type to "standard" instead of the default "basic" | 20 | | vwan | allow_branch_to_branch_traffic | Specifies branch to branch traffic is allowed | 21 | | vwan | disable_vpn_encryption | Specifies Vpn encryption is disabled | 22 | | vwan | office_365_local_breakout_category | Sets the office local breakout category | 23 | 24 | 25 | ### Example 26 | 27 | ```fsharp 28 | open Farmer 29 | open Farmer.Builders 30 | 31 | let myVwan = vwan { 32 | name "my-vwan" 33 | disable_vpn_encryption 34 | allow_branch_to_branch_traffic 35 | office_365_local_breakout_category Office365LocalBreakoutCategory.None 36 | standard_vwan 37 | } 38 | let deployment = arm { 39 | location Location.NorthEurope 40 | add_resource myVwan 41 | } 42 | ``` -------------------------------------------------------------------------------- /docs/content/api-overview/template-generation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Generating templates" 3 | date: 2020-02-05T09:13:36+01:00 4 | draft: false 5 | weight: 1 6 | --- 7 | 8 | Farmer supports several ways to "output" ARM templates. 9 | 10 | #### Generating JSON as a string 11 | You can generate an ARM template as a plain string: 12 | 13 | ```fsharp 14 | let json = 15 | deployment.Template 16 | |> Writer.toJson 17 | 18 | // prints out the JSON 19 | printfn "%s" json 20 | ``` 21 | 22 | #### Writing to a file 23 | You can write out the ARM template directly to a file, from which you can then deploy to Azure using whichever mechanism you already use e.g. Azure CLI, Powershell, REST API etc. 24 | 25 | ```fsharp 26 | deployment 27 | |> Writer.quickWrite "myTemplate" 28 | ``` 29 | 30 | Notice how we use F#'s pipe operator to "pipe" data from the template configuration into json before writing to a file. 31 | 32 | #### Integrated deployment to Azure 33 | You can also turn over deployment of the template directly to Farmer. In this case, it orchestrates commands to the Azure CLI as required. 34 | 35 | ```fsharp 36 | let response = 37 | deployment 38 | |> Deploy.tryExecute "myResourceGroup" Deploy.NoParameters 39 | 40 | match response with 41 | | Ok outputs -> printfn "Success! Outputs: %A" outputs 42 | | Error error -> printfn "Failed! %s" error 43 | ``` 44 | 45 | As you can see, the response of calling `tryExecute` is a `Result` object, which is either `Ok`, in which case any outputs returned from the template are made available as a `Map`, or an `Error`, which is the error returned by the Azure CLI. Alternatively, you can call `execute` which will throw an exception rather than return a Result. 46 | 47 | > You must have the Azure CLI installed on your machine in order for Farmer to perform deployments for you. 48 | 49 | #### Authenticating to Azure 50 | Azure CLI stores a login token on your machine, and Farmer will check for this. If you aren't logged in, Farmer will automatically start the interactive Azure CLI login process for you. 51 | 52 | For automated deployments e.g. continuous deployment or through scripts etc., you'll want to use an unattended deployment mode. Some CI systems, such as Azure Devops come with an pre-authenticated Azure CLI terminal from which you can run an application that uses Farmer. Alternatively, you can create a [service principal](../../deployment-guidance#how-do-i-create-a-service-principal), and supply them to the `Deploy.authenticate` function before calling `Deploy.execute`. 53 | 54 | You should use a secure mechanism for storing and supplying the credentials to Farmer. **Do not commit them into source control!** 55 | -------------------------------------------------------------------------------- /docs/content/arm-vs-farmer/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Farmer and ARM" 3 | date: 2020-05-23T18:11:25+02:00 4 | draft: false 5 | weight: 4 6 | --- 7 | 8 | | | Farmer | ARM Template | 9 | |-|-:|:-| 10 | | **Core ARM features** | 11 | | Repeatable deployments? | **Yes**, Farmer runs on top of ARM | **Yes** | 12 | | ARM deployment mechanisms? | **All**, plus easy-to-use F# deployment | **All** | 13 | | Variables support? | **Yes**, native support in F# | **Yes** | 14 | | Parameters support? | **Yes**, native support in F# or secure parameters | **Yes** | 15 | | Supported resources? | **All**, including **custom builders for ~50 popular resources** | **All** | 16 | | Declarative model support? | **Yes** | **Yes** | 17 | | Support for all ARM tools? | **Yes**, Farmer runs on top of ARM | **Yes** | 18 | | Linked Template support? | **No** - generally not required. | **Yes** | 19 | | **Authoring** | 20 | | Easy to author? | **Yes** | **No** | 21 | | Easy to read? | **Yes** | **No** | 22 | | Documented? | **Yes**, website and discoverable intellisense | **Limited**, documented but often out-of-date | 23 | | Editor support? | **Yes**, any F# editor including VS Code, VS and Rider | **Limited**, only VS Code has any support | 24 | | **Safety** | 25 | | Type-safe? | **Yes**, full support from the F# compiler and type system | **Limited** through VS Code extension and LSP | 26 | | Validation support? | **Edit-time, run-time, deploy-time** | **Deploy-time and limited edit-time** | 27 | | **Flexibility** | 28 | | Link resources easily? | **Yes** | **Not easily** complex path expressions must be known | 29 | | Compose resources together? | **Yes** | **Not easily** | 30 | | Create multiple resources simultaneously? | **Yes** | **No**, each resource must be defined separately | 31 | | Create resources in several ways? | **Yes**, builders, records, functions or classes | **No**, must use JSON | 32 | | Full programming language? | **Yes**, F# is a simple yet powerful programming language | **No**, JSON with limited functions | 33 | | Imperative model? | **Yes**, F# supports imperative programming | **No**, you must program in a declarative style | 34 | | **Interop and extensibility** | 35 | | Add your own ARM resources? | **Yes**, plug-in model to add new ARM resources | **N/A** 36 | | Create your own combinations of resources? | **Yes** | **No**, each resource must be defined separately | 37 | | Use external libraries? | **Yes**, use any NuGet packages during authoring and full .NET Core | **No**, fixed set of functions | 38 | | Use in .NET applications? | **Yes**, Farmer is a .NET Core library and can be used in-proc | **No**, JSON files | -------------------------------------------------------------------------------- /docs/content/contributing/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Contributing" 3 | date: 2020-06-15T03:57:42+02:00 4 | draft: false 5 | chapter: false 6 | weight: 8 7 | --- 8 | 9 | Thanks for thinking about contributing! Azure is a giant beast and help supporting more use-cases is always appreciated. To make it easier to contribute, we put together this little guide. Please take a few minutes to read through before starting work on a pull request (PR) to Farmer. 10 | 11 | ### The process (don't worry... this is not waterfall) 12 | 1. Open an issue, or comment on an existing open issue covering the resource you would like to work on. Basically, a PR from you should not come as a surprise. 13 | 1. Implement the 20% of features that cover 80% of the use cases. 14 | 1. PR against the `master` branch from your *fork*. 15 | 1. Add/update tests as required. 16 | 1. Create a new **.md* file with the name of your resource in the folder **/content/api-overview/resources/**. Eg. **container-registry.md** 17 | 1. Add a description, keywords, and an example to the docs page. 18 | 1. PRs need to pass build/test against both Linux & Windows build, and a review, before being merged in. 19 | 20 | ### TODO 21 | There's still more to document! 22 | 23 | * Validation best practices 24 | * Multiple resource builders 25 | * Linking resources (one-to-many relationships) 26 | * Post-deploy tasks -------------------------------------------------------------------------------- /docs/content/contributing/adding-resources/5-unit-testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "5. Unit Testing" 3 | draft: false 4 | chapter: false 5 | weight: 5 6 | --- 7 | 8 | Usually I would be pro writing the tests before you implement all this but it is important to get a feel for the moving parts. At this point you may want to write some tests so you can iterate quickly on getting the structure of your ARM template correct. 9 | 10 | The tests you will find in the project are black-box style tests that focus on the input of a resource and the output of the ARM template. If you want to create tests for your mapping functions that is fine but remember between the strong type system and making it difficult to have `null` values, those kind of tests seldom yield much benefit in F#. 11 | 12 | Of course, unit tests can only tell you so much when dealing with something as complex as Azure. Create a **fsx** file to run to check that your resource is deploying as expected. 13 | 14 | ```fsharp 15 | // container-registry.fsx 16 | #r "Newtonsoft.Json.dll" 17 | #r @"../Farmer/bin/Debug/netstandard2.0/Farmer.dll" 18 | 19 | open Farmer 20 | open Farmer.Resources.ContainerRegistry 21 | 22 | let myRegistry = containerRegistry { 23 | name "devonRegistry" 24 | sku ContainerRegistrySku.Basic 25 | enable_admin_user 26 | } 27 | 28 | let deployment = arm { 29 | location NorthEurope 30 | add_resource myRegistry 31 | output "registry" myRegistry.Name 32 | output "loginServer" myRegistry.LoginServer 33 | } 34 | 35 | deployment 36 | |> Deploy.execute "FarmerTest" Deploy.NoParameters 37 | |> printfn "%A" 38 | ``` 39 | 40 | Create a Resource Group to run it, here I called it "FarmerTest". 41 | 42 | Run `dotnet fsi container-registry.fsx` 43 | -------------------------------------------------------------------------------- /docs/content/contributing/create-pull-requests.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Creating Pull Requests" 3 | draft: false 4 | weight: 5 5 | --- 6 | 7 | > This article is not a detailed guide on how to create a pull request (PR). See [here](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) to learn more about how to work with pull requests on GitHub. 8 | 9 | The purpose of this article is to illustrate the main checklists you must go through before a PR will be considered for inclusion in Farmer. If you are new to Farmer, F# or GitHub - **don't worry**. The team will be happy to support you in getting your feature over the line. 10 | 11 | These are the following checks we'll normally put in place: 12 | 13 | #### 1. Create an issue first! 14 | Except for small pull requests, create an issue to discuss the feature. The last thing we want is for someone to spend hours of their time on a feature only for someone else to have started work on something similar, or for the admins of the project to reject it for whatever reason e.g. does not fit with the project etc. Creating an issue does not take long and will help save time for everyone. 15 | #### 2. Create Documentation 16 | Every PR to Farmer **must** have some documentation with it. If you modify a resource and add a new keyword, it **must** be added to the appropriate docs page. 17 | #### 3. Write Unit Tests 18 | Every PR to Farmer **should** have at least one test associated with it. If no tests are added, you can expect at least a request for one or an explanation as to why one is not necessary. 19 | #### 4. Write Release Notes 20 | Every PR to Farmer **must** include an entry to the `RELEASE_NOTES.md` file under the next release. Briefly explain the feature and ideally link to the PR number e.g. 21 | #### 5. Adhere to Coding Standards 22 | Here are some (very basic!) standards for the project: 23 | 24 | 1. Follow the coding style of the existing source. 25 | 2. Use 4 spaces for indentation. 26 | 3. As a last resort, adhere to [official](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/) style guide as a basis. 27 | -------------------------------------------------------------------------------- /docs/content/contributing/outputs-and-expressions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Outputs and ARM Expressions" 3 | date: 2020-06-19T23:50:54+02:00 4 | draft: false 5 | weight: 2 6 | --- 7 | 8 | Outputs can be created in Farmer for any [ARM Expression](../../api-overview/expressions), Resource Name or any optional string. ARM Expressions are most useful in this case for referring to values that only exist at *deployment time*, such as connection strings. 9 | 10 | ### 11 | 12 | ### Creating ARM Expressions 13 | Farmer ARM expressions are in reality just wrapped strings, and are easy to create. For example, the code to create a Storage Key property is similar to this: 14 | 15 | ```fsharp 16 | let buildKey accountName : ArmExpression = 17 | // Create the raw string of the expression 18 | let rawValue = 19 | $"concat('DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey=', listKeys('{accountName}', '2017-10-01').keys[0].value)" 20 | 21 | // Wrap the raw value in an ARM Expression and return it 22 | ArmExpression.create rawValue 23 | ``` 24 | 25 | Notice that you do *not* wrap the expression in square brackets [ ]; Farmer will do this when writing out the ARM template. 26 | 27 | ### Extracting the value of an ARM Expression 28 | ARM expressions also have the following members on them: 29 | * `Map` - standard map 30 | * `Bind` - standard bind 31 | * `Value` - Returns the raw string value 32 | * `Eval` - Returns the string as a formatted ARM expression i.e. surround in `[]` 33 | -------------------------------------------------------------------------------- /docs/content/images/arm-graph.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/arm-graph.jpg -------------------------------------------------------------------------------- /docs/content/images/cit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/cit.png -------------------------------------------------------------------------------- /docs/content/images/comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/comparison.png -------------------------------------------------------------------------------- /docs/content/images/deploy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/deploy.jpg -------------------------------------------------------------------------------- /docs/content/images/farmer-favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/farmer-favicon.png -------------------------------------------------------------------------------- /docs/content/images/farmer-flow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/farmer-flow.jpg -------------------------------------------------------------------------------- /docs/content/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/logo.png -------------------------------------------------------------------------------- /docs/content/images/tutorials/enterprise1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/tutorials/enterprise1.png -------------------------------------------------------------------------------- /docs/content/images/tutorials/enterprise2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/tutorials/enterprise2.png -------------------------------------------------------------------------------- /docs/content/images/tutorials/imperative-resource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/tutorials/imperative-resource.png -------------------------------------------------------------------------------- /docs/content/images/tutorials/multiple-web-apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/tutorials/multiple-web-apps.png -------------------------------------------------------------------------------- /docs/content/images/tutorials/serverless-etl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/tutorials/serverless-etl.png -------------------------------------------------------------------------------- /docs/content/images/tutorials/webapp-cosmos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/tutorials/webapp-cosmos.png -------------------------------------------------------------------------------- /docs/content/images/tutorials/webapp-keyvault-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/tutorials/webapp-keyvault-connection.png -------------------------------------------------------------------------------- /docs/content/images/tutorials/webapp-keyvault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/tutorials/webapp-keyvault.png -------------------------------------------------------------------------------- /docs/content/images/tutorials/webapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompositionalIT/farmer/3500fa102c18945288db8fa78c55f72c27750f30/docs/content/images/tutorials/webapp.png -------------------------------------------------------------------------------- /docs/content/links/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Links" 3 | date: 2020-11-02 4 | weight: 8 5 | --- 6 | 7 | In this page, you can find blog posts, videos and tweets on Farmer that will give you a better sense of the scenery and the dialogue around it. 8 | 9 | 10 | #### Blog Posts 11 | * [Compositional IT articles on Farmer](https://www.compositional-it.com/news-blog/tag/farmer/) 12 | * [Azure SQL Database deployment with Farmer, DbUp and GitHub Actions](https://www.azurefromthetrenches.com/azure-sql-database-deployment-with-farmer-dbup-and-github-actions/) 13 | * [Introduction to Farmer - IaC with Azure](https://www.svenmalvik.com/azure-first-farmer-project/) 14 | * [Farmer: Simpler ARM deployments with Octopus Deploy](https://octopus.com/blog/farmer-and-octopus-deploy) 15 | 16 | #### Videos 17 | * [Learn how to deploy complete .NET Web Apps to Azure in less than 5 minutes!](https://www.youtube.com/watch?v=5nRZwxMQUFE&feature=emb_title) 18 | * [Working with raw JSON ARM resources with Farmer](https://www.youtube.com/watch?v=a8pWqGqPKGg) 19 | * [Authoring ARM templates the easy way with Farmer](https://www.youtube.com/watch?v=w-tgwwAR8_Y) 20 | * [Making Infrastructure as Code Easier in Azure](https://www.youtube.com/watch?v=8E63s2QlbhA) 21 | * [Introduction to Farmer and Stepping up the game with ARM templates](https://www.youtube.com/watch?v=k0puV8XJ59E) -------------------------------------------------------------------------------- /docs/content/quickstarts/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Quickstarts" 3 | date: 2020-02-04T00:41:03+01:00 4 | weight: 2 5 | --- 6 | 7 | This section contains quickstarts to performing common tasks. 8 | 9 | * [Creating your first Farmer template](quickstart-1) 10 | * [Creating multiple resources](quickstart-2) 11 | * [Deploying to Azure](quickstart-3) 12 | * [The Farmer .NET Template](template) -------------------------------------------------------------------------------- /docs/content/quickstarts/quickstart-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Your first Farmer template" 3 | date: 2020-02-04T00:41:51+01:00 4 | draft: false 5 | weight: 1 6 | --- 7 | 8 | #### Introduction 9 | In this exercise, you'll: 10 | * create a web application with a fully-configured Application Insights instance 11 | * create an ARM deployment object and assign the web app to it 12 | * generate an ARM template 13 | 14 | #### Creating a Farmer app 15 | Create an F# console application using the .NET SDK and add the Farmer package in an empty directory: 16 | 17 | ```cmd 18 | dotnet new console -lang F# 19 | dotnet add package Farmer 20 | ``` 21 | 22 | > Farmer also has a [.NET template](../template/) to get started even more quickly! 23 | 24 | #### Defining a Farmer web application 25 | Open `Program.fs` and delete all the contents. 26 | 27 | > In Farmer, resources are defined using special code blocks that look somewhat json-esque, known as a "builder". In these builders you can quickly and easily configure a resource using special keywords, but unlike json you also have edit-time safety. 28 | 29 | Create a Farmer web application using the `webApp { }` builder: 30 | 31 | ```fsharp 32 | open Farmer 33 | open Farmer.Builders 34 | 35 | let myWebApp = webApp { 36 | name "yourFirstFarmerApp" 37 | } 38 | ``` 39 | 40 | Create an ARM template deployment object, before setting the location for the overall resource group and adding the web app into it. 41 | 42 | ```fsharp 43 | let deployment = arm { 44 | location Location.NorthEurope 45 | add_resource myWebApp 46 | } 47 | ``` 48 | 49 | #### Generating the ARM template 50 | Now you need to generate the ARM template from the deployment object to an ARM json file. 51 | 52 | Add the following code: 53 | 54 | ```fsharp 55 | deployment 56 | |> Writer.quickWrite "myFirstTemplate" 57 | ``` 58 | 59 | Run the application: 60 | 61 | ```cmd 62 | dotnet run 63 | ``` 64 | 65 | You should notice that the file `myFirstTemplate.json` has been created. 66 | 67 | The generated ARM template contains the following resources: 68 | 69 | * A web application 70 | * A server farm 71 | * An application insights instance 72 | 73 | The resources will be correctly configured with the appropriate dependencies set. 74 | 75 | #### The full application 76 | 77 | ```fsharp 78 | open Farmer 79 | open Farmer.Builders 80 | 81 | let myWebApp = webApp { 82 | name "yourFirstFarmerApp" 83 | } 84 | 85 | let deployment = arm { 86 | location Location.NorthEurope 87 | add_resource myWebApp 88 | } 89 | 90 | deployment 91 | |> Writer.quickWrite "myFirstTemplate" 92 | ``` 93 | -------------------------------------------------------------------------------- /docs/content/quickstarts/template.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "The Farmer .NET Template" 3 | date: 2020-02-04T00:41:51+01:00 4 | draft: false 5 | weight: 4 6 | --- 7 | 8 | Farmer comes with a .NET template that makes getting started easy. 9 | 10 | ### Creating a basic Farmer app 11 | The easiest way to create a Farmer app is to use the Farmer .NET Template. 12 | 13 | ```cmd 14 | dotnet new -i Farmer.Template 15 | dotnet new Farmer 16 | ``` 17 | 18 | > You only have to install the template once on your machine! 19 | 20 | This creates a new dotnet application solution and project that looks by default as follows: 21 | 22 | ```fsharp 23 | open Farmer 24 | open Farmer.Builders 25 | 26 | let deployment = arm { 27 | location Location.NorthEurope 28 | } 29 | 30 | printf "Generating ARM template..." 31 | deployment |> Writer.quickWrite "output" 32 | printfn "all done! Template written to output.json" 33 | ``` 34 | 35 | From here, you can add resources in the normal manner. 36 | 37 | ### Basic configuration options 38 | You can configure the template using the following optional arguments. 39 | 40 | #### ARM Template filename 41 | The name of the ARM template JSON file e.g. `--armTemplate myTemplate` 42 | 43 | #### Location 44 | The location to create resources in e.g. `--location WestUS` 45 | 46 | ### Deploy Configuration 47 | You can also configure the Farmer template to deploy to Azure out of the box using the `--ci` option. This has two modes of operation: 48 | 49 | #### Azure DevOps deployment 50 | This comes with a ready-made devops YAML file designed for simple CI/CD, using Farmer to generate ARM templates and Azdo to deploy using its own ARM Template deployment process. You should supply the following arguments: 51 | 52 | * **--ci**: Tells the template to create a Farmer app for use with Azure Devops. 53 | * **--azureSubscription**: Set the full name of the Azure Subscription that has been already configured in Azdo that has permission to deploy templates to Azure. 54 | * **--resourceGroup**: Set the name of the resource group that you wish to deploy to. 55 | 56 | #### Direct deployment 57 | If you prefer a deployment process that is not coupled to Azure Devops, you can create a [service principal](../../deployment-guidance/#how-do-i-create-a-service-principal) in Azure and use the generated credentials in Farmer. Farmer will use its own wrapper around the Azure REST API to deploy to Azure, reporting progress to the console. 58 | -------------------------------------------------------------------------------- /docs/content/support/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Commercial Support" 3 | date: 2020-06-27T17:57:05+02:00 4 | weight: 7 5 | --- 6 | 7 | The creators of Farmer, [Compositional IT](https://compositional-it.com), offer a professional [fully managed support package](https://www.compositional-it.com/consultancy/farmer/) which we strongly recommend for any organisations using Farmer on a commercial basis. It includes: 8 | 9 | * **Prioritised resolution** of any bugs. If you find a bug that's blocking you, we'll prioritise it and release a hot fix as soon as it's ready. 10 | * **Prioritised resolution** and escalation of issues. If there's a possible issue or question, we'll prioritise dealing with it. 11 | * **Prioritised feature requests**: Get new features that are important to you added first. 12 | * **Personalised support** and guidance via email, telephone or video. Speak to one of our team for advice and best practices on how to best manage deployments. 13 | * **Discounts** on our [F# and Azure training and coaching services](https://www.compositional-it.com/training-coaching/) 14 | 15 | #### Please [contact us](mailto:info@compositional-it.com) to find out more! -------------------------------------------------------------------------------- /docs/content/testimonials/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Testimonials" 3 | date: 2020-06-27T17:57:05+02:00 4 | weight: 7 5 | --- 6 | 7 | #### Please submit a pull request [here](https://github.com/CompositionalIT/farmer/blob/master/docs/content/testimonials/_index.md) with details of your success stories of using Farmer! 8 | 9 | > "We've been using Farmer to help rapidly onboard our customers onto Azure with repeatable processes, 10 | > particularly with the SAFE Stack. It's helping our team adopt best practices without passing on 11 | > expense to our customers." 12 | > 13 | > **Isaac Abraham, Director, [Compositional IT](https://compositional-it.com)** 14 | 15 | 16 | > "Farmer quickly became an essential tool for Continuous Deployment at our F# projects. 17 | > Clean DSL, great documentation, growing support of various Azure services and PR-friendly 18 | > approach made Farmer to be one of the best open source projects in these days." 19 | > 20 | > **Roman Provazník, F# Lead Developer, [CN Group](https://cngroup.dk)** 21 | 22 | > "[Holy moly, this was a breeze!](https://twitter.com/Jan_de_V/status/1276250776042692627)! I'm SO going to use this more often, even if it's just to get a baseline for a customer. Saves tons of time! 23 | > 24 | > **[Jan De Vries](https://twitter.com/Jan_de_V), Microsoft MVP** 25 | 26 | > "[Hey @isaac_abraham, #Farmer is kind of awesome](https://twitter.com/janekf/status/1305518187115696129). A few lines and my fav env is created. #ILike" 27 | > 28 | > **[Jan(ek) Fellien](https://twitter.com/janekf), Microsoft MVP** 29 | 30 | > "[Finally took a look at farmer just now](https://twitter.com/Danthar/status/1263406509951721474).... I should have looked months ago. I mean, I can actually read a farmer template. And it makes sense?!" 31 | > 32 | > **[Arjen Smits](https://twitter.com/Danthar)** 33 | 34 | > "With Farmer we were finally able to organize our Azure infrastructure. No messy names of storageaccounts anymore. Thanks for this great tool!" 35 | > 36 | > **[Tim Forkmann](https://twitter.com/tforkmann), Head of EnergyData at Danpower** 37 | 38 | > "Farmer allows us to keep our infrastructure easy to deploy and maintain. If we use Azure, we use it with Farmer! Thanks for creating it! 39 | > 40 | > **[Stefan Hausotte](https://twitter.com/_secana_), CEO of [Bitfalter](https://www.bitfalter.com)** 41 | -------------------------------------------------------------------------------- /docs/content/tutorials/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tutorials" 3 | date: 2020-10-24 4 | weight: 3 5 | --- 6 | 7 | This section contains tutorials for specific use-cases. Use these to help get ideas for how to model your Farmer resources! -------------------------------------------------------------------------------- /docs/content/tutorials/custom-output.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Custom Output with ARM Expressions" 3 | date: 2021-04-23 4 | draft: false 5 | --- 6 | 7 | #### Introduction 8 | 9 | Many resources have properties that are only set once the resource is created, such as a public IP's address or an ExpressRoute's circuit service key. It is often helpful to have these as output from the deployment so they are available to any downstream automation tasks. 10 | 11 | In this tutorial, you will deploy an ExpressRoute circuit, create a reference to the `serviceKey` property on the newly deployed circuit, and provide that as the ARM deployment output. 12 | 13 | #### Define the ExpressRoute circuit to deploy 14 | 15 | An ExpressRoute circuit provides direct connectivity into Azure over a telecommunication provider's network rather than traversing the Internet or a VPN. Once the circuit is created, the typical flow is to take the circuit's service key to the telecommunications provider so they can enable it for your business connectivity. 16 | 17 | ```fsharp 18 | open Farmer 19 | open Farmer.Builders 20 | 21 | let er = expressRoute { 22 | name "my-test-circuit" 23 | service_provider "Equinix" 24 | peering_location "Frankfurt" 25 | } 26 | ``` 27 | 28 | #### Reference the `serviceKey` Property 29 | 30 | ARM templates support expressions that are evaluated when the template is executed by ARM. These have many different capabilities, but in this case, we want to reference a newly deployed resource - the ExpressRoute circuit. 31 | 32 | First, you can use the type and name of the resource to create a `ResourceId`. Then, that `ResourceId` can be used to build a `reference` expression and retrieve a property of the resource. 33 | 34 | ```fsharp 35 | // Build an ARM resourceId type for the circuit. 36 | let erId = ResourceId.create(Arm.Network.expressRouteCircuits, er.Name) 37 | 38 | // Use that ID to build a reference expression and get a property of the referenced resource. 39 | let serviceKeyRef = ArmExpression.create ($"reference({erId.ArmExpression.Value}).serviceKey") 40 | ``` 41 | 42 | #### Adding the Deployment Output 43 | 44 | One or more outputs can be added to an `arm` computation expression to generate outputs from the deployment. An output is created using the name for the output an `ArmExpression`, such as `serviceKeyRef` created above. 45 | 46 | ```fsharp 47 | arm { 48 | location Location.WestEurope 49 | add_resource er 50 | output "er-service-key" serviceKeyRef 51 | } |> Writer.quickWrite "custom-output" 52 | ``` 53 | 54 | This results in a template with an output named "er-service-key" that will contain the value of the `serviceKey` property on the newly deployed ExpressRoute circuit. 55 | -------------------------------------------------------------------------------- /docs/content/tutorials/webapp-deploy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Deploy an ASP.NET app" 3 | date: 2020-10-24 4 | draft: false 5 | weight: 5 6 | --- 7 | 8 | #### Introduction 9 | This tutorial shows how to create the infrastructure required to host a ASP.NET web app, and how to automatically deploy that application with Farmer. We'll cover the following steps: 10 | 11 | 1. Creating and configuring a basic ASP.NET web application. 12 | 1. Creating a web app in Farmer. 13 | 1. Deploying the web app through Farmer. 14 | 15 | {{< figure src="../../images/tutorials/webapp.png" caption="[Full code available here](https://github.com/CompositionalIT/farmer/blob/master/samples/scripts/tutorials/webapp.fsx)">}} 16 | 17 | > Note: Your web application can be a C# web application - it does not need to be written in F#! 18 | 19 | #### Creating the ASP.NET web application 20 | Create a brand new ASP.NET web application: 21 | 22 | 1. Create a directory for your new application and enter it. 23 | 2. Using the dotnet SDK, create a new application: `dotnet new mvc`. 24 | 3. Notice that inside the project file (either `csproj` or `fsproj`), the Project SDK is already set to `Microsoft.NET.Sdk.Web`. This is more-or-less required for hosting in Azure. 25 | 4. Locally publish the application to a directory called `deploy`: `dotnet publish -c Release -o deploy`. 26 | 27 | > dotnet publish puts all built files and outputs into a single folder, and adds a `web.config` as required for e.g. Azure, as long as your Project SDK is set correctly. 28 | 29 | #### Create the Web App 30 | Create a new Farmer application which contains a web app. 31 | 32 | ```fsharp 33 | open Farmer 34 | open Farmer.Builders 35 | 36 | let webapplication = webApp { 37 | name "" 38 | } 39 | ``` 40 | 41 | #### Configure Web App to deploy your ASP.NET application 42 | ```fsharp 43 | let webapplication = webApp { 44 | ... 45 | zip_deploy @"" 46 | } 47 | ``` 48 | 49 | #### That's it! 50 | Deploy the web app by adding it to an ARM builder and deploy it to a resource group of your choosing. During the deployment process, you will notice the following: 51 | 52 | `Running ZIP deploy for ` 53 | 54 | > Farmer will automatically zip up the contents of the folder for you. 55 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.206", 4 | "rollForward": "latestMinor" 5 | } 6 | } -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | This PR closes # 2 | 3 | The changes in this PR are as follows: 4 | 5 | * ... 6 | * ... 7 | * ... 8 | 9 | I have read the [contributing guidelines](CONTRIBUTING.md) and have completed the following: 10 | 11 | * [ ] **Tested my code** end-to-end against a live Azure subscription. 12 | * [ ] **Updated the documentation** in the docs folder for the affected changes. 13 | * [ ] **Written unit tests** against the modified code that I have made. 14 | * [ ] **Updated the [release notes](RELEASE_NOTES.md)** with a new entry for this PR. 15 | * [ ] **Checked the coding standards** outlined in the [contributions guide](CONTRIBUTING.md) and ensured my code adheres to them. 16 | 17 | If I haven't completed any of the tasks above, I include the reasons why here: 18 | 19 | Below is a minimal example configuration that includes the new features, which can be used to deploy to Azure: 20 | 21 | ```fsharp 22 | ``` 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![](Logo.png) 2 | 3 | ## Farmer makes repeatable Azure deployments easy! 4 | 5 | See the full docs [here](https://compositionalit.github.io/farmer). 6 | 7 | Want to edit the docs? Check out the [docs folder](https://github.com/CompositionalIT/farmer/tree/master/docs). 8 | 9 | [![Build Status](https://compositional-it.visualstudio.com/Farmer/_apis/build/status/CompositionalIT.farmer?branchName=master)](https://compositional-it.visualstudio.com/Farmer/_build/latest?definitionId=14&branchName=master) 10 | 11 | [![Farmer on Nuget](https://img.shields.io/nuget/dt/Farmer?label=NuGet%20Downloads)](https://www.nuget.org/packages/farmer/) 12 | -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | farmer-deploy.json -------------------------------------------------------------------------------- /samples/SampleApp/Program.fs: -------------------------------------------------------------------------------- 1 | open Farmer 2 | open Farmer.Builders 3 | 4 | //TODO: Create resources here! 5 | 6 | let deployment = arm { 7 | location Location.NorthEurope 8 | 9 | //TODO: Assign resources here using the add_resource keyword 10 | } 11 | 12 | // Generate the ARM template here... 13 | deployment |> Writer.quickWrite @"generated-template" 14 | 15 | // Or deploy it directly to Azure here... (required Azure CLI installed!) 16 | // deployment 17 | // |> Deploy.execute "my-resource-group" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/SampleApp/SampleApp.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /samples/scripts/aks.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open System 4 | open System.IO 5 | open Farmer 6 | open Farmer.Builders 7 | open Farmer.ContainerService 8 | open Farmer.Vm 9 | 10 | let homeDir = Environment.GetFolderPath Environment.SpecialFolder.UserProfile 11 | 12 | let pubKey = 13 | [ homeDir; ".ssh"; "id_rsa.pub" ] 14 | |> String.concat (string Path.DirectorySeparatorChar) 15 | |> File.ReadAllText 16 | 17 | let aksSubnet = "containernet" 18 | 19 | let vnetName = sprintf "env%0i-vnet" 20 | let aksName = sprintf "env%0i-aks" 21 | let aksDns = aksName 22 | 23 | let makeVnet (n: int) = 24 | vnet { 25 | name (vnetName n) 26 | add_address_spaces [ "10.1.0.0/16" ] 27 | 28 | add_subnets [ 29 | subnet { 30 | name "default" 31 | prefix "10.1.0.0/24" 32 | } 33 | subnet { 34 | name aksSubnet 35 | prefix "10.1.30.0/25" 36 | } 37 | ] 38 | } 39 | :> IBuilder 40 | 41 | let msi = userAssignedIdentity { name "aks-user" } 42 | 43 | let makeAks (n: int) = 44 | aks { 45 | name (aksName n) 46 | tier Tier.Standard 47 | dns_prefix (aksDns n) 48 | enable_rbac 49 | add_identity msi 50 | 51 | add_agent_pools [ 52 | agentPool { 53 | name "linuxPool" 54 | vm_size VMSize.Standard_D2_v5 55 | count 3 56 | vnet (vnetName n) 57 | subnet aksSubnet 58 | } 59 | ] 60 | 61 | network_profile (azureCniNetworkProfile { service_cidr "10.250.0.0/16" }) 62 | linux_profile "aksuser" pubKey 63 | service_principal_use_msi 64 | } 65 | :> IBuilder 66 | 67 | let vnets = [ 1..4 ] |> Seq.map makeVnet |> List.ofSeq 68 | let akses = [ 1..4 ] |> Seq.map makeAks |> List.ofSeq 69 | 70 | arm { 71 | add_resource msi 72 | add_resources akses 73 | add_resources vnets 74 | } 75 | |> Writer.quickWrite "aks-on-vnet" 76 | -------------------------------------------------------------------------------- /samples/scripts/appinsights-loganalytics.fsx: -------------------------------------------------------------------------------- 1 | #r @"nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let workspace = logAnalytics { name "loganalytics-workspace" } 7 | 8 | let myAppInsights = appInsights { 9 | name "appInsights" 10 | log_analytics_workspace workspace 11 | } 12 | 13 | let myFunctions = functions { 14 | name "functions-app" 15 | link_to_app_insights myAppInsights.Name 16 | } 17 | 18 | let template = arm { 19 | location Location.NorthEurope 20 | add_resources [ workspace; myAppInsights; myFunctions ] 21 | } 22 | 23 | template |> Deploy.execute "deleteme" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/appinsights.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let myAppInsights = appInsights { name "isaacsAi" } 7 | 8 | let myFunctions = functions { 9 | name "mysuperwebapp" 10 | link_to_app_insights myAppInsights.Name 11 | } 12 | 13 | let template = arm { 14 | location Location.NorthEurope 15 | add_resource myAppInsights 16 | add_resource myFunctions 17 | } 18 | 19 | template |> Deploy.execute "deleteme" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/bastion.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | arm { 7 | location Location.EastUS 8 | 9 | add_resources [ 10 | vnet { 11 | name "private-network" 12 | add_address_spaces [ "10.1.0.0/16" ] 13 | 14 | add_subnets [ 15 | subnet { 16 | name "default" 17 | prefix "10.1.0.0/24" 18 | } 19 | subnet { 20 | name "AzureBastionSubnet" 21 | prefix "10.1.250.0/27" 22 | } 23 | ] 24 | } 25 | bastion { 26 | name "my-bastion-host" 27 | vnet "private-network" 28 | } 29 | ] 30 | } 31 | |> Writer.quickWrite "bastion" -------------------------------------------------------------------------------- /samples/scripts/cdn.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let isaacWebApp = webApp { 7 | name "isaacsuperweb" 8 | app_insights_off 9 | } 10 | 11 | let isaacStorage = storageAccount { name "isaacsuperstore" } 12 | 13 | let isaacCdn = cdn { 14 | name "isaacsupercdn" 15 | 16 | add_endpoints [ 17 | endpoint { 18 | origin isaacStorage 19 | optimise_for Cdn.OptimizationType.LargeFileDownload 20 | } 21 | endpoint { 22 | origin isaacWebApp 23 | disable_http 24 | } 25 | endpoint { 26 | name "custom-endpoint-name" 27 | origin "mysite.com" 28 | add_compressed_content [ "text/plain"; "text/html"; "text/css" ] 29 | query_string_caching_behaviour Cdn.BypassCaching 30 | } 31 | ] 32 | } 33 | 34 | let deployment = arm { add_resources [ isaacStorage; isaacCdn; isaacWebApp ] } 35 | 36 | deployment |> Writer.quickWrite "generated-template" -------------------------------------------------------------------------------- /samples/scripts/container-app.fsx: -------------------------------------------------------------------------------- 1 | #r @"../../src/Farmer/bin/Debug/netstandard2.0/Farmer.dll" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.ContainerApp 6 | open System 7 | 8 | let queueName = "myqueue" 9 | let storageName = $"{Guid.NewGuid().ToString().[0..5]}containerqueue" 10 | 11 | let myStorageAccount = storageAccount { 12 | name storageName 13 | add_queue queueName 14 | add_file_share "certs" 15 | } 16 | 17 | let env = containerEnvironment { 18 | name $"containerenv{Guid.NewGuid().ToString().[0..5]}" 19 | 20 | add_containers [ 21 | containerApp { 22 | name "aspnetsample" 23 | add_simple_container "mcr.microsoft.com/dotnet/samples" "aspnetapp" 24 | ingress_target_port 80us 25 | ingress_transport Auto 26 | add_http_scale_rule "http-scaler" { ConcurrentRequests = 10 } 27 | add_cpu_scale_rule "cpu-scaler" { Utilization = 50 } 28 | } 29 | containerApp { 30 | name "queuereaderapp" 31 | 32 | add_volumes [ 33 | Volume.emptyDir "empty-v" 34 | Volume.azureFile "certs-v" (ResourceName "certs") myStorageAccount.Name StorageAccessMode.ReadOnly 35 | ] 36 | 37 | add_containers [ 38 | container { 39 | name "queuereaderapp" 40 | public_docker_image "mcr.microsoft.com/azuredocs/containerapps-queuereader" "latest" 41 | cpu_cores 0.25 42 | memory 0.5 43 | ephemeral_storage 1. 44 | add_volume_mounts [ "empty-v", "/tmp"; "certs-v", "/certs" ] 45 | } 46 | ] 47 | 48 | replicas 1 10 49 | add_env_variable "QueueName" queueName 50 | add_secret_expression "queueconnectionstring" myStorageAccount.Key 51 | } 52 | ] 53 | } 54 | 55 | let template = arm { 56 | location Location.NorthEurope 57 | add_resources [ env; myStorageAccount ] 58 | } 59 | 60 | template |> Deploy.execute "containerappsdemo" [] -------------------------------------------------------------------------------- /samples/scripts/container-group.fsx: -------------------------------------------------------------------------------- 1 | #r @"C:\Users\isaac\code\farmer\src\Farmer\bin\Debug\net5.0\Farmer.dll" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.ContainerGroup 6 | 7 | let nginx = containerInstance { 8 | name "nginx" 9 | image "nginx:1.17.6-alpine" 10 | add_ports PublicPort [ 80us; 443us ] 11 | add_ports InternalPort [ 9090us ] 12 | memory 0.5 13 | cpu_cores 1 14 | } 15 | 16 | let profile = networkProfile { 17 | name "netprofile" 18 | vnet "containernet" 19 | subnet "ContainerSubnet" 20 | } 21 | 22 | let g = containerGroup { 23 | name "appWithHttpFrontend" 24 | operating_system Linux 25 | restart_policy AlwaysRestart 26 | add_udp_port 123us 27 | add_instances [ nginx ] 28 | network_profile profile 29 | } -------------------------------------------------------------------------------- /samples/scripts/container-instance-gpu.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.ContainerGroup 6 | 7 | let template = arm { 8 | location Location.WestEurope 9 | 10 | add_resources [ 11 | containerGroup { 12 | name "container-group-with-gpu" 13 | operating_system Linux 14 | restart_policy ContainerGroup.RestartOnFailure 15 | 16 | add_instances [ 17 | containerInstance { 18 | name "gpucontainer" 19 | image "mcr.microsoft.com/azuredocs/samples-tf-mnist-demo:gpu" 20 | memory 12.0 21 | cpu_cores 4.0 22 | 23 | gpu ( 24 | containerInstanceGpu { 25 | count 1 26 | sku Gpu.V100 27 | } 28 | ) 29 | } 30 | ] 31 | } 32 | ] 33 | } 34 | 35 | Writer.quickWrite "container-instance" template -------------------------------------------------------------------------------- /samples/scripts/container-instance.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let containerGroupUser = userAssignedIdentity { name "aciUser" } 7 | 8 | let template = arm { 9 | location Location.WestEurope 10 | 11 | add_resources [ 12 | containerGroupUser 13 | containerGroup { 14 | name "container-group-with-init" 15 | operating_system Linux 16 | restart_policy ContainerGroup.AlwaysRestart 17 | add_volumes [ volume_mount.empty_dir "html" ] 18 | 19 | add_init_containers [ 20 | initContainer { 21 | name "init-stuff" 22 | image "debian" 23 | add_volume_mount "html" "/usr/share/nginx/html" 24 | 25 | command_line [ 26 | "/bin/sh" 27 | "-c" 28 | "mkdir -p /usr/share/nginx/html && echo 'hello there' >> /usr/share/nginx/html/index.html" 29 | ] 30 | } 31 | ] 32 | 33 | add_identity containerGroupUser 34 | 35 | add_instances [ 36 | containerInstance { 37 | name "nginx" 38 | image "nginx:alpine" 39 | add_volume_mount "html" "/usr/share/nginx/html" 40 | 41 | add_public_ports [ 80us; 443us ] 42 | add_internal_ports [ 123us ] 43 | 44 | memory 0.5 45 | cpu_cores 0.2 46 | 47 | probes [ 48 | liveness { 49 | http "http://localhost:80/index.html" 50 | initial_delay_seconds 15 51 | } 52 | ] 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | 59 | Writer.quickWrite "container-instance" template -------------------------------------------------------------------------------- /samples/scripts/container-registry.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.ContainerRegistry 6 | 7 | let myRegistry = containerRegistry { 8 | name "devonRegistry" 9 | sku Basic 10 | enable_admin_user 11 | } 12 | 13 | let deployment = arm { 14 | location Location.NorthEurope 15 | add_resource myRegistry 16 | output "registry" myRegistry.Name 17 | output "loginServer" myRegistry.LoginServer 18 | output "user" myRegistry.Username 19 | output "pwd" myRegistry.Password 20 | output "pwd2" myRegistry.Password2 21 | } 22 | 23 | deployment |> Deploy.whatIf "FarmerTest" Deploy.NoParameters |> printfn "%A" -------------------------------------------------------------------------------- /samples/scripts/cosmos.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.CosmosDb 6 | 7 | let myCosmosDb = cosmosDb { 8 | name "isaacsappdb" 9 | account_name "isaacscosmosdb" 10 | throughput 400 11 | failover_policy NoFailover 12 | consistency_policy (BoundedStaleness(500, 1000)) 13 | 14 | add_containers [ 15 | cosmosContainer { 16 | name "myContainer" 17 | partition_key [ "/id" ] Hash 18 | add_index "/path" [ Number, Hash ] 19 | exclude_path "/excluded/*" 20 | } 21 | ] 22 | } 23 | 24 | let deployment = arm { 25 | location Location.NorthEurope 26 | add_resource myCosmosDb 27 | } 28 | 29 | deployment |> Deploy.execute "my-resource-group-name" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/data-lake.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let myLake = dataLake { 7 | name "isaacsLake" 8 | enable_encryption 9 | sku DataLake.Commitment_10TB 10 | } 11 | 12 | let deployment = arm { 13 | location Location.NorthEurope 14 | add_resource myLake 15 | } 16 | 17 | deployment |> Deploy.execute "my-resource-group-name" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/databricks.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open System 6 | 7 | let myVault = keyVault { name "my-vault" } 8 | 9 | let workspace = databricks { 10 | name "isaac-databricks" 11 | encrypt_with_key_vault myVault "workspace-encryption-key" 12 | key_vault_key_version Guid.Empty 13 | attach_to_vnet "databricks-vnet" "databricks-pub-snet" "databricks-priv-snet" 14 | } 15 | 16 | let deployment = arm { 17 | location Location.NorthEurope 18 | add_resource workspace 19 | } 20 | 21 | // Generate the ARM template here... 22 | deployment |> Deploy.execute "my-resource-group" [] -------------------------------------------------------------------------------- /samples/scripts/deployment-script.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let createFileScript = deploymentScript { 7 | name "custom-deploy-steps" 8 | force_update 9 | retention_interval 3 10 | env_vars [ EnvVar.createSecureParameter "foo" "secret-foo" ] 11 | supporting_script_uris [] 12 | /// Set the script content directly 13 | /// Format output as JSON and pipe to $AZ_SCRIPTS_OUTPUT_PATH to make it available as output. 14 | script_content """printf "{'date':'%s'"} "`date`" > $AZ_SCRIPTS_OUTPUT_PATH """ 15 | } 16 | 17 | let template = arm { 18 | add_resource createFileScript 19 | output "date" createFileScript.Outputs.["date"] 20 | } 21 | 22 | template |> Writer.quickWrite "dep-script" -------------------------------------------------------------------------------- /samples/scripts/diagnosticsetting.fsx: -------------------------------------------------------------------------------- 1 | #r "./libs/Newtonsoft.Json.dll" 2 | #r "../../src/Farmer/bin/Debug/netstandard2.0/Farmer.dll" 3 | 4 | open Farmer 5 | open Farmer.Builders 6 | open Farmer.DiagnosticSettings 7 | 8 | let data = storageAccount { name "isaacsuperdata" } 9 | let hub = eventHub { name "isaacsuperhub" } 10 | let logs = logAnalytics { name "isaacsuperlogs" } 11 | 12 | let web = webApp { 13 | name "isaacdiagsuperweb" 14 | app_insights_off 15 | } 16 | 17 | let mydiagnosticSetting = diagnosticSettings { 18 | name "myDiagnosticSetting" 19 | metrics_source web 20 | 21 | add_destination data 22 | add_destination logs 23 | add_destination hub 24 | loganalytics_output_type Dedicated 25 | capture_metrics [ "AllMetrics" ] 26 | 27 | capture_logs [ 28 | Logging.Web.Sites.AppServicePlatformLogs 29 | Logging.Web.Sites.AppServiceAntivirusScanAuditLogs 30 | Logging.Web.Sites.AppServiceAppLogs 31 | Logging.Web.Sites.AppServiceHTTPLogs 32 | ] 33 | 34 | add_tag "sample" "isaac" 35 | } 36 | 37 | let deployment = arm { add_resources [ data; web; hub; logs; mydiagnosticSetting ] } 38 | 39 | deployment |> Writer.quickWrite "diagnostics" 40 | 41 | deployment |> Deploy.execute "isaacdiagtest" [] -------------------------------------------------------------------------------- /samples/scripts/dns.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let dns = dnsZone { 7 | name "farmer.com" 8 | zone_type Dns.Public 9 | 10 | add_records [ 11 | cnameRecord { 12 | name "www2" 13 | ttl 3600 14 | cname "farmer.github.com" 15 | } 16 | aRecord { 17 | name "aName" 18 | ttl 7200 19 | add_ipv4_addresses [ "192.168.0.1"; "192.168.0.2" ] 20 | } 21 | aaaaRecord { 22 | name "aaaaName" 23 | ttl 7200 24 | add_ipv6_addresses [ "2001:0db8:85a3:0000:0000:8a2e:0370:7334" ] 25 | } 26 | txtRecord { 27 | name "txtName" 28 | ttl 3600 29 | add_values [ "v=spf1 include:spf.protection.outlook.com -all" ] 30 | } 31 | mxRecord { 32 | name "mxName" 33 | ttl 7200 34 | 35 | add_values [ 36 | 0, "farmer-com.mail.protection.outlook.com" 37 | 1, "farmer2-com.mail.protection.outlook.com" 38 | ] 39 | } 40 | nsRecord { 41 | name "nsRecord" 42 | ttl 172800 43 | add_nsd_names [ "my.other.dns.com." ] 44 | } 45 | soaRecord { 46 | host "ns1-09.azure-dns.com." 47 | ttl 3600 48 | email "test.microsoft.com" 49 | serial_number 1L 50 | minimum_ttl 2L 51 | refresh_time 3L 52 | retry_time 4L 53 | expire_time 5L 54 | } 55 | srvRecord { 56 | name "_sip._tcp.name" 57 | ttl 3600 58 | 59 | add_values [ 60 | { 61 | Priority = Some 100 62 | Weight = Some 1 63 | Port = Some 5061 64 | Target = Some "farmer.online.com." 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | 71 | let deployment = arm { 72 | location Location.NorthEurope 73 | add_resource dns 74 | } 75 | 76 | deployment |> Writer.quickWrite "dns-example" -------------------------------------------------------------------------------- /samples/scripts/eventgrid-fn.fsx: -------------------------------------------------------------------------------- 1 | #r "../../src/Farmer/bin/Debug/netstandard2.0/Farmer.dll" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | /// Send events to this function that was deployed separately 7 | let fnRef = 8 | { 9 | Arm.Web.siteFunctions.resourceId (ResourceName "gridFnApp", ResourceName "eventHandler") with 10 | ResourceGroup = Some "fn-rg" 11 | } 12 | |> Unmanaged 13 | 14 | /// The source will default to the resourceGroup() and event grid target will be the function handler. 15 | let grid = eventGrid { 16 | topic_name "src-rg-events" 17 | 18 | add_function_subscriber 19 | fnRef 20 | { 21 | MaxEventsPerBatch = 1u 22 | PreferredBatchSizeInKilobytes = 64u 23 | } 24 | [ 25 | SystemEvents.Resources.ResourceWriteSuccess 26 | SystemEvents.Resources.ResourceActionSuccess 27 | ] 28 | } 29 | 30 | // deploy into the resource group that we want to be the source of events 31 | let deployment = arm { add_resources [ grid ] } 32 | 33 | // Generate the ARM template here... 34 | deployment |> Writer.quickWrite "farmer-deploy" -------------------------------------------------------------------------------- /samples/scripts/eventgrid.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | /// Monitor this storage account 7 | let storageSource = storageAccount { 8 | name "isaacstorageacc" 9 | add_private_container "data" 10 | } 11 | 12 | /// Send events to this event hub 13 | let destinationHub = eventHub { 14 | name "isaachub" 15 | namespace_name "isaacns" 16 | } 17 | 18 | /// Tie them together using event grid. 19 | let eventHubGrid = eventGrid { 20 | topic_name "isaacHubTopic" 21 | source storageSource 22 | add_eventhub_subscriber destinationHub [ SystemEvents.Storage.BlobCreated; SystemEvents.Storage.BlobDeleted ] 23 | } 24 | 25 | let deployment = arm { add_resources [ storageSource; eventHubGrid; destinationHub ] } 26 | 27 | // Generate the ARM template here... 28 | deployment |> Writer.quickWrite "farmer-deploy" -------------------------------------------------------------------------------- /samples/scripts/eventhubs.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.EventHub 6 | 7 | let myEh = eventHub { 8 | name "first-hub" 9 | namespace_name "allmyevents" 10 | 11 | sku Standard 12 | enable_zone_redundant 13 | enable_auto_inflate 3 14 | add_authorization_rule "FirstRule" [ Listen; Send ] 15 | add_authorization_rule "SecondRule" AllAuthorizationRights 16 | 17 | partitions 2 18 | message_retention_days 3 19 | add_consumer_group "myGroup" 20 | } 21 | 22 | let secondHub = eventHub { 23 | name "second-hub" 24 | link_to_namespace "allmyevents" 25 | partitions 1 26 | message_retention_days 1 27 | } 28 | 29 | let deployment = arm { 30 | location Location.NorthEurope 31 | add_resource myEh 32 | add_resource secondHub 33 | } 34 | 35 | // Generate the ARM template here... 36 | deployment |> Writer.quickWrite "farmer-deploy" -------------------------------------------------------------------------------- /samples/scripts/functions.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let myFunctions = functions { name "isaacsuperfun" } 7 | 8 | let deployment = arm { 9 | location Location.NorthEurope 10 | add_resource myFunctions 11 | output "functionsPassword" myFunctions.PublishingPassword 12 | output "functionsAIKey" (myFunctions.AppInsightsKey |> Option.defaultValue ArmExpression.Empty) 13 | output "storageAccountKey" myFunctions.StorageAccountKey 14 | } 15 | 16 | deployment |> Deploy.execute "my-resource-group-name" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/iot.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let hub = iotHub { 7 | name "isaacsuperhub" 8 | sku IotHub.B1 9 | capacity 2 10 | partition_count 2 11 | retention_days 3 12 | enable_device_provisioning 13 | } 14 | 15 | let deployment = arm { 16 | location Location.NorthEurope 17 | add_resource hub 18 | output "iot_key" (hub.GetKey IotHub.IotHubOwner) 19 | output "iot_connection" (hub.GetConnectionString IotHub.RegistryReadWrite) 20 | } 21 | 22 | deployment |> Writer.quickWrite "generated-template" -------------------------------------------------------------------------------- /samples/scripts/keyvault-keys.fsx: -------------------------------------------------------------------------------- 1 | #r @"../../src/Farmer/bin/Debug/net5.0/Farmer.dll" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.KeyVault 6 | 7 | let vault = keyVault { 8 | name "TestFarmVault" 9 | sku Sku.Standard 10 | tenant_id Subscription.TenantId 11 | add_tag "test" "test" 12 | 13 | add_keys [ 14 | key { 15 | name "testKeyInline1" 16 | key_type KeyType.RSA_4096 17 | } 18 | key { 19 | name "testKeyInline2" 20 | key_type KeyType.EC_P256 21 | } 22 | ] 23 | } 24 | 25 | 26 | let myKey = key { 27 | name "testKey3" 28 | key_type KeyType.RSA_4096 29 | link_to_unmanaged_keyvault vault 30 | key_operations [ KeyOperation.Encrypt ] 31 | } 32 | 33 | let deployment = arm { 34 | location Location.EastUS 35 | add_resource vault 36 | add_resource myKey 37 | } 38 | 39 | deployment 40 | |> Writer.quickWrite (System.IO.Path.GetFileNameWithoutExtension __SOURCE_FILE__) -------------------------------------------------------------------------------- /samples/scripts/keyvault.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.KeyVault 6 | open System 7 | 8 | let policy = accessPolicy { 9 | object_id Guid.Empty 10 | certificate_permissions [ Certificate.List ] 11 | secret_permissions Secret.All 12 | key_permissions [ Key.List ] 13 | } 14 | 15 | let complexSecret = secret { 16 | name "myComplexSecret" 17 | content_type "application/text" 18 | enable_secret 19 | activation_date (DateTime.Today.AddDays -1.) 20 | expiration_date (DateTime.Today.AddDays 1.) 21 | } 22 | 23 | let store = storageAccount { name "foo" } 24 | let principal = PrincipalId(ArmExpression.create "GETS BACK OBJECT ID OF ACCOUNT") 25 | 26 | let vault = keyVault { 27 | name "MyVault" 28 | sku Sku.Standard 29 | tenant_id Subscription.TenantId 30 | 31 | enable_disk_encryption_access 32 | enable_resource_manager_access 33 | enable_soft_delete_with_purge_protection 34 | 35 | add_access_policy policy 36 | 37 | add_access_policies [ 38 | AccessPolicy.create principal 39 | AccessPolicy.create (principal, Secret.All) 40 | accessPolicy { 41 | object_id principal 42 | secret_permissions [ Secret.Get; Secret.Delete ] 43 | } 44 | ] 45 | 46 | disable_vm_access 47 | enable_recovery_mode 48 | enable_azure_services_bypass 49 | 50 | allow_default_traffic 51 | 52 | add_secret complexSecret 53 | add_secret "simpleSecret" 54 | add_secrets [ "firstSecret"; "secondSecret" ] 55 | add_secret ("thirdSecret", store.Key) 56 | add_tag "test" "test" 57 | } 58 | 59 | vault.Policies 60 | 61 | let deployment = arm { 62 | location Location.NorthEurope 63 | add_resource vault 64 | output "vault_uri" vault.VaultUri 65 | } 66 | 67 | deployment 68 | |> Writer.quickWrite (System.IO.Path.GetFileNameWithoutExtension __SOURCE_FILE__) -------------------------------------------------------------------------------- /samples/scripts/loganalytics.fsx: -------------------------------------------------------------------------------- 1 | #r "./libs/Newtonsoft.Json.dll" 2 | #r "../../src/Farmer/bin/Debug/netstandard2.0/Farmer.dll" 3 | 4 | open Farmer 5 | open Farmer.Builders 6 | 7 | let myAnalytics = logAnalytics { 8 | name "isaacla" 9 | retention_period 50 10 | enable_ingestion 11 | enable_query 12 | daily_cap 5 13 | } 14 | 15 | let deployment = arm { 16 | location Location.WestEurope 17 | add_resource myAnalytics 18 | } 19 | 20 | deployment 21 | |> Deploy.execute "test-resource-group" Deploy.NoParameters 22 | |> printfn "%A" -------------------------------------------------------------------------------- /samples/scripts/logicapp.fsx: -------------------------------------------------------------------------------- 1 | #r "../../src/Farmer/bin/Debug/netstandard2.0/Farmer.dll" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.Arm.LogicApps 6 | 7 | let emptyLogicApp = 8 | """ 9 | { 10 | "definition": { 11 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 12 | "actions": {}, 13 | "contentVersion": "1.0.0.0", 14 | "outputs": {}, 15 | "parameters": {}, 16 | "triggers": {} 17 | }, 18 | "parameters": {} 19 | } 20 | """ 21 | 22 | let myValueLogicApp = logicApp { 23 | name "value-test-logic-app" 24 | definition (ValueDefinition emptyLogicApp) 25 | add_tags [ ("environment", "dev"); ("created-by", "farmer") ] 26 | } 27 | 28 | let deployment = arm { 29 | location Location.CentralUS 30 | add_resource myValueLogicApp 31 | } 32 | 33 | deployment |> Writer.quickWrite "logicApp" -------------------------------------------------------------------------------- /samples/scripts/nested-resourcegroups.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget: Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let myStorage = storageAccount { name "myfarmerstorage" } 7 | 8 | let myVm = vm { 9 | name "farmer-test-vm" 10 | username "codat" 11 | } 12 | 13 | let nested = resourceGroup { 14 | name "farmer-test-inner" 15 | add_resource myStorage 16 | add_resource myVm 17 | output "foo" "bax" 18 | } 19 | 20 | let template = arm { 21 | location Location.UKSouth 22 | add_resource nested 23 | } 24 | 25 | template |> Writer.quickWrite "template" 26 | 27 | template 28 | |> Deploy.execute "farmer-test-rg" [ ("password-for-farmer-test-vm", "Codat121!") ] -------------------------------------------------------------------------------- /samples/scripts/operations-management.fsx: -------------------------------------------------------------------------------- 1 | #r "../../src/Farmer/bin/Debug/netstandard2.0/Farmer.dll" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let sentinelWorkspace = logAnalytics { 7 | name "my-sentinel-workspace" 8 | retention_period 30 9 | enable_query 10 | daily_cap 5 11 | } 12 | 13 | let omsName = $"SecurityInsights({sentinelWorkspace.Name.Value})" 14 | 15 | let sentinelSolution = oms { 16 | name omsName 17 | 18 | plan ( 19 | omsPlan { 20 | name omsName 21 | publisher "Microsoft" 22 | product "OMSGallery/SecurityInsights" 23 | } 24 | ) 25 | 26 | properties (omsProperties { workspace sentinelWorkspace }) 27 | } 28 | 29 | let deployment = arm { 30 | location Location.NorthCentralUS 31 | add_resource sentinelWorkspace 32 | add_resource sentinelSolution 33 | } 34 | 35 | deployment |> Writer.quickWrite "operationsManagement" -------------------------------------------------------------------------------- /samples/scripts/postgresql.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget: Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.PostgreSQL 6 | 7 | let myPostgres = postgreSQL { 8 | name "flexibleserver" 9 | admin_username "adminallthethings" 10 | storage_size 64 11 | storage_performance_tier Vm.DiskPerformanceTier.P10 12 | tier FlexibleTier.Burstable_B1ms 13 | enable_azure_firewall 14 | storage_autogrow true 15 | 16 | add_database ( 17 | postgreSQLDb { 18 | name "thedatabase" 19 | collation "en_US.utf8" 20 | } 21 | ) 22 | } 23 | 24 | let template = arm { 25 | location Location.NorthEurope 26 | add_resource myPostgres 27 | } 28 | 29 | // WARNING: 30 | // since there is currently no free tier for PostgreSQL, actually deploying this 31 | // *will* incur spending on your subscription. 32 | template |> Writer.quickWrite "postgres-example" -------------------------------------------------------------------------------- /samples/scripts/redis.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let myCache = redis { 7 | name "myredis" 8 | sku Redis.Standard 9 | capacity 0 10 | enable_non_ssl_port 11 | setting "maxclients" 256 12 | setting "maxmemory-reserved" 2 13 | setting "maxfragmentationmemory-reserved" 12 14 | setting "maxmemory-delta" 2 15 | } 16 | 17 | let template = arm { 18 | location Location.NorthEurope 19 | add_resource myCache 20 | } 21 | 22 | template |> Writer.quickWrite "my-resource-group-name" -------------------------------------------------------------------------------- /samples/scripts/safe-template.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let makeSafeApp (environment: string) theLocation storageSku webAppSku = 7 | let environment = environment.ToLower() 8 | let generateResourceName = sprintf "safe-%s-%s" environment 9 | 10 | let myStorageAccount = storageAccount { 11 | name (sprintf "safe%sstorage" environment) 12 | sku storageSku 13 | } 14 | 15 | let myWebApp = webApp { 16 | name (generateResourceName "web") 17 | sku webAppSku 18 | 19 | website_node_default_version "8.1.4" 20 | setting "public_path" "./public" 21 | setting "STORAGE_CONNECTIONSTRING" myStorageAccount.Key 22 | } 23 | 24 | arm { 25 | location theLocation 26 | 27 | add_resource myStorageAccount 28 | add_resource myWebApp 29 | 30 | output "webAppName" myWebApp.Name 31 | output "webAppPassword" myWebApp.PublishingPassword 32 | } 33 | 34 | makeSafeApp "dev" Location.NorthEurope Storage.Standard_LRS WebApp.Sku.F1 35 | |> Deploy.execute "my-resource-group-name" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/search.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.Search 6 | 7 | let mySearch = search { 8 | name "isaacsSearch" 9 | sku Basic 10 | } 11 | 12 | let deployment = arm { 13 | location Location.NorthEurope 14 | add_resource mySearch 15 | output "search-admin-key" mySearch.AdminKey 16 | output "search-query-key" mySearch.QueryKey 17 | } 18 | 19 | deployment |> Deploy.execute "my-resource-group-name" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/service-bus.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.ServiceBus 6 | 7 | let myServiceBus = serviceBus { 8 | name "allMyQueues" 9 | sku Standard 10 | min_tls_version TlsVersion.Tls12 11 | enable_zone_redundancy 12 | disable_public_network_access 13 | add_queues [ queue { name "queuenumberone" }; queue { name "queuenumbertwo" } ] 14 | 15 | add_topics [ 16 | topic { 17 | name "thetopic" 18 | 19 | add_subscriptions [ 20 | subscription { 21 | name "thesub" 22 | forward_to "queuenumberone" 23 | } 24 | ] 25 | } 26 | ] 27 | } 28 | 29 | let deployment = arm { 30 | location Location.NorthEurope 31 | add_resource myServiceBus 32 | output "NamespaceDefaultConnectionString" myServiceBus.NamespaceDefaultConnectionString 33 | output "DefaultSharedAccessPolicyPrimaryKey" myServiceBus.DefaultSharedAccessPolicyPrimaryKey 34 | } 35 | 36 | deployment |> Deploy.execute "service-bus-test" [] -------------------------------------------------------------------------------- /samples/scripts/sqlserver.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Sql 6 | 7 | let myDatabases = sqlServer { 8 | name "isaac_super_server" 9 | admin_username "admin_username" 10 | enable_azure_firewall 11 | 12 | add_databases [ 13 | sqlDb { name "poolDb1" } 14 | sqlDb { name "poolDb2" } 15 | sqlDb { 16 | name "dtuDb" 17 | sku Basic 18 | } 19 | sqlDb { 20 | name "memoryDb" 21 | sku M_8 22 | } 23 | sqlDb { 24 | name "cpuDb" 25 | sku Fsv2_8 26 | } 27 | sqlDb { 28 | name "businessCriticalDb" 29 | sku (BusinessCritical Gen5_2) 30 | } 31 | sqlDb { 32 | name "hyperscaleDb" 33 | sku (Hyperscale Gen5_2) 34 | } 35 | sqlDb { 36 | name "generalPurposeDb" 37 | sku (GeneralPurpose Gen5_8) 38 | db_size (1024 * 128) 39 | hybrid_benefit 40 | } 41 | sqlDb { 42 | name "serverless4to8cpu" 43 | sku (GeneralPurpose(S_Gen5(4, 8))) 44 | } 45 | ] 46 | } 47 | 48 | let template = arm { 49 | location Location.NorthEurope 50 | add_resource myDatabases 51 | } 52 | 53 | template |> Writer.quickWrite "sql-example" -------------------------------------------------------------------------------- /samples/scripts/storage.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let myStorage = storageAccount { 7 | name "myfarmerstorage" 8 | sku Storage.Sku.Standard_LRS 9 | 10 | add_queues [ 11 | storageQueue { 12 | name "queue1" 13 | metadata [ "environment", "dev"; "project", "farmer" ] 14 | } 15 | storageQueue { 16 | name "queue2" 17 | metadata [ "environment", "test"; "project", "barnyard" ] 18 | } 19 | ] 20 | 21 | add_private_container "container1" 22 | add_table "table1" 23 | add_tables [ "table2"; "table3" ] 24 | add_lifecycle_rule "cleanup" [ Storage.DeleteAfter 7 ] Storage.NoRuleFilters 25 | 26 | add_lifecycle_rule "test" [ 27 | Storage.DeleteAfter 1 28 | Storage.DeleteAfter 2 29 | Storage.ArchiveAfter 1 30 | ] [ "foo/bar" ] 31 | 32 | disable_public_network_access 33 | disable_blob_public_access 34 | disable_shared_key_access 35 | default_to_oauth_authentication 36 | restrict_to_azure_services [ Farmer.Arm.Storage.NetworkRuleSetBypass.AzureServices ] 37 | } 38 | 39 | let template = arm { add_resource myStorage } 40 | 41 | template |> Writer.quickWrite "template" 42 | template |> Deploy.execute "farmer-test-rg" [] -------------------------------------------------------------------------------- /samples/scripts/tutorials/aci-fsharp.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget: Farmer" 2 | 3 | let script = 4 | """ 5 | #r "nuget: Suave, Version=2.6.0" 6 | open Suave 7 | let config = { defaultConfig with bindings = [ HttpBinding.createSimple HTTP "0.0.0.0" 8080 ] } 8 | startWebServer config (Successful.OK "Hello Farmers!") 9 | """ 10 | 11 | open Farmer 12 | open Farmer.Builders 13 | 14 | let containers = containerGroup { 15 | name "my-app" 16 | 17 | add_instances [ 18 | containerInstance { 19 | name "fsi" 20 | image "mcr.microsoft.com/dotnet/sdk:5.0" 21 | command_line ("dotnet fsi /src/main.fsx".Split null |> List.ofArray) 22 | add_volume_mount "script-source" "/src" 23 | add_public_ports [ 8080us ] 24 | cpu_cores 0.2 25 | memory 0.5 26 | } 27 | ] 28 | 29 | public_dns "my-app-fsi-suave" [ TCP, 8080us ] 30 | add_volumes [ volume_mount.secret_string "script-source" "main.fsx" script ] 31 | } 32 | 33 | arm { 34 | location Location.EastUS 35 | add_resources [ containers ] 36 | } 37 | |> Writer.quickWrite "aci-fsharp" -------------------------------------------------------------------------------- /samples/scripts/tutorials/cosmos-backed-webapp.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.CosmosDb 6 | 7 | let theDatabase = cosmosDb { 8 | name "Tasks" 9 | account_name "isaac-to-do-app-cosmos" 10 | consistency_policy Session 11 | } 12 | 13 | let theWebApp = webApp { 14 | name "isaac-to-do-app" 15 | sku WebApp.Sku.B1 16 | setting "CosmosDb:Account" theDatabase.Endpoint 17 | setting "CosmosDb:Key" theDatabase.PrimaryKey 18 | setting "CosmosDb:DatabaseName" theDatabase.DbName 19 | setting "CosmosDb:ContainerName" "Items" 20 | } 21 | 22 | let template = arm { 23 | location Location.WestEurope 24 | add_resources [ theDatabase; theWebApp ] 25 | } 26 | 27 | template |> Writer.quickWrite @"generated-template" -------------------------------------------------------------------------------- /samples/scripts/tutorials/custom-output.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let er = expressRoute { 7 | name "my-test-circuit" 8 | service_provider "Equinix" 9 | peering_location "Frankfurt" 10 | } 11 | 12 | // Build an ARM resourceId type for the circuit. 13 | let erId = ResourceId.create (Arm.Network.expressRouteCircuits, er.Name) 14 | 15 | // Use that ID to build a reference expression and get a property of the referenced resource. 16 | let serviceKeyRef = 17 | ArmExpression.create ($"reference({erId.ArmExpression.Value}).serviceKey") 18 | 19 | // That reference can be used in the output for the template so that ARM can populate it from the newly deployed resource 20 | arm { 21 | location Location.WestEurope 22 | add_resource er 23 | output "er-service-key" serviceKeyRef 24 | } 25 | |> Writer.quickWrite "custom-output" -------------------------------------------------------------------------------- /samples/scripts/tutorials/keyvault-certs-app.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget: Suave, Version=2.6.0" 2 | 3 | open Suave 4 | open System.Security.Cryptography.X509Certificates 5 | 6 | let certWithKey = new X509Certificate2("/certs/key.pfx", "") 7 | let store = new X509Store(StoreName.Root, StoreLocation.CurrentUser) 8 | store.Open(OpenFlags.ReadWrite) 9 | store.Add(certWithKey) 10 | store.Close() 11 | 12 | let config = { 13 | defaultConfig with 14 | bindings = [ HttpBinding.createSimple (HTTPS certWithKey) "0.0.0.0" 443 ] 15 | } 16 | 17 | startWebServer config (Successful.OK "Hello Secure Farmers!") -------------------------------------------------------------------------------- /samples/scripts/tutorials/multiple-web-apps.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let plan = servicePlan { 7 | name "theFarm" 8 | sku WebApp.Sku.F1 9 | } 10 | 11 | let ai = appInsights { name "insights" } 12 | 13 | let planets = [ "jupiter"; "mars"; "pluto"; "venus" ] 14 | 15 | let webApps: IBuilder list = [ 16 | for planet in planets do 17 | webApp { 18 | name ("mywebapp-" + planet) 19 | link_to_service_plan plan 20 | link_to_app_insights ai 21 | } 22 | ] 23 | 24 | let template = arm { 25 | location Location.NorthEurope 26 | add_resource plan 27 | add_resource ai 28 | add_resources webApps 29 | } 30 | 31 | template |> Writer.quickWrite "my-resource-group-name" -------------------------------------------------------------------------------- /samples/scripts/tutorials/serverless-etl.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let database = sqlDb { 7 | name "isaacparseddata" 8 | sku Sql.DtuSku.S1 9 | } 10 | 11 | let transactionalDb = sqlServer { 12 | name "isaacetlserver" 13 | admin_username "theadministrator" 14 | add_databases [ database ] 15 | } 16 | 17 | let etlProcessor = functions { 18 | name "isaacetlprocessor" 19 | storage_account_name "isaacmydata" 20 | setting "sql-conn" (transactionalDb.ConnectionString database) 21 | } 22 | 23 | let template = arm { 24 | location Location.WestEurope 25 | add_resources [ transactionalDb; etlProcessor ] 26 | } 27 | 28 | // Generate the ARM template here... 29 | template |> Writer.quickWrite @"generated-template" 30 | 31 | // Or deploy it directly to Azure here... (required Azure CLI installed!) 32 | // template 33 | // |> Deploy.execute "my-resource-group" [ transactionalDb.PasswordParameter, "SQL PASSWORD GOES HERE" ] 34 | // |> printfn "%A" -------------------------------------------------------------------------------- /samples/scripts/tutorials/webapp-deploy.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let myWebApp = webApp { 7 | name "" 8 | zip_deploy @"" 9 | } 10 | 11 | let template = arm { 12 | location Location.NorthEurope 13 | add_resource myWebApp 14 | } 15 | 16 | template |> Deploy.execute "mywebapp" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/tutorials/webapp-keyvault.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let secretName = "storagekey" 7 | let vaultName = "isaacsupersecret" 8 | 9 | let datastore = storageAccount { name "isaacsuperstore" } 10 | 11 | let webapplication = webApp { 12 | name "isaacsuperweb" 13 | system_identity 14 | link_to_keyvault (ResourceName vaultName) 15 | secret_setting secretName 16 | } 17 | 18 | let secretsvault = keyVault { 19 | name vaultName 20 | add_secret (secretName, datastore.Key) 21 | add_access_policy (AccessPolicy.create webapplication.SystemIdentity) 22 | } 23 | 24 | let template = arm { 25 | location Location.WestEurope 26 | add_resources [ secretsvault; datastore; webapplication ] 27 | } 28 | 29 | // Generate the ARM template here... 30 | template |> Writer.quickWrite (__SOURCE_DIRECTORY__ + @"/generated-template") 31 | 32 | // Or deploy it directly to Azure here... (required Azure CLI installed!) 33 | // template 34 | // |> Deploy.execute "my-resource-group" Deploy.NoParameters 35 | // |> printfn "%A" -------------------------------------------------------------------------------- /samples/scripts/vm-spot-instance.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.Vm 6 | 7 | let myVm = vm { 8 | name "isaacsVM" 9 | username "isaac" 10 | spot_instance Deallocate 11 | vm_size Standard_A2 12 | operating_system WindowsServer_2012Datacenter 13 | os_disk 128 StandardSSD_LRS 14 | add_ssd_disk 128 15 | add_slow_disk 512 16 | diagnostics_support 17 | system_identity 18 | } 19 | 20 | let deployment = arm { 21 | location Location.NorthEurope 22 | add_resource myVm 23 | } 24 | 25 | deployment |> Deploy.execute "my-resource-group-name" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/vm.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.Vm 6 | 7 | let myVm = vm { 8 | name "isaacsVM" 9 | username "isaac" 10 | vm_size Standard_A2 11 | operating_system WindowsServer_2012Datacenter 12 | os_disk 128 StandardSSD_LRS 13 | add_ssd_disk 128 14 | add_slow_disk 512 15 | diagnostics_support 16 | system_identity 17 | } 18 | 19 | let deployment = arm { 20 | location Location.NorthEurope 21 | add_resource myVm 22 | } 23 | 24 | deployment |> Deploy.execute "my-resource-group-name" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/vnet-gateway.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.VirtualNetworkGateway 6 | open Farmer.Arm.Network 7 | 8 | let privateNet = vnet { 9 | name "my-vnet" 10 | add_address_spaces [ "10.30.0.0/16" ] 11 | 12 | add_subnets [ 13 | subnet { 14 | name "GatewaySubnet" 15 | prefix "10.30.254.0/28" 16 | } 17 | ] 18 | } 19 | 20 | /// In case you need to specify public IP details, create a public IP 21 | /// and assign it to the gateway with 'gateway_ip_config' 22 | let gatewayIp = { 23 | Name = ResourceName "gw-pip" 24 | Location = Location.NorthEurope 25 | DomainNameLabel = Some "mygateway" 26 | } 27 | 28 | let myGateway = gateway { 29 | name "er-gateway" 30 | er_gateway_sku ErGatewaySku.Standard 31 | vpn_type VpnType.RouteBased 32 | vnet "my-vnet" 33 | gateway_ip_config DynamicPrivateIp gatewayIp 34 | } 35 | 36 | let deployment = arm { 37 | location Location.NorthEurope 38 | add_resource privateNet 39 | add_resource myGateway 40 | add_resource gatewayIp 41 | } 42 | 43 | deployment |> Deploy.whatIf "FarmerTest" Deploy.NoParameters |> printfn "%A" -------------------------------------------------------------------------------- /samples/scripts/vnet.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | open Farmer.Network 6 | 7 | let serviceEndpointPolicy = 8 | Farmer.Arm.Network.serviceEndpointPolicies.resourceId "svc-endpt-policy" 9 | 10 | 11 | let privateNet = vnet { 12 | name "my-vnet" 13 | 14 | build_address_spaces [ 15 | addressSpace { 16 | space "10.28.0.0/16" 17 | build_subnet "services" 24 18 | build_subnet "corporate-west" 18 19 | build_subnet "corporate-east" 18 20 | build_subnet "GatewaySubnet" 29 21 | build_subnet_delegated "containers" 27 [ SubnetDelegationService.ContainerGroups ] 22 | 23 | build_subnet_service_endpoints "can-use-servicebus" 28 [ 24 | EndpointServiceType.ServiceBus, [ Location.EastUS ] 25 | ] 26 | 27 | build_subnet_service_endpoint_policies "can-use-storage" 28 [ 28 | EndpointServiceType.Storage, [ Location.EastUS ] 29 | ] [ serviceEndpointPolicy ] 30 | 31 | subnets [ 32 | buildSubnet "more-services" 24 33 | buildSubnetDelegations "more-containers" 27 [ SubnetDelegationService.ContainerGroups ] 34 | ] 35 | } 36 | addressSpace { 37 | space "10.30.0.0/16" 38 | subnets [ buildSubnet "stuff" 23; buildSubnet "more-stuff" 28 ] 39 | } 40 | ] 41 | } 42 | 43 | let deployment = arm { 44 | location Location.EastUS 45 | add_resource privateNet 46 | } 47 | 48 | deployment |> Writer.quickWrite "vnet-sample" -------------------------------------------------------------------------------- /samples/scripts/webapp-appinsights.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let template = 7 | let myWebApp = webApp { 8 | name "mysuperwebapp" 9 | sku WebApp.Sku.F1 10 | } 11 | 12 | arm { 13 | location Location.NorthEurope 14 | add_resource myWebApp 15 | } 16 | 17 | template |> Deploy.execute "my-resource-group-name" Deploy.NoParameters -------------------------------------------------------------------------------- /samples/scripts/webapp-storage.fsx: -------------------------------------------------------------------------------- 1 | #r "nuget:Farmer" 2 | 3 | open Farmer 4 | open Farmer.Builders 5 | 6 | let myStorage = storageAccount { 7 | name "mystorage" 8 | sku Storage.Sku.Standard_LRS 9 | add_lifecycle_rule "cleanup" [ Storage.DeleteAfter 7 ] Storage.NoRuleFilters 10 | 11 | add_lifecycle_rule "test" [ 12 | Storage.DeleteAfter 1 13 | Storage.DeleteAfter 2 14 | Storage.ArchiveAfter 1 15 | ] [ "foo/bar" ] 16 | } 17 | 18 | let myWebApp = webApp { 19 | name "mysuperwebapp" 20 | sku WebApp.Sku.S1 21 | app_insights_off 22 | setting "storage_key" myStorage.Key 23 | add_allowed_ip_restriction "allow everything" "0.0.0.0/0" 24 | add_denied_ip_restriction "deny" "1.2.3.4/31" 25 | } 26 | 27 | let deployment = arm { 28 | location Location.NorthEurope 29 | add_resource myStorage 30 | add_resource myWebApp 31 | output "storage_key" myStorage.Key 32 | output "web_password" myWebApp.PublishingPassword 33 | } 34 | 35 | deployment |> Deploy.execute "my-resource-group-name" Deploy.NoParameters -------------------------------------------------------------------------------- /src/Farmer/Aliases.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Aliases 3 | 4 | [] 5 | module BuilderExtensions = 6 | open Farmer.Builders 7 | open Farmer.Arm.Network 8 | 9 | type IPrivateEndpoints<'TConfig> with 10 | 11 | member this.AddPrivateEndpoint(state: 'TConfig, subnetId: LinkedResource) = 12 | this.AddPrivateEndpoint(state, SubnetReference.create subnetId) 13 | 14 | member this.AddPrivateEndpoint(state: 'TConfig, subnet: SubnetConfig) = 15 | this.AddPrivateEndpoint(state, SubnetReference.create subnet) 16 | 17 | member this.AddPrivateEndpoint(state, (subnetRef: LinkedResource, epName)) = 18 | this.AddPrivateEndpoint(state, (SubnetReference.create subnetRef, epName)) 19 | 20 | member this.AddPrivateEndpoint(state: 'TConfig, (vnetRef, subnetName): LinkedResource * ResourceName) = 21 | this.AddPrivateEndpoint(state, SubnetReference.create (vnetRef, subnetName)) 22 | 23 | member this.AddPrivateEndpoint(state, (vnetRef, subnetName, epName): LinkedResource * ResourceName * string) = 24 | this.AddPrivateEndpoint(state, ((SubnetReference.create (vnetRef, subnetName)), epName)) 25 | 26 | member this.AddPrivateEndpoint(state: 'TConfig, (vnet, subnetName): VirtualNetworkConfig * ResourceName) = 27 | this.AddPrivateEndpoint(state, SubnetReference.create (vnet, subnetName)) 28 | 29 | member this.AddPrivateEndpoints(state: 'TConfig, subnetIds: LinkedResource list) = 30 | this.AddPrivateEndpoints(state, subnetIds |> List.map SubnetReference.create |> Set) 31 | 32 | member this.AddPrivateEndpoints(state: 'TConfig, subnets: SubnetConfig list) = 33 | this.AddPrivateEndpoints(state, subnets |> List.map SubnetReference.create |> Set) 34 | 35 | let arm = Farmer.Builders.ResourceGroup.DeploymentBuilder() -------------------------------------------------------------------------------- /src/Farmer/Arm/AVS.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.AVS 3 | 4 | open Farmer 5 | let privateClouds = ResourceType("Microsoft.AVS/privateClouds", "2021-12-01") 6 | 7 | let privateCloudsScriptPackages = 8 | ResourceType("Microsoft.AVS/privateClouds/scriptPackages", "2021-12-01") 9 | 10 | let privateCloudsScriptCmdlets = 11 | ResourceType("Microsoft.AVS/privateClouds/scriptPackages/scriptCmdlets", "2021-12-01") -------------------------------------------------------------------------------- /src/Farmer/Arm/AutomationAccount.fs: -------------------------------------------------------------------------------- 1 | /// THIS IS A STUB 2 | /// https://learn.microsoft.com/en-us/azure/templates/microsoft.automation/automationaccounts 3 | [] 4 | module Farmer.Arm.AutomationAccounts 5 | 6 | open Farmer 7 | open System 8 | 9 | let automationAccounts = 10 | Farmer.ResourceType("Microsoft.Automation/automationAccounts", "2022-08-08") 11 | 12 | type AutomationAccount = { 13 | Name: Farmer.ResourceName 14 | } with 15 | 16 | interface Farmer.IArmResource with 17 | 18 | member this.ResourceId = automationAccounts.resourceId this.Name 19 | 20 | member this.JsonModel = "{}" -------------------------------------------------------------------------------- /src/Farmer/Arm/B2cTenant.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.B2cTenant 3 | 4 | open Farmer 5 | 6 | let b2cTenant = 7 | ResourceType("Microsoft.AzureActiveDirectory/b2cDirectories", "2021-04-01") 8 | 9 | type B2cDomainName = 10 | | B2cDomainName of string 11 | 12 | static member internal Empty = B2cDomainName "" 13 | 14 | member this.AsResourceName = 15 | match this with 16 | | B2cDomainName name -> ResourceName name 17 | 18 | type B2cTenant = { 19 | Name: B2cDomainName 20 | DisplayName: string 21 | DataResidency: Location 22 | CountryCode: string 23 | Tags: Map 24 | Sku: B2cTenant.Sku 25 | } with 26 | 27 | interface IArmResource with 28 | member this.ResourceId = accounts.resourceId this.Name.AsResourceName 29 | 30 | member this.JsonModel = {| 31 | b2cTenant.Create(this.Name.AsResourceName, this.DataResidency, tags = this.Tags) with 32 | sku = {| 33 | name = string this.Sku 34 | tier = "A0" 35 | |} 36 | properties = {| 37 | createTenantProperties = {| 38 | countryCode = this.CountryCode 39 | displayName = this.DisplayName 40 | |} 41 | |} 42 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/BingSearch.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.BingSearch 3 | 4 | open Farmer 5 | 6 | let accounts = ResourceType("Microsoft.Bing/accounts", "2020-06-10") 7 | 8 | [] 9 | let private kind = "Bing.Search.v7" 10 | 11 | type Accounts = { 12 | Name: ResourceName 13 | Location: Location 14 | Sku: BingSearch.Sku 15 | Tags: Map 16 | Statistics: FeatureFlag 17 | } with 18 | 19 | interface IArmResource with 20 | member this.ResourceId = accounts.resourceId this.Name 21 | 22 | member this.JsonModel = {| 23 | accounts.Create(this.Name, this.Location, tags = this.Tags) with 24 | sku = {| name = string this.Sku |} 25 | kind = kind 26 | properties = {| 27 | statisticsEnabled = this.Statistics.AsBoolean 28 | |} 29 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/Cache.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.Cache 3 | 4 | open Farmer 5 | open Farmer.Redis 6 | 7 | let redis = ResourceType("Microsoft.Cache/Redis", "2018-03-01") 8 | 9 | type Redis = { 10 | Name: ResourceName 11 | Location: Location 12 | Sku: {| Sku: Sku; Capacity: int |} 13 | RedisConfiguration: Map 14 | NonSslEnabled: bool option 15 | ShardCount: int option 16 | MinimumTlsVersion: TlsVersion option 17 | Tags: Map 18 | } with 19 | 20 | member this.Family = 21 | match this.Sku.Sku with 22 | | Basic 23 | | Standard -> 'C' 24 | | Premium -> 'P' 25 | 26 | interface IArmResource with 27 | member this.ResourceId = redis.resourceId this.Name 28 | 29 | member this.JsonModel = {| 30 | redis.Create(this.Name, this.Location, tags = this.Tags) with 31 | properties = {| 32 | sku = {| 33 | name = string this.Sku.Sku 34 | family = this.Family 35 | capacity = this.Sku.Capacity 36 | |} 37 | enableNonSslPort = this.NonSslEnabled |> Option.toNullable 38 | shardCount = this.ShardCount |> Option.toNullable 39 | minimumTlsVersion = 40 | this.MinimumTlsVersion 41 | |> Option.map (function 42 | | Tls10 -> "1.0" 43 | | Tls11 -> "1.1" 44 | | Tls12 -> "1.2") 45 | |> Option.toObj 46 | redisConfiguration = this.RedisConfiguration 47 | |} 48 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/CognitiveServices.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.CognitiveServices 3 | 4 | open Farmer 5 | 6 | let accounts = ResourceType("Microsoft.CognitiveServices/accounts", "2017-04-18") 7 | 8 | type Accounts = { 9 | Name: ResourceName 10 | Location: Location 11 | Sku: CognitiveServices.Sku 12 | Kind: CognitiveServices.Kind 13 | Tags: Map 14 | } with 15 | 16 | interface IArmResource with 17 | member this.ResourceId = accounts.resourceId this.Name 18 | 19 | member this.JsonModel = {| 20 | accounts.Create(this.Name, this.Location, tags = this.Tags) with 21 | sku = {| name = string this.Sku |} 22 | kind = this.Kind.ToString().Replace("_", ".") 23 | properties = {| |} 24 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/CommunicationServices.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.Communication 3 | 4 | open Farmer 5 | 6 | let communicationServices = 7 | ResourceType("Microsoft.Communication/communicationServices", "2020-08-20-preview") 8 | 9 | type CommunicationService = { 10 | Name: ResourceName 11 | DataLocation: DataLocation 12 | Tags: Map 13 | } with 14 | 15 | interface IArmResource with 16 | member this.ResourceId = communicationServices.resourceId this.Name 17 | 18 | member this.JsonModel = {| 19 | communicationServices.Create(this.Name, Location.Global, tags = this.Tags) with 20 | properties = {| 21 | dataLocation = this.DataLocation.ArmValue 22 | |} 23 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/ContainerRegistry.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.ContainerRegistry 3 | 4 | open Farmer 5 | open Farmer.ContainerRegistry 6 | 7 | let registries = 8 | ResourceType("Microsoft.ContainerRegistry/registries", "2019-05-01") 9 | 10 | type Registries = { 11 | Name: ResourceName 12 | Location: Location 13 | Sku: Sku 14 | AdminUserEnabled: bool 15 | Tags: Map 16 | } with 17 | 18 | interface IArmResource with 19 | member this.ResourceId = registries.resourceId this.Name 20 | 21 | member this.JsonModel = {| 22 | registries.Create(this.Name, this.Location, tags = this.Tags) with 23 | sku = {| name = this.Sku.ToString() |} 24 | properties = {| 25 | adminUserEnabled = this.AdminUserEnabled 26 | |} 27 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/DataLakeStore.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.DataLakeStore 3 | 4 | open Farmer 5 | open Farmer.DataLake 6 | 7 | let accounts = ResourceType("Microsoft.DataLakeStore/accounts", "2016-11-01") 8 | 9 | type Account = { 10 | Name: ResourceName 11 | Location: Location 12 | EncryptionState: FeatureFlag 13 | Sku: Sku 14 | Tags: Map 15 | } with 16 | 17 | interface IArmResource with 18 | member this.ResourceId = accounts.resourceId this.Name 19 | 20 | member this.JsonModel = {| 21 | accounts.Create(this.Name, this.Location, tags = this.Tags) with 22 | properties = {| 23 | newTier = this.Sku.ToString() 24 | encryptionState = this.EncryptionState.ToString() 25 | |} 26 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/Disk.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.Disk 3 | 4 | open System 5 | open Farmer 6 | 7 | let disks = ResourceType("Microsoft.Compute/disks", "2022-07-02") 8 | 9 | type DiskCreation = 10 | | Import of SourceUri: Uri * StorageAccountId: ResourceId 11 | | Empty of Size: int 12 | 13 | type Disk = { 14 | Name: ResourceName 15 | Location: Location 16 | Sku: Vm.DiskType option 17 | Zones: string list 18 | OsType: OS 19 | CreationData: DiskCreation 20 | Tags: Map 21 | Dependencies: ResourceId Set 22 | } with 23 | 24 | interface IArmResource with 25 | member this.ResourceId = disks.resourceId this.Name 26 | 27 | member this.JsonModel = {| 28 | disks.Create(this.Name, this.Location, dependsOn = this.Dependencies, tags = this.Tags) with 29 | sku = 30 | this.Sku 31 | |> Option.map (fun sku -> {| name = sku.ArmValue |} :> obj) 32 | |> Option.toObj 33 | zones = if this.Zones.IsEmpty then null else ResizeArray(this.Zones) 34 | properties = {| 35 | creationData = 36 | match this.CreationData with 37 | | Empty _ -> {| createOption = "Empty" |} :> obj 38 | | Import(sourceUri, storageAccountId) -> 39 | {| 40 | createOption = "Import" 41 | sourceUri = sourceUri.AbsoluteUri 42 | storageAccountId = storageAccountId.Eval() 43 | |} 44 | :> obj 45 | diskSizeGB = 46 | match this.CreationData with 47 | | Empty size -> size / 1 :> obj 48 | | _ -> null 49 | osType = 50 | this.OsType 51 | |> function 52 | | Linux -> "Linux" 53 | | Windows -> "Windows" 54 | |} 55 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/Insights.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.Insights 3 | 4 | open Farmer 5 | 6 | let private createComponents version = 7 | ResourceType("Microsoft.Insights/components", version) 8 | 9 | /// Classic AI instance 10 | let components = createComponents "2014-04-01" 11 | /// Workspace-enabled AI instance 12 | let componentsWorkspace = createComponents "2020-02-02" 13 | 14 | /// The type of AI instance to create. 15 | type InstanceKind = 16 | | Classic 17 | | Workspace of workspace: ResourceId 18 | 19 | member this.ResourceType = 20 | match this with 21 | | Classic -> components 22 | | Workspace _ -> componentsWorkspace 23 | 24 | type Components = { 25 | Name: ResourceName 26 | Location: Location 27 | LinkedWebsite: ResourceName option 28 | DisableIpMasking: bool 29 | SamplingPercentage: int 30 | InstanceKind: InstanceKind 31 | Tags: Map 32 | Dependencies: ResourceId Set 33 | } with 34 | 35 | interface IArmResource with 36 | member this.ResourceId = components.resourceId this.Name 37 | 38 | member this.JsonModel = 39 | let tags = 40 | match this.LinkedWebsite with 41 | | Some linkedWebsite -> 42 | this.Tags.Add( 43 | $"[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', '{linkedWebsite.Value}')]", 44 | "Resource" 45 | ) 46 | | None -> this.Tags 47 | 48 | {| 49 | this.InstanceKind.ResourceType.Create(this.Name, this.Location, this.Dependencies, tags) with 50 | kind = "web" 51 | properties = {| 52 | name = this.Name.Value 53 | Application_Type = "web" 54 | ApplicationId = 55 | match this.LinkedWebsite with 56 | | Some linkedWebsite -> linkedWebsite.Value 57 | | None -> null 58 | DisableIpMasking = this.DisableIpMasking 59 | SamplingPercentage = this.SamplingPercentage 60 | IngestionMode = 61 | match this.InstanceKind with 62 | | Workspace _ -> "LogAnalytics" 63 | | Classic -> null 64 | WorkspaceResourceId = 65 | match this.InstanceKind with 66 | | Workspace resourceId -> resourceId.Eval() 67 | | Classic -> null 68 | |} 69 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/LogAnalytics.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.LogAnalytics 3 | 4 | open Farmer 5 | 6 | let workspaces = 7 | ResourceType("Microsoft.OperationalInsights/workspaces", "2020-03-01-preview") 8 | 9 | type Workspace = { 10 | Name: ResourceName 11 | Location: Location 12 | RetentionPeriod: int option 13 | IngestionSupport: FeatureFlag option 14 | QuerySupport: FeatureFlag option 15 | DailyCap: int option 16 | Tags: Map 17 | } with 18 | 19 | interface IArmResource with 20 | member this.ResourceId = workspaces.resourceId this.Name 21 | 22 | member this.JsonModel = {| 23 | workspaces.Create(this.Name, this.Location, tags = this.Tags) with 24 | properties = {| 25 | sku = {| name = "PerGB2018" |} 26 | retentionInDays = this.RetentionPeriod |> Option.toNullable 27 | workspaceCapping = 28 | match this.DailyCap with 29 | | None -> null 30 | | Some cap -> {| dailyQuotaGb = cap |} |> box 31 | publicNetworkAccessForIngestion = this.IngestionSupport |> Option.map _.ArmValue |> Option.toObj 32 | publicNetworkAccessForQuery = this.QuerySupport |> Option.map _.ArmValue |> Option.toObj 33 | |} 34 | |} 35 | 36 | type LogAnalytics = 37 | static member getCustomerId resourceId = 38 | ArmExpression 39 | .reference(workspaces, resourceId) 40 | .Map(fun r -> r + ".customerId") 41 | .WithOwner(resourceId) 42 | 43 | static member getCustomerId(name, ?resourceGroup) = 44 | LogAnalytics.getCustomerId (ResourceId.create (workspaces, name, ?group = resourceGroup)) 45 | 46 | static member getPrimarySharedKey resourceId = 47 | ArmExpression 48 | .listKeys(workspaces, resourceId) 49 | .Map(fun r -> r + ".primarySharedKey") 50 | .WithOwner(resourceId) 51 | 52 | static member getPrimarySharedKey(name, ?resourceGroup) = 53 | LogAnalytics.getPrimarySharedKey (ResourceId.create (workspaces, name, ?group = resourceGroup)) -------------------------------------------------------------------------------- /src/Farmer/Arm/LogicApps.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.LogicApps 3 | 4 | open Farmer 5 | open System.Text.Json 6 | 7 | let workflows = ResourceType("Microsoft.Logic/workflows", "2019-05-01") 8 | 9 | type LogicApp = { 10 | Name: ResourceName 11 | Location: Location 12 | Definition: JsonDocument 13 | Tags: Map 14 | } with 15 | 16 | interface IArmResource with 17 | member this.ResourceId = workflows.resourceId this.Name 18 | 19 | member this.JsonModel = {| 20 | workflows.Create(this.Name, this.Location, tags = this.Tags) with 21 | properties = this.Definition 22 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/Maps.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.Maps 3 | 4 | open Farmer 5 | open Farmer.Maps 6 | 7 | let accounts = ResourceType("Microsoft.Maps/accounts", "2018-05-01") 8 | 9 | type Maps = { 10 | Name: ResourceName 11 | Location: Location 12 | Sku: Sku 13 | Tags: Map 14 | } with 15 | 16 | interface IArmResource with 17 | member this.ResourceId = accounts.resourceId this.Name 18 | 19 | member this.JsonModel = {| 20 | accounts.Create(this.Name, this.Location, tags = this.Tags) with 21 | sku = {| 22 | name = 23 | match this.Sku with 24 | | S0 -> "S0" 25 | | S1 -> "S1" 26 | |} 27 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/OperationsManagement.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.OperationsManagement 3 | 4 | open Farmer 5 | 6 | let oms = 7 | ResourceType("Microsoft.OperationsManagement/solutions", "2015-11-01-preview") 8 | 9 | type OMS = { 10 | Name: ResourceName 11 | Location: Location 12 | Plan: {| 13 | Name: string 14 | Product: string 15 | Publisher: string 16 | |} 17 | Properties: {| 18 | ContainedResources: ResourceId list 19 | ReferencedResources: ResourceId list 20 | WorkspaceResourceId: ResourceId 21 | |} 22 | Tags: Map 23 | } with 24 | 25 | interface IArmResource with 26 | member this.ResourceId = oms.resourceId this.Name 27 | 28 | member this.JsonModel = {| 29 | oms.Create(this.Name, this.Location, [ this.Properties.WorkspaceResourceId ], tags = this.Tags) with 30 | plan = {| 31 | name = this.Plan.Name 32 | publisher = this.Plan.Publisher 33 | product = this.Plan.Product 34 | promotionCode = "" 35 | |} 36 | properties = {| 37 | workspaceResourceId = this.Properties.WorkspaceResourceId.Eval() 38 | containedResources = this.Properties.ContainedResources |> List.map (fun cr -> cr.Eval()) 39 | referencedResources = this.Properties.ReferencedResources |> List.map (fun rr -> rr.Eval()) 40 | |} 41 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/Search.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.Search 3 | 4 | open Farmer 5 | open Farmer.Search 6 | 7 | let searchServices = ResourceType("Microsoft.Search/searchServices", "2015-08-19") 8 | 9 | type SearchService = { 10 | Name: ResourceName 11 | Location: Location 12 | Sku: Sku 13 | ReplicaCount: int 14 | PartitionCount: int 15 | Tags: Map 16 | } with 17 | 18 | member this.HostingMode = 19 | match this.Sku with 20 | | Standard3 HighDensity -> "highDensity" 21 | | _ -> "default" 22 | 23 | interface IArmResource with 24 | member this.ResourceId = searchServices.resourceId this.Name 25 | 26 | member this.JsonModel = {| 27 | searchServices.Create(this.Name, this.Location, tags = this.Tags) with 28 | sku = {| 29 | name = 30 | match this.Sku with 31 | | Free -> "free" 32 | | Basic -> "basic" 33 | | Standard -> "standard" 34 | | Standard2 -> "standard2" 35 | | Standard3 _ -> "standard3" 36 | | StorageOptimisedL1 -> "storage_optimized_l1" 37 | | StorageOptimisedL2 -> "storage_optimized_l2" 38 | |} 39 | properties = {| 40 | replicaCount = this.ReplicaCount 41 | partitionCount = this.PartitionCount 42 | hostingMode = this.HostingMode 43 | |} 44 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/SignalR.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.SignalRService 3 | 4 | open Farmer 5 | open Farmer.SignalR 6 | 7 | let signalR = ResourceType("Microsoft.SignalRService/signalR", "2018-10-01") 8 | 9 | type SignalR = { 10 | Name: ResourceName 11 | Location: Location 12 | Sku: Sku 13 | Capacity: int option 14 | AllowedOrigins: string list 15 | ServiceMode: ServiceMode 16 | Tags: Map 17 | } with 18 | 19 | interface IArmResource with 20 | member this.ResourceId = signalR.resourceId this.Name 21 | 22 | member this.JsonModel = {| 23 | signalR.Create(this.Name, this.Location, tags = this.Tags) with 24 | sku = {| 25 | name = 26 | match this.Sku with 27 | | Free -> "Free_F1" 28 | | Standard -> "Standard_S1" 29 | capacity = 30 | match this.Capacity with 31 | | Some c -> c.ToString() 32 | | None -> null 33 | |} 34 | properties = {| 35 | cors = 36 | match this.AllowedOrigins with 37 | | [] -> null 38 | | aos -> box {| allowedOrigins = aos |} 39 | features = [ 40 | {| 41 | flag = "ServiceMode" 42 | value = this.ServiceMode.ToString() 43 | |} 44 | ] 45 | |} 46 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/VirtualWan.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Arm.VirtualWan 3 | 4 | open Farmer 5 | 6 | let virtualWans = ResourceType("Microsoft.Network/virtualWans", "2020-07-01") 7 | 8 | [] 9 | type Office365LocalBreakoutCategory = 10 | | Optimize 11 | | OptimizeAndAllow 12 | | All 13 | | None 14 | 15 | member this.ArmValue = 16 | match this with 17 | | Optimize -> "Optimize" 18 | | OptimizeAndAllow -> "OptimizeAndAllow" 19 | | All -> "All" 20 | | None -> "None" 21 | 22 | [] 23 | type VwanType = 24 | | Standard 25 | | Basic 26 | 27 | member this.ArmValue = 28 | match this with 29 | | Standard -> "Standard" 30 | | Basic -> "Basic" 31 | 32 | type VirtualWan = { 33 | Name: ResourceName 34 | Location: Location 35 | AllowBranchToBranchTraffic: bool option 36 | DisableVpnEncryption: bool option 37 | Office365LocalBreakoutCategory: Office365LocalBreakoutCategory option 38 | VwanType: VwanType 39 | } with 40 | 41 | interface IArmResource with 42 | member this.ResourceId = virtualWans.resourceId this.Name 43 | 44 | member this.JsonModel = {| 45 | virtualWans.Create(this.Name, this.Location) with 46 | properties = {| 47 | allowBranchToBranchTraffic = this.AllowBranchToBranchTraffic |> Option.defaultValue false 48 | disableVpnEncryption = this.DisableVpnEncryption |> Option.defaultValue false 49 | office365LocalBreakoutCategory = 50 | (this.Office365LocalBreakoutCategory 51 | |> Option.defaultValue Office365LocalBreakoutCategory.None) 52 | .ArmValue 53 | ``type`` = this.VwanType.ArmValue 54 | |} 55 | |} -------------------------------------------------------------------------------- /src/Farmer/Arm/Webhook.fs: -------------------------------------------------------------------------------- 1 | /// THIS IS A STUB 2 | /// https://learn.microsoft.com/en-us/azure/templates/microsoft.automation/automationaccounts/webhooks 3 | [] 4 | module Farmer.Arm.Webhooks 5 | 6 | open Farmer 7 | open System 8 | 9 | let webhooks = 10 | Farmer.ResourceType("Microsoft.Automation/automationAccounts/webhooks", "2015-10-31") 11 | 12 | type Webhook = { 13 | Name: Farmer.ResourceName 14 | } with 15 | 16 | interface Farmer.IArmResource with 17 | 18 | member this.ResourceId = webhooks.resourceId this.Name 19 | 20 | member this.JsonModel = "{}" -------------------------------------------------------------------------------- /src/Farmer/Builders/Builders.BingSearch.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Builders.BingSearch 3 | 4 | open Farmer 5 | open Farmer.Arm.BingSearch 6 | open Farmer.BingSearch 7 | 8 | type BingSearch = 9 | /// Gets an ARM Expression key for any Bing Search instance. 10 | static member getKey(resourceId: ResourceId) = 11 | ArmExpression.create ($"listKeys({resourceId.ArmExpression.Value}, '{accounts.ApiVersion}').key1", resourceId) 12 | 13 | static member getKey(name: ResourceName) = 14 | BingSearch.getKey (accounts.resourceId name) 15 | 16 | type BingSearchConfig = { 17 | Name: ResourceName 18 | Sku: Sku 19 | Tags: Map 20 | Statistics: FeatureFlag 21 | } with 22 | 23 | /// Gets an ARM expression to the key of this Bing Search instance. 24 | member this.Key = BingSearch.getKey (accounts.resourceId this.Name) 25 | 26 | interface IBuilder with 27 | member this.ResourceId = accounts.resourceId this.Name 28 | 29 | member this.BuildResources location = [ 30 | { 31 | Name = this.Name 32 | Location = location 33 | Sku = this.Sku 34 | Tags = this.Tags 35 | Statistics = this.Statistics 36 | } 37 | ] 38 | 39 | type BingSearchBuilder() = 40 | member _.Yield _ = { 41 | Name = ResourceName.Empty 42 | Sku = F1 43 | Tags = Map.empty 44 | Statistics = FeatureFlag.Disabled 45 | } 46 | 47 | [] 48 | member _.Name(state: BingSearchConfig, name) = { state with Name = ResourceName name } 49 | 50 | [] 51 | member _.Sku(state: BingSearchConfig, sku) = { state with Sku = sku } 52 | 53 | [] 54 | member _.EnableStatistics(state: BingSearchConfig, value) = { state with Statistics = value } 55 | 56 | interface ITaggable with 57 | member _.Add state tags = { 58 | state with 59 | Tags = state.Tags |> Map.merge tags 60 | } 61 | 62 | let bingSearch = BingSearchBuilder() -------------------------------------------------------------------------------- /src/Farmer/Builders/Builders.CognitiveServices.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Builders.CognitiveServices 3 | 4 | open Farmer 5 | open Farmer.Arm.CognitiveServices 6 | open Farmer.CognitiveServices 7 | 8 | type CognitiveServices = 9 | /// Gets an ARM Expression key for any Cognitives Services instance. 10 | static member getKey(resourceId: ResourceId) = 11 | ArmExpression.create ($"listKeys({resourceId.ArmExpression.Value}, '{accounts.ApiVersion}').key1", resourceId) 12 | 13 | static member getKey(name: ResourceName) = 14 | CognitiveServices.getKey (accounts.resourceId name) 15 | 16 | type CognitiveServicesConfig = { 17 | Name: ResourceName 18 | Sku: Sku 19 | Api: Kind 20 | Tags: Map 21 | } with 22 | 23 | /// Gets an ARM expression to the key of this Cognitive Services instance. 24 | member this.Key = CognitiveServices.getKey (accounts.resourceId this.Name) 25 | 26 | interface IBuilder with 27 | member this.ResourceId = accounts.resourceId this.Name 28 | 29 | member this.BuildResources location = [ 30 | { 31 | Name = this.Name 32 | Location = location 33 | Sku = this.Sku 34 | Kind = this.Api 35 | Tags = this.Tags 36 | } 37 | ] 38 | 39 | type CognitiveServicesBuilder() = 40 | member _.Yield _ = { 41 | Name = ResourceName.Empty 42 | Sku = F0 43 | Api = AllInOne 44 | Tags = Map.empty 45 | } 46 | 47 | [] 48 | member _.Name(state: CognitiveServicesConfig, name) = { state with Name = ResourceName name } 49 | 50 | [] 51 | member _.Sku(state: CognitiveServicesConfig, sku) = { state with Sku = sku } 52 | 53 | [] 54 | member _.Api(state: CognitiveServicesConfig, api) = { state with Api = api } 55 | 56 | interface ITaggable with 57 | member _.Add state tags = { 58 | state with 59 | Tags = state.Tags |> Map.merge tags 60 | } 61 | 62 | let cognitiveServices = CognitiveServicesBuilder() -------------------------------------------------------------------------------- /src/Farmer/Builders/Builders.DataLake.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Builders.DataLake 3 | 4 | open Farmer 5 | open Farmer.DataLake 6 | open Farmer.Arm.DataLakeStore 7 | 8 | type DataLakeConfig = { 9 | Name: ResourceName 10 | EncryptionState: FeatureFlag 11 | Sku: Sku 12 | Tags: Map 13 | } with 14 | 15 | interface IBuilder with 16 | member this.ResourceId = accounts.resourceId this.Name 17 | 18 | member this.BuildResources location = [ 19 | { 20 | Name = this.Name 21 | Location = location 22 | EncryptionState = this.EncryptionState 23 | Sku = this.Sku 24 | Tags = this.Tags 25 | } 26 | ] 27 | 28 | type DataLakeBuilder() = 29 | member _.Yield _ = { 30 | Name = ResourceName "" 31 | EncryptionState = Disabled 32 | Sku = Sku.Consumption 33 | Tags = Map.empty 34 | } 35 | 36 | /// Sets the name of the data lake. 37 | [] 38 | member _.Name(state: DataLakeConfig, name) = { state with Name = ResourceName name } 39 | 40 | [] 41 | member _.EncryptionState(state: DataLakeConfig) = { state with EncryptionState = Enabled } 42 | 43 | [] 44 | member _.Sku(state: DataLakeConfig, sku) = { state with Sku = sku } 45 | 46 | interface ITaggable with 47 | member _.Add state tags = { 48 | state with 49 | Tags = state.Tags |> Map.merge tags 50 | } 51 | 52 | let dataLake = DataLakeBuilder() -------------------------------------------------------------------------------- /src/Farmer/Builders/Builders.Helpers.fs: -------------------------------------------------------------------------------- 1 | module Farmer.Helpers 2 | 3 | open System 4 | 5 | let sanitise filters maxLength (resourceName: ResourceName) = 6 | resourceName.Value.ToLower() 7 | |> Seq.filter (fun c -> Seq.exists (fun filter -> filter c) filters) 8 | |> Seq.truncate maxLength 9 | |> Seq.toArray 10 | |> String 11 | 12 | let sanitiseStorage = sanitise [ Char.IsLetterOrDigit ] 24 13 | let sanitiseSearch = sanitise [ Char.IsLetterOrDigit; (=) '-' ] 60 14 | let sanitiseDb = sanitise [ Char.IsLetterOrDigit ] 100 >> fun r -> r.ToLower() 15 | let sanitiseMaps = sanitise [ Char.IsLetterOrDigit; (=) '-'; (=) '.'; (=) '_' ] 98 16 | let sanitiseSignalR = sanitise [ Char.IsLetterOrDigit; (=) '-' ] 63 -------------------------------------------------------------------------------- /src/Farmer/Builders/Builders.LogicApps.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Builders.LogicApps 3 | 4 | open Farmer 5 | open Farmer.Arm.LogicApps 6 | open System.IO 7 | open System.Text.Json 8 | 9 | type Definition = 10 | | FileDefinition of path: string 11 | | ValueDefinition of definition: string 12 | 13 | type LogicAppConfig = { 14 | WorkflowName: ResourceName 15 | Definition: Definition 16 | Tags: Map 17 | } with 18 | 19 | member this.LogicAppWorkflowName = workflows.resourceId(this.WorkflowName).Name 20 | 21 | interface IBuilder with 22 | member this.ResourceId = workflows.resourceId this.WorkflowName 23 | 24 | member this.BuildResources location = [ 25 | { 26 | Name = this.LogicAppWorkflowName 27 | Location = location 28 | Definition = 29 | match this.Definition with 30 | | FileDefinition path -> 31 | let fileContent = File.ReadAllText(path) 32 | JsonDocument.Parse(fileContent) 33 | | ValueDefinition value -> JsonDocument.Parse(value) 34 | Tags = this.Tags 35 | } 36 | ] 37 | 38 | type LogicAppBuilder() = 39 | member _.Yield _ = { 40 | WorkflowName = ResourceName "logic-app-workflow" 41 | Definition = ValueDefinition """{"name":"logic-app-workflow"}""" 42 | Tags = Map.empty 43 | } 44 | 45 | [] 46 | member _.Name(state: LogicAppConfig, name) = { 47 | state with 48 | WorkflowName = ResourceName name 49 | } 50 | 51 | [] 52 | member _.Definition(state: LogicAppConfig, definition: Definition) = { state with Definition = definition } 53 | 54 | interface ITaggable with 55 | member _.Add state tags = { 56 | state with 57 | Tags = state.Tags |> Map.merge tags 58 | } 59 | 60 | let logicApp = LogicAppBuilder() -------------------------------------------------------------------------------- /src/Farmer/Builders/Builders.Maps.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Builders.Maps 3 | 4 | open Farmer 5 | open Farmer.Maps 6 | open Farmer.Helpers 7 | open Farmer.Arm.Maps 8 | 9 | type MapsConfig = { 10 | Name: ResourceName 11 | Sku: Sku 12 | Tags: Map 13 | } with 14 | 15 | interface IBuilder with 16 | member this.ResourceId = accounts.resourceId this.Name 17 | 18 | member this.BuildResources _ = [ 19 | { 20 | Name = this.Name 21 | Location = Location "global" 22 | Sku = this.Sku 23 | Tags = this.Tags 24 | } 25 | ] 26 | 27 | type MapsBuilder() = 28 | member _.Yield _ = { 29 | Name = ResourceName.Empty 30 | Sku = S0 31 | Tags = Map.empty 32 | } 33 | 34 | member _.Run(state: MapsConfig) = { 35 | state with 36 | Name = state.Name |> sanitiseMaps |> ResourceName 37 | } 38 | 39 | /// Sets the name of the Azure Maps instance. 40 | [] 41 | member _.Name(state: MapsConfig, name) = { state with Name = name } 42 | 43 | member this.Name(state: MapsConfig, name) = this.Name(state, ResourceName name) 44 | 45 | /// Sets the SKU of the Azure Maps instance. 46 | [] 47 | member _.Sku(state: MapsConfig, sku) = { state with Sku = sku } 48 | 49 | interface ITaggable with 50 | member _.Add state tags = { 51 | state with 52 | Tags = state.Tags |> Map.merge tags 53 | } 54 | 55 | let maps = MapsBuilder() -------------------------------------------------------------------------------- /src/Farmer/Builders/Builders.NatGateway.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Farmer.Builders.NatGateway 3 | 4 | open Farmer 5 | open Farmer.Arm.Network 6 | open Farmer.PublicIpAddress 7 | 8 | type NatGatewayConfig = { 9 | Name: ResourceName 10 | IdleTimeout: int 11 | Tags: Map 12 | } with 13 | 14 | interface IBuilder with 15 | member this.ResourceId = natGateways.resourceId this.Name 16 | 17 | member this.BuildResources location = [ 18 | // Currently just generate with a single public IP. 19 | { 20 | PublicIpAddress.Name = ResourceName $"{this.Name.Value}-publicip-1" 21 | AvailabilityZones = NoZone 22 | Location = location 23 | Sku = Sku.Standard 24 | AllocationMethod = AllocationMethod.Static 25 | AddressVersion = Network.AddressVersion.IPv4 26 | DomainNameLabel = None 27 | Tags = this.Tags 28 | } 29 | { 30 | NatGateway.Name = this.Name 31 | Location = location 32 | PublicIpAddresses = [ 33 | LinkedResource.Managed(publicIPAddresses.resourceId $"{this.Name.Value}-publicip-1") 34 | ] 35 | PublicIpPrefixes = [] 36 | IdleTimeout = this.IdleTimeout 37 | Tags = this.Tags 38 | } 39 | ] 40 | 41 | type NatGatewayBuilder() = 42 | member _.Yield _ = { 43 | Name = ResourceName.Empty 44 | IdleTimeout = 4 45 | Tags = Map.empty 46 | } 47 | 48 | [] 49 | member _.Name(state: NatGatewayConfig, name: string) = { state with Name = ResourceName name } 50 | 51 | [] 52 | member _.SetIdleTimeout(state: NatGatewayConfig, idleTimeout: int) = 53 | if idleTimeout > 120 then 54 | raiseFarmer "Maximum idle timeout is 120 minutes." 55 | 56 | { state with IdleTimeout = idleTimeout } 57 | 58 | let natGateway = NatGatewayBuilder() -------------------------------------------------------------------------------- /src/Farmer/Writer.fs: -------------------------------------------------------------------------------- 1 | module Farmer.Writer 2 | 3 | open System.IO 4 | open System 5 | open System.Reflection 6 | open Farmer 7 | 8 | module TemplateGeneration = 9 | let processTemplate (template: ArmTemplate) = {| 10 | ``$schema`` = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" 11 | contentVersion = "1.0.0.0" 12 | resources = template.Resources |> List.map _.JsonModel 13 | parameters = 14 | template.Parameters 15 | |> List.map (fun (SecureParameter p) -> p, {| ``type`` = "securestring" |}) 16 | |> Map.ofList 17 | outputs = 18 | template.Outputs 19 | |> List.map (fun (k, v) -> k, {| ``type`` = "string"; value = v |}) 20 | |> Map.ofList 21 | |} 22 | 23 | let branding () = 24 | let version = 25 | Assembly 26 | .GetExecutingAssembly() 27 | .GetCustomAttribute() 28 | .InformationalVersion 29 | 30 | printfn "==================================================" 31 | printfn "Farmer %s" version 32 | printfn "Repeatable deployments in Azure made easy!" 33 | printfn "==================================================" 34 | 35 | /// Returns a JSON string representing the supplied ARMTemplate. 36 | let toJson = TemplateGeneration.processTemplate >> Serialization.toJson 37 | 38 | /// Writes the provided JSON to a file based on the supplied template name. The postfix ".json" will automatically be added to the filename. 39 | let toFile folder templateName json = 40 | let filename = 41 | let filename = $"{templateName}.json" 42 | Path.Combine(folder, filename) 43 | 44 | let directory = Path.GetDirectoryName filename 45 | 46 | if not (Directory.Exists directory) then 47 | Directory.CreateDirectory directory |> ignore 48 | 49 | File.WriteAllText(filename, json) 50 | filename 51 | 52 | /// Converts the supplied ARMTemplate to JSON and then writes it out to the provided template name. The postfix ".json" will automatically be added to the filename. 53 | let quickWrite templateName (deployment: IDeploymentSource) = 54 | deployment.Deployment.Template |> toJson |> toFile "." templateName |> ignore -------------------------------------------------------------------------------- /src/Tests/AvailabilityTests.fs: -------------------------------------------------------------------------------- 1 | module AppInsightsAvailability 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Builders 6 | 7 | let tests = 8 | testList "AvailabilityTests" [ 9 | 10 | test "Create an availability test" { 11 | let ai = appInsights { name "ai" } 12 | 13 | let availabilityTest = availabilityTest { 14 | name "avTest" 15 | link_to_app_insights ai 16 | timeout 60 17 | frequency 800 18 | locations [ AvailabilityTest.TestSiteLocation.CentralUS ] 19 | web_test ("https://google.com" |> System.Uri |> AvailabilityTest.WebsiteUrl) 20 | } 21 | 22 | let template = arm { add_resources [ availabilityTest; ai ] } 23 | let jsn = template.Template |> Writer.toJson 24 | let jobj = jsn |> Newtonsoft.Json.Linq.JObject.Parse 25 | 26 | let hasWebTest = 27 | jobj.SelectToken("resources[?(@.name=='avTest')].properties.Configuration.WebTest") 28 | 29 | Expect.isNotNull hasWebTest "WebTest context missing" 30 | 31 | let availabilityLocation = 32 | jobj.SelectToken("resources[?(@.name=='avTest')].properties.Locations[0].Id") 33 | 34 | Expect.equal (availabilityLocation.ToString()) "us-fl-mia-edge" "WebTest location incorrect" 35 | let dependsAi = jobj.SelectToken("resources[?(@.name=='avTest')].dependsOn") 36 | Expect.isNotNull dependsAi "AppInsights dependency missing" 37 | } 38 | ] -------------------------------------------------------------------------------- /src/Tests/AzCli.fs: -------------------------------------------------------------------------------- 1 | module AzCli 2 | 3 | open Expecto 4 | open Farmer 5 | open System 6 | open TestHelpers 7 | 8 | let deployTo resourceGroupName parameters (deployment: IDeploymentSource) = 9 | printfn "Creating resource group %s..." resourceGroupName 10 | 11 | let deployResponse = 12 | deployment.Deployment |> Deploy.tryExecute resourceGroupName parameters 13 | 14 | let deleteResponse = Deploy.Az.delete resourceGroupName 15 | 16 | match deployResponse, deleteResponse with 17 | | Ok _, Ok _ -> () 18 | | Error e, _ -> raiseFarmer $"Something went wrong during the deployment: {e}" 19 | | _, Error e -> raiseFarmer $"Something went wrong during the delete: {e}" 20 | 21 | let endToEndTests = 22 | testList "End to end tests" [ 23 | test "Deploys and deletes a resource group" { 24 | let resourceGroupName = sprintf "farmer-integration-test-delete-%O" (Guid.NewGuid()) 25 | arm { location Location.NorthEurope } |> deployTo resourceGroupName [] 26 | } 27 | 28 | test "If parameters are missing, deployment is immediately rejected" { 29 | let deployment = createSimpleDeployment [ "p1" ] 30 | let result = deployment |> Deploy.tryExecute "sample-rg" [] 31 | Expect.equal result (Error "The following parameters are missing: p1. Please add them.") "" 32 | } 33 | ] 34 | 35 | let tests = 36 | testList "Azure CLI" [ 37 | test "Can connect to Az CLI" { 38 | match Deploy.Az.checkVersion Deploy.Az.MinimumVersion with 39 | | Ok _ -> () 40 | | Error msg -> raiseFarmer $"Version check failed: {msg}" 41 | } 42 | 43 | test "Az output is always JSON" { 44 | // account list always defaults to table, regardless of defaults? 45 | Deploy.Az.az "account list --all" 46 | |> Result.map 47 | Serialization.ofJson< 48 | {| 49 | id: Guid 50 | tenantId: Guid 51 | isDefault: bool 52 | |} array 53 | > 54 | |> ignore 55 | } 56 | ] -------------------------------------------------------------------------------- /src/Tests/B2cTenant.fs: -------------------------------------------------------------------------------- 1 | module B2cTenant 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.B2cTenant 6 | open Farmer.Builders 7 | open Newtonsoft.Json.Linq 8 | 9 | let tests = 10 | testList "B2c tenant tests" [ 11 | test "B2c tenant should generate the expected arm template" { 12 | let deployment = arm { 13 | location Location.FranceCentral 14 | 15 | add_resources [ 16 | b2cTenant { 17 | initial_domain_name "myb2c" 18 | display_name "My B2C tenant" 19 | sku Sku.PremiumP1 20 | country_code "FR" 21 | data_residency B2cDataResidency.Europe 22 | } 23 | ] 24 | } 25 | 26 | let jobj = deployment.Template |> Writer.toJson |> JObject.Parse 27 | 28 | let generatedTemplate = jobj.SelectToken("resources[0]") 29 | 30 | Expect.equal 31 | (generatedTemplate.SelectToken("apiVersion").ToString()) 32 | "2021-04-01" 33 | "Invalid ARM template api version" 34 | 35 | Expect.equal 36 | (generatedTemplate.SelectToken("type").ToString()) 37 | "Microsoft.AzureActiveDirectory/b2cDirectories" 38 | "Invalid ARM template type" 39 | 40 | Expect.equal 41 | (generatedTemplate.SelectToken("name").ToString()) 42 | "myb2c.onmicrosoft.com" 43 | "`name` should match .onmicrosoft.com" 44 | 45 | Expect.equal 46 | (generatedTemplate 47 | .SelectToken("properties.createTenantProperties.displayName") 48 | .ToString()) 49 | "My B2C tenant" 50 | "Invalid display name" 51 | 52 | Expect.equal 53 | (generatedTemplate.SelectToken("location").ToString()) 54 | "europe" 55 | "`location` should match with the provided `data_residency`" 56 | 57 | Expect.equal 58 | (generatedTemplate 59 | .SelectToken("properties.createTenantProperties.countryCode") 60 | .ToString()) 61 | "FR" 62 | "Invalid country code" 63 | 64 | Expect.equal (generatedTemplate.SelectToken("sku.name").ToString()) "PremiumP1" "Invalid sku" 65 | } 66 | ] -------------------------------------------------------------------------------- /src/Tests/BingSearch.fs: -------------------------------------------------------------------------------- 1 | module BingSearch 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Builders 6 | open Farmer.BingSearch 7 | open Farmer.Arm 8 | open System 9 | open TestHelpers 10 | 11 | let private asJson (arm: IArmResource) = 12 | arm.JsonModel 13 | |> convertTo< 14 | {| 15 | kind: string 16 | properties: {| statisticsEnabled: bool |} 17 | |} 18 | > 19 | 20 | let tests = 21 | testList "Bing Search" [ 22 | test "Basic test" { 23 | let tags = [ "a", "1"; "b", "2" ] 24 | 25 | let swa = bingSearch { 26 | name "test" 27 | sku S0 28 | add_tags tags 29 | statistics Enabled 30 | } 31 | 32 | let baseArm = (swa :> IBuilder).BuildResources(Location.WestEurope).[0] 33 | let bsArm = baseArm :?> BingSearch.Accounts 34 | let jsonModel = asJson baseArm 35 | Expect.equal bsArm.Name (ResourceName "test") "Name" 36 | Expect.equal bsArm.Location Location.WestEurope "Location" 37 | Expect.isTrue jsonModel.properties.statisticsEnabled "Statistics enabled in json" 38 | Expect.equal bsArm.Statistics FeatureFlag.Enabled "Statistics enabled" 39 | Expect.equal bsArm.Sku S0 "Sku" 40 | Expect.equal jsonModel.kind "Bing.Search.v7" "kind" 41 | Expect.equal bsArm.Tags (Map tags) "Tags" 42 | } 43 | 44 | test "Default options test" { 45 | let swa = bingSearch { name "test" } 46 | 47 | let baseArm = (swa :> IBuilder).BuildResources(Location.WestEurope).[0] 48 | let bsArm = baseArm :?> BingSearch.Accounts 49 | let jsonModel = asJson baseArm 50 | Expect.equal bsArm.Name (ResourceName "test") "Name" 51 | Expect.equal bsArm.Location Location.WestEurope "Location" 52 | Expect.isFalse jsonModel.properties.statisticsEnabled "Statistics not enabled in json" 53 | Expect.equal bsArm.Statistics Disabled "Statistics not enabled" 54 | Expect.equal bsArm.Sku F1 "Sku" 55 | Expect.equal jsonModel.kind "Bing.Search.v7" "kind" 56 | Expect.isEmpty bsArm.Tags "Tags" 57 | } 58 | ] -------------------------------------------------------------------------------- /src/Tests/CognitiveServices.fs: -------------------------------------------------------------------------------- 1 | module CognitiveServices 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Builders 6 | open Farmer.CognitiveServices 7 | open Microsoft.Azure.Management.CognitiveServices 8 | open Microsoft.Rest 9 | open System 10 | 11 | let dummyClient = 12 | new CognitiveServicesManagementClient(Uri "http://management.azure.com", TokenCredentials "NotNullOrWhiteSpace") 13 | 14 | let getResourceAtIndex o = 15 | o |> getResourceAtIndex dummyClient.SerializationSettings 16 | 17 | let tests = 18 | testList "Cognitive Services" [ 19 | test "Basic Cognitive Services test" { 20 | let service = cognitiveServices { 21 | name "test" 22 | api TextAnalytics 23 | sku S0 24 | add_tags [ "a", "1"; "b", "2" ] 25 | } 26 | 27 | let model: Models.CognitiveServicesAccount = service |> getResourceAtIndex 0 28 | 29 | Expect.equal model.Name "test" "Name is wrong" 30 | Expect.equal model.Kind "TextAnalytics" "Kind is wrong" 31 | Expect.equal model.Sku.Name "S0" "Sku is wrong" 32 | 33 | Expect.sequenceEqual 34 | (model.Tags |> Seq.map (fun x -> x.Key, x.Value)) 35 | [ "a", "1"; "b", "2" ] 36 | "Tags are wrong" 37 | } 38 | 39 | test "Key is correctly calculated on a CS instance" { 40 | let service = cognitiveServices { name "test" } 41 | 42 | Expect.equal 43 | service.Key.Owner.Value.ArmExpression.Value 44 | "resourceId('Microsoft.CognitiveServices/accounts', 'test')" 45 | "Owner is wrong" 46 | 47 | Expect.equal 48 | service.Key.Value 49 | "listKeys(resourceId('Microsoft.CognitiveServices/accounts', 'test'), '2017-04-18').key1" 50 | "Key is wrong" 51 | } 52 | 53 | test "Key is correctly calculated with a resource group" { 54 | let key = 55 | CognitiveServices.getKey ( 56 | ResourceId.create (Arm.CognitiveServices.accounts, ResourceName "test", "resource group") 57 | ) 58 | 59 | Expect.equal 60 | key.Value 61 | "listKeys(resourceId('resource group', 'Microsoft.CognitiveServices/accounts', 'test'), '2017-04-18').key1" 62 | "Key is wrong" 63 | } 64 | ] -------------------------------------------------------------------------------- /src/Tests/CommunicationServices.fs: -------------------------------------------------------------------------------- 1 | module CommunicationServices 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Arm 6 | open Farmer.Builders 7 | 8 | let tests = 9 | testList "Communication Services" [ 10 | test "Basic test" { 11 | let tags = [ "a", "1"; "b", "2" ] 12 | 13 | let swa = communicationService { 14 | name "test" 15 | add_tags tags 16 | data_location DataLocation.Australia 17 | } 18 | 19 | let baseArm = (swa :> IBuilder).BuildResources(Location.WestEurope).[0] 20 | let bsArm = baseArm :?> CommunicationService 21 | Expect.equal bsArm.Name (ResourceName "test") "Name" 22 | Expect.equal bsArm.DataLocation DataLocation.Australia "Data Location" 23 | Expect.equal bsArm.Tags (Map tags) "Tags" 24 | } 25 | 26 | test "Default options test" { 27 | let swa = communicationService { name "test" } 28 | 29 | let baseArm = (swa :> IBuilder).BuildResources(Location.WestEurope).[0] 30 | let bsArm = baseArm :?> CommunicationService 31 | Expect.equal bsArm.Name (ResourceName "test") "Name" 32 | Expect.equal bsArm.DataLocation DataLocation.UnitedStates "Data Location" 33 | Expect.isEmpty bsArm.Tags "Tags" 34 | } 35 | ] -------------------------------------------------------------------------------- /src/Tests/EventHub.fs: -------------------------------------------------------------------------------- 1 | module EventHub 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Builders 6 | open Farmer.EventHub 7 | 8 | let tests = 9 | testList "EventHub" [ 10 | test "Gets key on a Hub correctly" { 11 | let hub = eventHub { name "foo" } 12 | 13 | Expect.equal 14 | hub.DefaultKey.Owner.Value.ArmExpression.Value 15 | "resourceId('Microsoft.EventHub/namespaces/eventhubs', 'foo')" 16 | "Incorrect owner" 17 | 18 | Expect.equal 19 | hub.DefaultKey.Value 20 | "listkeys(resourceId('Microsoft.EventHub/namespaces/AuthorizationRules', 'foo-ns', 'RootManageSharedAccessKey'), '2017-04-01').primaryConnectionString" 21 | "Incorrect key" 22 | } 23 | test "Does not explicitly create default consumer group" { 24 | let hub = eventHub { 25 | name "test-event-hub" 26 | // When using Basic tier, attempting to explicitly create a "$Default" consumer group 27 | // will give an error because Basic doesn't support creating consumer groups. 28 | sku EventHubSku.Basic 29 | } 30 | 31 | let defaultResourceName = ResourceName "$Default" 32 | let defaultConsumerGroupExists = hub.ConsumerGroups.Contains defaultResourceName 33 | Expect.isFalse defaultConsumerGroupExists "Created a default consumer group" 34 | } 35 | ] -------------------------------------------------------------------------------- /src/Tests/Helpers.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module TestHelpers 3 | 4 | open Farmer 5 | open Microsoft.Rest.Serialization 6 | 7 | let createSimpleDeployment parameters = { 8 | Location = Location.NorthEurope 9 | PostDeployTasks = [] 10 | Template = { 11 | Outputs = [] 12 | Parameters = parameters |> List.map SecureParameter 13 | Resources = [] 14 | } 15 | RequiredResourceGroups = [] 16 | Tags = Map.empty 17 | } 18 | 19 | let convertTo<'T> = Serialization.toJson >> Serialization.ofJson<'T> 20 | 21 | let farmerToMs<'T when 'T: null> (serializationSettings: Newtonsoft.Json.JsonSerializerSettings) data = 22 | data 23 | |> Serialization.toJson 24 | |> fun json -> SafeJsonConvert.DeserializeObject<'T>(json, serializationSettings) 25 | 26 | let getResourceAtIndex serializationSettings index (builder: #IBuilder) = 27 | builder.BuildResources Location.WestEurope 28 | |> fun r -> r.[index].JsonModel |> farmerToMs serializationSettings 29 | 30 | let findAzureResources<'T when 'T: null> 31 | (serializationSettings: Newtonsoft.Json.JsonSerializerSettings) 32 | (deployment: IDeploymentSource) 33 | = 34 | let template = 35 | deployment.Deployment.Template |> Writer.TemplateGeneration.processTemplate 36 | 37 | template.resources 38 | |> Seq.map Serialization.toJson 39 | |> Seq.choose (fun json -> 40 | SafeJsonConvert.DeserializeObject<'T>(json, serializationSettings) 41 | |> Option.ofObj) 42 | |> Seq.toList 43 | 44 | type TypedArmTemplate<'ResT> = { Resources: 'ResT array } 45 | 46 | let getFirstResourceOrFail (template: TypedArmTemplate<'ResourceType>) = 47 | if Array.length template.Resources < 1 then 48 | raiseFarmer "Template had no resources" 49 | 50 | template.Resources.[0] 51 | 52 | let toTemplate loc (d: IBuilder) = 53 | let a = arm { 54 | location loc 55 | add_resource d 56 | } 57 | 58 | a.Template 59 | 60 | let toTypedTemplate<'ResourceType> loc = 61 | toTemplate loc 62 | >> Writer.toJson 63 | >> Serialization.ofJson> 64 | >> getFirstResourceOrFail -------------------------------------------------------------------------------- /src/Tests/IotHub.fs: -------------------------------------------------------------------------------- 1 | module IotHub 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Builders 6 | open Farmer.IotHub 7 | open Microsoft.Azure.Management.DeviceProvisioningServices 8 | open Microsoft.Azure.Management.DeviceProvisioningServices.Models 9 | open Microsoft.Azure.Management.IotHub 10 | open Microsoft.Azure.Management.IotHub.Models 11 | open Microsoft.Rest 12 | open Microsoft.Rest.Serialization 13 | open System 14 | 15 | let iotClient = 16 | new IotHubClient(Uri "http://management.azure.com", TokenCredentials "NotNullOrWhiteSpace") 17 | 18 | let provisioningClient = 19 | new IotHubClient(Uri "http://management.azure.com", TokenCredentials "NotNullOrWhiteSpace") 20 | 21 | let tests = 22 | testList "IOT Hub" [ 23 | test "Can create a basic hub" { 24 | let resource = 25 | let hub = iotHub { 26 | name "isaacsuperhub" 27 | sku B1 28 | capacity 2 29 | partition_count 2 30 | retention_days 3 31 | } 32 | 33 | arm { add_resource hub } 34 | |> findAzureResources iotClient.SerializationSettings 35 | |> List.head 36 | 37 | Expect.equal resource.Name "isaacsuperhub" "Hub name does not match" 38 | Expect.equal resource.Sku.Name "B1" "Sku name is incorrect" 39 | Expect.equal resource.Sku.Capacity (Nullable 2L) "Sku capacity is incorrect" 40 | 41 | let events = resource.Properties.EventHubEndpoints.["events"] 42 | Expect.equal events.PartitionCount (Nullable 2) "Partition count is incorrect" 43 | Expect.equal events.RetentionTimeInDays (Nullable 3L) "Retention time is incorrect" 44 | } 45 | 46 | test "Creates a provisioning service" { 47 | let resource = 48 | let hub = iotHub { 49 | name "iothub" 50 | enable_device_provisioning 51 | } 52 | 53 | let deployment = arm { add_resource hub } 54 | 55 | deployment.Template.Resources.[1].JsonModel 56 | |> Serialization.toJson 57 | |> fun json -> 58 | SafeJsonConvert.DeserializeObject( 59 | json, 60 | provisioningClient.SerializationSettings 61 | ) 62 | 63 | Expect.equal resource.Sku.Capacity (Nullable 1L) "Sku capacity is incorrect" 64 | Expect.equal resource.Sku.Name "S1" "Sku name capacity is incorrect" 65 | } 66 | ] -------------------------------------------------------------------------------- /src/Tests/LogAnalytics.fs: -------------------------------------------------------------------------------- 1 | module LogAnalytics 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Arm 6 | open Farmer.Builders 7 | open Farmer.Helpers 8 | open Microsoft.Azure.Management.OperationalInsights 9 | open Microsoft.Azure.Management.OperationalInsights.Models 10 | open Microsoft.Rest 11 | open System 12 | 13 | let dummyClient = 14 | new OperationalInsightsManagementClient(Uri "http://management.azure.com", TokenCredentials "NotNullOrWhiteSpace") 15 | 16 | let asAzureResource (ws: WorkspaceConfig) = 17 | arm { add_resource ws } 18 | |> findAzureResources dummyClient.SerializationSettings 19 | |> List.head 20 | |> fun r -> 21 | r.Validate() 22 | r 23 | 24 | let tests = 25 | testList "Log analytics" [ 26 | test "Creates a log analytics workspace" { 27 | let config = logAnalytics { 28 | name "myFarmer" 29 | retention_period 30 30 | enable_query 31 | enable_ingestion 32 | } 33 | 34 | let workspace = asAzureResource config 35 | 36 | Expect.equal workspace.Location "[resourceGroup().location]" "Incorrect Location" 37 | Expect.equal workspace.Name "myFarmer" "Incorrect Name" 38 | Expect.equal workspace.PublicNetworkAccessForIngestion "Enabled" "Incorrect IngestionSupport" 39 | Expect.equal workspace.PublicNetworkAccessForQuery "Enabled" "QuerySupport" 40 | Expect.equal workspace.Sku.Name "PerGB2018" "Incorrect Sku" 41 | Expect.equal workspace.RetentionInDays (Nullable 30) "Incorrect Retention In Days" 42 | } 43 | 44 | test "Ingestion and Query are disabled by default" { 45 | let workspace = logAnalytics { name "" } |> asAzureResource 46 | 47 | Expect.equal workspace.RetentionInDays (Nullable()) "Retention Period should be off by default" 48 | Expect.equal workspace.PublicNetworkAccessForQuery null "Query should be off by default" 49 | Expect.equal workspace.PublicNetworkAccessForIngestion null "Ingestion should be off by default" 50 | } 51 | 52 | test "Can't create log analytics with retention period outside 30 and 730 " { 53 | for days in [ 29; 731 ] do 54 | Expect.throws 55 | (fun _ -> logAnalytics { retention_period days } |> ignore) 56 | (sprintf "Should have thrown for %d" days) 57 | } 58 | ] -------------------------------------------------------------------------------- /src/Tests/LogicApps.fs: -------------------------------------------------------------------------------- 1 | module LogicApps 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Arm 6 | open Farmer.Builders 7 | open Farmer.Helpers 8 | open Microsoft.Azure.Management.Logic 9 | open Microsoft.Azure.Management.Logic.Models 10 | open Microsoft.Rest 11 | open System 12 | open System.Text.Json 13 | 14 | let dummyClient = 15 | new LogicManagementClient(Uri "http://management.azure.com", TokenCredentials "NotNullOrWhiteSpace") 16 | 17 | let asAzureResource (lac: LogicAppConfig) = 18 | arm { add_resource lac } 19 | |> findAzureResources dummyClient.SerializationSettings 20 | |> List.head 21 | |> fun r -> 22 | r.Validate() 23 | r 24 | 25 | let tests = 26 | testList "Logic Apps" [ 27 | test "Creates a logic app workflow" { 28 | let config = logicApp { name "test-logic-app" } 29 | let workflow = asAzureResource config 30 | 31 | Expect.equal workflow.Name "test-logic-app" "Incorrect workflow name" 32 | } 33 | test "Populates a value-based logic app definition" { 34 | // this is the required bare minimum for an empty logic app 35 | // for it to not be set to "null" after parsing 36 | let value = 37 | """ 38 | { 39 | "definition": { 40 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 41 | "actions": {}, 42 | "contentVersion": "1.0.0.0", 43 | "outputs": {}, 44 | "parameters": {}, 45 | "triggers": {} 46 | }, 47 | "parameters": {} 48 | } 49 | """ 50 | 51 | let config = logicApp { 52 | name "test-logic-app" 53 | definition (ValueDefinition value) 54 | } 55 | 56 | let workflow = asAzureResource config 57 | 58 | Expect.isNotNull workflow.Definition "Did not set logic app definition" 59 | } 60 | test "Populates a file-based logic app definition" { 61 | let path = "./test-data/blank-logic-app.json" 62 | 63 | let config = logicApp { 64 | name "test-logic-app" 65 | definition (FileDefinition path) 66 | } 67 | 68 | let workflow = asAzureResource config 69 | Expect.isNotNull workflow.Definition "Did not load definition from file" 70 | } 71 | ] -------------------------------------------------------------------------------- /src/Tests/Maps.fs: -------------------------------------------------------------------------------- 1 | module Maps 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Builders 6 | open Farmer.Maps 7 | open System 8 | open Microsoft.Azure.Management.Maps 9 | open Microsoft.Azure.Management.Maps.Models 10 | open Microsoft.Rest 11 | 12 | let client = 13 | new MapsManagementClient(Uri "http://management.azure.com", TokenCredentials "NotNullOrWhiteSpace") 14 | 15 | let tests = 16 | testList "Maps" [ 17 | test "Can create a basic maps account" { 18 | let resource = 19 | let account = maps { 20 | name "mymaps~@" 21 | sku S0 22 | } 23 | 24 | arm { add_resource account } 25 | |> findAzureResources client.SerializationSettings 26 | |> List.head 27 | 28 | resource.Validate() 29 | Expect.equal resource.Name "mymaps" "" 30 | Expect.equal resource.Sku.Name "S0" "" 31 | } 32 | ] -------------------------------------------------------------------------------- /src/Tests/ResourceGroup.fs: -------------------------------------------------------------------------------- 1 | module ResourceGroup 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Arm.ResourceGroup 6 | open Farmer.Builders 7 | 8 | let tests = 9 | testList "Resource Group" [ 10 | test "Creates a resource group" { 11 | let rg = createResourceGroup "myRg" Location.EastUS 12 | Expect.equal rg.Name.Value "myRg" "Incorrect name on resource group" 13 | Expect.equal rg.Location Location.EastUS "Incorrect location on resource group" 14 | Expect.equal rg.Dependencies Set.empty "Resource group should have no dependencies" 15 | Expect.equal rg.Tags Map.empty "Resource group should have no tags" 16 | } 17 | test "Supports multiple nested deployments to the same resource group" { 18 | let nestedRgs = 19 | [ 1..3 ] 20 | |> List.map (fun i -> 21 | resourceGroup { 22 | name "target-rg" 23 | add_resource (storageAccount { name $"stg{i}" }) 24 | } 25 | :> IBuilder) 26 | 27 | let rg = resourceGroup { 28 | name "outer-rg" 29 | add_resources nestedRgs 30 | } 31 | 32 | Expect.hasLength rg.Template.Resources 3 "all three resource groups should be added" 33 | 34 | let nestedResources = 35 | rg.Template.Resources 36 | |> List.map (fun x -> x :?> ResourceGroupDeployment) 37 | |> List.collect (fun x -> x.Resources) 38 | |> List.map (fun x -> x.ResourceId.Name.Value) 39 | 40 | Expect.equal nestedResources [ "stg1"; "stg2"; "stg3" ] "all three storage accounts should be nested" 41 | } 42 | test "zip_deploy should be performed when declared in a nested resource" { 43 | let webApp = webApp { 44 | name "webapp" 45 | zip_deploy "deploy" 46 | } 47 | 48 | let oneNestedLevel = resourceGroup { add_resource webApp } 49 | let twoNestedLevels = resourceGroup { add_resource oneNestedLevel } 50 | let threeNestedLevels = arm { add_resource twoNestedLevels } 51 | 52 | Expect.isNonEmpty 53 | (threeNestedLevels :> IDeploymentSource).Deployment.PostDeployTasks 54 | "The zip_deploy should create a post deployment task" 55 | } 56 | ] -------------------------------------------------------------------------------- /src/Tests/RoleAssignment.fs: -------------------------------------------------------------------------------- 1 | module RoleAssignment 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Arm 6 | 7 | let tests = 8 | testList "RoleAssignment" [ 9 | test "Produces opaque resource scope" { 10 | let actual: IArmResource = { 11 | Name = ResourceName "assignment" 12 | RoleDefinitionId = Roles.Contributor 13 | PrincipalId = ArmExpression.create "1" |> PrincipalId 14 | PrincipalType = PrincipalType.User 15 | Scope = privateClouds.resourceId "mySDDC" |> UnmanagedResource 16 | Dependencies = Set.empty 17 | } 18 | 19 | "Expected matching scope" 20 | |> Expect.stringContains 21 | (Newtonsoft.Json.JsonConvert.SerializeObject actual.JsonModel) 22 | "\"scope\":\"[resourceId('Microsoft.AVS/privateClouds', 'mySDDC')]\"" 23 | } 24 | ] -------------------------------------------------------------------------------- /src/Tests/StaticWebApp.fs: -------------------------------------------------------------------------------- 1 | module StaticWebApp 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Builders 6 | open Farmer.WebApp 7 | open Farmer.Arm 8 | open System 9 | 10 | let tests = 11 | testList "Static Web App Tests" [ 12 | test "Creates a basic static web app" { 13 | let swa = staticWebApp { 14 | name "foo" 15 | api_location "api" 16 | app_location "app" 17 | artifact_location "artifact" 18 | branch "feature" 19 | repository "https://compositional-it.com" 20 | } 21 | 22 | let swaArm = 23 | (swa :> IBuilder).BuildResources(Location.WestEurope).[0] :?> StaticSite 24 | 25 | Expect.equal swaArm.ApiLocation (Some "api") "Api" 26 | Expect.equal swaArm.Name (ResourceName "foo") "Name" 27 | Expect.equal swaArm.AppLocation "app" "AppLocation" 28 | Expect.equal swaArm.AppArtifactLocation (Some "artifact") "ArtifactLocation" 29 | Expect.equal swaArm.Branch "feature" "Branch" 30 | Expect.equal swaArm.Repository (Uri "https://compositional-it.com") "Repository" 31 | } 32 | test "Defaults to master branch" { 33 | let swa = staticWebApp { 34 | name "foo" 35 | repository "https://compositional-it.com" 36 | } 37 | 38 | let swaArm = 39 | (swa :> IBuilder).BuildResources(Location.WestEurope).[0] :?> StaticSite 40 | 41 | Expect.equal swaArm.Branch "master" "Branch" 42 | } 43 | test "Supports app settings" { 44 | let swa = staticWebApp { 45 | name "foo" 46 | repository "https://compositional-it.com" 47 | app_settings [ "foo", "bar"; "blip", "blop" ] 48 | 49 | } 50 | 51 | let swaArm = 52 | (swa :> IBuilder).BuildResources(Location.WestEurope).[1] :?> StaticSites.Config 53 | 54 | Expect.equal swaArm.Properties (Map [ "foo", "bar"; "blip", "blop" ]) "App Settings not set" 55 | } 56 | ] -------------------------------------------------------------------------------- /src/Tests/Types.fs: -------------------------------------------------------------------------------- 1 | module Types 2 | 3 | open Expecto 4 | open Farmer 5 | open Farmer.Builders 6 | open System 7 | open Newtonsoft.Json.Linq 8 | 9 | let tests = 10 | testList "Type Tests" [ 11 | test "Creates deterministic GUID correctly" { 12 | let actual = DeterministicGuid.create "hello" 13 | Expect.equal (Guid.Parse "4fbe461c-3438-55c4-941e-d1c2013210c5") actual "Incorrect GUID" 14 | } 15 | test "Location.ResourceGroup emits correct ARM expression" { 16 | Expect.equal 17 | Location.ResourceGroup.ArmValue 18 | "[resourceGroup().location]" 19 | "Incorrect expression emitted for Location.ResourceGroup" 20 | } 21 | test "Default location for 'arm' builder uses resourceGroup location" { 22 | let deployment = 23 | let dummyResource = storageAccount { name "mystorageaccount74785" } 24 | arm { add_resource dummyResource } 25 | 26 | let jobj = deployment.Template |> Writer.toJson |> JToken.Parse 27 | 28 | Expect.equal 29 | (jobj.SelectToken "resources[?(@.name=='mystorageaccount74785')].location") 30 | (JValue "[resourceGroup().location]") 31 | "Default location on resource should be resource group." 32 | } 33 | ] -------------------------------------------------------------------------------- /src/Tests/test-data/azure-firewall.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": {}, 5 | "parameters": {}, 6 | "resources": [ 7 | { 8 | "apiVersion": "2020-07-01", 9 | "dependsOn": [ 10 | "[resourceId('Microsoft.Network/virtualHubs', 'farmer_vhub')]" 11 | ], 12 | "location": "northeurope", 13 | "name": "farmer_firewall", 14 | "properties": { 15 | "hubIPAddresses": { 16 | "publicIPs": { 17 | "addresses": [], 18 | "count": 2 19 | } 20 | }, 21 | "sku": { 22 | "name": "AZFW_Hub", 23 | "tier": "Standard" 24 | }, 25 | "virtualHub": { 26 | "id": "[resourceId('Microsoft.Network/virtualHubs', 'farmer_vhub')]" 27 | } 28 | }, 29 | "type": "Microsoft.Network/azureFirewalls", 30 | "zones": [ 31 | "1", 32 | "2" 33 | ] 34 | }, 35 | { 36 | "apiVersion": "2020-07-01", 37 | "dependsOn": [ 38 | "[resourceId('Microsoft.Network/virtualWans', 'farmer-vwan')]" 39 | ], 40 | "location": "northeurope", 41 | "name": "farmer_vhub", 42 | "properties": { 43 | "addressPrefix": "100.73.255.0/24", 44 | "routeTable": { 45 | "routes": [] 46 | }, 47 | "sku": "Standard", 48 | "virtualWan": { 49 | "id": "[resourceId('Microsoft.Network/virtualWans', 'farmer-vwan')]" 50 | } 51 | }, 52 | "type": "Microsoft.Network/virtualHubs" 53 | }, 54 | { 55 | "apiVersion": "2020-07-01", 56 | "location": "northeurope", 57 | "name": "farmer-vwan", 58 | "properties": { 59 | "allowBranchToBranchTraffic": true, 60 | "disableVpnEncryption": true, 61 | "office365LocalBreakoutCategory": "None", 62 | "type": "Standard" 63 | }, 64 | "type": "Microsoft.Network/virtualWans" 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /src/Tests/test-data/blank-logic-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "definition": { 3 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", 4 | "actions": {}, 5 | "contentVersion": "1.0.0.0", 6 | "outputs": {}, 7 | "parameters": {}, 8 | "triggers": {} 9 | }, 10 | "parameters": {} 11 | } -------------------------------------------------------------------------------- /src/Tests/test-data/virtual-wan.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "outputs": {}, 5 | "parameters": {}, 6 | "resources": [ 7 | { 8 | "apiVersion": "2020-07-01", 9 | "location": "northeurope", 10 | "name": "farmer-vwan", 11 | "properties": { 12 | "allowBranchToBranchTraffic": true, 13 | "disableVpnEncryption": true, 14 | "office365LocalBreakoutCategory": "None", 15 | "type": "Standard" 16 | }, 17 | "type": "Microsoft.Network/virtualWans" 18 | } 19 | ] 20 | } --------------------------------------------------------------------------------