protected async Task <(HttpStatusCode Code, Uri PollUrl)> PostFileAsync(IPublishingProfile pp, HttpClient client, string path) { var slotHost = pp.GitUrl.Split(':')[0]; var zipDeployUrl = new UriBuilder() { Scheme = "https", Host = slotHost, Path = "/api/zipdeploy", Query = "isAsync=true" }.ToString(); using (var fs = File.OpenRead(path)) { var(code, pollUrl) = await HttpPolicy.ExecuteAsync(async() => { Console.WriteLine($"HTTP: POST {fs.Length / 1024.0:f1} KiB to {zipDeployUrl}"); using (var response = await client.PostAsync(zipDeployUrl, new StreamContent(fs))) { var pollUrl = response.Headers.Location; Console.WriteLine(" > " + response.StatusCode); return(response.StatusCode, pollUrl); } }); return(code, pollUrl); } }
protected HttpClient CreateHttpClient(IPublishingProfile pp) { var handler = new HttpClientHandler { Credentials = new NetworkCredential(pp.GitUsername, pp.GitPassword) }; return(new HttpClient(handler) { Timeout = TimeSpan.FromMinutes(30) }); }
private static FtpWebRequest CreateRequest(IPublishingProfile publishingProfile, Uri address, string method) { var request = (FtpWebRequest)WebRequest.Create(address); request.Method = method; request.KeepAlive = true; request.UseBinary = true; request.UsePassive = false; request.Credentials = new NetworkCredential(publishingProfile.FtpUsername, publishingProfile.FtpPassword); request.ConnectionGroupName = "group"; return(request); }
private static void UploadFileToWebApp(IPublishingProfile profile, string filePath, string fileName = null) { if (HttpMockServer.Mode != HttpRecorderMode.Playback) { string host = profile.FtpUrl.Split(new char[] { '/' }, 2)[0]; using (var ftpClient = new FtpClient(new FtpClientConfiguration { Host = host, Username = profile.FtpUsername, Password = profile.FtpPassword })) { var fileinfo = new FileInfo(filePath); ftpClient.LoginAsync().GetAwaiter().GetResult(); if (!ftpClient.ListDirectoriesAsync().GetAwaiter().GetResult().Any(fni => fni.Name == "site")) { ftpClient.CreateDirectoryAsync("site").GetAwaiter().GetResult(); } ftpClient.ChangeWorkingDirectoryAsync("./site").GetAwaiter().GetResult(); if (!ftpClient.ListDirectoriesAsync().GetAwaiter().GetResult().Any(fni => fni.Name == "wwwroot")) { ftpClient.CreateDirectoryAsync("wwwroot").GetAwaiter().GetResult(); } ftpClient.ChangeWorkingDirectoryAsync("./wwwroot").GetAwaiter().GetResult(); if (!ftpClient.ListDirectoriesAsync().GetAwaiter().GetResult().Any(fni => fni.Name == "webapps")) { ftpClient.CreateDirectoryAsync("webapps").GetAwaiter().GetResult(); } ftpClient.ChangeWorkingDirectoryAsync("./webapps").GetAwaiter().GetResult(); if (fileName == null) { fileName = Path.GetFileName(filePath); } while (fileName.Contains("/")) { int slash = fileName.IndexOf("/"); string subDir = fileName.Substring(0, slash); ftpClient.CreateDirectoryAsync(subDir).GetAwaiter().GetResult(); ftpClient.ChangeWorkingDirectoryAsync("./" + subDir); fileName = fileName.Substring(slash + 1); } using (var writeStream = ftpClient.OpenFileWriteStreamAsync(fileName).GetAwaiter().GetResult()) { var fileReadStream = fileinfo.OpenRead(); fileReadStream.CopyToAsync(writeStream).GetAwaiter().GetResult(); } } } }
/// <summary> /// Create a function app, upload the files for his running state. The files are run.csx, function.json and host.json /// DO NOT WORK ATM : the inputs / outputs of the function app does not works. /// </summary> public static void CreateFunctionApp() { string appName = SdkContext.RandomResourceName(functionAppPrefix, 20); string suffix = ".azurewebsites.net"; string appUrl = appName + suffix; spin.setMessage("Creating function app " + appName + " in resource group " + rgName + "..."); //Console.WriteLine("Creating function app " + appName + " in resource group " + rgName + "..."); //Console.ReadLine(); IFunctionApp app1 = azure.AppServices.FunctionApps.Define(appName) .WithRegion(Region.EuropeWest) .WithExistingResourceGroup(rgName) .Create(); spin.Stop(); Console.WriteLine("Created Function App"); Console.WriteLine(app1); Console.WriteLine(""); spin.setMessage("Deploying to function app" + appName + " with FTP..."); spin.Start(); IPublishingProfile profile = app1.GetPublishingProfile(); Utilities.UploadFileToFunctionApp(profile, Path.Combine(Utilities.ProjectPath, "FunctionAppCore", "host.json")); Utilities.UploadFileToFunctionApp(profile, Path.Combine(Utilities.ProjectPath, "FunctionAppCore", "IoTHubTrigger", "function.json"), "IoTHubTrigger/function.json"); Utilities.UploadFileToFunctionApp(profile, Path.Combine(Utilities.ProjectPath, "FunctionAppCore", "IoTHubTrigger", "run.csx"), "IoTHubTrigger/run.csx"); //sync triggers app1.SyncTriggers(); spin.Stop(); Console.WriteLine("Deployment iotHubTrigger to web app" + app1.Name + " completed"); //warm up //Console.WriteLine("Warming up " + appUrl + "/api/IoTHubTrigger"); //Utilities.PostAddress("http://" + appUrl + "/api/IoTHubTrigger", "toto"); //SdkContext.DelayProvider.Delay(5000); //Console.WriteLine("Curling..."); //Console.WriteLine(Utilities.PostAddress("http://" + appUrl + "/api/IoTHubTrigger", "toto")); }
/** * Azure App Service basic sample for managing function apps. * - Create 4 function apps under the same new app service plan: * - Deploy to 1 using FTP * - Deploy to 2 using local Git repository * - Deploy to 3 using a publicly available Git repository * - Deploy to 4 using a GitHub repository with continuous integration */ public static void RunSample(IAzure azure) { // New resources string suffix = ".azurewebsites.net"; string app1Name = SdkContext.RandomResourceName("webapp1-", 20); string app2Name = SdkContext.RandomResourceName("webapp2-", 20); string app3Name = SdkContext.RandomResourceName("webapp3-", 20); string app4Name = SdkContext.RandomResourceName("webapp4-", 20); string app1Url = app1Name + suffix; string app2Url = app2Name + suffix; string app3Url = app3Name + suffix; string app4Url = app4Name + suffix; string rgName = SdkContext.RandomResourceName("rg1NEMV_", 24); try { //============================================================ // Create a function app with a new app service plan Utilities.Log("Creating function app " + app1Name + " in resource group " + rgName + "..."); IFunctionApp app1 = azure.AppServices.FunctionApps.Define(app1Name) .WithRegion(Region.USWest) .WithNewResourceGroup(rgName) .Create(); Utilities.Log("Created function app " + app1.Name); Utilities.Print(app1); //============================================================ // Deploy to app 1 through FTP Utilities.Log("Deploying a function app to " + app1Name + " through FTP..."); IPublishingProfile profile = app1.GetPublishingProfile(); Utilities.UploadFileToFtp(profile, Path.Combine(Utilities.ProjectPath, "Asset", "square-function-app", "host.json")); Utilities.UploadFileToFtp(profile, Path.Combine(Utilities.ProjectPath, "Asset", "square-function-app", "square", "function.json"), "square/function.json"); Utilities.UploadFileToFtp(profile, Path.Combine(Utilities.ProjectPath, "Asset", "square-function-app", "square", "index.js"), "square/index.js"); Utilities.Log("Deployment square app to web app " + app1.Name + " completed"); Utilities.Print(app1); // warm up Utilities.Log("Warming up " + app1Url + "/api/square..."); Utilities.PostAddress("http://" + app1Url + "/api/square", "625"); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + app1Url + "/api/square..."); Utilities.Log(Utilities.PostAddress("http://" + app1Url + "/api/square", "625")); //============================================================ // Create a second function app with local git source control Utilities.Log("Creating another function app " + app2Name + " in resource group " + rgName + "..."); IAppServicePlan plan = azure.AppServices.AppServicePlans.GetById(app1.AppServicePlanId); IFunctionApp app2 = azure.AppServices.FunctionApps.Define(app2Name) .WithExistingAppServicePlan(plan) .WithExistingResourceGroup(rgName) .WithExistingStorageAccount(app1.StorageAccount) .WithLocalGitSourceControl() .Create(); Utilities.Log("Created function app " + app2.Name); Utilities.Print(app2); //============================================================ // Deploy to app 2 through local Git Utilities.Log("Deploying a local Tomcat source to " + app2Name + " through Git..."); profile = app2.GetPublishingProfile(); Utilities.DeployByGit(profile, "square-function-app"); Utilities.Log("Deployment to function app " + app2.Name + " completed"); Utilities.Print(app2); // warm up Utilities.Log("Warming up " + app2Url + "/api/square..."); Utilities.PostAddress("http://" + app2Url + "/api/square", "725"); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + app2Url + "/api/square..."); Utilities.Log("Square of 725 is " + Utilities.PostAddress("http://" + app2Url + "/api/square", "725")); //============================================================ // Create a 3rd function app with a public GitHub repo in Azure-Samples Utilities.Log("Creating another function app " + app3Name + "..."); IFunctionApp app3 = azure.AppServices.FunctionApps.Define(app3Name) .WithExistingAppServicePlan(plan) .WithNewResourceGroup(rgName) .WithExistingStorageAccount(app2.StorageAccount) .DefineSourceControl() .WithPublicGitRepository("https://github.com/jianghaolu/square-function-app-sample") .WithBranch("master") .Attach() .Create(); Utilities.Log("Created function app " + app3.Name); Utilities.Print(app3); // warm up Utilities.Log("Warming up " + app3Url + "/api/square..."); Utilities.PostAddress("http://" + app3Url + "/api/square", "825"); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + app3Url + "/api/square..."); Utilities.Log("Square of 825 is " + Utilities.PostAddress("http://" + app3Url + "/api/square", "825")); //============================================================ // Create a 4th function app with a personal GitHub repo and turn on continuous integration Utilities.Log("Creating another function app " + app4Name + "..."); IFunctionApp app4 = azure.AppServices.FunctionApps .Define(app4Name) .WithExistingAppServicePlan(plan) .WithExistingResourceGroup(rgName) .WithExistingStorageAccount(app3.StorageAccount) // Uncomment the following lines to turn on 4th scenario //.DefineSourceControl() // .WithContinuouslyIntegratedGitHubRepository("username", "reponame") // .WithBranch("master") // .WithGitHubAccessToken("YOUR GITHUB PERSONAL TOKEN") // .Attach() .Create(); Utilities.Log("Created function app " + app4.Name); Utilities.Print(app4); // warm up Utilities.Log("Warming up " + app4Url + "..."); Utilities.CheckAddress("http://" + app4Url); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + app4Url + "..."); Utilities.Log(Utilities.CheckAddress("http://" + app4Url)); } finally { try { Utilities.Log("Deleting Resource Group: " + rgName); azure.ResourceGroups.DeleteByName(rgName); Utilities.Log("Deleted Resource Group: " + rgName); } catch (NullReferenceException) { Utilities.Log("Did not create any resources in Azure. No clean up is necessary"); } catch (Exception g) { Utilities.Log(g); } } }
/** * Azure App Service basic sample for managing function apps. * - Create 4 function apps under the same new app service plan: * - Deploy to 1 using FTP * - Deploy to 2 using local Git repository * - Deploy to 3 using a publicly available Git repository * - Deploy to 4 using a GitHub repository with continuous integration */ public static void RunSample(IAzure azure) { // New resources string suffix = ".azurewebsites.net"; string app1Name = SdkContext.RandomResourceName("webapp1-", 20); string app2Name = SdkContext.RandomResourceName("webapp2-", 20); string app1Url = app1Name + suffix; string app2Url = app2Name + suffix; string rgName = SdkContext.RandomResourceName("rg1NEMV_", 24); try { //============================================================ // Create a function app with admin level auth Utilities.Log("Creating function app " + app1Name + " in resource group " + rgName + " with admin level auth..."); IFunctionApp app1 = azure.AppServices.FunctionApps.Define(app1Name) .WithRegion(Region.USWest) .WithNewResourceGroup(rgName) .WithLocalGitSourceControl() .Create(); Utilities.Log("Created function app " + app1.Name); Utilities.Print(app1); //============================================================ // Create a second function app with function level auth Utilities.Log("Creating another function app " + app2Name + " in resource group " + rgName + " with function level auth..."); IAppServicePlan plan = azure.AppServices.AppServicePlans.GetById(app1.AppServicePlanId); IFunctionApp app2 = azure.AppServices.FunctionApps.Define(app2Name) .WithExistingAppServicePlan(plan) .WithExistingResourceGroup(rgName) .WithExistingStorageAccount(app1.StorageAccount) .WithLocalGitSourceControl() .Create(); Utilities.Log("Created function app " + app2.Name); Utilities.Print(app2); //============================================================ // Deploy to app 1 through Git Utilities.Log("Deploying a local function app to " + app1Name + " through Git..."); IPublishingProfile profile = app1.GetPublishingProfile(); Utilities.DeployByGit(profile, "square-function-app-admin-auth"); // warm up Utilities.Log("Warming up " + app1Url + "/api/square..."); Utilities.PostAddress("http://" + app1Url + "/api/square", "625"); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + app1Url + "/api/square..."); Utilities.Log("Square of 625 is " + Utilities.PostAddress("http://" + app1Url + "/api/square?code=" + app1.GetMasterKey(), "625")); //============================================================ // Deploy to app 2 through Git Utilities.Log("Deploying a local function app to " + app2Name + " through Git..."); profile = app2.GetPublishingProfile(); Utilities.DeployByGit(profile, "square-function-app-function-auth"); Utilities.Log("Deployment to function app " + app2.Name + " completed"); Utilities.Print(app2); string masterKey = app2.GetMasterKey(); var functionsHeader = new Dictionary <string, string>(); functionsHeader["x-functions-key"] = masterKey; string response = Utilities.CheckAddress("http://" + app2Url + "/admin/functions/square/keys", functionsHeader); Regex pattern = new Regex(@"""name"":""default"",""value"":""([\w=/]+)"""); Match matcher = pattern.Match(response); string functionKey = matcher.Captures[0].Value; // warm up Utilities.Log("Warming up " + app2Url + "/api/square..."); Utilities.PostAddress("http://" + app2Url + "/api/square", "725"); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + app2Url + "/api/square..."); Utilities.Log("Square of 725 is " + Utilities.PostAddress("http://" + app2Url + "/api/square?code=" + functionKey, "725")); } finally { try { Utilities.Log("Deleting Resource Group: " + rgName); azure.ResourceGroups.DeleteByName(rgName); Utilities.Log("Deleted Resource Group: " + rgName); } catch (NullReferenceException) { Utilities.Log("Did not create any resources in Azure. No clean up is necessary"); } catch (Exception g) { Utilities.Log(g); } } }
protected async Task <bool> WaitForCompleteAsync(IPublishingProfile ppSlot, Deployment latestDeployment, (HttpStatusCode Code, Uri PollUrl) upload, bool withSwap)
/** * Azure App Service basic sample for managing function apps. * - Create a function app under the same new app service plan: * - Deploy to app using FTP * - stream logs for 30 seconds */ public static void RunSample(IAzure azure) { // New resources string suffix = ".azurewebsites.net"; string appName = SdkContext.RandomResourceName("webapp1-", 20); string appUrl = appName + suffix; string rgName = SdkContext.RandomResourceName("rg1NEMV_", 24); try { //============================================================ // Create a function app with a new app service plan Utilities.Log("Creating function app " + appName + " in resource group " + rgName + "..."); IFunctionApp app = azure.AppServices.FunctionApps.Define(appName) .WithRegion(Region.USWest) .WithNewResourceGroup(rgName) .Create(); Utilities.Log("Created function app " + app.Name); Utilities.Print(app); //============================================================ // Deploy to app 1 through FTP Utilities.Log("Deploying a function app to " + appName + " through FTP..."); IPublishingProfile profile = app.GetPublishingProfile(); Utilities.UploadFileToFunctionApp(profile, Path.Combine(Utilities.ProjectPath, "Asset", "square-function-app", "host.json")); Utilities.UploadFileToFunctionApp(profile, Path.Combine(Utilities.ProjectPath, "Asset", "square-function-app", "square", "function.json"), "square/function.json"); Utilities.UploadFileToFunctionApp(profile, Path.Combine(Utilities.ProjectPath, "Asset", "square-function-app", "square", "index.js"), "square/index.js"); // sync triggers app.SyncTriggers(); Utilities.Log("Deployment square app to web app " + app.Name + " completed"); Utilities.Print(app); // warm up Utilities.Log("Warming up " + appUrl + "/api/square..."); Utilities.PostAddress("http://" + appUrl + "/api/square", "625"); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + appUrl + "/api/square..."); Utilities.Log(Utilities.PostAddress("http://" + appUrl + "/api/square", "625")); //============================================================ // Listen to logs synchronously for 30 seconds using (var stream = app.StreamApplicationLogs()) { var reader = new StreamReader(stream); Utilities.Log("Streaming logs from function app " + appName + "..."); string line = reader.ReadLine(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); Task.Factory.StartNew(() => { Utilities.PostAddress("http://" + appUrl + "/api/square", "625"); SdkContext.DelayProvider.Delay(10000); Utilities.PostAddress("http://" + appUrl + "/api/square", "725"); SdkContext.DelayProvider.Delay(10000); Utilities.PostAddress("http://" + appUrl + "/api/square", "825"); }); while (line != null && stopWatch.ElapsedMilliseconds < 30000) { Utilities.Log(line); line = reader.ReadLine(); } } } finally { try { Utilities.Log("Deleting Resource Group: " + rgName); azure.ResourceGroups.DeleteByName(rgName); Utilities.Log("Deleted Resource Group: " + rgName); } catch (NullReferenceException) { Utilities.Log("Did not create any resources in Azure. No clean up is necessary"); } catch (Exception g) { Utilities.Log(g); } } }
/** * Azure App Service basic sample for managing function apps. * - Create 3 function apps under the same new app service plan and with the same storage account * - Deploy 1 & 2 via Git a function that calculates the square of a number * - Deploy 3 via Web Deploy * - Enable app level authentication for the 1st function app * - Verify the 1st function app can be accessed with the admin key * - Enable function level authentication for the 2nd function app * - Verify the 2nd function app can be accessed with the function key * - Enable function level authentication for the 3rd function app * - Verify the 3rd function app can be accessed with the function key */ public static void RunSample(IAzure azure) { // New resources string suffix = ".azurewebsites.net"; string app1Name = SdkContext.RandomResourceName("webapp1-", 20); string app2Name = SdkContext.RandomResourceName("webapp2-", 20); string app3Name = SdkContext.RandomResourceName("webapp3-", 20); string app1Url = app1Name + suffix; string app2Url = app2Name + suffix; string app3Url = app3Name + suffix; string rgName = SdkContext.RandomResourceName("rg1NEMV_", 24); try { //============================================================ // Create a function app with admin level auth Utilities.Log("Creating function app " + app1Name + " in resource group " + rgName + " with admin level auth..."); IFunctionApp app1 = azure.AppServices.FunctionApps.Define(app1Name) .WithRegion(Region.USWest) .WithNewResourceGroup(rgName) .WithLocalGitSourceControl() .Create(); Utilities.Log("Created function app " + app1.Name); Utilities.Print(app1); //============================================================ // Create a second function app with function level auth Utilities.Log("Creating another function app " + app2Name + " in resource group " + rgName + " with function level auth..."); IAppServicePlan plan = azure.AppServices.AppServicePlans.GetById(app1.AppServicePlanId); IFunctionApp app2 = azure.AppServices.FunctionApps.Define(app2Name) .WithExistingAppServicePlan(plan) .WithExistingResourceGroup(rgName) .WithExistingStorageAccount(app1.StorageAccount) .WithLocalGitSourceControl() .Create(); Utilities.Log("Created function app " + app2.Name); Utilities.Print(app2); //============================================================ // Create a third function app with function level auth Utilities.Log("Creating another function app " + app3Name + " in resource group " + rgName + " with function level auth..."); IFunctionApp app3 = azure.AppServices.FunctionApps.Define(app3Name) .WithExistingAppServicePlan(plan) .WithExistingResourceGroup(rgName) .WithExistingStorageAccount(app1.StorageAccount) .WithLocalGitSourceControl() .Create(); Utilities.Log("Created function app " + app3.Name); Utilities.Print(app3); //============================================================ // Deploy to app 1 through Git Utilities.Log("Deploying a local function app to " + app1Name + " through Git..."); IPublishingProfile profile = app1.GetPublishingProfile(); Utilities.DeployByGit(profile, "square-function-app-admin-auth"); // warm up Utilities.Log("Warming up " + app1Url + "/api/square..."); Utilities.PostAddress("http://" + app1Url + "/api/square", "625"); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + app1Url + "/api/square..."); Utilities.Log("Square of 625 is " + Utilities.PostAddress("http://" + app1Url + "/api/square?code=" + app1.GetMasterKey(), "625")); //============================================================ // Deploy to app 2 through Git Utilities.Log("Deploying a local function app to " + app2Name + " through Git..."); profile = app2.GetPublishingProfile(); Utilities.DeployByGit(profile, "square-function-app-function-auth"); Utilities.Log("Deployment to function app " + app2.Name + " completed"); Utilities.Print(app2); string functionKey = app2.ListFunctionKeys("square").Values.First(); // warm up Utilities.Log("Warming up " + app2Url + "/api/square..."); Utilities.PostAddress("http://" + app2Url + "/api/square", "725"); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + app2Url + "/api/square..."); Utilities.Log("Square of 725 is " + Utilities.PostAddress("http://" + app2Url + "/api/square?code=" + functionKey, "725")); Utilities.Log("Adding a new key to function app " + app2.Name + "..."); var newKey = app2.AddFunctionKey("square", "newkey", null); Utilities.Log("CURLing " + app2Url + "/api/square..."); Utilities.Log("Square of 825 is " + Utilities.PostAddress("http://" + app2Url + "/api/square?code=" + newKey.Value, "825")); //============================================================ // Deploy to app 3 through web deploy Utilities.Log("Deploying a local function app to " + app3Name + " throuh web deploy..."); app3.Deploy() .WithPackageUri("https://github.com/Azure/azure-libraries-for-net/raw/master/Samples/Asset/square-function-app-function-auth.zip") .WithExistingDeploymentsDeleted(false) .Execute(); Utilities.Log("Deployment to function app " + app3.Name + " completed"); Utilities.Log("Adding a new key to function app " + app3.Name + "..."); app3.AddFunctionKey("square", "newkey", "mysecretkey"); // warm up Utilities.Log("Warming up " + app3Url + "/api/square..."); Utilities.PostAddress("http://" + app3Url + "/api/square", "925"); SdkContext.DelayProvider.Delay(5000); Utilities.Log("CURLing " + app3Url + "/api/square..."); Utilities.Log("Square of 925 is " + Utilities.PostAddress("http://" + app3Url + "/api/square?code=mysecretkey", "925")); } finally { try { Utilities.Log("Deleting Resource Group: " + rgName); azure.ResourceGroups.DeleteByName(rgName); Utilities.Log("Deleted Resource Group: " + rgName); } catch (NullReferenceException) { Utilities.Log("Did not create any resources in Azure. No clean up is necessary"); } catch (Exception g) { Utilities.Log(g); } } }
public static async Task UploadFilesAsync(this IWebApp site, DirectoryInfo from, string to, IPublishingProfile publishingProfile, ILogger logger) { foreach (var info in from.GetFileSystemInfos("*")) { var address = new Uri( "ftp://" + publishingProfile.FtpUrl + to + info.FullName.Substring(from.FullName.Length + 1).Replace('\\', '/')); if (info is FileInfo file) { logger.LogInformation($"Uploading {file.FullName} to {address}"); var request = CreateRequest(publishingProfile, address, WebRequestMethods.Ftp.UploadFile); using (var fileStream = File.OpenRead(file.FullName)) { using (var requestStream = await request.GetRequestStreamAsync()) { await fileStream.CopyToAsync(requestStream); } } await request.GetResponseAsync(); } if (info is DirectoryInfo directory) { var request = CreateRequest(publishingProfile, address, WebRequestMethods.Ftp.MakeDirectory); await request.GetResponseAsync(); await UploadFilesAsync(site, directory, to + directory.Name + '/', publishingProfile, logger); } } }