private async static Task CreateAzureStorageAccount(SetupInformation info)
        {
            var key = await AzureManagementUtility.CreateStorageAccount(info.AzureAccessToken,
                                                                        info.AzureTargetSubscriptionId,
                                                                        info.AzureResourceGroupName,
                                                                        info.AzureServicePlanName,
                                                                        info.AzureBlobStorageName,
                                                                        info.AzureLocationDisplayName);

            info.AzureStorageKey = key;
        }
        private async static Task BuildAndDeployJob(SetupInformation info, String jobName, String jobPath, String basePath, JobType jobType)
        {
            // Run PowerShell script to build and deploy
            Hashtable packageBuildParameters = new Hashtable();

            packageBuildParameters.Add("ProjectPath", jobPath);

            var    powerShellScriptPath = (new System.IO.FileInfo($@"{basePath}\OfficeDevPnP.PartnerPack.Setup\Scripts\MsBuildWebJob.ps1")).FullName;
            string packageBuildResult   = Run.RunScript(powerShellScriptPath, packageBuildParameters);

            if (packageBuildResult.Contains("Build FAILED"))
            {
                // TODO: Handle exception
            }

            // Create the WebJob ZIP file
            var binPath = Path.Combine(jobPath, @"bin\Release");
            var zipPath = $@"{basePath}\OfficeDevPnP.PartnerPack.Setup\{jobName}.zip";

            // Remove any already existing file
            if (System.IO.File.Exists(zipPath))
            {
                System.IO.File.Delete(zipPath);
            }

            System.IO.Compression.ZipFile.CreateFromDirectory(binPath, zipPath);

            // Get the Azure App Service Publishing Credentials
            var xmlPublishingSettings = XElement.Parse(info.AzureAppPublishingSettings);

            if (xmlPublishingSettings != null)
            {
                var xmlPublishProfile = xmlPublishingSettings.Element("publishProfile");

                if (xmlPublishProfile != null)
                {
                    var username = xmlPublishProfile.Attribute("userName").Value;
                    var password = xmlPublishProfile.Attribute("userPWD").Value;

                    // Upload the WebJobB
                    await AzureManagementUtility.UploadWebJob(info.AzureAppServiceName, username, password, jobName, zipPath, jobType);
                }
            }

            // Remove the ZIP file after having created the job
            if (System.IO.File.Exists(zipPath))
            {
                System.IO.File.Delete(zipPath);
            }
        }
        private async static Task CreateAzureAppService(SetupInformation info)
        {
            var appSettings = new AzureAppServiceSetting[3];

            appSettings[0] = new AzureAppServiceSetting {
                Name = "WEBSITE_LOAD_CERTIFICATES", Value = "*"
            };
            appSettings[1] = new AzureAppServiceSetting {
                Name = "WEBJOBS_IDLE_TIMEOUT", Value = "10000"
            };
            appSettings[2] = new AzureAppServiceSetting {
                Name = "SCM_COMMAND_IDLE_TIMEOUT", Value = "10000"
            };

            await AzureManagementUtility.CreateAppServiceWebSite(info.AzureAccessToken,
                                                                 info.AzureTargetSubscriptionId,
                                                                 info.AzureResourceGroupName,
                                                                 info.AzureServicePlanName,
                                                                 info.AzureAppServiceName,
                                                                 info.AzureLocationDisplayName,
                                                                 appSettings);

            var certificate = info.AuthenticationCertificate;
            var pfxBlob     = certificate.Export(X509ContentType.Pfx, info.SslCertificatePassword);

            await AzureManagementUtility.UploadCertificateToAzureAppService(info.AzureAccessToken,
                                                                            info.AzureTargetSubscriptionId,
                                                                            info.AzureResourceGroupName,
                                                                            info.AzureAppServiceName,
                                                                            info.AzureLocationDisplayName,
                                                                            pfxBlob,
                                                                            info.SslCertificatePassword);

            info.AzureAppPublishingSettings = await AzureManagementUtility.GetAppServiceWebSitePublishingSettings(
                info.AzureAccessToken,
                info.AzureTargetSubscriptionId,
                info.AzureResourceGroupName,
                info.AzureAppServiceName);
        }
        private async static Task CreateAzureResourceGroup(SetupInformation info)
        {
            await AzureManagementUtility.RegisterAzureProvider(info.AzureAccessToken,
                                                               info.AzureTargetSubscriptionId,
                                                               "Microsoft.Storage");

            await AzureManagementUtility.RegisterAzureProvider(info.AzureAccessToken,
                                                               info.AzureTargetSubscriptionId,
                                                               "Microsoft.Web");

            info.AzureResourceGroupName = $"{info.AzureAppServiceName}-resource-group";
            info.AzureServicePlanName   = $"{info.AzureAppServiceName}-plan";

            await AzureManagementUtility.CreateResourceGroup(info.AzureAccessToken,
                                                             info.AzureTargetSubscriptionId,
                                                             info.AzureResourceGroupName,
                                                             info.AzureLocationId);

            await AzureManagementUtility.CreateServicePlan(info.AzureAccessToken,
                                                           info.AzureTargetSubscriptionId,
                                                           info.AzureResourceGroupName,
                                                           info.AzureServicePlanName,
                                                           info.AzureLocationDisplayName);
        }
        private async static Task RegisterAzureADApplication(SetupInformation info)
        {
            // Fix the App URL
            if (!info.AzureWebAppUrl.EndsWith("/"))
            {
                info.AzureWebAppUrl = info.AzureWebAppUrl + "/";
            }

            // Load the App Manifest template
            Stream stream = typeof(SetupManager)
                            .Assembly
                            .GetManifestResourceStream("OfficeDevPnP.PartnerPack.Setup.Resources.azure-ad-app-manifest.json");

            using (StreamReader sr = new StreamReader(stream))
            {
                // Get the JSON manifest
                var jsonApplication = sr.ReadToEnd();

                var application   = JsonConvert.DeserializeObject <AzureAdApplication>(jsonApplication);
                var keyCredential = JsonConvert.DeserializeObject <KeyCredential>(info.AzureAppKeyCredential);

                application.displayName    = info.ApplicationName;
                application.homepage       = info.AzureWebAppUrl;
                application.identifierUris = new List <String>();
                application.identifierUris.Add(info.ApplicationUniqueUri);
                application.keyCredentials = new List <KeyCredential>();
                application.keyCredentials.Add(keyCredential);
                application.replyUrls = new List <String>();
                application.replyUrls.Add(info.AzureWebAppUrl);

                // Generate the Application Shared Secret
                var    startDate = DateTime.Now;
                Byte[] bytes     = new Byte[32];
                using (var rand = System.Security.Cryptography.RandomNumberGenerator.Create())
                {
                    rand.GetBytes(bytes);
                }
                info.AzureAppSharedSecret       = System.Convert.ToBase64String(bytes);
                application.passwordCredentials = new List <object>();
                application.passwordCredentials.Add(new AzureAdApplicationPasswordCredential
                {
                    CustomKeyIdentifier = null,
                    StartDate           = startDate.ToString("o"),
                    EndDate             = startDate.AddYears(2).ToString("o"),
                    KeyId = Guid.NewGuid().ToString(),
                    Value = info.AzureAppSharedSecret,
                });

                // Get an Access Token to create the application via Microsoft Graph
                var office365AzureADAccessToken = await AzureManagementUtility.GetAccessTokenSilentAsync(
                    AzureManagementUtility.MicrosoftGraphResourceId,
                    ConfigurationManager.AppSettings["O365:ClientId"]);

                var azureAdApplicationCreated = false;

                // Create the Azure AD Application
                try
                {
                    await CreateAzureADApplication(info, application, office365AzureADAccessToken);

                    azureAdApplicationCreated = true;
                }
                catch (ApplicationException ex)
                {
                    var graphError = JsonConvert.DeserializeObject <GraphError>(((HttpException)ex.InnerException).Message);
                    if (graphError != null && graphError.error.code == "Request_BadRequest" &&
                        graphError.error.message.Contains("identifierUris already exists"))
                    {
                        // We need to remove the existing application

                        // Thus, retrieve it
                        String jsonApplications = await HttpHelper.MakeGetRequestForStringAsync(
                            String.Format("{0}applications?$filter=identifierUris/any(c:c+eq+'{1}')",
                                          AzureManagementUtility.MicrosoftGraphBetaBaseUri,
                                          HttpUtility.UrlEncode(info.ApplicationUniqueUri)),
                            office365AzureADAccessToken);

                        var applications        = JsonConvert.DeserializeObject <AzureAdApplications>(jsonApplications);
                        var applicationToUpdate = applications.Applications.FirstOrDefault();
                        if (applicationToUpdate != null)
                        {
                            // Remove it
                            await HttpHelper.MakeDeleteRequestAsync(
                                String.Format("{0}applications/{1}",
                                              AzureManagementUtility.MicrosoftGraphBetaBaseUri,
                                              applicationToUpdate.Id),
                                office365AzureADAccessToken);

                            // And add it again
                            await CreateAzureADApplication(info, application, office365AzureADAccessToken);

                            azureAdApplicationCreated = true;
                        }
                    }
                }

                if (azureAdApplicationCreated)
                {
                    // TODO: We should upload the logo
                    // property mainLogo: stream of the application via PATCH
                }
            }
        }
        private async static Task CreateInfrastructuralSiteCollectionAsync(SetupInformation info)
        {
            Uri infrastructureSiteUri = new Uri(info.InfrastructuralSiteUrl);
            Uri tenantAdminUri        = new Uri(infrastructureSiteUri.Scheme + "://" +
                                                infrastructureSiteUri.Host.Replace(".sharepoint.com", "-admin.sharepoint.com"));
            Uri sharepointUri = new Uri(infrastructureSiteUri.Scheme + "://" +
                                        infrastructureSiteUri.Host + "/");
            var siteUrl           = info.InfrastructuralSiteUrl.Substring(info.InfrastructuralSiteUrl.IndexOf("sharepoint.com/") + 14);
            var siteCreated       = false;
            var siteAlreadyExists = false;

            var accessToken = await AzureManagementUtility.GetAccessTokenSilentAsync(
                tenantAdminUri.ToString(), ConfigurationManager.AppSettings["O365:ClientId"]);

            AuthenticationManager am = new AuthenticationManager();

            using (var adminContext = am.GetAzureADAccessTokenAuthenticatedContext(
                       tenantAdminUri.ToString(), accessToken))
            {
                adminContext.RequestTimeout = Timeout.Infinite;

                var tenant = new Tenant(adminContext);

                // Check if the site already exists, and eventually removes it from the Recycle Bin
                if (tenant.CheckIfSiteExists(info.InfrastructuralSiteUrl, "Recycled"))
                {
                    tenant.DeleteSiteCollectionFromRecycleBin(info.InfrastructuralSiteUrl);
                }

                siteAlreadyExists = tenant.SiteExists(info.InfrastructuralSiteUrl);
                if (!siteAlreadyExists)
                {
                    // Configure the Site Collection properties
                    SiteEntity newSite = new SiteEntity();
                    newSite.Description          = "PnP Partner Pack - Infrastructural Site Collection";
                    newSite.Lcid                 = (uint)info.InfrastructuralSiteLCID;
                    newSite.Title                = newSite.Description;
                    newSite.Url                  = info.InfrastructuralSiteUrl;
                    newSite.SiteOwnerLogin       = info.InfrastructuralSitePrimaryAdmin;
                    newSite.StorageMaximumLevel  = 1000;
                    newSite.StorageWarningLevel  = 900;
                    newSite.Template             = "STS#0";
                    newSite.TimeZoneId           = info.InfrastructuralSiteTimeZone;
                    newSite.UserCodeMaximumLevel = 0;
                    newSite.UserCodeWarningLevel = 0;

                    // Create the Site Collection and wait for its creation (we're asynchronous)
                    tenant.CreateSiteCollection(newSite, true, true, (top) =>
                    {
                        if (top == TenantOperationMessage.CreatingSiteCollection)
                        {
                            var maxProgress = (100 / (Int32)SetupStep.Completed);
                            info.ViewModel.SetupProgress += 1;
                            if (info.ViewModel.SetupProgress >= maxProgress)
                            {
                                info.ViewModel.SetupProgress = maxProgress;
                            }
                        }
                        return(false);
                    });
                }
            }

            await Task.Delay(5000);

            using (var adminContext = am.GetAzureADAccessTokenAuthenticatedContext(
                       tenantAdminUri.ToString(), accessToken))
            {
                adminContext.RequestTimeout = Timeout.Infinite;

                var  tenant = new Tenant(adminContext);
                Site site   = tenant.GetSiteByUrl(info.InfrastructuralSiteUrl);
                Web  web    = site.RootWeb;

                adminContext.Load(site, s => s.Url);
                adminContext.Load(web, w => w.Url);
                adminContext.ExecuteQueryRetry();

                // Enable Secondary Site Collection Administrator
                if (!String.IsNullOrEmpty(info.InfrastructuralSiteSecondaryAdmin))
                {
                    Microsoft.SharePoint.Client.User secondaryOwner = web.EnsureUser(info.InfrastructuralSiteSecondaryAdmin);
                    secondaryOwner.IsSiteAdmin = true;
                    secondaryOwner.Update();

                    web.SiteUsers.AddUser(secondaryOwner);
                    adminContext.ExecuteQueryRetry();
                }
                siteCreated = true;
            }

            if (siteAlreadyExists || siteCreated)
            {
                accessToken = await AzureManagementUtility.GetAccessTokenSilentAsync(
                    sharepointUri.ToString(), ConfigurationManager.AppSettings["O365:ClientId"]);

                using (ClientContext clientContext = am.GetAzureADAccessTokenAuthenticatedContext(
                           info.InfrastructuralSiteUrl, accessToken))
                {
                    clientContext.RequestTimeout = Timeout.Infinite;

                    Site site = clientContext.Site;
                    Web  web  = site.RootWeb;

                    clientContext.Load(site, s => s.Url);
                    clientContext.Load(web, w => w.Url);
                    clientContext.ExecuteQueryRetry();

                    // Override settings within templates, before uploading them
                    UpdateProvisioningTemplateParameter("Responsive", "SPO-Responsive.xml",
                                                        "AzureWebSiteUrl", info.AzureWebAppUrl);
                    UpdateProvisioningTemplateParameter("Overrides", "PnP-Partner-Pack-Overrides.xml",
                                                        "AzureWebSiteUrl", info.AzureWebAppUrl);

                    // Apply the templates to the target site
                    ApplyProvisioningTemplate(web, "Infrastructure", "PnP-Partner-Pack-Infrastructure-Jobs.xml");
                    ApplyProvisioningTemplate(web, "Infrastructure", "PnP-Partner-Pack-Infrastructure-Templates.xml");
                    ApplyProvisioningTemplate(web, "", "PnP-Partner-Pack-Infrastructure-Contents.xml");

                    // We to it twice to force the content types, due to a small bug in the provisioning engine
                    ApplyProvisioningTemplate(web, "", "PnP-Partner-Pack-Infrastructure-Contents.xml");
                }
            }
            else
            {
                // TODO: Handle some kind of exception ...
            }
        }