Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure App Service PAAS deployment #65

Merged
merged 22 commits into from
May 1, 2018
Merged

Azure App Service PAAS deployment #65

merged 22 commits into from
May 1, 2018

Conversation

theprash
Copy link
Member

Adds ARM template and Azure deployment to FAKE script for #51.

I needed to introduce an environment variable to allow changing the public content path because the Azure deployment I was using required putting the server at the root.

This depends on #64.

@isaacabraham
Copy link
Member

Closes #51

@isaacabraham isaacabraham changed the title Azure deployment Azure App Service PAAS deployment Apr 26, 2018
@@ -81,6 +81,12 @@
"dataType": "bool",
"defaultValue": "false",
"description": "use NPM instead of default Yarn for JS package management"
},
"Azure": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about introducing a choice parameter, say "Deploy" with two options for now: Docker and Azure - both options do not have much sense if combined

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I've made that change.

"sku": { "name": "F1" },
"name": "[variables('appServicePlan')]",
"apiVersion": "2016-09-01",
"location": "West Europe"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no experience with ARM but wondering whether all of the options here, like location should be hard-coded?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SKU and Location can both be parameterised (the Location definitely). @theprash The script is already using the Region type, which contains consts for all Azure regions, for the deployment object so you can use that as a means to supply this as an input parameter to the template. The SKU I don't think is available in code - this template has an example of showing the different SKUs available and how to set them as input values.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a pricingTier parameter for the SKU.

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the Web version, you get a web.config file which is used by IIS / Azure App Service to start up the application through dotnet and to pass through traffic from IIS to the app.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that would be applicable only to Azure option? Should this be a conditional then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this would've been conditional, yes. @theprash thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theimowski @isaacabraham, I thought it might be better to avoid a conditional branch on the project type just to reduce complexity a little bit if there's little or no downside in using the .Web version for all versions of the template. Aside from the web.config file, it also adds some extra entries in Server.deps.json which seem to not be very important, but I don't know what they're for.

I'm happy to make it conditional though...?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I've done it 😀

client.UploadData(destinationUri, IO.File.ReadAllBytes zipFile) |> ignore
)

"Publish" ==> "DeployWebApp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put that into a single chain?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically this is what the dependencies are (the ARM deploy only sets up Azure infrastructure) but @isaacabraham has already put them in a single chain and I'm happy with it.

@@ -124,6 +186,11 @@ Target "Docker" (fun _ ->
==> "Docker"
#endif

#if (Azure)
"Clean" ==> "Publish" ==> "DeployWebApp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above

@theimowski
Copy link
Member

Started reviewing, but didn't yet manage to test myself

isaacabraham and others added 4 commits April 27, 2018 11:26
Simplify FAKE chain.
Improve error reporting for missing ARM arguments.
# Conflicts:
#	Content/build.fsx
#	Content/src/Server/ServerGiraffe.fs
#	Content/src/Server/ServerSaturn.fs
#	Content/src/Server/ServerSuave.fs
@@ -125,11 +138,15 @@
"condition": "(Fulma != \"login\")"
},
{
"exclude": "**/Dockerfile",
"exclude": "Dockerfile",
"condition": "(!Docker)"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the condition should now change to "(Deploy != \"docker\")"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops I missed those. Fix now, thanks.

"condition": "(!Docker)"
},
{
"exclude": "**/yarn.lock",
"exclude": "arm-template.json",
"condition": "(!Azure)"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to above

let armTemplate = @"arm-template.json"
let resourceGroupName = "safe-" + environment
let subscriptionId = try getBuildParam "subscriptionId" |> Guid.Parse with _ -> failwith "Invalid Subscription ID. This should be your Azure Subscription ID."
let clientId = try getBuildParam "clientId" |> Guid.Parse with _ -> failwith "Invalid Client ID. This should be the Client ID of a Native application registered in Azure with permission to create resources in your subscription."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I generate clientId? Create a "Web App" in azure then what?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be documented properly but you need to create an app in Active Directory, set it to "Native" and then give it permissions to Windows Azure. The application id is the client id you need to use.

@theimowski
Copy link
Member

Is the public_path env var a must?

@isaacabraham
Copy link
Member

@theimowski what should we use instead? Environment variables usually work quite well across most systems I imagine.

@theprash
Copy link
Member Author

@theimowski, We needed to be able to configure the public folder path in order for the Azure deploy to work, at least with the method that we're using here. The server had to be at the root of the published folder so it could not refer to a "Client" directory outside of itself. I discussed with @isaacabraham a few different ways we could do this including adding a config file, which is not well supported yet by FAKE for .NET Core. We thought an environment variable would be the least intrusive method for now. It can be set easily for the app in Azure and other systems as Isaac mentioned above.

@theimowski
Copy link
Member

Sorry I didn't make myself clear. I meant to ask if its required to have any variance for the public_path and if so why's that?

@theimowski
Copy link
Member

Ok thanks, then again I'd it in for making that conditional just for azure option

@isaacabraham
Copy link
Member

Yeah. We could've deployed both the "client" and "server" folders as now but that makes things somewhat more complicated in terms of build / packaging I think. Perhaps we'll revisit this in the future but for now I think what is here should work (apart from your comment about making the environment bit Azure-only - I think that we can do that.)

@isaacabraham
Copy link
Member

@theimowski I've now created a (lengthy) guide on the docs site in the template-deployment branch. Can you give it a run through and see if it works / makes sense for you? We can then release this alongside that.

@isaacabraham
Copy link
Member

I've now also added support for Suave, which is hosted in App Service slightly differently than Giraffe.

@theimowski
Copy link
Member

I'll have a look on Tuesday

@@ -150,7 +181,8 @@
}
}
]
},"**/*.fsx": {
},
"**/*.fs*": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this change intentional?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see you also now use //#if instead of #if in .fsx script - is that this change that allowed that? Maybe we can get rid of this entry completely then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theprash one for you.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was required to enable templating in the FSPROJ file. I believe the entry is still required for FSX too. The commenting of #if was not related to this change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, makes sense. So you commented #if in fsx for better editor support? I think I like it this way now

@theimowski
Copy link
Member

theimowski commented May 1, 2018

I started testing the appservice target and run into some dll hell issue after entering the code to browser:

Finished Target: Bundle
Starting Target: ArmTemplate (==> Bundle)
Deploying template 'arm-template.json' to resource group 'safe-795cba0b' in subscription '{subscriptionId}'...
Redirect assembly from 'System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' to 'System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code {code} to authenticate.
Running build failed.
Error:
System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Rest.ClientRuntime.Azure, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified.
File name: 'Microsoft.Rest.ClientRuntime.Azure, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
   at Microsoft.Azure.Management.ResourceManager.Fluent.ResourceManager.Authenticated..ctor(RestClient restClient)
   at FSI_0005.Cit.Helpers.Arm.authenticateDevice@128-2.Invoke(AuthenticationResult _arg2)
   at Microsoft.FSharp.Control.AsyncBuilderImpl.args@825-1.Invoke(a a)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.FSharp.Control.AsyncBuilderImpl.commit[a](AsyncImplResult`1 res)
   at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronouslyInCurrentThread[a](CancellationToken token, FSharpAsync`1 computation)
   at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously[a](CancellationToken token, FSharpAsync`1 computation, FSharpOption`1 timeout)
   at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously[T](FSharpAsync`1 computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken)
   at FSI_0005.Build.clo@89-12.Invoke(Unit _arg1)
   at Fake.TargetHelper.runSingleTarget(TargetTemplate`1 target) in D:\code\fake\src\app\FakeLib\TargetHelper.fs:line 626

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  %home%\sandbox\test\packages\build\FAKE\tools\FAKE.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: DisplayName = Microsoft.Rest.ClientRuntime.Azure, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
 (Fully-specified)
LOG: Appbase = file:///%home%/sandbox/test/packages/build/FAKE/tools/
LOG: Initial PrivatePath = NULL
Calling assembly : Microsoft.Azure.Management.ResourceManager.Fluent, Version=1.0.0.60, Culture=neutral, PublicKeyToken=31bf3856ad364e35.
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: Using application configuration file: %home%\sandbox\test\packages\build\FAKE\tools\FAKE.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Post-policy reference: Microsoft.Rest.ClientRuntime.Azure, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
LOG: Attempting download of new URL file:///%home%/sandbox/test/packages/build/FAKE/tools/Microsoft.Rest.ClientRuntime.Azure.DLL.
LOG: Attempting download of new URL file:///%home%/sandbox/test/packages/build/FAKE/tools/Microsoft.Rest.ClientRuntime.Azure/Microsoft.Rest.ClientRuntime.Azure.DLL.
LOG: Attempting download of new URL file:///%home%/sandbox/test/packages/build/FAKE/tools/Microsoft.Rest.ClientRuntime.Azure.EXE.
LOG: Attempting download of new URL file:///%home%/sandbox/test/packages/build/FAKE/tools/Microsoft.Rest.ClientRuntime.Azure/Microsoft.Rest.ClientRuntime.Azure.EXE.
LOG: Attempting download of new URL file:///%home%/sandbox/test/packages/build/Microsoft.Azure.Management.ResourceManager.Fluent/lib/netstandard1.3/Microsoft.Rest.ClientRuntime.Azure.DLL.
LOG: Attempting download of new URL file:///%home%/sandbox/test/packages/build/Microsoft.Azure.Management.ResourceManager.Fluent/lib/netstandard1.3/Microsoft.Rest.ClientRuntime.Azure/Microsoft.Rest.ClientRuntime.Azure.DLL.
LOG: Attempting download of new URL file:///%home%/sandbox/test/packages/build/Microsoft.Azure.Management.ResourceManager.Fluent/lib/netstandard1.3/Microsoft.Rest.ClientRuntime.Azure.EXE.
LOG: Attempting download of new URL file:///%home%/sandbox/test/packages/build/Microsoft.Azure.Management.ResourceManager.Fluent/lib/netstandard1.3/Microsoft.Rest.ClientRuntime.Azure/Microsoft.Rest.ClientRuntime.Azure.EXE.


---------------------------------------------------------------------
Build Time Report
---------------------------------------------------------------------
Target              Duration
------              --------
Clean               00:00:00.1522493
InstallDotNetCore   00:00:00.1603336
InstallClient       00:00:02.2953899
Build               00:00:20.5715311
Bundle              00:00:03.3486279
ArmTemplate         Failure
Total:              00:01:24.0550037
---------------------------------------------------------------------
Status:             Failure
---------------------------------------------------------------------
---------------------------------------------------------------------

Not the binding redirect for System.Net.Http at the beginning - not sure if that's desired?

@isaacabraham
Copy link
Member

@theimowski The binding redirect with System.Net.Http is a red herring here I think. I've seen this issue this weekend with Microsoft.Rest.ClientRuntime.Azure. The "fix" is (and don't laugh) to make a change to the FAKE script e.g. add a new line anywhere in the script, and then re-run it. It then works.

I have no idea why this is happening or how to prevent it from occurring in the first place.

@theimowski
Copy link
Member

WTF, do we know why's that? If not the "fix" should be documented for sure

@isaacabraham
Copy link
Member

@theimowski I have no idea. Can you test it - maybe it just happens on my machine!

@isaacabraham
Copy link
Member

I'm also considering moving away from the SDK and shelling out to the az command line tool instead (and we would install that during the fake script).

@theimowski
Copy link
Member

Yes it worked :) Something FAKE-related? @matthid any ideas why resaving a fake script may have impact on assembly binding? This is using FAKE 4

@matthid
Copy link
Contributor

matthid commented May 1, 2018

So does this happen again after that and you need to make another change or does it always work now?

@theimowski
Copy link
Member

Works now without need to make more changes. The Redirect assembly from 'System.Net.Http warning is still there, but works anyway

@matthid
Copy link
Contributor

matthid commented May 1, 2018

No idea, so it is not reproduce-able at all?

@theimowski
Copy link
Member

LGTM - I managed to deploy Saturn and Suave versions to azure.
Awesome work, huge props @theprash and @isaacabraham !

Can we add a note to docs about the FAKE modify-file "fix" until we know what's the real issue here?

@theimowski theimowski merged commit fe17e18 into SAFE-Stack:master May 1, 2018
@matthid
Copy link
Contributor

matthid commented May 1, 2018

Can we add a note to docs about the FAKE modify-file "fix" until we know what's the real issue here?

Or upgrade to latest version of fake ;)

@theimowski
Copy link
Member

Yes that's probably a good idea anyway - added #70

@theimowski
Copy link
Member

This is now released in 0.15
Thanks again

@theprash theprash deleted the azure-deploy branch May 1, 2018 10:18
@jarlestabell
Copy link

Amazing work, thanks! :)
BTW: I also got the Could not load file or assembly 'Microsoft.Rest.ClientRuntime.Azure, Version=3.0.0.0... error at first and applied the fix (dummy edit of the Fake script). And then it worked.

@isaacabraham
Copy link
Member

@jarlestabell yeah. It's annoying - hopefully moving to FAKE 5 will "fix" this, but we don't know exactly why it's happening :-/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants