public static async Task <string> GetCurrentAemoPassword(TraceWriter log) { log.Info($"Initializing get current AEMO password in persistence: {DateTime.Now}"); var KeyVaultUrlFromConfig = Environment.GetEnvironmentVariable("KeyVaultUrl"); var environment = Environment.GetEnvironmentVariable("Environment"); var keyVaultUrl = string.Format("{0}/secrets/AemoFtpPassword", KeyVaultUrlFromConfig); SecretBundle secretBundle = new SecretBundle(); var azureServiceTokenProvider = new AzureServiceTokenProvider(); try { log.Info($"Creating kv client..."); var keyVaultClient = new KeyVaultClient( new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)); log.Info($"Retrieving secret {keyVaultUrl}"); secretBundle = await keyVaultClient.GetSecretAsync(keyVaultUrl) .ConfigureAwait(false); } catch (Exception exp) { var exceptionMessage = $"<h1>AEMO Password Changer Workflow Message</h1></br><span style = \"color:red;\">Could not get current AEMO password from {keyVaultUrl} with response {exp.Message}</span>"; log.Error(exceptionMessage); await Messaging.SendNotification(log, false, emailSubjectOverride : $"The [{environment}] AEMO password changer failed to change the AEMO password!", emailHtmlBodyOverride : exceptionMessage, slackBodyOverride : exceptionMessage, smsBodyOverride : exceptionMessage, slackIconOverride : ":bandit:"); } log.Info($"Finalizing get current AEMO password in persistenced: {DateTime.Now}"); return(secretBundle.Value); }
/// <summary>Start or stop the function apps</summary> /// <param name="log"></param> /// <param name="accessToken"></param> /// <param name="start"></param> /// <param name="resourceGroupToStopStart"></param> /// <param name="appService"></param> private static async Task <bool> StartFunctionApp(TraceWriter log, string accessToken, bool start, string resourceGroupToStopStart, string appService) { string action = start == true ? "start" : "stop"; log.Info($"Initializing {action} function app {resourceGroupToStopStart}|{appService}: {DateTime.Now}"); var subscriptionId = Environment.GetEnvironmentVariable("SubscriptionId"); var environment = Environment.GetEnvironmentVariable("Environment"); var baseUrl = "https://management.azure.com/subscriptions/" + subscriptionId + "/resourceGroups/" + resourceGroupToStopStart + "/providers/Microsoft.Web/sites/" + appService + "/" + action + "?api-version=2016-08-01"; Dictionary <string, string> headers = new Dictionary <string, string>() { { "Authorization", string.Format("Bearer {0}", accessToken) } }; var httpClient = HttpClientHelper.GetOrCreateClient(baseUrl, headers); var response = await httpClient.PostAsync(baseUrl, null); if (response.StatusCode != HttpStatusCode.OK) { var exceptionMessage = $"<h1>AEMO Password Changer Workflow Message</h1></br><span style = \"color:red;\">{action} function app {appService} failed with response {response.Content.ReadAsStringAsync().Result}</span>"; log.Error(exceptionMessage); await Messaging.SendNotification(log, false, emailSubjectOverride : $"The [{environment}] AEMO password changer failed to change the AEMO password!", emailHtmlBodyOverride : exceptionMessage, slackBodyOverride : exceptionMessage, smsBodyOverride : exceptionMessage, slackIconOverride : ":bandit:"); return(false); } log.Info($"Finalizing {action} function app {resourceGroupToStopStart}|{appService}: {DateTime.Now}"); return(true); }
/// <summary>Cycle through lists of apps to stop</summary> /// <param name="log"></param> /// <param name="accessToken"></param> /// <param name="rgsWithApps"></param> /// <param name="start"></param> private static async Task <bool> StartStopApps(TraceWriter log, string accessToken, List <string> rgsWithApps, bool start) { bool allStopped = false; var environment = Environment.GetEnvironmentVariable("Environment"); foreach (var item in rgsWithApps) { var rgWithApp = item.Split('|').ToList(); bool appStopped = await StartFunctionApp(log, accessToken, start, rgWithApp[0], rgWithApp[1]); if (appStopped == false) { var exceptionMessage = $"<h1>AEMO Password Changer Workflow Message</h1></br><span style = \"color:red;\">Not all apps were stopped. ABORTING: {DateTime.Now}</span>"; log.Error(exceptionMessage); await Messaging.SendNotification(log, false, emailSubjectOverride : $"The [{environment}] AEMO password changer failed to change the AEMO password!", emailHtmlBodyOverride : exceptionMessage, slackBodyOverride : exceptionMessage, smsBodyOverride : exceptionMessage, slackIconOverride : ":bandit:"); break; } allStopped = true; } return(allStopped); }
public static async Task <string> RotateAndAddNewAemoPassword(TraceWriter log, string lastAemoFtpPassword, string aemoFtpPassword, bool fakeIt) { log.Info($"Initializing rotate and add new AEMO password in persistence: {DateTime.Now}"); if (fakeIt) { var environment = Environment.GetEnvironmentVariable("Environment"); log.Info($"Faking password change: {DateTime.Now}"); return($"Note: the application is running in test mode and therefore the [{environment}] AEMO password was not changed"); } else { var keyVaultUrlFromConfig = Environment.GetEnvironmentVariable("KeyVaultUrl"); var environment = Environment.GetEnvironmentVariable("Environment"); var aemoOldPasswordUrl = string.Format("{0}/secrets/LastAemoFtpPassword", keyVaultUrlFromConfig); var aemoNewPasswordUrl = string.Format("{0}/secrets/AemoFtpPassword", keyVaultUrlFromConfig); SecretBundle secretBundle = new SecretBundle(); var azureServiceTokenProvider = new AzureServiceTokenProvider(); var keyVaultClient = new KeyVaultClient( new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)); try { log.Info($"Creating kv client..."); log.Info($"update secret LastAemoFtpPassword (first 4 characters):{lastAemoFtpPassword.Substring(0, 4)}"); secretBundle = await keyVaultClient.SetSecretAsync(keyVaultUrlFromConfig, string.Format("LastAemoFtpPassword"), lastAemoFtpPassword) .ConfigureAwait(false); } catch (Exception exp) { var exceptionMessage = $"<h1>AEMO Password Changer Workflow Message</h1></br><span style = \"color:red;\">Could not update old password AEMO password from (first 4 characters) {aemoOldPasswordUrl.Substring(0, 4)} with response {exp.Message}</span>"; log.Error(exceptionMessage); await Messaging.SendNotification(log, false, emailSubjectOverride : $"The [{environment}] AEMO password changer failed to change the AEMO password!", emailHtmlBodyOverride : exceptionMessage, slackBodyOverride : exceptionMessage, smsBodyOverride : exceptionMessage, slackIconOverride : ":bandit:"); } try { log.Info($"update newPassword secret newPassword (first 4 characters):{aemoFtpPassword.Substring(0, 4)}"); secretBundle = await keyVaultClient.SetSecretAsync(keyVaultUrlFromConfig, "AemoFtpPassword", aemoFtpPassword) .ConfigureAwait(false); } catch (Exception exp) { log.Error($"Could not update new password AEMO password from {aemoNewPasswordUrl} with response {exp.Message}"); } log.Info($"Finalizing rotate and add new AEMO password in persistence: {DateTime.Now}"); return(secretBundle.Id); } }
/// <summary>Change the AEMO password</summary> /// <param name="log"></param> /// <param name="newPassword"></param> /// <param name="oldAemoPassword"></param> private static async Task <bool> ChangeAemoPassword(TraceWriter log, string newPassword, string oldAemoPassword, bool fakeIt) { log.Info($"Initializing ChangeAemoPassword: {DateTime.Now}"); if (fakeIt) { log.Info($"Faking password change: {DateTime.Now}"); return(true); } else { var aemoPasswordRestUrl = Environment.GetEnvironmentVariable("AemoPasswordRestUrl"); var aemoOriginalWebSite = Environment.GetEnvironmentVariable("AemoOriginalWebSite"); var aemoUserName = Environment.GetEnvironmentVariable("AemoUserName"); var environment = Environment.GetEnvironmentVariable("Environment"); var httpClient = HttpClientHelper.GetOrCreateClient(aemoPasswordRestUrl); AemoPayload aemoPayload = new AemoPayload { AemoUrl = aemoOriginalWebSite, UserName = aemoUserName, OldPassword = oldAemoPassword, NewPassword = newPassword }; string payload = JsonConvert.SerializeObject(aemoPayload); var content = new StringContent(payload, Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync(aemoPasswordRestUrl, content); AemoResponseDetail responseDetail = JsonConvert.DeserializeObject <AemoResponseDetail>(await response.Content.ReadAsStringAsync()); if (response.StatusCode != HttpStatusCode.OK) { var exceptionMessage = $"<h1>AEMO Password Changer Workflow Message</h1></br><span style = \"color:red;\">Could not change Aemo password: (StatusCode:{ response.StatusCode}). Response { responseDetail.Result}</span>"; log.Error(exceptionMessage); await Messaging.SendNotification(log, false, emailSubjectOverride : $"The [{environment}] AEMO password changer failed to change the AEMO password!", emailHtmlBodyOverride : exceptionMessage, slackBodyOverride : exceptionMessage, smsBodyOverride : exceptionMessage, slackIconOverride : ":bandit:"); return(false); } log.Info($"Aemo password changes succesfully: (StatusCode:{response.StatusCode}). Response {responseDetail.Result}"); log.Info($"Finalizing ChangeAemoPassword: {DateTime.Now}"); return(true); } }
/// <summary>Get an Azure access_token</summary> /// <param name="log"></param> public static async Task <string> Authenticate(TraceWriter log) { log.Info($"Initializing Azure authentication: {DateTime.Now}"); var tenant_id = Environment.GetEnvironmentVariable("TenantId"); var grant_type = Environment.GetEnvironmentVariable("GrantType"); var client_id = Environment.GetEnvironmentVariable("ClientId"); var client_secret = Environment.GetEnvironmentVariable("ClientSecret"); var resource = Environment.GetEnvironmentVariable("Resource"); var environment = Environment.GetEnvironmentVariable("Environment"); var token_url = "https://login.microsoftonline.com/" + tenant_id + "/oauth2/token"; // log.Info($"The token_url is: {token_url}"); var httpClient = HttpClientHelper.GetOrCreateClient(token_url); var payload = $"client_id={client_id}&client_secret={client_secret}&grant_type={grant_type}&resource={resource}"; // log.Info($"The payload is: {payload}"); var content = new StringContent(payload, Encoding.UTF8, "application/x-www-form-urlencoded"); var response = await httpClient.PostAsync(token_url, content); if (response.StatusCode != HttpStatusCode.OK) { var exceptionMessage = string.Format("<h1>AEMO Password Changer Workflow Message</h1></br><span style = \"color:red;\">Access Token cannot be obtained, process terminating: {0}</span>", response.Content.ReadAsStringAsync().Result); log.Error(exceptionMessage); await Messaging.SendNotification(log, false, emailSubjectOverride : $"The [{environment}] AEMO password changer failed to change the AEMO password!", emailHtmlBodyOverride : exceptionMessage, slackBodyOverride : exceptionMessage, smsBodyOverride : exceptionMessage, slackIconOverride : ":bandit:"); return(null); } log.Info($"Request for token was successful, starting deserialization"); AuthenticationTokenObject tokenResponse = JsonConvert.DeserializeObject <AuthenticationTokenObject>(await response.Content.ReadAsStringAsync()); log.Info($"access_token is: {tokenResponse.AccessToken.Substring(0, 10)}XXXXXXX"); log.Info($"Finalizing Azure authentication: {DateTime.Now}"); return(tokenResponse.AccessToken); }
public static async Task Run([TimerTrigger("%WorkflowInitiatorTimer%")] TimerInfo myTimer, TraceWriter log) { log.Info($"Initializing AEMO workflow: {DateTime.Now}"); bool fakeIt = true; var fakeItIsASetting = Environment.GetEnvironmentVariable("FakeIt") == null ? false : true; if (fakeItIsASetting) { fakeIt = bool.Parse(Environment.GetEnvironmentVariable("FakeIt")); } // Authenticate for Azure REST API var accessToken = await AuthenticateAzure.Authenticate(log); if (string.IsNullOrEmpty(accessToken)) { throw new NullReferenceException("The access_token was returned as null"); } var resourceGroupAndFunctionAppList = Environment.GetEnvironmentVariable("ResourceGroupAndFunctionAppList"); var environment = Environment.GetEnvironmentVariable("Environment"); var emailHtmlBody = Environment.GetEnvironmentVariable("EmailHtmlBody"); var slackBody = Environment.GetEnvironmentVariable("SlackBody"); var smsBody = Environment.GetEnvironmentVariable("SmsBody"); // A list of all apps that require stop, update config and start List <string> rgsWithApps = resourceGroupAndFunctionAppList.Split(';').ToList(); // Did we stop all the apps? bool allStopped = await StartStopApps(log, accessToken, rgsWithApps, false); if (allStopped) { log.Info($"Function apps successfully stopped: {DateTime.Now}"); // TODO : Move this inside if stopped maybe? var aemoFtpPassword = await Persistence.GetCurrentAemoPassword(log); string newPassword = PasswordGenerator.GeneratePassword(true, true, true, true, false, 8); // Call AEMO password changer bool passwordChanged = await ChangeAemoPassword(log, newPassword, aemoFtpPassword, fakeIt); if (passwordChanged) { string rotatedPasswordUrlFromKeyVault = await Persistence.RotateAndAddNewAemoPassword(log, aemoFtpPassword, newPassword, fakeIt); // Start function app bool allStarted = await StartStopApps(log, accessToken, rgsWithApps, true); if (allStarted) { log.Info($"Function app successfully started: {DateTime.Now}"); } emailHtmlBody = string.Format(emailHtmlBody, environment, rotatedPasswordUrlFromKeyVault); slackBody = string.Format(slackBody, environment, rotatedPasswordUrlFromKeyVault); smsBody = string.Format(smsBody, environment, rotatedPasswordUrlFromKeyVault); // send notifiaction to developers await Messaging.SendNotification(log, fakeIt, emailHtmlBodyOverride : emailHtmlBody, slackBodyOverride : slackBody, smsBodyOverride : smsBody); } else { var exceptionMessage = $"<h1>AEMO Password Changer Workflow Message</h1></br><span style = \"color:red;\">Could not change the AMO password: {DateTime.Now}</span>"; log.Error(exceptionMessage); await Messaging.SendNotification(log, false, emailSubjectOverride : $"The [{environment}] AEMO password changer failed to change the AEMO password!", emailHtmlBodyOverride : exceptionMessage, slackBodyOverride : exceptionMessage, smsBodyOverride : exceptionMessage, slackIconOverride : ":bandit:"); } } else { // Start function app bool allStarted = await StartStopApps(log, accessToken, rgsWithApps, true); if (allStarted) { log.Info($"Function app successfully started: {DateTime.Now}"); } } log.Info($"Finalizing AEMO workflow: {DateTime.Now}"); }