From 9d814ae640cbb87610ac6c6814106a70b1c16890 Mon Sep 17 00:00:00 2001 From: Rob Chappelle Date: Tue, 21 May 2019 00:23:14 +0000 Subject: [PATCH 01/37] Added README.md file --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..0ca446aab --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Introduction +TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. + +# Getting Started +TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: +1. Installation process +2. Software dependencies +3. Latest releases +4. API references + +# Build and Test +TODO: Describe and show how to build your code and run the tests. + +# Contribute +TODO: Explain how other users and developers can contribute to make your code better. + +If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files: +- [ASP.NET Core](https://github.com/aspnet/Home) +- [Visual Studio Code](https://github.com/Microsoft/vscode) +- [Chakra Core](https://github.com/Microsoft/ChakraCore) \ No newline at end of file From 9b5c6b0121b936aa449b9a9a9aa340d56974d092 Mon Sep 17 00:00:00 2001 From: Rob Chappelle Date: Mon, 10 Jun 2019 18:50:26 +0000 Subject: [PATCH 02/37] Added file CONTRIBUTING.md --- CONTRIBUTING.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..889eb364d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,38 @@ +--- +ms.reviewedAt: Date of last review in mm/dd/yyyy format +ms.reviewedBy: alias of person that last reviewed this file +--- + +# Contributing + +Welcome contributors to your project then list particular contribution procedures your team follows including but not limited to the sections below. + +Sample text may go like this. *Welcome and thank you for your interest +in contributing to **project-name**! Before contributing to this +project, please review this document for policies and procedures which +will ease the contribution and review process for everyone. If you have +questions, please contact **contact-method**. This project adopted Inner +Source +[model](https://oe-documentation.azurewebsites.net/inner-source/index.html).* + +## Issues and Feature Requests + +Describe procedures or other requirements on filing issues, fixing bugs, proposing new features, etc that you have including references to roadmaps or other considerations contributors should make before submitting anything to the project team for review. + +## Style Guidelines + +Include any code and/or documentation style guidelines, linting requirements, etc. + +## Pull Request Process + +Give very detailed instructions on your PR procedure to ensure contributions are made in the way your teams wants/expects them. This eases the process for everyone. An example process could include the following but this should align with your team's processes. + +1. Ensure builds are still successful and tests, including any added or updated tests, pass prior to submitting the pull request. +2. Update any documentation, user and contributor, that is impacted by your changes. +3. Increase the version numbers in any examples and the `README.md` to the new version that this pull request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. Include your change description in `CHANGELOG.md` file as part of pull request. +5. You may merge the pull request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. + +## License Information + +Inform the user of the license(s) under which this project accepts and distributes contributions. If you have any license agreement or other legal requirements, include those here as well. \ No newline at end of file From 2b9ae9f8a124c811f72bc198360954ddbca92eaa Mon Sep 17 00:00:00 2001 From: Rob Chappelle Date: Tue, 22 Oct 2019 22:27:21 +0000 Subject: [PATCH 03/37] Updated README.md --- README.md | 120 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 100 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 0ca446aab..60790e7c9 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,100 @@ -# Introduction -TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. - -# Getting Started -TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: -1. Installation process -2. Software dependencies -3. Latest releases -4. API references - -# Build and Test -TODO: Describe and show how to build your code and run the tests. - -# Contribute -TODO: Explain how other users and developers can contribute to make your code better. - -If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files: -- [ASP.NET Core](https://github.com/aspnet/Home) -- [Visual Studio Code](https://github.com/Microsoft/vscode) -- [Chakra Core](https://github.com/Microsoft/ChakraCore) \ No newline at end of file +--- +ArtifactType: nupkg, executable, azure-web-app, azure-cloud-service, etc. More requirements for artifact type standardization may come later. +Documentation: URL +Language: typescript, csharp, java, js, python, golang, powershell, markdown, etc. More requirements for language names standardization may come later. +Platform: windows, node, linux, ubuntu16, azure-function, etc. More requirements for platform standardization may come later. +Stackoverflow: URL +Tags: comma,separated,list,of,tags +--- + +# Project Title. MUST BE topmost header + +One Paragraph of project description goes here. Including links to other user docs or a project website is good here as well. This paragraph will be used as a blurb on CodeHub. Please make the first paragraph short and to the point. + +You can expand on project description in subsequent paragraphs. It is a good practice to explain how this project is used and what other projects depend on it. + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. + +### Prerequisites + +What things you need to install the software and how to install them + +``` powershell +Give examples +``` + +### Installing + +A step by step series of examples that tell you how to get a development environment running + +1. Describe what needs to be done first + + ``` batch + Give an example of performing step 1 + ``` + +2. And then repeat for each step + + ``` sh + Another example, this time for step 2 + ``` + +## Running the tests + +Explain how to run the tests for this project that are relevant to users. You can also link to the testing portion of [CONTRIBUTING.md](CONTRIBUTING.md) for tests relevant to contributors. + +### End-to-end tests + +Explain what these tests test and why + +``` +Give an example +``` + +### Unit tests + +Explain what these test and why + +``` +Give examples +``` + +## Deployment + +Add additional notes about how to deploy this on a live system + +## Built With + +Documenting some of the main tools used to build this project, manage dependencies, etc will help users get more information if they are trying to understand or having difficulties getting the project up and running. + +* Link to some dependency manager +* Link to some framework or build tool +* Link to some compiler, linting tool, bundler, etc + +## Contributing + +Please read our [CONTRIBUTING.md](CONTRIBUTING.md) which outlines all of our policies, procedures, and requirements for contributing to this project. + +## Versioning and changelog + +We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](link-to-tags-or-other-release-location). + +It is a good practice to keep `CHANGELOG.md` file in repository that can be updated as part of a pull request. + +## Authors + +List main authors of this project with a couple of words about their contribution. + +Also insert a link to the `owners.txt` file if it exists as well as any other dashboard or other resources that lists all contributors to the project. + +## License + +This project is licensed under the < INSERT LICENSE NAME > - see the [LICENSE](LICENSE) file for details + +## Acknowledgments + +* Hat tip to anyone whose code was used +* Inspiration +* etc From 27d6290e0fde7113e4a1ddf97150b5e49c67ef75 Mon Sep 17 00:00:00 2001 From: Rob Chappelle Date: Tue, 22 Oct 2019 22:36:16 +0000 Subject: [PATCH 04/37] Updated CONTRIBUTING.md --- CONTRIBUTING.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 889eb364d..fa2d46a63 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,5 @@ --- -ms.reviewedAt: Date of last review in mm/dd/yyyy format -ms.reviewedBy: alias of person that last reviewed this file + --- # Contributing @@ -35,4 +34,4 @@ Give very detailed instructions on your PR procedure to ensure contributions are ## License Information -Inform the user of the license(s) under which this project accepts and distributes contributions. If you have any license agreement or other legal requirements, include those here as well. \ No newline at end of file +Inform the user of the license(s) under which this project accepts and distributes contributions. If you have any license agreement or other legal requirements, include those here as well. From 0cbdd971ae60a517a8f660b9136b5f7bd6fef8a9 Mon Sep 17 00:00:00 2001 From: Rob Chappelle Date: Mon, 13 Apr 2020 20:51:00 +0000 Subject: [PATCH 05/37] Added .gitignore --- .gitignore | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..e5fb30ed8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,336 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates +target/ + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Code coverage report data files +coveragetool +reports/ +*.cobertura.xml From 845664b26c6648318d0f018a03a8adca9bbdde2a Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Tue, 6 Apr 2021 20:17:17 -0400 Subject: [PATCH 06/37] On branch master Skeleton, does not pass test. new file: pom.xml new file: src/main/arm/createUiDefinition.json new file: src/main/arm/mainTemplate.json Signed-off-by: Ed Burns --- pom.xml | 54 ++++++ src/main/arm/createUiDefinition.json | 271 +++++++++++++++++++++++++++ src/main/arm/mainTemplate.json | 122 ++++++++++++ 3 files changed, 447 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/arm/createUiDefinition.json create mode 100644 src/main/arm/mainTemplate.json diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..17ca0f0b9 --- /dev/null +++ b/pom.xml @@ -0,0 +1,54 @@ + + + + + 4.0.0 + + com.oracle.weblogic.azure + wls-on-aks-azure-marketplace + 1.0.0 + + + com.microsoft.azure.iaas + azure-javaee-iaas-parent + 1.0.7 + + + + jar + wls-on-aks-azure-marketplace + + + + + pid-35859ecc-219b-4930-9431-38c95bb76cee + -TestParameter '@{"PasswordMinLength"=6}' + + + + + + diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json new file mode 100644 index 000000000..194911769 --- /dev/null +++ b/src/main/arm/createUiDefinition.json @@ -0,0 +1,271 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "basics": [ + { + "name": "About", + "type": "Microsoft.Common.InfoBox", + "options": { + "icon": "None", + "text": "Template version ${project.version}" + }, + "visible": "[bool('${template.version.visible}')]" + }, + { + "name": "identity", + "type": "Microsoft.ManagedIdentity.IdentitySelector", + "label": "Managed Identity Configuration", + "toolTip": { + "userAssignedIdentity": "Add user-assigned identities to enable the app deployment." + }, + "defaultValue": { + "systemAssignedIdentity": "Off" + }, + "options": { + "hideSystemAssignedIdentity": true, + "hideUserAssignedIdentity": false + } + } + ], + "steps": [ + { + "name": "AKSCluster", + "label": "Configure AKS cluster", + "subLabel": { + "preValidation": "Provide required info for AKS cluster configuration", + "postValidation": "Done" + }, + "bladeTitle": "Configure AKS cluster", + "elements": [ + { + "name": "createAKSCluster", + "type": "Microsoft.Common.OptionsGroup", + "label": "Create a new AKS cluster?", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to create a new AKS cluster, or select 'No' to provide an existing AKS cluster.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "clusterInfo", + "type": "Microsoft.Common.Section", + "label": "Provide information for an existing AKS cluster", + "elements": [ + { + "name": "aksClusterSelector", + "type": "Microsoft.Solutions.ResourceSelector", + "label": "Select AKS cluster", + "toolTip": "Select the existing AKS cluster.", + "resourceType": "Microsoft.ContainerService/managedClusters", + "options": { + "filter": { + "subscription": "onBasics", + "location": "onBasics" + } + } + } + ], + "visible": "[not(bool(steps('AKSCluster').createAKSCluster))]" + } + ] + }, + { + "name": "ACRInstance", + "label": "Configure ACR instance", + "subLabel": { + "preValidation": "Provide required info for ACR instance configuration", + "postValidation": "Done" + }, + "bladeTitle": "Configure ACR instance", + "elements": [ + { + "name": "createACR", + "type": "Microsoft.Common.OptionsGroup", + "label": "Create a new ACR instance?", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to create a new ACR instance, or select 'No' to provide an existing ACR instance.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "acrInfo", + "type": "Microsoft.Common.Section", + "label": "Provide information for an existing ACR instance", + "elements": [ + { + "name": "acrSelector", + "type": "Microsoft.Solutions.ResourceSelector", + "label": "Select ACR instance", + "toolTip": "Select the existing ACR instance.", + "resourceType": "Microsoft.ContainerRegistry/registries", + "options": { + "filter": { + "subscription": "onBasics", + "location": "onBasics" + } + } + } + ], + "visible": "[not(bool(steps('ACRInstance').createACR))]" + } + ] + }, + { + "name": "Application", + "label": "Configure Java EE Application", + "subLabel": { + "preValidation": "Provide required info for application", + "postValidation": "Done" + }, + "bladeTitle": "Configure Java EE Application", + "elements": [ + { + "name": "uploadAppPackage", + "type": "Microsoft.Common.OptionsGroup", + "label": "Deploy your application package?", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to deploy your application, or select 'No' to deploy a default 'hello world' open liberty application.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "appPackageInfo", + "type": "Microsoft.Common.Section", + "label": "Provide information for your application package", + "elements": [ + { + "name": "appPackageUrl", + "type": "Microsoft.Common.FileUpload", + "label": "Application package (.war)", + "toolTip": "The application war package to deploy.", + "constraints": { + "required": true + }, + "options": { + "multiple": false, + "uploadMode": "url", + "openMode": "binary" + } + }, + { + "name": "contextRoot", + "type": "Microsoft.Common.TextBox", + "label": "Application context root", + "defaultValue": "/", + "toolTip": "Specify the context root of your application.", + "constraints": { + "required": true, + "regex": "^\/.*$", + "validationMessage": "The value must start with '/'." + } + } + ], + "visible": "[bool(steps('Application').uploadAppPackage)]" + }, + { + "name": "useOpenLibertyImage", + "type": "Microsoft.Common.OptionsGroup", + "label": "Application server runtime", + "defaultValue": "Open Liberty", + "toolTip": "Select 'Open Liberty' if you prefer Open Source runtime, or select 'WebSphere Liberty' if you prefer commercial version of Open Liberty.", + "constraints": { + "allowedValues": [ + { + "label": "Open Liberty", + "value": "true" + }, + { + "label": "WebSphere Liberty", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "useJava8", + "type": "Microsoft.Common.OptionsGroup", + "label": "Application Java runtime", + "defaultValue": "Java 8", + "toolTip": "Select Java version for your application.", + "constraints": { + "allowedValues": [ + { + "label": "Java 8", + "value": "true" + }, + { + "label": "Java 11", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "appReplicas", + "type": "Microsoft.Common.TextBox", + "label": "Number of application replicas", + "defaultValue": "2", + "toolTip": "The number of application replicas to deploy.", + "constraints": { + "required": true, + "regex": "^(1|2|3|4|5)$", + "validationMessage": "Number of application replicas to deploy, limit 1-5." + } + } + ] + } + ], + "outputs": { + "location": "[location()]", + "identity": "[basics('identity')]", + "createAKSCluster": "[bool(steps('AKSCluster').createAKSCluster)]", + "aksClusterName": "[last(split(steps('AKSCluster').clusterInfo.aksClusterSelector.id, '/'))]", + "aksClusterRGName": "[last(take(split(steps('AKSCluster').clusterInfo.aksClusterSelector.id, '/'), 5))]", + "createACR": "[bool(steps('ACRInstance').createACR)]", + "acrName": "[last(split(steps('ACRInstance').acrInfo.acrSelector.id, '/'))]", + "uploadAppPackage": "[bool(steps('Application').uploadAppPackage)]", + "appPackageUrl": "[steps('Application').appPackageInfo.appPackageUrl]", + "contextRoot": "[steps('Application').appPackageInfo.contextRoot]", + "useOpenLibertyImage": "[bool(steps('Application').useOpenLibertyImage)]", + "useJava8": "[bool(steps('Application').useJava8)]", + "appReplicas": "[int(steps('Application').appReplicas)]" + } + } +} diff --git a/src/main/arm/mainTemplate.json b/src/main/arm/mainTemplate.json new file mode 100644 index 000000000..4b7b6d13b --- /dev/null +++ b/src/main/arm/mainTemplate.json @@ -0,0 +1,122 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "_artifactsLocation": { + "type": "string", + "defaultValue": "[deployment().properties.templateLink.uri]" + }, + "_artifactsLocationSasToken": { + "type": "securestring", + "defaultValue": "" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "identity": { + "type": "object" + }, + "createAKSCluster": { + "defaultValue": true, + "type": "bool" + }, + "aksClusterName": { + "type": "string", + "defaultValue": "" + }, + "aksClusterRGName": { + "type": "string", + "defaultValue": "" + }, + "createACR": { + "defaultValue": true, + "type": "bool" + }, + "acrName": { + "type": "string", + "defaultValue": "" + }, + "uploadAppPackage": { + "defaultValue": false, + "type": "bool" + }, + "appPackageUrl": { + "defaultValue": "", + "type": "string" + }, + "contextRoot": { + "defaultValue": "/", + "type": "string" + }, + "useOpenLibertyImage": { + "defaultValue": true, + "type": "bool" + }, + "useJava8": { + "defaultValue": true, + "type": "bool" + }, + "appReplicas": { + "type": "int", + "defaultValue": 1 + }, + "guidValue": { + "defaultValue": "[newGuid()]", + "type": "string" + } + }, + "variables": { + "const_aksClusterRGName": "[if(parameters('createAKSCluster'), resourceGroup().name, parameters('aksClusterRGName'))]", + "const_appName": "[concat('app', variables('const_suffix'))]", + "const_appPackageUrl": "[if(parameters('uploadAppPackage'), parameters('appPackageUrl'), 'N/A')]", + "const_arguments": "[concat(variables('const_aksClusterRGName'), ' ', variables('name_aksClusterName'), ' ', variables('name_acrName'), ' ', parameters('uploadAppPackage'), ' ', variables('const_appPackageUrl'), ' ', parameters('contextRoot'), ' ', parameters('useOpenLibertyImage'), ' ', parameters('useJava8'), ' ', parameters('appReplicas'), ' ', variables('const_appName'))]", + "const_scriptLocation": "[uri(parameters('_artifactsLocation'), 'scripts/')]", + "const_suffix": "[take(replace(parameters('guidValue'), '-', ''), 6)]", + "name_acrName": "[if(parameters('createACR'), concat('acr', variables('const_suffix')), parameters('acrName'))]", + "name_aksClusterName": "[if(parameters('createAKSCluster'), concat('cluster', variables('const_suffix')), parameters('aksClusterName'))]", + "name_deploymentScriptName": "[concat('script', variables('const_suffix'))]" + }, + "resources": [ + { + "apiVersion": "2019-09-01", + "name": "[${tracking.pid}]", + "type": "Microsoft.Resources/deployments", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + ] + } + } + } + ], + "outputs": { + "aksClusterName": { + "value": "[variables('name_aksClusterName')]", + "type": "string" + }, + "aksClusterRGName": { + "value": "[variables('const_aksClusterRGName')]", + "type": "string" + }, + "acrName": { + "value": "[variables('name_acrName')]", + "type": "string" + }, + "appImage": { + "value": "[if(parameters('uploadAppPackage'), variables('const_appName'), 'Open Liberty/WebSphere Liberty base image')]", + "type": "string" + }, + "appName": { + "value": "[variables('const_appName')]", + "type": "string" + }, + "result": { + "value": "[reference(variables('name_deploymentScriptName')).outputs]", + "type": "object" + } + } +} From f2bfd11483659f27f82de5595347feab12fd58b7 Mon Sep 17 00:00:00 2001 From: galiacheng Date: Wed, 7 Apr 2021 09:10:59 +0800 Subject: [PATCH 07/37] Update sample code format to bash in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 60790e7c9..fe8e53a45 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ These instructions will get you a copy of the project up and running on your loc What things you need to install the software and how to install them -``` powershell +``` bash Give examples ``` From e01ceff6b4f92e328f317ed8faebb710c3e80b2d Mon Sep 17 00:00:00 2001 From: Zheng Chang Date: Wed, 7 Apr 2021 09:27:31 +0800 Subject: [PATCH 08/37] Test push --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe8e53a45..1db064630 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ These instructions will get you a copy of the project up and running on your loc ### Prerequisites -What things you need to install the software and how to install them +What things you need to install the software and how to install them. ``` bash Give examples From 80fa9c61ac47c047c1df2c43f753521618dc8c1b Mon Sep 17 00:00:00 2001 From: Jianguo Ma Date: Wed, 7 Apr 2021 09:58:00 +0800 Subject: [PATCH 09/37] test push --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1db064630..fe8e53a45 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ These instructions will get you a copy of the project up and running on your loc ### Prerequisites -What things you need to install the software and how to install them. +What things you need to install the software and how to install them ``` bash Give examples From 7e28068bb82073961bf4ebc6d0516e5df8513c07 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Mon, 12 Apr 2021 19:16:39 -0400 Subject: [PATCH 10/37] On branch master Iterating modified: src/main/arm/createUiDefinition.json https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1310331 Signed-off-by: Ed Burns --- src/main/arm/createUiDefinition.json | 73 +++++++++++++++------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index 194911769..51ddb2e9a 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -198,43 +198,32 @@ "visible": "[bool(steps('Application').uploadAppPackage)]" }, { - "name": "useOpenLibertyImage", - "type": "Microsoft.Common.OptionsGroup", - "label": "Application server runtime", - "defaultValue": "Open Liberty", - "toolTip": "Select 'Open Liberty' if you prefer Open Source runtime, or select 'WebSphere Liberty' if you prefer commercial version of Open Liberty.", - "constraints": { - "allowedValues": [ - { - "label": "Open Liberty", - "value": "true" - }, - { - "label": "WebSphere Liberty", - "value": "false" - } - ], - "required": true + "name": "fromImageText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "This value is appended to 'container-registry.oracle.com/middleware/weblogic:' and used in the Dockerfile FROM statement.", + "link": { + "label": "Must be a valid tag value from Oracle Container Registry", + "uri": "https://aka.ms/wls-aks-fromImage-tag" + } } }, { - "name": "useJava8", - "type": "Microsoft.Common.OptionsGroup", - "label": "Application Java runtime", - "defaultValue": "Java 8", - "toolTip": "Select Java version for your application.", + "name": "fromImage", + "type": "Microsoft.Common.TextBox", + "label": "WebLogic Docker tag", + "defaultValue": "12.2.1.4-ol8", + "toolTip": "Docker tag that comes after 'container-registry.oracle.com/middleware/weblogic:' in the fromImage option to 'imagetool'.", + "multiLine": false, "constraints": { - "allowedValues": [ - { - "label": "Java 8", - "value": "true" - }, + "required": true, + "validations": [ { - "label": "Java 11", - "value": "false" + "regex": "^[a-z0-9\\-\\.]+$", + "message": "Must be a valid Docker tag for WebLogic on Oracle Container Registry" } - ], - "required": true + ] } }, { @@ -250,6 +239,25 @@ } } ] + }, + { + "name": "section_autoScaling", + "type": "Microsoft.Common.Section", + "label": "Configure horizontal pod autoscaling", + "elements": [ + { + "name": "enableAutoScalingText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here will cause the template to provision Prometheus and Grafana with scaling rules to enable horizontal pod auto-scaling.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/wls-aks-autoscaling" + } + } + } + ] } ], "outputs": { @@ -263,8 +271,7 @@ "uploadAppPackage": "[bool(steps('Application').uploadAppPackage)]", "appPackageUrl": "[steps('Application').appPackageInfo.appPackageUrl]", "contextRoot": "[steps('Application').appPackageInfo.contextRoot]", - "useOpenLibertyImage": "[bool(steps('Application').useOpenLibertyImage)]", - "useJava8": "[bool(steps('Application').useJava8)]", + "fromImage": "[bool(steps('Application').fromImage)]", "appReplicas": "[int(steps('Application').appReplicas)]" } } From 58a50a7733a7e56a747b72b7a4daacf86a82f1c3 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Mon, 12 Apr 2021 19:37:57 -0400 Subject: [PATCH 11/37] On branch master modified: src/main/arm/createUiDefinition.json https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1310331 Signed-off-by: Ed Burns --- src/main/arm/createUiDefinition.json | 1278 +++++++++++++++++++++++++- 1 file changed, 1277 insertions(+), 1 deletion(-) diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index 51ddb2e9a..06d085b1f 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -240,10 +240,1286 @@ } ] }, + { + "name": "section_sslConfiguration", + "type": "Microsoft.Common.Section", + "label": "TLS/SSL Configuration", + "elements": [{ + "name": "sslConfigurationText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here will cause the template to provision WebLogic Administration Console on HTTPS (Secure) port, with your own TLS/SSL Certificate.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-config" + } + } + }, + { + "name": "enableCustomSSL", + "type": "Microsoft.Common.OptionsGroup", + "label": "Configure WebLogic Administration Console on HTTPS (Secure) port, with your own TLS/SSL Certificate?", + "defaultValue": "No", + "toolTip": "Select 'Yes' to configure WebLogic Administration Console on HTTPS (Secure) port with your own SSL Certificate.", + "constraints": { + "allowedValues": [{ + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ], + "required": false + } + }, + { + "name": "sslConfigurationAccessOption", + "type": "Microsoft.Common.OptionsGroup", + "visible": "[steps('section_sslConfiguration').enableCustomSSL]", + "label": "How would you like to provide required configuration", + "defaultValue": "Upload existing KeyStores", + "toolTip": "Select 'Upload existing KeyStores' to use local stored KeyStores.", + "constraints": { + "allowedValues": [ + { + "label": "Upload existing KeyStores", + "value": "uploadConfig" + }, + { + "label": "Use KeyStores stored in Azure Key Vault", + "value": "keyVaultStoredConfig" + } + ], + "required": false + } + }, + { + "name": "uploadedCustomSSLSettings", + "type": "Microsoft.Common.Section", + "visible": "[and(steps('section_sslConfiguration').enableCustomSSL, equals(steps('section_sslConfiguration').sslConfigurationAccessOption, 'uploadConfig'))]", + "label": "TLS/SSL Configuration Settings", + "elements": [ + { + "name": "sslKeystoreInfo0", + "type": "Microsoft.Common.InfoBox", + "visible": "true", + "options": { + "icon": "Info", + "text": "You must provide different files for identity and trust KeyStores. Select here for more details.", + "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-configuration" + } + }, + { + "name": "uploadedCustomIdentityKeyStoreData", + "type": "Microsoft.Common.FileUpload", + "label": "Identity KeyStore Data file(.jks,.p12)", + "toolTip": "Identity KeyStore for TLS/SSL configuration", + "constraints": { + "required": true, + "accept": ".jks,.p12" + }, + "options": { + "multiple": false, + "uploadMode": "file", + "openMode": "binary" + }, + "visible": true + }, + { + "name": "uploadedCustomIdentityKeyStorePassphrase", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": " The passphrase for the Identity KeyStore", + "constraints": { + "required": true, + "regex": "^.{6,}$", + "validationMessage": "Keypass must be at least 6 characters long." + }, + "options": { + "hideConfirmation": false + }, + "visible": true + }, + { + "name": "uploadedCustomIdentityKeyStoreType", + "type": "Microsoft.Common.DropDown", + "visible": "true", + "label": "The Identity KeyStore type (JKS,PKCS12)", + "defaultValue": "JKS", + "toolTip": "One of the supported KeyStore types", + "constraints": { + "allowedValues": [ + { + "label": "JKS", + "value": "JKS" + }, + { + "label": "PKCS12", + "value": "PKCS12" + } + ], + "required": true + } + }, + { + "name": "uploadedPrivateKeyAlias", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The alias of the server's private key within the Identity KeyStore", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "uploadedPrivateKeyPassPhrase", + "type": "Microsoft.Common.PasswordBox", + "visible": "true", + "label": { + "password": "The passphrase for the server's private key within the Identity KeyStore", + "confirmPassword": "Confirm passphrase" + }, + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^.{6,}$", + "validationMessage": "Keypass must be at least 6 characters long." + }, + "options": { + "hideConfirmation": false + } + }, + { + "name": "uploadedCustomTrustKeyStoreData", + "type": "Microsoft.Common.FileUpload", + "label": "Trust KeyStore Data file(.jks,.p12)", + "toolTip": "Trust KeyStore for TLS/SSL configuration.", + "constraints": { + "required": true, + "accept": ".jks,.p12" + }, + "options": { + "multiple": false, + "uploadMode": "file", + "openMode": "binary" + }, + "visible": true + }, + { + "name": "uploadedCustomTrustKeyStorePassPhrase", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": " The passphrase for the Trust KeyStore", + "constraints": { + "required": true, + "regex": "^.{6,}$", + "validationMessage": "Keypass must be at least 6 characters long." + }, + "options": { + "hideConfirmation": false + }, + "visible": true + }, + { + "name": "uploadedCustomTrustKeyStoreType", + "type": "Microsoft.Common.DropDown", + "visible": "true", + "label": "The Trust KeyStore type (JKS,PKCS12)", + "defaultValue": "JKS", + "toolTip": "One of the supported KeyStore types", + "constraints": { + "allowedValues": [ + { + "label": "JKS", + "value": "JKS" + }, + { + "label": "PKCS12", + "value": "PKCS12" + } + ], + "required": true + } + } + ] + }, + { + "name": "keyVaultStoredCustomSSLSettings", + "type": "Microsoft.Common.Section", + "visible": "[and(steps('section_sslConfiguration').enableCustomSSL, equals(steps('section_sslConfiguration').sslConfigurationAccessOption, 'keyVaultStoredConfig'))]", + "label": "TLS/SSL Configuration Settings", + "elements": [ + { + "name": "sslKeystoreInfo1", + "type": "Microsoft.Common.InfoBox", + "visible": "true", + "options": { + "icon": "Info", + "text": "You must provide different files for identity and trust KeyStores. Select here for more details.", + "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-configuration" + } + }, + { + "name": "keyVaultText", + "type": "Microsoft.Common.TextBlock", + "visible": "true", + "options": { + "text": "Enabling a HTTPS (Secure) port for the Administration Console requires you to obtain a valid TLS/SSL Certificate. The template will look for the certificate and other configuration items in the Azure Key Vault specified here.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-key-vault" + } + } + }, + { + "name": "adminSSLKeyVaultResourceGroup", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "Resource group name in current subscription containing the Key Vault", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", + "validationMessage": "[if(greater(length(steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.adminSSLKeyVaultResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" + } + }, + { + "name": "adminSSLKeyVaultName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "Name of the Azure Key Vault containing secrets for the TLS/SSL Certificate", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^(?=.{3,24}$)[a-zA-Z](([a-z0-9A-Z]*|(?:\\-[^\\-][a-z0-9A-Z]*))*)$", + "validationMessage": "[if(or(greater(length(steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.adminSSLKeyVaultName), 24), less(length(steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.adminSSLKeyVaultName), 3)),'Vault name must be between 3-24 alphanumeric characters. The name must begin with a letter, end with a letter or digit, and not contain consecutive hyphens.','Vault name must only contain alphanumeric characters and dashes and cannot start with a number')]" + } + }, + { + "name": "keyVaultCustomIdentityKeyStoreDataSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the Identity KeyStore Data", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomIdentityKeyStorePassPhraseSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Identity KeyStore", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomIdentityKeyStoreType", + "type": "Microsoft.Common.DropDown", + "visible": "true", + "label": "The Identity KeyStore type (JKS,PKCS12)", + "defaultValue": "JKS", + "toolTip": "One of the supported KeyStore types", + "constraints": { + "allowedValues": [{ + "label": "JKS", + "value": "JKS" + }, + { + "label": "PKCS12", + "value": "PKCS12" + } + ], + "required": true + } + }, + { + "name": "keyVaultPrivateKeyAliasSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the Private Key Alias", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultPrivateKeyPassPhraseSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Private Key", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomTrustKeyStoreDataSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the Trust KeyStore Data", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomTrustKeyStorePassPhraseSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Trust KeyStore", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomTrustKeyStoreType", + "type": "Microsoft.Common.DropDown", + "visible": "true", + "label": "The Trust KeyStore type (JKS,PKCS12)", + "defaultValue": "JKS", + "toolTip": "One of the supported KeyStore types", + "constraints": { + "allowedValues": [{ + "label": "JKS", + "value": "JKS" + }, + { + "label": "PKCS12", + "value": "PKCS12" + } + ], + "required": true + } + } + ] + } + ] + }, + { + "name": "section_appGateway", + "type": "Microsoft.Common.Section", + "label": "Azure Application Gateway", + "elements": [ + { + "name": "connectToAGText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here will cause the template to provision an Azure Application Gateway (WAF_v2 or later SKU), a public IP, and a backend pool consisting of the worker nodes in the cluster. Further configuration may be necessary after deployment.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-overview" + } + } + }, + { + "name": "enableAppGateway", + "type": "Microsoft.Common.OptionsGroup", + "label": "Connect to Azure Application Gateway?", + "defaultValue": "No", + "toolTip": "Select 'Yes' to create an Azure Application Gateway as the load balancer for the cluster.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ], + "required": false + } + }, + { + "name": "keyVaultText00", + "type": "Microsoft.Common.TextBlock", + "visible": "[steps('section_appGateway').enableAppGateway]", + "options": { + "text": "Choose an option for providing the TLS/SSL certificate and whether or not to deny public traffic to the managed servers:" + } + }, + { + "name": "keyVaultText01", + "type": "Microsoft.Common.TextBlock", + "visible": "[steps('section_appGateway').enableAppGateway]", + "options": { + "text": "    ⁃ Upload a TLS/SSL certificate: Upload the pre-signed certificate now." + } + }, + { + "name": "keyVaultText02", + "type": "Microsoft.Common.TextBlock", + "visible": "[steps('section_appGateway').enableAppGateway]", + "options": { + "text": "    ⁃ Identify an Azure Key Vault: The Key Vault must already contain the certificate and its password stored as secrets." + } + }, + { + "name": "keyVaultText03", + "type": "Microsoft.Common.TextBlock", + "visible": "[steps('section_appGateway').enableAppGateway]", + "options": { + "text": "    ⁃ Generate a self-signed certificate: generate a self-signed certificate and apply it during deployment.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-key-vault" + } + } + }, + { + "name": "certificateOption", + "type": "Microsoft.Common.OptionsGroup", + "label": "Select desired TLS/SSL certificate option", + "defaultValue": "Upload a TLS/SSL certificate", + "toolTip": "Select desired TLS/SSL certificate option", + "constraints": { + "allowedValues": [ + { + "label": "Upload a TLS/SSL certificate", + "value": "haveCert" + }, + { + "label": "Identify an Azure Key Vault", + "value": "haveKeyVault" + }, + { + "label": "Generate a self-signed certificate", + "value": "generateCert" + } + ], + "required": true + }, + "visible": "[steps('section_appGateway').enableAppGateway]" }, + { + "name": "keyVaultSSLCertData", + "type": "Microsoft.Common.FileUpload", + "label": "TLS/SSL certificate(.pfx)", + "toolTip": "TLS/SSL certificate used for App Gateway", + "constraints": { + "required": true, + "accept": ".pfx" + }, + "options": { + "multiple": false, + "uploadMode": "file", + "openMode": "binary" + }, + "visible": "[equals(steps('section_appGateway').certificateOption, 'haveCert')]" + }, + { + "name": "appGatewaySSLCertPassword", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": "TLS/SSL certificate password", + "constraints": { + "required": "[equals(steps('section_appGateway').certificateOption, 'haveCert')]", + "regex": "^((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])|(?=.*[0-9])(?=.*[a-z])(?=.*[!@#$%^&*])|(?=.*[0-9])(?=.*[A-Z])(?=.*[!@#$%^&*])|(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*])).{6,128}$", + "validationMessage": "The password must contain at least 6 characters, with at least 1 uppercase letter, 1 lowercase letter and 1 number." + }, + "options": { + "hideConfirmation": false + }, + "visible": "[equals(steps('section_appGateway').certificateOption, 'haveCert')]" + }, + { + "name": "keyVaultResourceGroup", + "type": "Microsoft.Common.TextBox", + "label": "Resource group name in current subscription containing the Key Vault", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", + "validationMessage": "[if(greater(length(steps('section_appGateway').keyVaultResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" + }, + "visible": "[equals(steps('section_appGateway').certificateOption, 'haveKeyVault')]" + }, + { + "name": "keyVaultName", + "type": "Microsoft.Common.TextBox", + "label": "Name of the Azure Key Vault containing secrets for the certificate for TLS/SSL Termination", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^(?=.{3,24}$)[a-zA-Z](([a-z0-9A-Z]*|(?:\\-[^\\-][a-z0-9A-Z]*))*)$", + "validationMessage": "[if(or(greater(length(steps('section_appGateway').keyVaultName), 24), less(length(steps('section_appGateway').keyVaultName), 3)),'Vault name must be between 3-24 alphanumeric characters. The name must begin with a letter, end with a letter or digit, and not contain consecutive hyphens.','Vault name must only contain alphanumeric characters and dashes and cannot start with a number')]" + }, + "visible": "[equals(steps('section_appGateway').certificateOption, 'haveKeyVault')]" + }, + { + "name": "keyVaultSSLCertDataSecretName", + "type": "Microsoft.Common.TextBox", + "label": "The name of the secret in the specified Key Vault whose value is the TLS/SSL certificate data", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + }, + "visible": "[equals(steps('section_appGateway').certificateOption, 'haveKeyVault')]" + }, + { + "name": "keyVaultSSLCertPasswordSecretName", + "type": "Microsoft.Common.TextBox", + "label": "The name of the secret in the specified Key Vault whose value is the password for the TLS/SSL certificate", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + }, + "visible": "[equals(steps('section_appGateway').certificateOption, 'haveKeyVault')]" + }, + { + "name": "infoGatewayIndentity", + "type": "Microsoft.Common.InfoBox", + "visible": "[and(equals(steps('section_appGateway').certificateOption, 'generateCert'), less(length(steps('section_appGateway').gatewayIdentity.userAssignedIdentities), 1))]", + "options": { + "icon": "Error", + "text": "This option will create a self-signed TLS/SSL certificate for gateway TLS/SSL termination. This option requires at least one user-assigned identity to access Azure resources.", + "uri": "https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/deployment-script-template" + } + }, + { + "name": "gatewayIdentity", + "type": "Microsoft.ManagedIdentity.IdentitySelector", + "label": "Managed Identity Configuration", + "toolTip": { + "userAssignedIdentity": "Add user-assigned identities to enable creation of TLS/SSL certificate." + }, + "defaultValue": { + "systemAssignedIdentity": "Off" + }, + "options": { + "hideSystemAssignedIdentity": true, + "hideUserAssignedIdentity": false + }, + "visible": "[equals(steps('section_appGateway').certificateOption, 'generateCert')]" + }, + { + "name": "denyPublicTrafficForManagedServer", + "type": "Microsoft.Common.OptionsGroup", + "label": "Deny public traffic for managed server?", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to deny traffic from the public Internet from reaching managed servers directly. This setting has a higher priority than 'Ports and port ranges to expose' in basic blade.", + "constraints": { + "allowedValues": [ + { + "label": "No", + "value": false + }, + { + "label": "Yes", + "value": true + } + ], + "required": true + }, + "visible": "[steps('section_appGateway').enableAppGateway]" + } + ], + "visible": true + }, + { + "name": "section_dnsConfiguration", + "type": "Microsoft.Common.Section", + "label": "DNS Configuration", + "elements": [ + { + "name": "dnsConfigurationText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here will cause the template to provision Oracle WebLogic Server Administration Console and Application Gateway using a custom DNS Name (for example: applications.contoso.com)", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-dns" + } + } + }, + { + "name": "enableCustomDNS", + "type": "Microsoft.Common.OptionsGroup", + "visible": true, + "label": "Configure Custom DNS Alias?", + "defaultValue": "No", + "toolTip": "Select 'Yes' to configure Custom DNS Alias.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ], + "required": false + } + }, + { + "name": "customDNSSettings", + "type": "Microsoft.Common.Section", + "label": "DNS Configuration Settings", + "visible": "[bool(steps('section_dnsConfiguration').enableCustomDNS)]", + "elements": [ + { + "name": "bringDNSZone", + "type": "Microsoft.Common.OptionsGroup", + "label": "Use an existing Azure DNS Zone", + "defaultValue": "No", + "toolTip": "Select 'Yes' to configure Custom DNS Alias based on an existing Azure DNS Zone. Select 'No' to create an Azure DNS Zone and Custom DNS Alias.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ] + } + }, + { + "name": "createDNSZoneText", + "type": "Microsoft.Common.InfoBox", + "visible": "[not(bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone))]", + "options": { + "icon": "Info", + "text": "You must perform DNS Domain Delegation at your DNS Registry after deployment.", + "uri": "https://aka.ms/dns-domain-delegation" + } + }, + { + "name": "deplymentScriptInfo", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "If you choose to use an existing Azure DNS Zone, you must select a user-assigned managed identity to enable the necessary configuration." + }, + "visible": "[steps('section_dnsConfiguration').customDNSSettings.bringDNSZone]" + }, + { + "name": "dnszoneName", + "type": "Microsoft.Common.TextBox", + "label": "DNS Zone Name", + "defaultValue": "", + "toolTip": "Use only letters and numbers and periods to separate Domains", + "constraints": { + "required": true, + "regex": "^([0-9a-zA-Z_-]{1,63}\\.){1,33}[0-9a-zA-Z_-]{1,63}$", + "validationMessage": "There must be between 2 and 34 labels. For example, \"contoso.com\" has 2 labels. Each label must contain between 1 and 63 characters. Each label must only contain letters, numbers, underscores, and dashes." + } + }, + { + "name": "dnsZoneResourceGroup", + "type": "Microsoft.Common.TextBox", + "label": "Name of the resource group contains the DNS Zone in current subscription", + "defaultValue": "", + "toolTip": "Name of the resource group which contains the DNS Zone in current subscription", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", + "validationMessage": "[if(greater(length(steps('section_dnsConfiguration').existingDNSZonesSettings.dnsZoneResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" + }, + "visible": "[steps('section_dnsConfiguration').customDNSSettings.bringDNSZone]" + }, + { + "name": "dnszoneAdminConsoleLabel", + "type": "Microsoft.Common.TextBox", + "label": "Label for Oracle WebLogic Administration Console", + "defaultValue": "admin", + "toolTip": "Specify a label to generate subdomain of Oracle WebLogic Administration Console", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^([0-9a-zA-Z_-]{1,63}\\.){0,33}[0-9a-zA-Z_-]{1,63}$", + "message": "Each label must contain between 1 and 63 characters. Each label must only contain letters, numbers, underscores, and dashes." + }, + { + "isValid": "[less(sub(length(concat(steps('section_dnsConfiguration').customDNSSettings.dnszoneAdminConsoleLabel,'.',steps('section_dnsConfiguration').customDNSSettings.dnszoneName)),length(replace(concat(steps('section_dnsConfiguration').customDNSSettings.dnszoneAdminConsoleLabel,'.',steps('section_dnsConfiguration').customDNSSettings.dnszoneName), '.', ''))),34)]", + "message": "Subdomain must be between 2 and 34 labels. For example, \"admin.contoso.com\" has 3 labels." + } + ] + } + }, + { + "name": "dnszoneGatewayLabel", + "type": "Microsoft.Common.TextBox", + "label": "Label for Application Gateway", + "defaultValue": "www", + "toolTip": "Specify a label to generate subdomain of Application Gateway", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^([0-9a-zA-Z_-]{1,63}\\.){0,33}[0-9a-zA-Z_-]{1,63}$", + "message": "Each label must contain between 1 and 63 characters. Each label must only contain letters, numbers, underscores, and dashes." + }, + { + "isValid": "[less(sub(length(concat(if(empty(steps('section_dnsConfiguration').customDNSSettings.dnszoneGatewayLabel), '', steps('section_dnsConfiguration').customDNSSettings.dnszoneGatewayLabel),'.',steps('section_dnsConfiguration').customDNSSettings.dnszoneName)),length(replace(concat(if(empty(steps('section_dnsConfiguration').customDNSSettings.dnszoneGatewayLabel), '', steps('section_dnsConfiguration').customDNSSettings.dnszoneGatewayLabel),'.',steps('section_dnsConfiguration').customDNSSettings.dnszoneName), '.', ''))),34)]", + "message": "Subdomain must be between 2 and 34 labels. For example, \"applications.contoso.com\" has 3 labels." + } + ] + }, + "visible": "[and(bool(steps('section_appGateway').enableAppGateway), bool(steps('section_dnsConfiguration').enableCustomDNS))]" + }, + { + "name": "reuseIdentity", + "type": "Microsoft.Common.OptionsGroup", + "label": "Use the Managed Identity seleted previousely?", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to reuse the Managed Identity seleted previousely, 'No' to input a new identity.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ] + }, + "visible": "[and(greater(length(steps('section_appGateway').gatewayIdentity.userAssignedIdentities),0), bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone))]" + }, + { + "name": "infoDNSIndentity", + "type": "Microsoft.Common.InfoBox", + "visible": "[and(bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone), less(length(steps('section_appGateway').gatewayIdentity.userAssignedIdentities),1), less(length(steps('section_dnsConfiguration').customDNSSettings.dnsIdentity.userAssignedIdentities), 1))]", + "options": { + "icon": "Error", + "text": "This option will use Azure deployment scripts to update records to your Azure DNS Zone. You have to add at least one user-assigned identity to access Azure resources.", + "uri": "https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/deployment-script-template" + } + }, + { + "name": "dnsIdentity", + "type": "Microsoft.ManagedIdentity.IdentitySelector", + "label": "Managed Identity Configuration", + "toolTip": { + "userAssignedIdentity": "Add user-assigned identities to grant the resource access to Azure resources." + }, + "defaultValue": { + "systemAssignedIdentity": "Off" + }, + "options": { + "hideSystemAssignedIdentity": true, + "hideUserAssignedIdentity": false + }, + "visible": "[if(greater(length(steps('section_appGateway').gatewayIdentity.userAssignedIdentities),0),and(equals(steps('section_dnsConfiguration').customDNSSettings.reuseIdentity, false), bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone)), bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone))]" + } + ] + } + ] + }, + { + "name": "section_database", + "type": "Microsoft.Common.Section", + "label": "Database", + "subLabel": { + "preValidation": "Configure integrations to database", + "postValidation": "Done" + }, + "bladeTitle": "Database", + "elements": [ + { + "name": "aboutDatabase", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here and providing the configuration will cause the template to configure the WebLogic Server to connect to the desired pre-existing database. The database must be network accessible to the VNET and subnets created by the template." + } + }, + { + "name": "enableDB", + "type": "Microsoft.Common.OptionsGroup", + "label": "Connect to database?", + "defaultValue": "No", + "toolTip": "Select 'Yes' and provide required info to configure the connection to a database.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "databaseConnectionInfo", + "type": "Microsoft.Common.Section", + "label": "Connection settings", + "elements": [ + { + "name": "databaseType", + "type": "Microsoft.Common.DropDown", + "label": "Choose database type", + "toolTip": "Choose database type", + "defaultValue": "Oracle database", + "constraints": { + "allowedValues": [ + { + "label": "Azure database for PostgreSQL", + "value": "postgresql" + }, + { + "label": "Oracle database", + "value": "oracle" + }, + { + "label": "Azure SQL", + "value": "sqlserver" + } + ], + "required": true + }, + "visible": true + }, + { + "name": "jdbcDataSourceName", + "type": "Microsoft.Common.TextBox", + "label": "JNDI Name", + "toolTip": "The JNDI name for the database JDBC connection", + "defaultValue": "", + "constraints": { + "required": "[bool(steps('section_database').enableDB)]", + "regex": "^[a-z0-9A-Z/]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters, numbers, and slashes (/)." + }, + "visible": true + }, + { + "name": "dsConnectionURL", + "type": "Microsoft.Common.TextBox", + "label": "DataSource Connection String", + "toolTip": "The JDBC connection string for the database", + "defaultValue": "", + "constraints": { + "required": "[bool(steps('section_database').enableDB)]", + "regex": "[concat('^jdbc:', coalesce(steps('section_database').databaseConnectionInfo.databaseType, ''), '.*$')]", + "validationMessage": "A valid JDBC URL for the chosen database type must be provided" + }, + "visible": true + }, + { + "name": "dbUser", + "type": "Microsoft.Common.TextBox", + "label": "Database username", + "toolTip": "Use only letters and numbers", + "defaultValue": "", + "constraints": { + "required": "[bool(steps('section_database').enableDB)]", + "regex": "^(?!\\-)([a-z0-9A-Z@\\-]{1,128})([^\\-])$", + "validationMessage": "The value must be 1-128 characters long and must only contain letters, numbers, hyphen(-) and the at sign, no hyphen allowed at the beginning and the end of database username." + }, + "visible": true + }, + { + "name": "dbPassword", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Database Password", + "confirmPassword": "Confirm password" + }, + "toolTip": "Database Password", + "constraints": { + "required": "[bool(steps('section_database').enableDB)]", + "regex": "^((?=.*[0-9])(?=.*[a-zA-Z!@#$%^&*])).{5,128}$", + "validationMessage": "The password must be between five and 128 characters long and have at least one number." + }, + "options": { + "hideConfirmation": false + }, + "visible": true + } + ], + "visible": "[bool(steps('section_database').enableDB)]" + } + ] + }, + { + "name": "section_aad", + "label": "Azure Active Directory", + "subLabel": { + "preValidation": "Configure the connection to Azure Active Directory.", + "postValidation": "Done" + }, + "bladeTitle": "Azure Active Directory", + "elements": [ + { + "name": "aboutAad", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "icon": "None", + "text": "Selecting 'Yes' here and providing the configuration will cause the template to configure the connection to Azure Active Directory.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-aad-ldap" + } + } + }, + { + "name": "enableAAD", + "type": "Microsoft.Common.OptionsGroup", + "label": "Connect to Azure Active Directory?", + "defaultValue": "No", + "toolTip": "Select 'Yes' and provide required info to configure the connection to Azure Active Directory.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "aadInfo", + "type": "Microsoft.Common.Section", + "label": "Connection settings", + "elements": [ + { + "name": "aadsServerHost", + "type": "Microsoft.Common.TextBox", + "label": "Server Host", + "toolTip": "The LDAP server host.", + "defaultValue": "", + "constraints": { + "required": true, + "regex": "(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]", + "validationMessage": "The value must be a valid host name." + } + }, + { + "name": "aadsPublicIP", + "type": "Microsoft.Common.TextBox", + "label": "Secure LDAP external IP address", + "toolTip": "Secure LDAP external IP address.", + "constraints": { + "required": true, + "regex": "\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b", + "validationMessage": "The value must be a valid IP address." + } + }, + { + "name": "aadsPortNumber", + "type": "Microsoft.Common.TextBox", + "label": "Port", + "toolTip": "The port number of LDAP Server, default is 636.", + "defaultValue": "636", + "constraints": { + "required": true, + "regex": "^[0-9]+$", + "validationMessage": "The value must be numbers." + } + }, + { + "name": "wlsLDAPProviderName", + "type": "Microsoft.Common.TextBox", + "label": "Provider Name", + "defaultValue": "AzureActiveDirectoryProvider", + "toolTip": "The value used for creating authentication provider name of WebLogic Server.", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{3,50}$", + "validationMessage": "The Provider Name must be between 3 and 50 characters long and contain letters, numbers only." + } + }, + { + "name": "wlsLDAPPrincipal", + "type": "Microsoft.Common.TextBox", + "label": "Principal", + "toolTip": "The Distinguished Name (DN) of the LDAP user that WebLogic Server should use to connect to the LDAP server.", + "constraints": { + "required": true, + "regex": ".+,dc|DC=([^,]+),dc|DC=([^,]+)$", + "validationMessage": "The value must be valid LDAP user distinguished name." + } + }, + { + "name": "wlsLDAPPrincipalPassword", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password for Principal", + "confirmPassword": "Confirm password" + }, + "toolTip": "The credential (usually a password) used to connect to the LDAP server.", + "constraints": { + "required": true + } + }, + { + "name": "wlsLDAPUserBaseDN", + "type": "Microsoft.Common.TextBox", + "label": "User Base DN", + "toolTip": "The base distinguished name (DN) of the tree in the LDAP directory that contains users.", + "constraints": { + "required": true, + "regex": ".+,dc|DC=([^,]+),dc|DC=([^,]+)$", + "validationMessage": "The value must be valid LDAP user based distinguished name." + } + }, + { + "name": "wlsLDAPGroupBaseDN", + "type": "Microsoft.Common.TextBox", + "label": "Group Base DN", + "toolTip": "The base distinguished name (DN) of the tree in the LDAP directory that contains groups.", + "constraints": { + "required": true, + "regex": ".+,dc|DC=([^,]+),dc|DC=([^,]+)$", + "validationMessage": "The value must be valid LDAP group based distinguished name." + } + }, + { + "name": "wlsLDAPSSLCertificate", + "type": "Microsoft.Common.FileUpload", + "label": "Client certificate for TLS/SSL Configuration (.cer)", + "toolTip": "Client certificate of AAD LADP server, used to configure the Trust Keystore to enable TLS/SSL in Oracle WebLogic Server.", + "constraints": { + "required": true, + "accept": ".cer" + }, + "options": { + "multiple": false, + "uploadMode": "file", + "openMode": "binary" + } + } + ], + "visible": "[bool(steps('section_aad').enableAAD)]" + } + ] + }, + { + "name": "section_elk", + "label": "Elasticsearch and Kibana", + "subLabel": { + "preValidation": "Configure the connection to Elasticsearch and Kibana to store WLS logs.", + "postValidation": "Done" + }, + "bladeTitle": "Elasticsearch and Kibana", + "elements": [ + { + "name": "aboutelk", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "icon": "None", + "text": "Selecting 'Yes' here and providing the configuration will cause the template to configure Elasticsearch and Kibana to store WLS logs.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-elk-tutorial" + } + } + }, + { + "name": "enableELK", + "type": "Microsoft.Common.OptionsGroup", + "label": "Export logs to Elasticsearch?", + "defaultValue": "No", + "toolTip": "Select 'Yes' and provide required info to configure the connection to Elasticsearch.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "elkInfo", + "type": "Microsoft.Common.Section", + "label": "Connection settings", + "elements": [ + { + "name": "elkMemoryRequiredText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "icon": "None", + "text": "To ensure Logstash works correctly, the selected Virtual Machines have at least 2.5GB memory.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-elk" + } + } + }, + { + "name": "elkMemoryRequired", + "type": "Microsoft.Common.InfoBox", + "options": { + "icon": "Error", + "text": "Your selected Virtual Machines have less than 2.5GB memory to set up Elasticsearch and Kibana, please go to Basics -> Virtual machine size to change it, recommended size is Standard_A2_v2." + }, + "visible": "[and(contains('Standard_A1,Basic_A1,Standard_B1ms,Standard_A1_v2,Standard_F1,Standard_F1s', basics('vmSizeSelect')),bool(steps('section_elk').enableELK))]" + }, + { + "name": "elasticsearchEndpoint", + "type": "Microsoft.Common.TextBox", + "label": "Elasticsearch endpoint URL", + "toolTip": "Elasticsearch endpoint.", + "defaultValue": "https://example.location.azure.elastic-cloud.com:9243", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*):[0-9]{1,6}$", + "message": "The value must be a valid endpoint." + }, + { + "isValid": "[not(contains('Standard_A1,Basic_A1,Standard_B1ms,Standard_A1_v2,Standard_F1,Standard_F1s', basics('vmSizeSelect')))]", + "message": "Your selected Virtual Machines have less than 2.5GB memory to set up Elasticsearch and Kibana, please go to Basics -> Virtual machine size to change it, recommended size is Standard_A2_v2." + } + ] + } + }, + { + "name": "elasticsearchUserName", + "type": "Microsoft.Common.TextBox", + "label": "Elasticsearch User Name", + "defaultValue": "elastic", + "toolTip": "User name of Elasticsearch account.", + "constraints": { + "required": true, + "regex": "^(?!\\-)([a-z0-9A-Z@\\-]{1,128})([^\\-])", + "validationMessage": "The value must be valid Elasticsearch user name." + } + }, + { + "name": "elasticsearchPassword", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password for Elasticsearch account", + "confirmPassword": "Confirm password" + }, + "toolTip": "Password for Elasticsearch account.", + "constraints": { + "required": true + } + }, + { + "name": "logsToIntegrate", + "type": "Microsoft.Common.DropDown", + "label": "WebLogic Server logs to export", + "toolTip": "The logs selected will be exported to Elasticsearch.", + "defaultValue": [ + "Server Log" + ], + "multiselect": true, + "selectAll": true, + "multiLine": true, + "defaultDescription": "The logs selected will be exported to Elasticsearch", + "constraints": { + "allowedValues": [ + { + "label": "Data Source Log", + "description": "Export Datasource logs to Elasticsearch.", + "value": "DataSourceLog" + }, + { + "label": "Domain Log", + "description": "Export Domain logs to Elasticsearch.", + "value": "DomainLog" + }, + { + "label": "HTTP Access Log", + "description": "Export HTTP logs to Elasticsearch.", + "value": "HTTPAccessLog" + }, + { + "label": "Node Manager Logs", + "description": "Export Node Manager logs to Elasticsearch.", + "value": "NodeManagerLog" + }, + { + "label": "Server Log", + "description": "Export Server logs to Elasticsearch.", + "value": "ServerLog" + }, + { + "label": "Standard error and output", + "description": "Export standard error and output to Elasticsearch.", + "value": "StandardErrorAndOutput" + } + ], + "required": true + } + } + ], + "visible": "[bool(steps('section_elk').enableELK)]" + } + ] + }, { "name": "section_autoScaling", "type": "Microsoft.Common.Section", - "label": "Configure horizontal pod autoscaling", + "label": "Autoscaling", "elements": [ { "name": "enableAutoScalingText", From 7478f9d137af10d36723aac48f251ca2328a0aee Mon Sep 17 00:00:00 2001 From: haixia Date: Wed, 14 Apr 2021 04:53:00 +0800 Subject: [PATCH 12/37] UI definition for weblogic and auto scaling. --- src/main/arm/createUiDefinition.json | 407 ++++++++++++++++++++++----- 1 file changed, 334 insertions(+), 73 deletions(-) diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index 06d085b1f..707268bee 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -4,6 +4,145 @@ "version": "0.1.2-preview", "parameters": { "basics": [ + { + "name": "basicsRequired", + "type": "Microsoft.Common.Section", + "label": "Credentials for WebLogic", + "elements": [ + { + "name": "wlsUserName", + "type": "Microsoft.Common.TextBox", + "label": "Username for WebLogic Administrator", + "defaultValue": "weblogic", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + }, + "visible": true + }, + { + "name": "wlsPassword", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password for WebLogic Administrator", + "confirmPassword": "Confirm password" + }, + "toolTip": "Password for WebLogic Administrator", + "constraints": { + "required": true, + "regex": "^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{12,}$", + "validationMessage": "The password must contain at least 12 characters, with at least 1 uppercase letter, 1 lowercase letter and 1 number, and special characters are not allowed." + }, + "options": { + "hideConfirmation": false + }, + "visible": true + }, + { + "name": "wdtRuntimePassword", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password for model WDT runtime encrytion", + "confirmPassword": "Confirm password" + }, + "toolTip": "The Model WDT runtime encrytion secret.", + "constraints": { + "required": true, + "regex": "^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{12,}$", + "validationMessage": "The password must contain at least 12 characters, with at least 1 uppercase letter, 1 lowercase letter and 1 number, and special characters are not allowed." + }, + "options": { + "hideConfirmation": false + }, + "visible": true + }, + { + "name": "identity", + "type": "Microsoft.ManagedIdentity.IdentitySelector", + "label": "Managed Identity Configuration", + "toolTip": { + "userAssignedIdentity": "Add user-assigned identities to enable the app deployment." + }, + "defaultValue": { + "systemAssignedIdentity": "Off" + }, + "options": { + "hideSystemAssignedIdentity": true, + "hideUserAssignedIdentity": false + } + } + ], + "visible": true + }, + { + "name": "basicsOptional", + "type": "Microsoft.Common.Section", + "label": "Optional Basic Configuration", + "elements": [ + { + "name": "basicsOptionalAcceptDefaults", + "type": "Microsoft.Common.OptionsGroup", + "label": "Accept defaults for optional configuration?", + "defaultValue": "Yes", + "toolTip": "Select 'No' to edit optional basic configuration.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "false" + }, + { + "label": "No", + "value": "true" + } + ], + "required": true + } + }, + { + "name": "managedServerPrefix", + "type": "Microsoft.Common.TextBox", + "label": "Managed Server Prefix", + "toolTip": "The string to prepend to the name of the managed server.", + "defaultValue": "managed-server", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{3,20}$", + "validationMessage": "The prefix must be between 3 and 20 characters long and contain letters, numbers only." + }, + "visible": "[bool(basics('basicsOptional').basicsOptionalAcceptDefaults)]" + }, + { + "name": "wlsDomainName", + "type": "Microsoft.Common.TextBox", + "label": "WebLogic Domain Name", + "toolTip": "The name of the WebLogic Domain to create.", + "defaultValue": "domain1", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{3,20}$", + "validationMessage": "The Domain Name must be between 3 and 20 characters long and contain letters, numbers only." + }, + "visible": "[bool(basics('basicsOptional').basicsOptionalAcceptDefaults)]" + }, + { + "name": "wlsDomainUID", + "type": "Microsoft.Common.TextBox", + "label": "WebLogic Domain UID", + "toolTip": "The UID of the WebLogic Domain to create.", + "defaultValue": "sample-domain1", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{3,20}$", + "validationMessage": "The Domain UID must be between 3 and 20 characters long and contain letters, numbers only." + }, + "visible": "[bool(basics('basicsOptional').basicsOptionalAcceptDefaults)]" + } + ], + "visible": true + }, { "name": "About", "type": "Microsoft.Common.InfoBox", @@ -12,21 +151,6 @@ "text": "Template version ${project.version}" }, "visible": "[bool('${template.version.visible}')]" - }, - { - "name": "identity", - "type": "Microsoft.ManagedIdentity.IdentitySelector", - "label": "Managed Identity Configuration", - "toolTip": { - "userAssignedIdentity": "Add user-assigned identities to enable the app deployment." - }, - "defaultValue": { - "systemAssignedIdentity": "Off" - }, - "options": { - "hideSystemAssignedIdentity": true, - "hideUserAssignedIdentity": false - } } ], "steps": [ @@ -202,11 +326,11 @@ "type": "Microsoft.Common.TextBlock", "visible": true, "options": { - "text": "This value is appended to 'container-registry.oracle.com/middleware/weblogic:' and used in the Dockerfile FROM statement.", - "link": { - "label": "Must be a valid tag value from Oracle Container Registry", - "uri": "https://aka.ms/wls-aks-fromImage-tag" - } + "text": "This value is appended to 'container-registry.oracle.com/middleware/weblogic:' and used in the Dockerfile FROM statement.", + "link": { + "label": "Must be a valid tag value from Oracle Container Registry", + "uri": "https://aka.ms/wls-aks-fromImage-tag" + } } }, { @@ -241,40 +365,42 @@ ] }, { - "name": "section_sslConfiguration", - "type": "Microsoft.Common.Section", - "label": "TLS/SSL Configuration", - "elements": [{ - "name": "sslConfigurationText", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "Selecting 'Yes' here will cause the template to provision WebLogic Administration Console on HTTPS (Secure) port, with your own TLS/SSL Certificate.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-config" - } - } - }, - { - "name": "enableCustomSSL", - "type": "Microsoft.Common.OptionsGroup", - "label": "Configure WebLogic Administration Console on HTTPS (Secure) port, with your own TLS/SSL Certificate?", - "defaultValue": "No", - "toolTip": "Select 'Yes' to configure WebLogic Administration Console on HTTPS (Secure) port with your own SSL Certificate.", - "constraints": { - "allowedValues": [{ - "label": "Yes", - "value": true - }, - { - "label": "No", - "value": false - } - ], - "required": false - } - }, + "name": "section_sslConfiguration", + "type": "Microsoft.Common.Section", + "label": "TLS/SSL Configuration", + "elements": [ + { + "name": "sslConfigurationText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here will cause the template to provision WebLogic Administration Console on HTTPS (Secure) port, with your own TLS/SSL Certificate.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-config" + } + } + }, + { + "name": "enableCustomSSL", + "type": "Microsoft.Common.OptionsGroup", + "label": "Configure WebLogic Administration Console on HTTPS (Secure) port, with your own TLS/SSL Certificate?", + "defaultValue": "No", + "toolTip": "Select 'Yes' to configure WebLogic Administration Console on HTTPS (Secure) port with your own SSL Certificate.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ], + "required": false + } + }, { "name": "sslConfigurationAccessOption", "type": "Microsoft.Common.OptionsGroup", @@ -456,11 +582,11 @@ ] }, { - "name": "keyVaultStoredCustomSSLSettings", - "type": "Microsoft.Common.Section", + "name": "keyVaultStoredCustomSSLSettings", + "type": "Microsoft.Common.Section", "visible": "[and(steps('section_sslConfiguration').enableCustomSSL, equals(steps('section_sslConfiguration').sslConfigurationAccessOption, 'keyVaultStoredConfig'))]", - "label": "TLS/SSL Configuration Settings", - "elements": [ + "label": "TLS/SSL Configuration Settings", + "elements": [ { "name": "sslKeystoreInfo1", "type": "Microsoft.Common.InfoBox", @@ -543,7 +669,8 @@ "defaultValue": "JKS", "toolTip": "One of the supported KeyStore types", "constraints": { - "allowedValues": [{ + "allowedValues": [ + { "label": "JKS", "value": "JKS" }, @@ -615,7 +742,8 @@ "defaultValue": "JKS", "toolTip": "One of the supported KeyStore types", "constraints": { - "allowedValues": [{ + "allowedValues": [ + { "label": "JKS", "value": "JKS" }, @@ -627,10 +755,10 @@ "required": true } } - ] - } - ] - }, + ] + } + ] + }, { "name": "section_appGateway", "type": "Microsoft.Common.Section", @@ -727,7 +855,8 @@ ], "required": true }, - "visible": "[steps('section_appGateway').enableAppGateway]" }, + "visible": "[steps('section_appGateway').enableAppGateway]" + }, { "name": "keyVaultSSLCertData", "type": "Microsoft.Common.FileUpload", @@ -1532,23 +1661,155 @@ "uri": "https://aka.ms/wls-aks-autoscaling" } } + }, + { + "name": "enableautoScaling", + "type": "Microsoft.Common.OptionsGroup", + "label": "Enable auto scaling?", + "defaultValue": "No", + "toolTip": "Select 'Yes' and provide required info to configure the connection to Prometheus and Grafana.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "autoScalingInfo", + "type": "Microsoft.Common.Section", + "label": "Prometheus and Grafana settings", + "elements": [ + { + "name": "wlsPrometheusRulesInfo", + "type": "Microsoft.Common.InfoBox", + "options": { + "icon": "Info", + "text": "Prometheus rules allow you to define alert conditions based on Prometheus expression language expressions and to send notifications about firing alerts to the scaling service.", + "uri": "https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/" + } + }, + { + "name": "wlsPrometheusRules", + "type": "Microsoft.Common.TextBox", + "label": "Prometheus rules for WebLogic cluster scaling", + "multiLine": true, + "placeholder": "", + "defaultValue": "- record: webapp:webapp_config_open_sessions_current_count:avg\n expr: avg(webapp_config_open_sessions_current_count{webapp=\"myapp2\"})\n- alert: scaleup\n for: 1m\n expr: webapp:webapp_config_open_sessions_current_count:avg > 10\n annotations:\n description: 'Scale up when current sessions is greater than 15.'\n summary: 'Firing when total sessions active greater than 15.'\n- alert: scaledown\n for: 1m\n expr: webapp:webapp_config_open_sessions_current_count:avg < 4\n annotations:\n description: 'Scale down when current sessions is less than 4.'\n summary: 'Firing when total sessions active less than 4'", + "toolTip": "Define the scaling up and scaling down rules.", + "constraints": { + "required": true + }, + "visible": true + }, + { + "name": "alertNameForScaleUp", + "type": "Microsoft.Common.TextBox", + "label": "Alert name for scaling up", + "placeholder": "", + "defaultValue": "scaleup", + "toolTip": "Use only allowed characters", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." + }, + "visible": true + }, + { + "name": "alertNameForScaleDown", + "type": "Microsoft.Common.TextBox", + "label": "Alert name for scaling down", + "placeholder": "", + "defaultValue": "scaledown", + "toolTip": "Use only allowed characters", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." + }, + "visible": true + }, + { + "name": "grafanaDashboards", + "type": "Microsoft.Common.FileUpload", + "label": "Grafana dashboards", + "toolTip": "", + "constraints": { + "required": false, + "accept": ".json" + }, + "options": { + "multiple": true, + "uploadMode": "file", + "openMode": "text", + "encoding": "UTF-8" + }, + "visible": true + }, + { + "name": "prometheusAdditionalScrapeConfigsInfo", + "type": "Microsoft.Common.InfoBox", + "options": { + "icon": "Info", + "text": "The additional scrape configs specify a set of targets and parameters describing how to scrape them.\nThis application provides default scrape configs to scrape all managed ports of in the created WebLogic domain.", + "uri": "https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config" + } + }, + { + "name": "prometheusAdditionalScrapeConfigs", + "type": "Microsoft.Common.FileUpload", + "label": "Prometheus additional scrape configs", + "toolTip": "", + "constraints": { + "accept": ".yaml" + }, + "options": { + "multiple": false, + "uploadMode": "file", + "openMode": "text", + "encoding": "UTF-8" + }, + "visible": true + } + ], + "visible": "[bool(steps('section_autoScaling').enableautoScaling)]" } ] } ], "outputs": { - "location": "[location()]", - "identity": "[basics('identity')]", - "createAKSCluster": "[bool(steps('AKSCluster').createAKSCluster)]", + "acrName": "[last(split(steps('ACRInstance').acrInfo.acrSelector.id, '/'))]", + "alertNameForScaleDown": "[steps('section_autoScaling').autoScalingInfo.alertNameForScaleDown]", + "alertNameForScaleUp": "[steps('section_autoScaling').autoScalingInfo.alertNameForScaleUp]", "aksClusterName": "[last(split(steps('AKSCluster').clusterInfo.aksClusterSelector.id, '/'))]", "aksClusterRGName": "[last(take(split(steps('AKSCluster').clusterInfo.aksClusterSelector.id, '/'), 5))]", - "createACR": "[bool(steps('ACRInstance').createACR)]", - "acrName": "[last(split(steps('ACRInstance').acrInfo.acrSelector.id, '/'))]", - "uploadAppPackage": "[bool(steps('Application').uploadAppPackage)]", "appPackageUrl": "[steps('Application').appPackageInfo.appPackageUrl]", + "appReplicas": "[int(steps('Application').appReplicas)]", "contextRoot": "[steps('Application').appPackageInfo.contextRoot]", + "createACR": "[bool(steps('ACRInstance').createACR)]", + "createAKSCluster": "[bool(steps('AKSCluster').createAKSCluster)]", + "enableautoScaling": "[bool(steps('section_autoScaling').enableautoScaling)]", "fromImage": "[bool(steps('Application').fromImage)]", - "appReplicas": "[int(steps('Application').appReplicas)]" + "grafanaDashboards": "[steps('section_autoScaling').autoScalingInfo.alertNameForScaleDown]", + "identity": "[basics('basicsRequired').identity]", + "location": "[location()]", + "managedServerPrefix": "[basics('basicsOptional').managedServerPrefix]", + "uploadAppPackage": "[bool(steps('Application').uploadAppPackage)]", + "prometheusAdditionalScrapeConfigs": "[steps('section_autoScaling').autoScalingInfo.prometheusAdditionalScrapeConfigs]", + "wdtRuntimePassword": "[basics('basicsRequired').wdtRuntimePassword]", + "wlsDomainName": "[basics('basicsOptional').wlsDomainName]", + "wlsDomainUID": "[basics('basicsOptional').wlsDomainUID]", + "wlsPassword": "[basics('basicsRequired').wlsPassword]", + "wlsPrometheusRules": "[steps('section_autoScaling').autoScalingInfo.wlsPrometheusRules]", + "wlsUserName": "[basics('basicsRequired').wlsUserName]" } } -} +} \ No newline at end of file From 2bf47be9f52039cd8c6860f4344fe0cdee5c048b Mon Sep 17 00:00:00 2001 From: galiacheng Date: Thu, 15 Apr 2021 16:57:17 +0800 Subject: [PATCH 13/37] =?UTF-8?q?Enhance=20UI=EF=BC=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Put AKS cluster info, ACR info, Oracle WebLogic Image info to the second blade. 2. Remove context root from Java EE application blade. 3. Basics: add Oracle SSO account 4. ELK blade: add checkbox of operator logs --- src/main/arm/createUiDefinition.json | 300 ++++++++++++++++----------- 1 file changed, 183 insertions(+), 117 deletions(-) diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index 707268bee..269b56d50 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -58,6 +58,47 @@ }, "visible": true }, + { + "name": "ocrSSOInfo", + "type": "Microsoft.Common.InfoBox", + "visible": true, + "options": { + "icon": "Info", + "text": "Provide an Oracle Single Sign-On (SSO) account to access the Oracle Registry Server. Click the link to create Oracle SSO account.", + "uri": "https://profile.oracle.com/myprofile/account/create-account.jspx" + } + }, + { + "name": "ocrSSOUserName", + "type": "Microsoft.Common.TextBox", + "label": "Username for Oracle Single Sign-On authentication", + "defaultValue": "example@foo.com", + "toolTip": "Username for Oracle Single Sign-On authentication to login the Oracle Container Registry.", + "constraints": { + "required": true, + "regex": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$", + "validationMessage": "The value must be an email address." + }, + "visible": true + }, + { + "name": "ocrSSOPassword", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password for Oracle Single Sign-On authentication", + "confirmPassword": "Confirm password" + }, + "toolTip": "Password for Oracle Single Sign-On authentication to login the Oracle Container Registry.", + "constraints": { + "required": true, + "regex": "^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d\\$\\&\\+\\,:;\\=\\?@#|'.\\^\\*!\\-_~/'\\[\\]\\{\\}\"]{8,}$", + "validationMessage": "The password must contain at least 8 characters, with at least 1 uppercase letter, 1 lowercase letter and 1 number, and special characters, but should not contain > < ( ) % ; \\." + }, + "options": { + "hideConfirmation": false + }, + "visible": true + }, { "name": "identity", "type": "Microsoft.ManagedIdentity.IdentitySelector", @@ -155,7 +196,7 @@ ], "steps": [ { - "name": "AKSCluster", + "name": "section_aks", "label": "Configure AKS cluster", "subLabel": { "preValidation": "Provide required info for AKS cluster configuration", @@ -163,31 +204,31 @@ }, "bladeTitle": "Configure AKS cluster", "elements": [ - { - "name": "createAKSCluster", - "type": "Microsoft.Common.OptionsGroup", - "label": "Create a new AKS cluster?", - "defaultValue": "Yes", - "toolTip": "Select 'Yes' to create a new AKS cluster, or select 'No' to provide an existing AKS cluster.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": "true" - }, - { - "label": "No", - "value": "false" - } - ], - "required": true - } - }, { "name": "clusterInfo", "type": "Microsoft.Common.Section", - "label": "Provide information for an existing AKS cluster", + "label": "Azure Kubernetes Service", "elements": [ + { + "name": "createAKSCluster", + "type": "Microsoft.Common.OptionsGroup", + "label": "Create a new AKS cluster?", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to create a new AKS cluster, or select 'No' to provide an existing AKS cluster.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, { "name": "aksClusterSelector", "type": "Microsoft.Solutions.ResourceSelector", @@ -199,47 +240,71 @@ "subscription": "onBasics", "location": "onBasics" } - } - } - ], - "visible": "[not(bool(steps('AKSCluster').createAKSCluster))]" - } - ] - }, - { - "name": "ACRInstance", - "label": "Configure ACR instance", - "subLabel": { - "preValidation": "Provide required info for ACR instance configuration", - "postValidation": "Done" - }, - "bladeTitle": "Configure ACR instance", - "elements": [ - { - "name": "createACR", - "type": "Microsoft.Common.OptionsGroup", - "label": "Create a new ACR instance?", - "defaultValue": "Yes", - "toolTip": "Select 'Yes' to create a new ACR instance, or select 'No' to provide an existing ACR instance.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": "true" }, - { - "label": "No", - "value": "false" - } - ], - "required": true - } + "visible": "[not(bool(steps('section_aks').clusterInfo.createAKSCluster))]" + }, + { + "name": "aksNodeCount", + "type": "Microsoft.Common.Slider", + "min": 1, + "max": 1000, + "label": "Node count", + "defaultValue": 2, + "showStepMarkers": false, + "toolTip": "The number of nodes that should be created along with the cluster. You will be able to resize the cluster later", + "constraints": { + "required": true + }, + "visible": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]" + }, + { + "name": "nodeVMSizeSelector", + "type": "Microsoft.Compute.SizeSelector", + "label": "Size", + "toolTip": "", + "recommendedSizes": [ + "Standard_DS2_v2" + ], + "constraints": { + "allowedSizes": [], + "excludedSizes": [], + "numAvailabilityZonesRequired": 3, + "zone": "3" + }, + "options": { + "hideDiskTypeFilter": false + }, + "osPlatform": "Linux", + "count": 2, + "visible": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]" + } + ] }, { "name": "acrInfo", "type": "Microsoft.Common.Section", - "label": "Provide information for an existing ACR instance", + "label": "Azure Container Registry", "elements": [ + { + "name": "createACR", + "type": "Microsoft.Common.OptionsGroup", + "label": "Create a new ACR instance?", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to create a new ACR instance, or select 'No' to provide an existing ACR instance.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, { "name": "acrSelector", "type": "Microsoft.Solutions.ResourceSelector", @@ -251,15 +316,52 @@ "subscription": "onBasics", "location": "onBasics" } + }, + "visible": "[not(bool(steps('section_aks').acrInfo.createACR))]" + } + ] + }, + { + "name": "imageInfo", + "type": "Microsoft.Common.Section", + "label": "Oracle WebLogic Image", + "elements": [ + { + "name": "fromImageText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "This value is appended to 'container-registry.oracle.com/middleware/weblogic:' and used in the Dockerfile FROM statement. \nOracle Standard Terms and Restrictions terms must be agreed. \nClick the following link to make sure you have agree the terms and check the valid tags.", + "link": { + "label": "Must be a valid tag value from Oracle Container Registry", + "uri": "https://aka.ms/wls-aks-fromImage-tag" + } + } + }, + { + "name": "fromImage", + "type": "Microsoft.Common.TextBox", + "label": "WebLogic Docker tag", + "defaultValue": "12.2.1.4-ol8", + "toolTip": "Docker tag that comes after 'container-registry.oracle.com/middleware/weblogic:' in the fromImage option to 'imagetool'.", + "multiLine": false, + "constraints": { + "required": true, + "validations": [ + { + "regex": "^[a-z0-9\\-\\.]+$", + "message": "Must be a valid Docker tag for WebLogic on Oracle Container Registry" + } + ] } } ], - "visible": "[not(bool(steps('ACRInstance').createACR))]" + "visible": true } ] }, { - "name": "Application", + "name": "section_application", "label": "Configure Java EE Application", "subLabel": { "preValidation": "Provide required info for application", @@ -295,7 +397,7 @@ { "name": "appPackageUrl", "type": "Microsoft.Common.FileUpload", - "label": "Application package (.war)", + "label": "Application package (.war,.ear)", "toolTip": "The application war package to deploy.", "constraints": { "required": true @@ -305,50 +407,9 @@ "uploadMode": "url", "openMode": "binary" } - }, - { - "name": "contextRoot", - "type": "Microsoft.Common.TextBox", - "label": "Application context root", - "defaultValue": "/", - "toolTip": "Specify the context root of your application.", - "constraints": { - "required": true, - "regex": "^\/.*$", - "validationMessage": "The value must start with '/'." - } } ], - "visible": "[bool(steps('Application').uploadAppPackage)]" - }, - { - "name": "fromImageText", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "This value is appended to 'container-registry.oracle.com/middleware/weblogic:' and used in the Dockerfile FROM statement.", - "link": { - "label": "Must be a valid tag value from Oracle Container Registry", - "uri": "https://aka.ms/wls-aks-fromImage-tag" - } - } - }, - { - "name": "fromImage", - "type": "Microsoft.Common.TextBox", - "label": "WebLogic Docker tag", - "defaultValue": "12.2.1.4-ol8", - "toolTip": "Docker tag that comes after 'container-registry.oracle.com/middleware/weblogic:' in the fromImage option to 'imagetool'.", - "multiLine": false, - "constraints": { - "required": true, - "validations": [ - { - "regex": "^[a-z0-9\\-\\.]+$", - "message": "Must be a valid Docker tag for WebLogic on Oracle Container Registry" - } - ] - } + "visible": "[bool(steps('section_application').uploadAppPackage)]" }, { "name": "appReplicas", @@ -1593,10 +1654,10 @@ } }, { - "name": "logsToIntegrate", + "name": "domainLogsToIntegrate", "type": "Microsoft.Common.DropDown", - "label": "WebLogic Server logs to export", - "toolTip": "The logs selected will be exported to Elasticsearch.", + "label": "WebLogic Domain logs to export", + "toolTip": "The logs selected will be exported to the Elasticsearch server.", "defaultValue": [ "Server Log" ], @@ -1639,6 +1700,11 @@ ], "required": true } + }, + { + "name": "operatorLog", + "type": "Microsoft.Common.CheckBox", + "label": "WebLogic Operator logs" } ], "visible": "[bool(steps('section_elk').enableELK)]" @@ -1740,7 +1806,7 @@ { "name": "grafanaDashboards", "type": "Microsoft.Common.FileUpload", - "label": "Grafana dashboards", + "label": "Grafana dashboards (.json)", "toolTip": "", "constraints": { "required": false, @@ -1766,7 +1832,7 @@ { "name": "prometheusAdditionalScrapeConfigs", "type": "Microsoft.Common.FileUpload", - "label": "Prometheus additional scrape configs", + "label": "Prometheus additional scrape configs (.yaml)", "toolTip": "", "constraints": { "accept": ".yaml" @@ -1786,23 +1852,23 @@ } ], "outputs": { - "acrName": "[last(split(steps('ACRInstance').acrInfo.acrSelector.id, '/'))]", + "acrName": "[last(split(steps('section_aks').acrInfo.acrSelector.id, '/'))]", "alertNameForScaleDown": "[steps('section_autoScaling').autoScalingInfo.alertNameForScaleDown]", "alertNameForScaleUp": "[steps('section_autoScaling').autoScalingInfo.alertNameForScaleUp]", - "aksClusterName": "[last(split(steps('AKSCluster').clusterInfo.aksClusterSelector.id, '/'))]", - "aksClusterRGName": "[last(take(split(steps('AKSCluster').clusterInfo.aksClusterSelector.id, '/'), 5))]", - "appPackageUrl": "[steps('Application').appPackageInfo.appPackageUrl]", - "appReplicas": "[int(steps('Application').appReplicas)]", - "contextRoot": "[steps('Application').appPackageInfo.contextRoot]", - "createACR": "[bool(steps('ACRInstance').createACR)]", - "createAKSCluster": "[bool(steps('AKSCluster').createAKSCluster)]", + "aksClusterName": "[last(split(steps('section_aks').clusterInfo.aksClusterSelector.id, '/'))]", + "aksClusterRGName": "[last(take(split(steps('section_aks').clusterInfo.aksClusterSelector.id, '/'), 5))]", + "appPackageUrl": "[steps('section_application').appPackageInfo.appPackageUrl]", + "appReplicas": "[int(steps('section_application').appReplicas)]", + "contextRoot": "[steps('section_application').appPackageInfo.contextRoot]", + "createACR": "[bool(steps('section_aks').createACR)]", + "createAKSCluster": "[bool(steps('section_aks').createAKSCluster)]", "enableautoScaling": "[bool(steps('section_autoScaling').enableautoScaling)]", - "fromImage": "[bool(steps('Application').fromImage)]", + "fromImage": "[bool(steps('section_application').fromImage)]", "grafanaDashboards": "[steps('section_autoScaling').autoScalingInfo.alertNameForScaleDown]", "identity": "[basics('basicsRequired').identity]", "location": "[location()]", "managedServerPrefix": "[basics('basicsOptional').managedServerPrefix]", - "uploadAppPackage": "[bool(steps('Application').uploadAppPackage)]", + "uploadAppPackage": "[bool(steps('section_application').uploadAppPackage)]", "prometheusAdditionalScrapeConfigs": "[steps('section_autoScaling').autoScalingInfo.prometheusAdditionalScrapeConfigs]", "wdtRuntimePassword": "[basics('basicsRequired').wdtRuntimePassword]", "wlsDomainName": "[basics('basicsOptional').wlsDomainName]", From 20893c5d001bbabd239498a7b8eddb8d8f6fb338 Mon Sep 17 00:00:00 2001 From: galiacheng Date: Fri, 16 Apr 2021 10:25:08 +0800 Subject: [PATCH 14/37] UI reviewed by team. --- src/main/arm/createUiDefinition.json | 877 ++++++++++++++------------- 1 file changed, 446 insertions(+), 431 deletions(-) diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index 269b56d50..3dccd3927 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -277,6 +277,12 @@ "osPlatform": "Linux", "count": 2, "visible": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]" + }, + { + "name": "enableAzureFileShare", + "type": "Microsoft.Common.CheckBox", + "label": "Create Azure file share service", + "visible": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]" } ] }, @@ -357,24 +363,340 @@ } ], "visible": true + }, + { + "name": "jeeAppInfo", + "type": "Microsoft.Common.Section", + "label": "Java EE Application", + "elements": [ + { + "name": "uploadAppPackage", + "type": "Microsoft.Common.OptionsGroup", + "label": "Deploy your application package?", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to deploy your application, or select 'No' to deploy a default 'hello world' open liberty application.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "appPackageUrl", + "type": "Microsoft.Common.FileUpload", + "label": "Application package (.war,.ear)", + "toolTip": "The application war package to deploy.", + "constraints": { + "required": true + }, + "options": { + "multiple": false, + "uploadMode": "url", + "openMode": "binary" + }, + "visible": "[bool(steps('section_aks').jeeAppInfo.uploadAppPackage)]" + }, + { + "name": "appReplicas", + "type": "Microsoft.Common.TextBox", + "label": "Number of application replicas", + "defaultValue": "2", + "toolTip": "The number of application replicas to deploy.", + "constraints": { + "required": true, + "regex": "^(1|2|3|4|5)$", + "validationMessage": "Number of application replicas to deploy, limit 1-5." + } + } + ], + "visible": true } ] }, { - "name": "section_application", - "label": "Configure Java EE Application", + "name": "section_appGateway", + "type": "Microsoft.Common.Section", + "label": "Networking", "subLabel": { - "preValidation": "Provide required info for application", + "preValidation": "Provide required info for networking", "postValidation": "Done" }, - "bladeTitle": "Configure Java EE Application", + "bladeTitle": "Networking", "elements": [ { - "name": "uploadAppPackage", + "name": "connectToAGText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here will cause the template to provision Load Balancer service or Ingress service for WebLogic Administration Console and WebLogic cluster.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-overview" + } + } + }, + { + "name": "lbSVCInfo", + "type": "Microsoft.Common.Section", + "label": "Standard Load Balancer service", + "elements": [ + { + "name": "lbSVC", + "type": "Microsoft.Common.EditableGrid", + "ariaLabel": "Enter information", + "label": "Standard Load Balancer service", + "constraints": { + "width": "Full", + "rows": { + "count": { + "min": 1, + "max": 10 + } + }, + "columns": [ + { + "id": "colName", + "header": "Service Name", + "width": "1fr", + "element": { + "type": "Microsoft.Common.TextBox", + "placeholder": "Full name", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^[a-z0-9A-Z]{1,30}$", + "message": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." + } + ] + } + } + }, + { + "id": "colGender", + "header": "Target", + "width": "1fr", + "element": { + "name": "dropDown1", + "type": "Microsoft.Common.DropDown", + "placeholder": "Select a target...", + "constraints": { + "allowedValues": [ + { + "label": "Admin Console", + "value": "adConsole" + }, + { + "label": "cluster-1", + "value": "cluster1" + } + ], + "required": true + } + } + }, + { + "id": "colName", + "header": "Port", + "width": "1fr", + "element": { + "type": "Microsoft.Common.TextBox", + "placeholder": "Full name", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^[0-9]{1,5}$", + "message": "Only numbers are allowed, and the value must be 1-65535." + } + ] + } + } + } + ] + } + }, + { + "name": "enableInternalLB", + "type": "Microsoft.Common.CheckBox", + "label": "Use Internal Load Balancer" + } + ], + "visible": true + }, + { + "name": "ingressInfo", + "type": "Microsoft.Common.Section", + "label": "Ingress controller", + "elements": [ + { + "name": "ingressSVC", + "type": "Microsoft.Common.EditableGrid", + "ariaLabel": "Enter information", + "label": "Ingresses", + "constraints": { + "width": "Full", + "rows": { + "count": { + "min": 1, + "max": 10 + } + }, + "columns": [ + { + "id": "colName", + "header": "Service Name", + "width": "1fr", + "element": { + "type": "Microsoft.Common.TextBox", + "placeholder": "Full name", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^[a-z0-9A-Z]{1,30}$", + "message": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." + } + ] + } + } + }, + { + "id": "colGender", + "header": "Target", + "width": "1fr", + "element": { + "name": "dropDown1", + "type": "Microsoft.Common.DropDown", + "placeholder": "Select a target...", + "constraints": { + "allowedValues": [ + { + "label": "Admin Console", + "value": "adConsole" + }, + { + "label": "cluster-1", + "value": "cluster1" + } + ], + "required": true + } + } + } + ] + } + }, + { + "name": "enableAppGateway", + "type": "Microsoft.Common.DropDown", + "label": "Eanble Azure Application Gateway?", + "placeholder": "", + "defaultValue": "No", + "toolTip": "", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ], + "required": true + }, + "visible": true + }, + { + "name": "keyVaultResourceGroup", + "type": "Microsoft.Common.TextBox", + "label": "Resource group name in current subscription containing the Key Vault", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", + "validationMessage": "[if(greater(length(steps('section_appGateway').ingressInfo.keyVaultResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" + }, + "visible": "[bool(steps('section_appGateway').ingressInfo.enableAppGateway)]" + }, + { + "name": "keyVaultName", + "type": "Microsoft.Common.TextBox", + "label": "Name of the Azure Key Vault containing secrets for the certificate for TLS/SSL Termination", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^(?=.{3,24}$)[a-zA-Z](([a-z0-9A-Z]*|(?:\\-[^\\-][a-z0-9A-Z]*))*)$", + "validationMessage": "[if(or(greater(length(steps('section_appGateway').keyVaultName), 24), less(length(steps('section_appGateway').keyVaultName), 3)),'Vault name must be between 3-24 alphanumeric characters. The name must begin with a letter, end with a letter or digit, and not contain consecutive hyphens.','Vault name must only contain alphanumeric characters and dashes and cannot start with a number')]" + }, + "visible": "[bool(steps('section_appGateway').ingressInfo.enableAppGateway)]" + }, + { + "name": "keyVaultSSLCertDataSecretName", + "type": "Microsoft.Common.TextBox", + "label": "The name of the secret in the specified Key Vault whose value is the TLS/SSL certificate data", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + }, + "visible": "[bool(steps('section_appGateway').ingressInfo.enableAppGateway)]" + }, + { + "name": "keyVaultSSLCertPasswordSecretName", + "type": "Microsoft.Common.TextBox", + "label": "The name of the secret in the specified Key Vault whose value is the password for the TLS/SSL certificate", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + }, + "visible": "[bool(steps('section_appGateway').ingressInfo.enableAppGateway)]" + } + ], + "visible": true + } + ] + }, + { + "name": "section_autoScaling", + "type": "Microsoft.Common.Section", + "label": "Autoscaling", + "elements": [ + { + "name": "enableAutoScalingText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here will cause the template to provision Prometheus and Grafana with scaling rules to enable horizontal pod auto-scaling.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/wls-aks-autoscaling" + } + } + }, + { + "name": "enableautoScaling", "type": "Microsoft.Common.OptionsGroup", - "label": "Deploy your application package?", - "defaultValue": "Yes", - "toolTip": "Select 'Yes' to deploy your application, or select 'No' to deploy a default 'hello world' open liberty application.", + "label": "Enable auto scaling?", + "defaultValue": "No", + "toolTip": "Select 'Yes' and provide required info to configure the connection to Prometheus and Grafana.", "constraints": { "allowedValues": [ { @@ -390,38 +712,104 @@ } }, { - "name": "appPackageInfo", + "name": "autoScalingInfo", "type": "Microsoft.Common.Section", - "label": "Provide information for your application package", + "label": "Prometheus and Grafana settings", "elements": [ { - "name": "appPackageUrl", - "type": "Microsoft.Common.FileUpload", - "label": "Application package (.war,.ear)", - "toolTip": "The application war package to deploy.", + "name": "wlsPrometheusRulesInfo", + "type": "Microsoft.Common.InfoBox", + "options": { + "icon": "Info", + "text": "Prometheus rules allow you to define alert conditions based on Prometheus expression language expressions and to send notifications about firing alerts to the scaling service.", + "uri": "https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/" + } + }, + { + "name": "wlsPrometheusRules", + "type": "Microsoft.Common.TextBox", + "label": "Prometheus rules for WebLogic cluster scaling", + "multiLine": true, + "placeholder": "", + "defaultValue": "- record: webapp:webapp_config_open_sessions_current_count:avg\n expr: avg(webapp_config_open_sessions_current_count{webapp=\"myapp2\"})\n- alert: scaleup\n for: 1m\n expr: webapp:webapp_config_open_sessions_current_count:avg > 10\n annotations:\n description: 'Scale up when current sessions is greater than 15.'\n summary: 'Firing when total sessions active greater than 15.'\n- alert: scaledown\n for: 1m\n expr: webapp:webapp_config_open_sessions_current_count:avg < 4\n annotations:\n description: 'Scale down when current sessions is less than 4.'\n summary: 'Firing when total sessions active less than 4'", + "toolTip": "Define the scaling up and scaling down rules.", "constraints": { "required": true }, + "visible": true + }, + { + "name": "alertNameForScaleUp", + "type": "Microsoft.Common.TextBox", + "label": "Alert name for scaling up", + "placeholder": "", + "defaultValue": "scaleup", + "toolTip": "Use only allowed characters", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." + }, + "visible": true + }, + { + "name": "alertNameForScaleDown", + "type": "Microsoft.Common.TextBox", + "label": "Alert name for scaling down", + "placeholder": "", + "defaultValue": "scaledown", + "toolTip": "Use only allowed characters", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." + }, + "visible": true + }, + { + "name": "grafanaDashboards", + "type": "Microsoft.Common.FileUpload", + "label": "Grafana dashboards (.json)", + "toolTip": "", + "constraints": { + "required": false, + "accept": ".json" + }, "options": { - "multiple": false, - "uploadMode": "url", - "openMode": "binary" + "multiple": true, + "uploadMode": "file", + "openMode": "text", + "encoding": "UTF-8" + }, + "visible": true + }, + { + "name": "prometheusAdditionalScrapeConfigsInfo", + "type": "Microsoft.Common.InfoBox", + "options": { + "icon": "Info", + "text": "The additional scrape configs specify a set of targets and parameters describing how to scrape them.\nThis application provides default scrape configs to scrape all managed ports of in the created WebLogic domain.", + "uri": "https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config" } + }, + { + "name": "prometheusAdditionalScrapeConfigs", + "type": "Microsoft.Common.FileUpload", + "label": "Prometheus additional scrape configs (.yaml)", + "toolTip": "", + "constraints": { + "accept": ".yaml" + }, + "options": { + "multiple": false, + "uploadMode": "file", + "openMode": "text", + "encoding": "UTF-8" + }, + "visible": true } ], - "visible": "[bool(steps('section_application').uploadAppPackage)]" - }, - { - "name": "appReplicas", - "type": "Microsoft.Common.TextBox", - "label": "Number of application replicas", - "defaultValue": "2", - "toolTip": "The number of application replicas to deploy.", - "constraints": { - "required": true, - "regex": "^(1|2|3|4|5)$", - "validationMessage": "Number of application replicas to deploy, limit 1-5." - } + "visible": "[bool(steps('section_autoScaling').enableautoScaling)]" } ] }, @@ -788,271 +1176,37 @@ "visible": "true", "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Trust KeyStore", "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - } - }, - { - "name": "keyVaultCustomTrustKeyStoreType", - "type": "Microsoft.Common.DropDown", - "visible": "true", - "label": "The Trust KeyStore type (JKS,PKCS12)", - "defaultValue": "JKS", - "toolTip": "One of the supported KeyStore types", - "constraints": { - "allowedValues": [ - { - "label": "JKS", - "value": "JKS" - }, - { - "label": "PKCS12", - "value": "PKCS12" - } - ], - "required": true - } - } - ] - } - ] - }, - { - "name": "section_appGateway", - "type": "Microsoft.Common.Section", - "label": "Azure Application Gateway", - "elements": [ - { - "name": "connectToAGText", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "Selecting 'Yes' here will cause the template to provision an Azure Application Gateway (WAF_v2 or later SKU), a public IP, and a backend pool consisting of the worker nodes in the cluster. Further configuration may be necessary after deployment.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-overview" - } - } - }, - { - "name": "enableAppGateway", - "type": "Microsoft.Common.OptionsGroup", - "label": "Connect to Azure Application Gateway?", - "defaultValue": "No", - "toolTip": "Select 'Yes' to create an Azure Application Gateway as the load balancer for the cluster.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": true - }, - { - "label": "No", - "value": false - } - ], - "required": false - } - }, - { - "name": "keyVaultText00", - "type": "Microsoft.Common.TextBlock", - "visible": "[steps('section_appGateway').enableAppGateway]", - "options": { - "text": "Choose an option for providing the TLS/SSL certificate and whether or not to deny public traffic to the managed servers:" - } - }, - { - "name": "keyVaultText01", - "type": "Microsoft.Common.TextBlock", - "visible": "[steps('section_appGateway').enableAppGateway]", - "options": { - "text": "    ⁃ Upload a TLS/SSL certificate: Upload the pre-signed certificate now." - } - }, - { - "name": "keyVaultText02", - "type": "Microsoft.Common.TextBlock", - "visible": "[steps('section_appGateway').enableAppGateway]", - "options": { - "text": "    ⁃ Identify an Azure Key Vault: The Key Vault must already contain the certificate and its password stored as secrets." - } - }, - { - "name": "keyVaultText03", - "type": "Microsoft.Common.TextBlock", - "visible": "[steps('section_appGateway').enableAppGateway]", - "options": { - "text": "    ⁃ Generate a self-signed certificate: generate a self-signed certificate and apply it during deployment.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-key-vault" - } - } - }, - { - "name": "certificateOption", - "type": "Microsoft.Common.OptionsGroup", - "label": "Select desired TLS/SSL certificate option", - "defaultValue": "Upload a TLS/SSL certificate", - "toolTip": "Select desired TLS/SSL certificate option", - "constraints": { - "allowedValues": [ - { - "label": "Upload a TLS/SSL certificate", - "value": "haveCert" - }, - { - "label": "Identify an Azure Key Vault", - "value": "haveKeyVault" - }, - { - "label": "Generate a self-signed certificate", - "value": "generateCert" + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." } - ], - "required": true - }, - "visible": "[steps('section_appGateway').enableAppGateway]" - }, - { - "name": "keyVaultSSLCertData", - "type": "Microsoft.Common.FileUpload", - "label": "TLS/SSL certificate(.pfx)", - "toolTip": "TLS/SSL certificate used for App Gateway", - "constraints": { - "required": true, - "accept": ".pfx" - }, - "options": { - "multiple": false, - "uploadMode": "file", - "openMode": "binary" - }, - "visible": "[equals(steps('section_appGateway').certificateOption, 'haveCert')]" - }, - { - "name": "appGatewaySSLCertPassword", - "type": "Microsoft.Common.PasswordBox", - "label": { - "password": "Password", - "confirmPassword": "Confirm password" - }, - "toolTip": "TLS/SSL certificate password", - "constraints": { - "required": "[equals(steps('section_appGateway').certificateOption, 'haveCert')]", - "regex": "^((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])|(?=.*[0-9])(?=.*[a-z])(?=.*[!@#$%^&*])|(?=.*[0-9])(?=.*[A-Z])(?=.*[!@#$%^&*])|(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*])).{6,128}$", - "validationMessage": "The password must contain at least 6 characters, with at least 1 uppercase letter, 1 lowercase letter and 1 number." - }, - "options": { - "hideConfirmation": false - }, - "visible": "[equals(steps('section_appGateway').certificateOption, 'haveCert')]" - }, - { - "name": "keyVaultResourceGroup", - "type": "Microsoft.Common.TextBox", - "label": "Resource group name in current subscription containing the Key Vault", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", - "validationMessage": "[if(greater(length(steps('section_appGateway').keyVaultResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" - }, - "visible": "[equals(steps('section_appGateway').certificateOption, 'haveKeyVault')]" - }, - { - "name": "keyVaultName", - "type": "Microsoft.Common.TextBox", - "label": "Name of the Azure Key Vault containing secrets for the certificate for TLS/SSL Termination", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^(?=.{3,24}$)[a-zA-Z](([a-z0-9A-Z]*|(?:\\-[^\\-][a-z0-9A-Z]*))*)$", - "validationMessage": "[if(or(greater(length(steps('section_appGateway').keyVaultName), 24), less(length(steps('section_appGateway').keyVaultName), 3)),'Vault name must be between 3-24 alphanumeric characters. The name must begin with a letter, end with a letter or digit, and not contain consecutive hyphens.','Vault name must only contain alphanumeric characters and dashes and cannot start with a number')]" - }, - "visible": "[equals(steps('section_appGateway').certificateOption, 'haveKeyVault')]" - }, - { - "name": "keyVaultSSLCertDataSecretName", - "type": "Microsoft.Common.TextBox", - "label": "The name of the secret in the specified Key Vault whose value is the TLS/SSL certificate data", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - }, - "visible": "[equals(steps('section_appGateway').certificateOption, 'haveKeyVault')]" - }, - { - "name": "keyVaultSSLCertPasswordSecretName", - "type": "Microsoft.Common.TextBox", - "label": "The name of the secret in the specified Key Vault whose value is the password for the TLS/SSL certificate", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - }, - "visible": "[equals(steps('section_appGateway').certificateOption, 'haveKeyVault')]" - }, - { - "name": "infoGatewayIndentity", - "type": "Microsoft.Common.InfoBox", - "visible": "[and(equals(steps('section_appGateway').certificateOption, 'generateCert'), less(length(steps('section_appGateway').gatewayIdentity.userAssignedIdentities), 1))]", - "options": { - "icon": "Error", - "text": "This option will create a self-signed TLS/SSL certificate for gateway TLS/SSL termination. This option requires at least one user-assigned identity to access Azure resources.", - "uri": "https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/deployment-script-template" - } - }, - { - "name": "gatewayIdentity", - "type": "Microsoft.ManagedIdentity.IdentitySelector", - "label": "Managed Identity Configuration", - "toolTip": { - "userAssignedIdentity": "Add user-assigned identities to enable creation of TLS/SSL certificate." - }, - "defaultValue": { - "systemAssignedIdentity": "Off" - }, - "options": { - "hideSystemAssignedIdentity": true, - "hideUserAssignedIdentity": false - }, - "visible": "[equals(steps('section_appGateway').certificateOption, 'generateCert')]" - }, - { - "name": "denyPublicTrafficForManagedServer", - "type": "Microsoft.Common.OptionsGroup", - "label": "Deny public traffic for managed server?", - "defaultValue": "Yes", - "toolTip": "Select 'Yes' to deny traffic from the public Internet from reaching managed servers directly. This setting has a higher priority than 'Ports and port ranges to expose' in basic blade.", - "constraints": { - "allowedValues": [ - { - "label": "No", - "value": false - }, - { - "label": "Yes", - "value": true + }, + { + "name": "keyVaultCustomTrustKeyStoreType", + "type": "Microsoft.Common.DropDown", + "visible": "true", + "label": "The Trust KeyStore type (JKS,PKCS12)", + "defaultValue": "JKS", + "toolTip": "One of the supported KeyStore types", + "constraints": { + "allowedValues": [ + { + "label": "JKS", + "value": "JKS" + }, + { + "label": "PKCS12", + "value": "PKCS12" + } + ], + "required": true } - ], - "required": true - }, - "visible": "[steps('section_appGateway').enableAppGateway]" + } + ] } - ], - "visible": true + ] }, { "name": "section_dnsConfiguration", @@ -1710,145 +1864,6 @@ "visible": "[bool(steps('section_elk').enableELK)]" } ] - }, - { - "name": "section_autoScaling", - "type": "Microsoft.Common.Section", - "label": "Autoscaling", - "elements": [ - { - "name": "enableAutoScalingText", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "Selecting 'Yes' here will cause the template to provision Prometheus and Grafana with scaling rules to enable horizontal pod auto-scaling.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/wls-aks-autoscaling" - } - } - }, - { - "name": "enableautoScaling", - "type": "Microsoft.Common.OptionsGroup", - "label": "Enable auto scaling?", - "defaultValue": "No", - "toolTip": "Select 'Yes' and provide required info to configure the connection to Prometheus and Grafana.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": "true" - }, - { - "label": "No", - "value": "false" - } - ], - "required": true - } - }, - { - "name": "autoScalingInfo", - "type": "Microsoft.Common.Section", - "label": "Prometheus and Grafana settings", - "elements": [ - { - "name": "wlsPrometheusRulesInfo", - "type": "Microsoft.Common.InfoBox", - "options": { - "icon": "Info", - "text": "Prometheus rules allow you to define alert conditions based on Prometheus expression language expressions and to send notifications about firing alerts to the scaling service.", - "uri": "https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/" - } - }, - { - "name": "wlsPrometheusRules", - "type": "Microsoft.Common.TextBox", - "label": "Prometheus rules for WebLogic cluster scaling", - "multiLine": true, - "placeholder": "", - "defaultValue": "- record: webapp:webapp_config_open_sessions_current_count:avg\n expr: avg(webapp_config_open_sessions_current_count{webapp=\"myapp2\"})\n- alert: scaleup\n for: 1m\n expr: webapp:webapp_config_open_sessions_current_count:avg > 10\n annotations:\n description: 'Scale up when current sessions is greater than 15.'\n summary: 'Firing when total sessions active greater than 15.'\n- alert: scaledown\n for: 1m\n expr: webapp:webapp_config_open_sessions_current_count:avg < 4\n annotations:\n description: 'Scale down when current sessions is less than 4.'\n summary: 'Firing when total sessions active less than 4'", - "toolTip": "Define the scaling up and scaling down rules.", - "constraints": { - "required": true - }, - "visible": true - }, - { - "name": "alertNameForScaleUp", - "type": "Microsoft.Common.TextBox", - "label": "Alert name for scaling up", - "placeholder": "", - "defaultValue": "scaleup", - "toolTip": "Use only allowed characters", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." - }, - "visible": true - }, - { - "name": "alertNameForScaleDown", - "type": "Microsoft.Common.TextBox", - "label": "Alert name for scaling down", - "placeholder": "", - "defaultValue": "scaledown", - "toolTip": "Use only allowed characters", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." - }, - "visible": true - }, - { - "name": "grafanaDashboards", - "type": "Microsoft.Common.FileUpload", - "label": "Grafana dashboards (.json)", - "toolTip": "", - "constraints": { - "required": false, - "accept": ".json" - }, - "options": { - "multiple": true, - "uploadMode": "file", - "openMode": "text", - "encoding": "UTF-8" - }, - "visible": true - }, - { - "name": "prometheusAdditionalScrapeConfigsInfo", - "type": "Microsoft.Common.InfoBox", - "options": { - "icon": "Info", - "text": "The additional scrape configs specify a set of targets and parameters describing how to scrape them.\nThis application provides default scrape configs to scrape all managed ports of in the created WebLogic domain.", - "uri": "https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config" - } - }, - { - "name": "prometheusAdditionalScrapeConfigs", - "type": "Microsoft.Common.FileUpload", - "label": "Prometheus additional scrape configs (.yaml)", - "toolTip": "", - "constraints": { - "accept": ".yaml" - }, - "options": { - "multiple": false, - "uploadMode": "file", - "openMode": "text", - "encoding": "UTF-8" - }, - "visible": true - } - ], - "visible": "[bool(steps('section_autoScaling').enableautoScaling)]" - } - ] } ], "outputs": { From 596c2f460eecf2f025bfb02e42c62698fc32e292 Mon Sep 17 00:00:00 2001 From: galiacheng Date: Mon, 19 Apr 2021 13:44:37 +0800 Subject: [PATCH 15/37] Deploy AKS using bicep # build template `bicep build main.bicep` # deploy template `az deployment group create -f main.json -g ` --- src/main/arm/main.bicep | 27 ++++++++ src/main/arm/modules/aks.bicep | 120 +++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 src/main/arm/main.bicep create mode 100644 src/main/arm/modules/aks.bicep diff --git a/src/main/arm/main.bicep b/src/main/arm/main.bicep new file mode 100644 index 000000000..12c235181 --- /dev/null +++ b/src/main/arm/main.bicep @@ -0,0 +1,27 @@ +@maxLength(12) +@minLength(1) +param aksAgentPoolName string = 'agentpool' +@maxValue(10000) +@minValue(1) +param aksAgentPoolNodeCount int = 3 +param aksAgentPoolVMSize string = 'Standard_DS2_v2' +param aksClusterNamePrefix string = 'wlsonaks' +param aksVersion string = 'default' +param location string = 'eastus' + +/* + * Deploy AKS cluster +*/ +module AKSClusterDeployment './modules/aks.bicep' = { + name: 'aksDeploy' + params: { + aksAgentPoolName: '${aksAgentPoolName}' + aksAgentPoolNodeCount: int('${aksAgentPoolNodeCount}') + aksAgentPoolVMSize: '${aksAgentPoolVMSize}' + aksClusterNamePrefix: '${aksClusterNamePrefix}' + aksVersion: '${aksVersion}' + location: '${location}' + } +} + +output aksClusterName string = AKSClusterDeployment.outputs.aksClusterName diff --git a/src/main/arm/modules/aks.bicep b/src/main/arm/modules/aks.bicep new file mode 100644 index 000000000..0ca9740a3 --- /dev/null +++ b/src/main/arm/modules/aks.bicep @@ -0,0 +1,120 @@ +@maxLength(12) +@minLength(1) +param aksAgentPoolName string = 'agentpool' +@maxValue(10000) +@minValue(1) +param aksAgentPoolNodeCount int = 3 +param aksAgentPoolVMSize string = 'Standard_DS2_v2' +param aksClusterNamePrefix string = 'wlsonaks' +param aksVersion string = 'default' +param location string = 'eastus' + +var aksAgentPoolOSDiskSizeGB = 128 +var aksAgentPoolMaxPods = 110 +var aksAvailabilityZones = [ + '1' + '2' + '3' +] +// Generate a unique AKS name scoped to subscription. +// Create different cluster name for different deployment to avoid template validation error. +var aksClusterNameDefault = concat(aksClusterNamePrefix, '0', uniqueString(subscription().subscriptionId)) +var aksClusterNameForSV = concat(aksClusterNamePrefix, '1', uniqueString(subscription().subscriptionId)) +var AKSAPIVersion = '2021-02-01' + +resource aksClusterLatest 'Microsoft.ContainerService/managedClusters@2021-02-01' = if (contains(aksVersion, 'default')) { + name: aksClusterNameDefault + location: location + properties: { + dnsPrefix: '${aksClusterNameDefault}-dns' + agentPoolProfiles: [ + { + name: aksAgentPoolName + count: aksAgentPoolNodeCount + vmSize: aksAgentPoolVMSize + osDiskSizeGB: aksAgentPoolOSDiskSizeGB + osDiskType: 'Managed' + kubeletDiskType: 'OS' + maxPods: aksAgentPoolMaxPods + type: 'VirtualMachineScaleSets' + availabilityZones: aksAvailabilityZones + nodeLabels: {} + mode: 'System' + osType: 'Linux' + } + ] + addonProfiles: { + KubeDashboard: { + enabled: false + } + azurepolicy: { + enabled: false + } + httpApplicationRouting: { + enabled: false + } + omsAgent: { + enabled: false + } + } + enableRBAC: true + networkProfile: { + networkPlugin: 'kubenet' + loadBalancerSku: 'standard' + } + } + identity: { + // enable system identity. + type: 'SystemAssigned' + } +} + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2021-02-01' = if (!contains(aksVersion, 'default')) { + name: aksClusterNameForSV + location: location + properties: { + kubernetesVersion: '${aksVersion}' + dnsPrefix: '${aksClusterNameForSV}-dns' + agentPoolProfiles: [ + { + name: aksAgentPoolName + count: aksAgentPoolNodeCount + vmSize: aksAgentPoolVMSize + osDiskSizeGB: aksAgentPoolOSDiskSizeGB + osDiskType: 'Managed' + kubeletDiskType: 'OS' + maxPods: aksAgentPoolMaxPods + type: 'VirtualMachineScaleSets' + availabilityZones: aksAvailabilityZones + nodeLabels: {} + mode: 'System' + osType: 'Linux' + } + ] + addonProfiles: { + KubeDashboard: { + enabled: false + } + azurepolicy: { + enabled: false + } + httpApplicationRouting: { + enabled: false + } + omsAgent: { + enabled: false + } + } + enableRBAC: true + networkProfile: { + networkPlugin: 'kubenet' + loadBalancerSku: 'standard' + } + } + identity: { + // enable system identity. + type: 'SystemAssigned' + } +} + +output aksClusterName string = '${aksVersion}' == 'default' ? aksClusterNameDefault : aksClusterNameForSV From 8cdac440c257ec00a2693ae6d026f51eedbf38a7 Mon Sep 17 00:00:00 2001 From: galiacheng Date: Mon, 19 Apr 2021 16:18:01 +0800 Subject: [PATCH 16/37] Allow to enable azure monitoring. --- src/main/arm/createUiDefinition.json | 6 +++ src/main/arm/main.bicep | 52 ++++++++++++++++++++---- src/main/arm/modules/aks.bicep | 60 ++++++++++++++++++++++++---- 3 files changed, 103 insertions(+), 15 deletions(-) diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index 3dccd3927..4e3f4708f 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -278,6 +278,12 @@ "count": 2, "visible": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]" }, + { + "name": "enableAzureMonitoring", + "type": "Microsoft.Common.CheckBox", + "label": "Create Azure file share service", + "visible": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]" + }, { "name": "enableAzureFileShare", "type": "Microsoft.Common.CheckBox", diff --git a/src/main/arm/main.bicep b/src/main/arm/main.bicep index 12c235181..7f66f4b11 100644 --- a/src/main/arm/main.bicep +++ b/src/main/arm/main.bicep @@ -1,26 +1,64 @@ +/* +* Terms +* aci is short for Azure Container Insight +* aks is short for Azure Kubernetes Service +* +* Run the template: +* $ bicep build main.bicep +* $ az deployment group create -f main.json -g +*/ + +@description('true to use resource or workspace permissions. false to require workspace permissions.') +param aciResourcePermissions bool = true + +@description('Number of days to retain data in Azure Monitor workspace.') +param aciRetentionInDays int = 120 + +@description('Pricing tier: PerGB2018 or legacy tiers (Free, Standalone, PerNode, Standard or Premium) which are not available to all customers.') +param aciWorkspaceSku string = 'pergb2018' + @maxLength(12) @minLength(1) +@description('The name for this node pool. Node pool must contain only lowercase letters and numbers. For Linux node pools the name cannot be longer than 12 characters.') param aksAgentPoolName string = 'agentpool' + @maxValue(10000) @minValue(1) +@description('The number of nodes that should be created along with the cluster. You will be able to resize the cluster later.') param aksAgentPoolNodeCount int = 3 + +@description('The size of the virtual machines that will form the nodes in the cluster. This cannot be changed after creating the cluster') param aksAgentPoolVMSize string = 'Standard_DS2_v2' + +@description('Prefix for cluster name. Only The name can contain only letters, numbers, underscores and hyphens. The name must start with letter or number.') param aksClusterNamePrefix string = 'wlsonaks' + param aksVersion string = 'default' + +@description('true to create a new AKS cluster.') +param createAKSCluster bool = true + +@description('In addition to the CPU and memory metrics included in AKS by default, you can enable Container Insights for more comprehensive data on the overall performance and health of your cluster. Billing is based on data ingestion and retention settings.') +param enableAzureMonitoring bool = false + param location string = 'eastus' /* * Deploy AKS cluster */ -module AKSClusterDeployment './modules/aks.bicep' = { +module AKSClusterDeployment './modules/aks.bicep' = if(createAKSCluster){ name: 'aksDeploy' params: { - aksAgentPoolName: '${aksAgentPoolName}' - aksAgentPoolNodeCount: int('${aksAgentPoolNodeCount}') - aksAgentPoolVMSize: '${aksAgentPoolVMSize}' - aksClusterNamePrefix: '${aksClusterNamePrefix}' - aksVersion: '${aksVersion}' - location: '${location}' + aciResourcePermissions: aciResourcePermissions + aciRetentionInDays: aciRetentionInDays + aciWorkspaceSku: aciWorkspaceSku + aksAgentPoolName: aksAgentPoolName + aksAgentPoolNodeCount: aksAgentPoolNodeCount + aksAgentPoolVMSize: aksAgentPoolVMSize + aksClusterNamePrefix: aksClusterNamePrefix + aksVersion: aksVersion + enableAzureMonitoring: enableAzureMonitoring + location: location } } diff --git a/src/main/arm/modules/aks.bicep b/src/main/arm/modules/aks.bicep index 0ca9740a3..e353fc910 100644 --- a/src/main/arm/modules/aks.bicep +++ b/src/main/arm/modules/aks.bicep @@ -1,14 +1,45 @@ +@description('true to use resource or workspace permissions. false to require workspace permissions.') +param aciResourcePermissions bool = true + +@description('Number of days to retain data in Azure Monitor workspace.') +param aciRetentionInDays int = 120 + +@description('Pricing tier: PerGB2018 or legacy tiers (Free, Standalone, PerNode, Standard or Premium) which are not available to all customers.') +param aciWorkspaceSku string = 'pergb2018' + @maxLength(12) @minLength(1) +@description('The name for this node pool. Node pool must contain only lowercase letters and numbers. For Linux node pools the name cannot be longer than 12 characters.') param aksAgentPoolName string = 'agentpool' + @maxValue(10000) @minValue(1) +@description('The number of nodes that should be created along with the cluster. You will be able to resize the cluster later.') param aksAgentPoolNodeCount int = 3 + +@description('The size of the virtual machines that will form the nodes in the cluster. This cannot be changed after creating the cluster') param aksAgentPoolVMSize string = 'Standard_DS2_v2' + +@description('Prefix for cluster name. Only The name can contain only letters, numbers, underscores and hyphens. The name must start with letter or number.') param aksClusterNamePrefix string = 'wlsonaks' + param aksVersion string = 'default' + +@description('In addition to the CPU and memory metrics included in AKS by default, you can enable Container Insights for more comprehensive data on the overall performance and health of your cluster. Billing is based on data ingestion and retention settings.') +param enableAzureMonitoring bool = false + param location string = 'eastus' +var aciWorkspaceName = 'Workspace-${guid(subscription().subscriptionId)}-${location}' +var aciDisableOmsAgent = { + enabled: false +} +var aciEnableOmsAgent = { + enabled: true + config: { + logAnalyticsWorkspaceResourceID: azureMonitoringWorkspace.id + } +} var aksAgentPoolOSDiskSizeGB = 128 var aksAgentPoolMaxPods = 110 var aksAvailabilityZones = [ @@ -18,11 +49,26 @@ var aksAvailabilityZones = [ ] // Generate a unique AKS name scoped to subscription. // Create different cluster name for different deployment to avoid template validation error. -var aksClusterNameDefault = concat(aksClusterNamePrefix, '0', uniqueString(subscription().subscriptionId)) -var aksClusterNameForSV = concat(aksClusterNamePrefix, '1', uniqueString(subscription().subscriptionId)) -var AKSAPIVersion = '2021-02-01' +var aksClusterNameDefault = '${aksClusterNamePrefix}0${uniqueString(subscription().subscriptionId)}' +var aksClusterNameForSV = '${aksClusterNamePrefix}1${uniqueString(subscription().subscriptionId)}' -resource aksClusterLatest 'Microsoft.ContainerService/managedClusters@2021-02-01' = if (contains(aksVersion, 'default')) { +resource azureMonitoringWorkspace 'Microsoft.OperationalInsights/workspaces@2020-08-01' = if (enableAzureMonitoring) { + name: aciWorkspaceName + location: location + properties: { + sku: { + name: aciWorkspaceSku + } + retentionInDays: aciRetentionInDays + features: { + searchVersion: 1 + legacy: 0 + enableLogAccessUsingOnlyResourcePermissions: aciResourcePermissions + } + } +} + +resource aksClusterDefault 'Microsoft.ContainerService/managedClusters@2021-02-01' = if (contains(aksVersion, 'default')) { name: aksClusterNameDefault location: location properties: { @@ -53,9 +99,7 @@ resource aksClusterLatest 'Microsoft.ContainerService/managedClusters@2021-02-01 httpApplicationRouting: { enabled: false } - omsAgent: { - enabled: false - } + omsAgent: enableAzureMonitoring ? aciEnableOmsAgent : aciDisableOmsAgent } enableRBAC: true networkProfile: { @@ -102,7 +146,7 @@ resource aksCluster 'Microsoft.ContainerService/managedClusters@2021-02-01' = if enabled: false } omsAgent: { - enabled: false + enabled: bool('${enableAzureMonitoring}') } } enableRBAC: true From fcba1886a02c092bc1b32e2063935b98346e5edd Mon Sep 17 00:00:00 2001 From: galiacheng Date: Mon, 19 Apr 2021 16:34:32 +0800 Subject: [PATCH 17/37] Fix unique name. --- src/main/arm/modules/aks.bicep | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/arm/modules/aks.bicep b/src/main/arm/modules/aks.bicep index e353fc910..3cba1c250 100644 --- a/src/main/arm/modules/aks.bicep +++ b/src/main/arm/modules/aks.bicep @@ -30,7 +30,9 @@ param enableAzureMonitoring bool = false param location string = 'eastus' -var aciWorkspaceName = 'Workspace-${guid(subscription().subscriptionId)}-${location}' +param utcValue string = utcNow() + +var aciWorkspaceName = 'Workspace-${guid(utcValue)}-${location}' var aciDisableOmsAgent = { enabled: false } @@ -49,8 +51,8 @@ var aksAvailabilityZones = [ ] // Generate a unique AKS name scoped to subscription. // Create different cluster name for different deployment to avoid template validation error. -var aksClusterNameDefault = '${aksClusterNamePrefix}0${uniqueString(subscription().subscriptionId)}' -var aksClusterNameForSV = '${aksClusterNamePrefix}1${uniqueString(subscription().subscriptionId)}' +var aksClusterNameDefault = '${aksClusterNamePrefix}0${uniqueString(utcValue)}' +var aksClusterNameForSV = '${aksClusterNamePrefix}1${uniqueString(utcValue)}' resource azureMonitoringWorkspace 'Microsoft.OperationalInsights/workspaces@2020-08-01' = if (enableAzureMonitoring) { name: aciWorkspaceName From 993714b848e2b9551ab7dde81f1ee14c0ce54b12 Mon Sep 17 00:00:00 2001 From: Edward Burns Date: Tue, 20 Apr 2021 00:22:19 +0000 Subject: [PATCH 18/37] Merged PR 318531: mvn -Pbicep -Ddev -Passembly clean install mvn -Pbicep -Ddev -Passembly clean install https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1311178 renamed: src/main/arm/main.bicep -> src/main/bicep/mainTemplate.bicep renamed: src/main/arm/modules/aks.bicep -> src/main/bicep/modules/aks.bicep modified: pom.xml deleted: src/main/arm/mainTemplate.json Signed-off-by: Ed Burns --- pom.xml | 2 +- src/main/arm/mainTemplate.json | 122 ------------------ .../main.bicep => bicep/mainTemplate.bicep} | 0 src/main/{arm => bicep}/modules/aks.bicep | 0 4 files changed, 1 insertion(+), 123 deletions(-) delete mode 100644 src/main/arm/mainTemplate.json rename src/main/{arm/main.bicep => bicep/mainTemplate.bicep} (100%) rename src/main/{arm => bicep}/modules/aks.bicep (100%) diff --git a/pom.xml b/pom.xml index 17ca0f0b9..a3f73fe56 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ com.microsoft.azure.iaas azure-javaee-iaas-parent - 1.0.7 + 1.0.8 diff --git a/src/main/arm/mainTemplate.json b/src/main/arm/mainTemplate.json deleted file mode 100644 index 4b7b6d13b..000000000 --- a/src/main/arm/mainTemplate.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "_artifactsLocation": { - "type": "string", - "defaultValue": "[deployment().properties.templateLink.uri]" - }, - "_artifactsLocationSasToken": { - "type": "securestring", - "defaultValue": "" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "identity": { - "type": "object" - }, - "createAKSCluster": { - "defaultValue": true, - "type": "bool" - }, - "aksClusterName": { - "type": "string", - "defaultValue": "" - }, - "aksClusterRGName": { - "type": "string", - "defaultValue": "" - }, - "createACR": { - "defaultValue": true, - "type": "bool" - }, - "acrName": { - "type": "string", - "defaultValue": "" - }, - "uploadAppPackage": { - "defaultValue": false, - "type": "bool" - }, - "appPackageUrl": { - "defaultValue": "", - "type": "string" - }, - "contextRoot": { - "defaultValue": "/", - "type": "string" - }, - "useOpenLibertyImage": { - "defaultValue": true, - "type": "bool" - }, - "useJava8": { - "defaultValue": true, - "type": "bool" - }, - "appReplicas": { - "type": "int", - "defaultValue": 1 - }, - "guidValue": { - "defaultValue": "[newGuid()]", - "type": "string" - } - }, - "variables": { - "const_aksClusterRGName": "[if(parameters('createAKSCluster'), resourceGroup().name, parameters('aksClusterRGName'))]", - "const_appName": "[concat('app', variables('const_suffix'))]", - "const_appPackageUrl": "[if(parameters('uploadAppPackage'), parameters('appPackageUrl'), 'N/A')]", - "const_arguments": "[concat(variables('const_aksClusterRGName'), ' ', variables('name_aksClusterName'), ' ', variables('name_acrName'), ' ', parameters('uploadAppPackage'), ' ', variables('const_appPackageUrl'), ' ', parameters('contextRoot'), ' ', parameters('useOpenLibertyImage'), ' ', parameters('useJava8'), ' ', parameters('appReplicas'), ' ', variables('const_appName'))]", - "const_scriptLocation": "[uri(parameters('_artifactsLocation'), 'scripts/')]", - "const_suffix": "[take(replace(parameters('guidValue'), '-', ''), 6)]", - "name_acrName": "[if(parameters('createACR'), concat('acr', variables('const_suffix')), parameters('acrName'))]", - "name_aksClusterName": "[if(parameters('createAKSCluster'), concat('cluster', variables('const_suffix')), parameters('aksClusterName'))]", - "name_deploymentScriptName": "[concat('script', variables('const_suffix'))]" - }, - "resources": [ - { - "apiVersion": "2019-09-01", - "name": "[${tracking.pid}]", - "type": "Microsoft.Resources/deployments", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [ - ] - } - } - } - ], - "outputs": { - "aksClusterName": { - "value": "[variables('name_aksClusterName')]", - "type": "string" - }, - "aksClusterRGName": { - "value": "[variables('const_aksClusterRGName')]", - "type": "string" - }, - "acrName": { - "value": "[variables('name_acrName')]", - "type": "string" - }, - "appImage": { - "value": "[if(parameters('uploadAppPackage'), variables('const_appName'), 'Open Liberty/WebSphere Liberty base image')]", - "type": "string" - }, - "appName": { - "value": "[variables('const_appName')]", - "type": "string" - }, - "result": { - "value": "[reference(variables('name_deploymentScriptName')).outputs]", - "type": "object" - } - } -} diff --git a/src/main/arm/main.bicep b/src/main/bicep/mainTemplate.bicep similarity index 100% rename from src/main/arm/main.bicep rename to src/main/bicep/mainTemplate.bicep diff --git a/src/main/arm/modules/aks.bicep b/src/main/bicep/modules/aks.bicep similarity index 100% rename from src/main/arm/modules/aks.bicep rename to src/main/bicep/modules/aks.bicep From 1cf0db00cc62fe0549b5ac931569b2080d7e03a7 Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Wed, 21 Apr 2021 02:30:15 +0000 Subject: [PATCH 19/37] Merged PR 318634: Proposed pids module. Proposed pids module. - create pid.bicep for prod build - create pid-dev.bicep for deve build - put pids to the output of pid.bicep - use value from pid.bicep output for pid deployment Changes to be committed: modified: pom.xml modified: src/main/bicep/mainTemplate.bicep new file: src/main/bicep/modules/pids/empty.bicep new file: src/main/bicep/modules/pids/pid-dev.bicep new file: src/main/bicep/modules/pids/pid.bicep Related work items: #1314703 --- pom.xml | 47 +++++++++-------------- src/main/bicep/mainTemplate.bicep | 44 +++++++++++++++++++-- src/main/bicep/modules/pids/empty.bicep | 7 ++++ src/main/bicep/modules/pids/pid-dev.bicep | 11 ++++++ src/main/bicep/modules/pids/pid.bicep | 11 ++++++ 5 files changed, 87 insertions(+), 33 deletions(-) create mode 100644 src/main/bicep/modules/pids/empty.bicep create mode 100644 src/main/bicep/modules/pids/pid-dev.bicep create mode 100644 src/main/bicep/modules/pids/pid.bicep diff --git a/pom.xml b/pom.xml index a3f73fe56..805a9725b 100644 --- a/pom.xml +++ b/pom.xml @@ -22,33 +22,22 @@ --> - 4.0.0 - - com.oracle.weblogic.azure - wls-on-aks-azure-marketplace - 1.0.0 - - - com.microsoft.azure.iaas - azure-javaee-iaas-parent - 1.0.8 - - - - jar - wls-on-aks-azure-marketplace - - - - - pid-35859ecc-219b-4930-9431-38c95bb76cee - -TestParameter '@{"PasswordMinLength"=6}' - - - - - + 4.0.0 + + com.oracle.weblogic.azure + wls-on-aks-azure-marketplace + 1.0.0 + + + com.microsoft.azure.iaas + azure-javaee-iaas-parent + 1.0.9 + + + + jar + wls-on-aks-azure-marketplace + + -TestParameter '@{"PasswordMinLength"=6}' + diff --git a/src/main/bicep/mainTemplate.bicep b/src/main/bicep/mainTemplate.bicep index 7f66f4b11..5c403e215 100644 --- a/src/main/bicep/mainTemplate.bicep +++ b/src/main/bicep/mainTemplate.bicep @@ -4,8 +4,11 @@ * aks is short for Azure Kubernetes Service * * Run the template: -* $ bicep build main.bicep -* $ az deployment group create -f main.json -g +* $ bicep build mainTemplate.bicep +* $ az deployment group create -f mainTemplate.json -g +* +* Build marketplace offer for test: +* $ mvn -Pbicep -Ddev -Passembly clean install */ @description('true to use resource or workspace permissions. false to require workspace permissions.') @@ -43,11 +46,30 @@ param enableAzureMonitoring bool = false param location string = 'eastus' +var defaultPidDeploymentName = 'pid' + +/* +* Beginning of the offer deployment +*/ +module pids './modules/pids/pid.bicep' = { + name: 'initialization' +} + +/* +* Deploy a pid to tract an offer deployment starts +*/ +module pidStart './modules/pids/pid.bicep' = { + name: 'wls-aks-start-pid-deployment' + params:{ + name: pids.outputs.wlsAKSStart == '' ? defaultPidDeploymentName: pids.outputs.wlsAKSStart + } +} + /* - * Deploy AKS cluster +* Deploy AKS cluster */ module AKSClusterDeployment './modules/aks.bicep' = if(createAKSCluster){ - name: 'aksDeploy' + name: 'aks-cluster-deployment' params: { aciResourcePermissions: aciResourcePermissions aciRetentionInDays: aciRetentionInDays @@ -62,4 +84,18 @@ module AKSClusterDeployment './modules/aks.bicep' = if(createAKSCluster){ } } +/* +* Deploy a pid to tract an offer deployment ends +* Make sure all the dependencies added to dependsOn +*/ +module pidEnd './modules/pids/pid.bicep' = { + name: 'wls-aks-end-pid-deployment' + params:{ + name: pids.outputs.wlsAKSEnd == '' ? defaultPidDeploymentName: pids.outputs.wlsAKSEnd + } + dependsOn:[ + AKSClusterDeployment + ] +} + output aksClusterName string = AKSClusterDeployment.outputs.aksClusterName diff --git a/src/main/bicep/modules/pids/empty.bicep b/src/main/bicep/modules/pids/empty.bicep new file mode 100644 index 000000000..8b9f99e11 --- /dev/null +++ b/src/main/bicep/modules/pids/empty.bicep @@ -0,0 +1,7 @@ +/* +* Used to create an empty deployment +* Example: +* module emptyDeployment './empty.bicep' = { +* name: name +* } +*/ diff --git a/src/main/bicep/modules/pids/pid-dev.bicep b/src/main/bicep/modules/pids/pid-dev.bicep new file mode 100644 index 000000000..01aa0f9bd --- /dev/null +++ b/src/main/bicep/modules/pids/pid-dev.bicep @@ -0,0 +1,11 @@ +// Deployment for pids. + +param name string = 'pid' + +// create a pid deployment if there is a specified name +module pidStart './empty.bicep' = if (name != 'pid'){ + name: name +} + +output wlsAKSEnd string = '17328b4d-841f-57b5-a9c5-861ad48f9d0d' +output wlsAKSStart string = 'c46a11b1-e8d2-5053-9741-45294b2e15c9' diff --git a/src/main/bicep/modules/pids/pid.bicep b/src/main/bicep/modules/pids/pid.bicep new file mode 100644 index 000000000..8313a6462 --- /dev/null +++ b/src/main/bicep/modules/pids/pid.bicep @@ -0,0 +1,11 @@ +// Deployment for pids. + +param name string = 'pid' + +// create a pid deployment if there is a specified name +module pidStart './empty.bicep' = if (name != 'pid'){ + name: name +} + +output wlsAKSEnd string = '2571f846-2f66-5c22-9fe6-38ecea7889ac' +output wlsAKSStart string = '3e6acde5-9a62-5488-9fd4-87c46f4105f4' From e924fa32e2049da4f1f802b50210ac3ad33adb10 Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Thu, 13 May 2021 18:45:51 +0000 Subject: [PATCH 20/37] Merged PR 323147: Create a simple WebLogic Cluster https://dev.azure.com/devdiv/DevDiv/_git/wls-on-aks-azure-marketplace/pullrequest/323147 Allow to create Azure resource: AKS ACR AKS monitoring Storage account for persistent volume Initialize a simple WLS cluster with One domain One cluster with name cluster-1 Support to deploy multiple applications Refactoring Update private bicep file name with prefix _ Depends on pr-to-azure-javaee-iaas-19 Offer for test https://portal.azure.com/#create/microsoft_javaeeonazure_test.20210108-galia-previewcluster Changes modified: pom.xml increase version number modified: src/main/arm/createUiDefinition.json APPROVED remove uncompleted feature from UI add option for aks pv/pvc creation new file: src/main/arm/scripts/buildWLSDockerImage.sh APPROVED run in vm extension, to build docker image new file: src/main/arm/scripts/domain.yaml.template APPROVED domain resource description template new file: src/main/arm/scripts/model.properties APPROVED model properties new file: src/main/arm/scripts/model.yaml APPROVED model configuration new file: src/main/arm/scripts/pv.yaml.template APPROVED pv template new file: src/main/arm/scripts/pvc.yaml.template APPROVED pvc template new file: src/main/arm/scripts/setupWLSDomain.sh APPROVED run in deployment script create vm resource to build docker image set up wls domain modified: src/main/bicep/mainTemplate.bicep APPROVED add deployment script module add acr module add storage module new file: src/main/bicep/modules/azure-resoruces/acr.bicep APPROVED script to deploy acr modified: src/main/bicep/modules/azure-resoruces/aks.bicep APPROVED add required parameters new file: src/main/bicep/modules/azure-resoruces/storage.bicep APPROVED script to deploy storage new file: src/main/bicep/modules/deployment-scripts/ds-create-wls-cluster.bicep APPROVED deployment script to set up WLS domain modified: src/main/bicep/modules/pids/empty.bicep APPROVED add comments modified: src/main/bicep/modules/pids/pid-dev.bicep APPROVED add comments modified: src/main/bicep/modules/pids/pid.bicep APPROVED add comments modified: src/main/bicep/modules/setupWebLogicCluster.bicep APPROVED modified: src/main/bicep/modules/mainTemplate.bicep APPROVED. --- pom.xml | 4 +- src/main/arm/createUiDefinition.json | 1581 ++--------------- src/main/arm/scripts/buildWLSDockerImage.sh | 254 +++ src/main/arm/scripts/domain.yaml.template | 141 ++ src/main/arm/scripts/model.properties | 6 + src/main/arm/scripts/model.yaml | 46 + src/main/arm/scripts/pv.yaml.template | 28 + src/main/arm/scripts/pvc.yaml.template | 18 + src/main/arm/scripts/setupWLSDomain.sh | 492 +++++ src/main/bicep/mainTemplate.bicep | 130 +- .../bicep/modules/_azure-resoruces/_acr.bicep | 43 + .../_aks.bicep} | 67 +- .../modules/_azure-resoruces/_storage.bicep | 53 + .../_ds-create-wls-cluster.bicep | 57 + src/main/bicep/modules/_pids/_empty.bicep | 10 + .../pid-dev.bicep => _pids/_pid-dev.bicep} | 5 +- .../{pids/pid.bicep => _pids/_pid.bicep} | 5 +- src/main/bicep/modules/pids/empty.bicep | 7 - .../bicep/modules/setupWebLogicCluster.bicep | 197 ++ 19 files changed, 1582 insertions(+), 1562 deletions(-) create mode 100644 src/main/arm/scripts/buildWLSDockerImage.sh create mode 100644 src/main/arm/scripts/domain.yaml.template create mode 100644 src/main/arm/scripts/model.properties create mode 100644 src/main/arm/scripts/model.yaml create mode 100644 src/main/arm/scripts/pv.yaml.template create mode 100644 src/main/arm/scripts/pvc.yaml.template create mode 100644 src/main/arm/scripts/setupWLSDomain.sh create mode 100644 src/main/bicep/modules/_azure-resoruces/_acr.bicep rename src/main/bicep/modules/{aks.bicep => _azure-resoruces/_aks.bicep} (76%) create mode 100644 src/main/bicep/modules/_azure-resoruces/_storage.bicep create mode 100644 src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep create mode 100644 src/main/bicep/modules/_pids/_empty.bicep rename src/main/bicep/modules/{pids/pid-dev.bicep => _pids/_pid-dev.bicep} (52%) rename src/main/bicep/modules/{pids/pid.bicep => _pids/_pid.bicep} (52%) delete mode 100644 src/main/bicep/modules/pids/empty.bicep create mode 100644 src/main/bicep/modules/setupWebLogicCluster.bicep diff --git a/pom.xml b/pom.xml index 805a9725b..1bf4faa96 100644 --- a/pom.xml +++ b/pom.xml @@ -26,12 +26,12 @@ com.oracle.weblogic.azure wls-on-aks-azure-marketplace - 1.0.0 + 1.0.1 com.microsoft.azure.iaas azure-javaee-iaas-parent - 1.0.9 + 1.0.10 diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index 4e3f4708f..46b800699 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -17,8 +17,16 @@ "toolTip": "Use only letters and numbers", "constraints": { "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + "validations": [ + { + "regex": "^[a-z0-9A-Z]{1,30}$", + "message": "The value must be 1-30 characters long and must only contain letters and numbers." + }, + { + "isValid": "[greaterOrEquals(length(basics('basicsRequired').identity.userAssignedIdentities),1)]", + "message": "Please select at least one user assigned managed identity from User assigned managed identity control below." + } + ] }, "visible": true }, @@ -44,10 +52,10 @@ "name": "wdtRuntimePassword", "type": "Microsoft.Common.PasswordBox", "label": { - "password": "Password for model WDT runtime encrytion", + "password": "Password for WebLogic Deploy Tooling runtime encrytion", "confirmPassword": "Confirm password" }, - "toolTip": "The Model WDT runtime encrytion secret.", + "toolTip": "The model WebLogic Deploy Tooling runtime encrytion secret.", "constraints": { "required": true, "regex": "^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{12,}$", @@ -99,12 +107,22 @@ }, "visible": true }, + { + "name": "errInfo", + "type": "Microsoft.Common.InfoBox", + "visible": "[less(length(basics('basicsRequired').identity.userAssignedIdentities),1)]", + "options": { + "icon": "Error", + "text": "Please select at least one user assigned managed identity.", + "uri": "https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-portal" + } + }, { "name": "identity", "type": "Microsoft.ManagedIdentity.IdentitySelector", "label": "Managed Identity Configuration", "toolTip": { - "userAssignedIdentity": "Add user-assigned identities to enable the app deployment." + "userAssignedIdentity": "Add user assigned identities to enable the WebLogic domain deployment." }, "defaultValue": { "systemAssignedIdentity": "Off" @@ -145,13 +163,13 @@ { "name": "managedServerPrefix", "type": "Microsoft.Common.TextBox", - "label": "Managed Server Prefix", + "label": "Name prefix for Managed Server", "toolTip": "The string to prepend to the name of the managed server.", "defaultValue": "managed-server", "constraints": { "required": true, - "regex": "^[a-z0-9A-Z]{3,20}$", - "validationMessage": "The prefix must be between 3 and 20 characters long and contain letters, numbers only." + "regex": "^[a-z0-9A-Z\\-]{3,20}$", + "validationMessage": "The prefix must be between 3 and 20 characters long and contain letters, numbers and -." }, "visible": "[bool(basics('basicsOptional').basicsOptionalAcceptDefaults)]" }, @@ -176,14 +194,72 @@ "defaultValue": "sample-domain1", "constraints": { "required": true, - "regex": "^[a-z0-9A-Z]{3,20}$", - "validationMessage": "The Domain UID must be between 3 and 20 characters long and contain letters, numbers only." + "regex": "^[a-z0-9A-Z\\-]{3,20}$", + "validationMessage": "The Domain UID must be between 3 and 20 characters long and contain letters, numbers and -." + }, + "visible": "[bool(basics('basicsOptional').basicsOptionalAcceptDefaults)]" + }, + { + "name": "wlsClusterSize", + "type": "Microsoft.Common.Slider", + "min": 5, + "max": 1000, + "label": "Cluster size", + "defaultValue": 5, + "showStepMarkers": false, + "toolTip": "The maximum size of the WebLogic cluster.", + "constraints": { + "required": true }, "visible": "[bool(basics('basicsOptional').basicsOptionalAcceptDefaults)]" } ], "visible": true }, + { + "name": "howToReportIssues", + "type": "Microsoft.Common.Section", + "label": "Report issues, get help, and share feedback", + "elements": [ + { + "name": "howToReportIssueText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "If you encounter problems during the deployment of Oracle WebLogic Server, report them here.", + "link": { + "label": "Issue tracker", + "uri": "https://aka.ms/arm-oraclelinux-wls-issues" + } + } + }, + { + "name": "howToJoinSlack", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "If you want to interact directly with the Oracle WebLogic community, join the public Slack channel named 'oracle-weblogic'.", + "link": { + "label": "Join Slack", + "uri": "https://aka.ms/arm-oraclelinux-wls-slack" + } + } + }, + { + "name": "survey", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "To get free help with Azure migration from the development team, fill out this survey.", + "link": { + "label": "Take survey", + "uri": "https://aka.ms/wls-on-azure-survey" + } + } + } + ], + "visible": true + }, { "name": "About", "type": "Microsoft.Common.InfoBox", @@ -281,13 +357,13 @@ { "name": "enableAzureMonitoring", "type": "Microsoft.Common.CheckBox", - "label": "Create Azure file share service", + "label": "Enable Azure Monitoring", "visible": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]" }, { "name": "enableAzureFileShare", "type": "Microsoft.Common.CheckBox", - "label": "Create Azure file share service", + "label": "Create Persistent Volume using Azure File share service", "visible": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]" } ] @@ -317,6 +393,16 @@ "required": true } }, + { + "name": "acrInfo", + "type": "Microsoft.Common.InfoBox", + "visible": "[not(bool(steps('section_aks').acrInfo.createACR))]", + "options": { + "icon": "Info", + "text": "Make sure the Azure Container Registry has enabled admin user.", + "uri": "https://docs.microsoft.com/en-us/azure/container-registry/container-registry-authentication#admin-account" + } + }, { "name": "acrSelector", "type": "Microsoft.Solutions.ResourceSelector", @@ -404,7 +490,7 @@ "required": true }, "options": { - "multiple": false, + "multiple": true, "uploadMode": "url", "openMode": "binary" }, @@ -426,1476 +512,31 @@ "visible": true } ] - }, - { - "name": "section_appGateway", - "type": "Microsoft.Common.Section", - "label": "Networking", - "subLabel": { - "preValidation": "Provide required info for networking", - "postValidation": "Done" - }, - "bladeTitle": "Networking", - "elements": [ - { - "name": "connectToAGText", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "Selecting 'Yes' here will cause the template to provision Load Balancer service or Ingress service for WebLogic Administration Console and WebLogic cluster.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-overview" - } - } - }, - { - "name": "lbSVCInfo", - "type": "Microsoft.Common.Section", - "label": "Standard Load Balancer service", - "elements": [ - { - "name": "lbSVC", - "type": "Microsoft.Common.EditableGrid", - "ariaLabel": "Enter information", - "label": "Standard Load Balancer service", - "constraints": { - "width": "Full", - "rows": { - "count": { - "min": 1, - "max": 10 - } - }, - "columns": [ - { - "id": "colName", - "header": "Service Name", - "width": "1fr", - "element": { - "type": "Microsoft.Common.TextBox", - "placeholder": "Full name", - "constraints": { - "required": true, - "validations": [ - { - "regex": "^[a-z0-9A-Z]{1,30}$", - "message": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." - } - ] - } - } - }, - { - "id": "colGender", - "header": "Target", - "width": "1fr", - "element": { - "name": "dropDown1", - "type": "Microsoft.Common.DropDown", - "placeholder": "Select a target...", - "constraints": { - "allowedValues": [ - { - "label": "Admin Console", - "value": "adConsole" - }, - { - "label": "cluster-1", - "value": "cluster1" - } - ], - "required": true - } - } - }, - { - "id": "colName", - "header": "Port", - "width": "1fr", - "element": { - "type": "Microsoft.Common.TextBox", - "placeholder": "Full name", - "constraints": { - "required": true, - "validations": [ - { - "regex": "^[0-9]{1,5}$", - "message": "Only numbers are allowed, and the value must be 1-65535." - } - ] - } - } - } - ] - } - }, - { - "name": "enableInternalLB", - "type": "Microsoft.Common.CheckBox", - "label": "Use Internal Load Balancer" - } - ], - "visible": true - }, - { - "name": "ingressInfo", - "type": "Microsoft.Common.Section", - "label": "Ingress controller", - "elements": [ - { - "name": "ingressSVC", - "type": "Microsoft.Common.EditableGrid", - "ariaLabel": "Enter information", - "label": "Ingresses", - "constraints": { - "width": "Full", - "rows": { - "count": { - "min": 1, - "max": 10 - } - }, - "columns": [ - { - "id": "colName", - "header": "Service Name", - "width": "1fr", - "element": { - "type": "Microsoft.Common.TextBox", - "placeholder": "Full name", - "constraints": { - "required": true, - "validations": [ - { - "regex": "^[a-z0-9A-Z]{1,30}$", - "message": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." - } - ] - } - } - }, - { - "id": "colGender", - "header": "Target", - "width": "1fr", - "element": { - "name": "dropDown1", - "type": "Microsoft.Common.DropDown", - "placeholder": "Select a target...", - "constraints": { - "allowedValues": [ - { - "label": "Admin Console", - "value": "adConsole" - }, - { - "label": "cluster-1", - "value": "cluster1" - } - ], - "required": true - } - } - } - ] - } - }, - { - "name": "enableAppGateway", - "type": "Microsoft.Common.DropDown", - "label": "Eanble Azure Application Gateway?", - "placeholder": "", - "defaultValue": "No", - "toolTip": "", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": true - }, - { - "label": "No", - "value": false - } - ], - "required": true - }, - "visible": true - }, - { - "name": "keyVaultResourceGroup", - "type": "Microsoft.Common.TextBox", - "label": "Resource group name in current subscription containing the Key Vault", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", - "validationMessage": "[if(greater(length(steps('section_appGateway').ingressInfo.keyVaultResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" - }, - "visible": "[bool(steps('section_appGateway').ingressInfo.enableAppGateway)]" - }, - { - "name": "keyVaultName", - "type": "Microsoft.Common.TextBox", - "label": "Name of the Azure Key Vault containing secrets for the certificate for TLS/SSL Termination", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^(?=.{3,24}$)[a-zA-Z](([a-z0-9A-Z]*|(?:\\-[^\\-][a-z0-9A-Z]*))*)$", - "validationMessage": "[if(or(greater(length(steps('section_appGateway').keyVaultName), 24), less(length(steps('section_appGateway').keyVaultName), 3)),'Vault name must be between 3-24 alphanumeric characters. The name must begin with a letter, end with a letter or digit, and not contain consecutive hyphens.','Vault name must only contain alphanumeric characters and dashes and cannot start with a number')]" - }, - "visible": "[bool(steps('section_appGateway').ingressInfo.enableAppGateway)]" - }, - { - "name": "keyVaultSSLCertDataSecretName", - "type": "Microsoft.Common.TextBox", - "label": "The name of the secret in the specified Key Vault whose value is the TLS/SSL certificate data", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - }, - "visible": "[bool(steps('section_appGateway').ingressInfo.enableAppGateway)]" - }, - { - "name": "keyVaultSSLCertPasswordSecretName", - "type": "Microsoft.Common.TextBox", - "label": "The name of the secret in the specified Key Vault whose value is the password for the TLS/SSL certificate", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - }, - "visible": "[bool(steps('section_appGateway').ingressInfo.enableAppGateway)]" - } - ], - "visible": true - } - ] - }, - { - "name": "section_autoScaling", - "type": "Microsoft.Common.Section", - "label": "Autoscaling", - "elements": [ - { - "name": "enableAutoScalingText", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "Selecting 'Yes' here will cause the template to provision Prometheus and Grafana with scaling rules to enable horizontal pod auto-scaling.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/wls-aks-autoscaling" - } - } - }, - { - "name": "enableautoScaling", - "type": "Microsoft.Common.OptionsGroup", - "label": "Enable auto scaling?", - "defaultValue": "No", - "toolTip": "Select 'Yes' and provide required info to configure the connection to Prometheus and Grafana.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": "true" - }, - { - "label": "No", - "value": "false" - } - ], - "required": true - } - }, - { - "name": "autoScalingInfo", - "type": "Microsoft.Common.Section", - "label": "Prometheus and Grafana settings", - "elements": [ - { - "name": "wlsPrometheusRulesInfo", - "type": "Microsoft.Common.InfoBox", - "options": { - "icon": "Info", - "text": "Prometheus rules allow you to define alert conditions based on Prometheus expression language expressions and to send notifications about firing alerts to the scaling service.", - "uri": "https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/" - } - }, - { - "name": "wlsPrometheusRules", - "type": "Microsoft.Common.TextBox", - "label": "Prometheus rules for WebLogic cluster scaling", - "multiLine": true, - "placeholder": "", - "defaultValue": "- record: webapp:webapp_config_open_sessions_current_count:avg\n expr: avg(webapp_config_open_sessions_current_count{webapp=\"myapp2\"})\n- alert: scaleup\n for: 1m\n expr: webapp:webapp_config_open_sessions_current_count:avg > 10\n annotations:\n description: 'Scale up when current sessions is greater than 15.'\n summary: 'Firing when total sessions active greater than 15.'\n- alert: scaledown\n for: 1m\n expr: webapp:webapp_config_open_sessions_current_count:avg < 4\n annotations:\n description: 'Scale down when current sessions is less than 4.'\n summary: 'Firing when total sessions active less than 4'", - "toolTip": "Define the scaling up and scaling down rules.", - "constraints": { - "required": true - }, - "visible": true - }, - { - "name": "alertNameForScaleUp", - "type": "Microsoft.Common.TextBox", - "label": "Alert name for scaling up", - "placeholder": "", - "defaultValue": "scaleup", - "toolTip": "Use only allowed characters", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." - }, - "visible": true - }, - { - "name": "alertNameForScaleDown", - "type": "Microsoft.Common.TextBox", - "label": "Alert name for scaling down", - "placeholder": "", - "defaultValue": "scaledown", - "toolTip": "Use only allowed characters", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." - }, - "visible": true - }, - { - "name": "grafanaDashboards", - "type": "Microsoft.Common.FileUpload", - "label": "Grafana dashboards (.json)", - "toolTip": "", - "constraints": { - "required": false, - "accept": ".json" - }, - "options": { - "multiple": true, - "uploadMode": "file", - "openMode": "text", - "encoding": "UTF-8" - }, - "visible": true - }, - { - "name": "prometheusAdditionalScrapeConfigsInfo", - "type": "Microsoft.Common.InfoBox", - "options": { - "icon": "Info", - "text": "The additional scrape configs specify a set of targets and parameters describing how to scrape them.\nThis application provides default scrape configs to scrape all managed ports of in the created WebLogic domain.", - "uri": "https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config" - } - }, - { - "name": "prometheusAdditionalScrapeConfigs", - "type": "Microsoft.Common.FileUpload", - "label": "Prometheus additional scrape configs (.yaml)", - "toolTip": "", - "constraints": { - "accept": ".yaml" - }, - "options": { - "multiple": false, - "uploadMode": "file", - "openMode": "text", - "encoding": "UTF-8" - }, - "visible": true - } - ], - "visible": "[bool(steps('section_autoScaling').enableautoScaling)]" - } - ] - }, - { - "name": "section_sslConfiguration", - "type": "Microsoft.Common.Section", - "label": "TLS/SSL Configuration", - "elements": [ - { - "name": "sslConfigurationText", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "Selecting 'Yes' here will cause the template to provision WebLogic Administration Console on HTTPS (Secure) port, with your own TLS/SSL Certificate.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-config" - } - } - }, - { - "name": "enableCustomSSL", - "type": "Microsoft.Common.OptionsGroup", - "label": "Configure WebLogic Administration Console on HTTPS (Secure) port, with your own TLS/SSL Certificate?", - "defaultValue": "No", - "toolTip": "Select 'Yes' to configure WebLogic Administration Console on HTTPS (Secure) port with your own SSL Certificate.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": true - }, - { - "label": "No", - "value": false - } - ], - "required": false - } - }, - { - "name": "sslConfigurationAccessOption", - "type": "Microsoft.Common.OptionsGroup", - "visible": "[steps('section_sslConfiguration').enableCustomSSL]", - "label": "How would you like to provide required configuration", - "defaultValue": "Upload existing KeyStores", - "toolTip": "Select 'Upload existing KeyStores' to use local stored KeyStores.", - "constraints": { - "allowedValues": [ - { - "label": "Upload existing KeyStores", - "value": "uploadConfig" - }, - { - "label": "Use KeyStores stored in Azure Key Vault", - "value": "keyVaultStoredConfig" - } - ], - "required": false - } - }, - { - "name": "uploadedCustomSSLSettings", - "type": "Microsoft.Common.Section", - "visible": "[and(steps('section_sslConfiguration').enableCustomSSL, equals(steps('section_sslConfiguration').sslConfigurationAccessOption, 'uploadConfig'))]", - "label": "TLS/SSL Configuration Settings", - "elements": [ - { - "name": "sslKeystoreInfo0", - "type": "Microsoft.Common.InfoBox", - "visible": "true", - "options": { - "icon": "Info", - "text": "You must provide different files for identity and trust KeyStores. Select here for more details.", - "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-configuration" - } - }, - { - "name": "uploadedCustomIdentityKeyStoreData", - "type": "Microsoft.Common.FileUpload", - "label": "Identity KeyStore Data file(.jks,.p12)", - "toolTip": "Identity KeyStore for TLS/SSL configuration", - "constraints": { - "required": true, - "accept": ".jks,.p12" - }, - "options": { - "multiple": false, - "uploadMode": "file", - "openMode": "binary" - }, - "visible": true - }, - { - "name": "uploadedCustomIdentityKeyStorePassphrase", - "type": "Microsoft.Common.PasswordBox", - "label": { - "password": "Password", - "confirmPassword": "Confirm password" - }, - "toolTip": " The passphrase for the Identity KeyStore", - "constraints": { - "required": true, - "regex": "^.{6,}$", - "validationMessage": "Keypass must be at least 6 characters long." - }, - "options": { - "hideConfirmation": false - }, - "visible": true - }, - { - "name": "uploadedCustomIdentityKeyStoreType", - "type": "Microsoft.Common.DropDown", - "visible": "true", - "label": "The Identity KeyStore type (JKS,PKCS12)", - "defaultValue": "JKS", - "toolTip": "One of the supported KeyStore types", - "constraints": { - "allowedValues": [ - { - "label": "JKS", - "value": "JKS" - }, - { - "label": "PKCS12", - "value": "PKCS12" - } - ], - "required": true - } - }, - { - "name": "uploadedPrivateKeyAlias", - "type": "Microsoft.Common.TextBox", - "visible": "true", - "label": "The alias of the server's private key within the Identity KeyStore", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - } - }, - { - "name": "uploadedPrivateKeyPassPhrase", - "type": "Microsoft.Common.PasswordBox", - "visible": "true", - "label": { - "password": "The passphrase for the server's private key within the Identity KeyStore", - "confirmPassword": "Confirm passphrase" - }, - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^.{6,}$", - "validationMessage": "Keypass must be at least 6 characters long." - }, - "options": { - "hideConfirmation": false - } - }, - { - "name": "uploadedCustomTrustKeyStoreData", - "type": "Microsoft.Common.FileUpload", - "label": "Trust KeyStore Data file(.jks,.p12)", - "toolTip": "Trust KeyStore for TLS/SSL configuration.", - "constraints": { - "required": true, - "accept": ".jks,.p12" - }, - "options": { - "multiple": false, - "uploadMode": "file", - "openMode": "binary" - }, - "visible": true - }, - { - "name": "uploadedCustomTrustKeyStorePassPhrase", - "type": "Microsoft.Common.PasswordBox", - "label": { - "password": "Password", - "confirmPassword": "Confirm password" - }, - "toolTip": " The passphrase for the Trust KeyStore", - "constraints": { - "required": true, - "regex": "^.{6,}$", - "validationMessage": "Keypass must be at least 6 characters long." - }, - "options": { - "hideConfirmation": false - }, - "visible": true - }, - { - "name": "uploadedCustomTrustKeyStoreType", - "type": "Microsoft.Common.DropDown", - "visible": "true", - "label": "The Trust KeyStore type (JKS,PKCS12)", - "defaultValue": "JKS", - "toolTip": "One of the supported KeyStore types", - "constraints": { - "allowedValues": [ - { - "label": "JKS", - "value": "JKS" - }, - { - "label": "PKCS12", - "value": "PKCS12" - } - ], - "required": true - } - } - ] - }, - { - "name": "keyVaultStoredCustomSSLSettings", - "type": "Microsoft.Common.Section", - "visible": "[and(steps('section_sslConfiguration').enableCustomSSL, equals(steps('section_sslConfiguration').sslConfigurationAccessOption, 'keyVaultStoredConfig'))]", - "label": "TLS/SSL Configuration Settings", - "elements": [ - { - "name": "sslKeystoreInfo1", - "type": "Microsoft.Common.InfoBox", - "visible": "true", - "options": { - "icon": "Info", - "text": "You must provide different files for identity and trust KeyStores. Select here for more details.", - "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-configuration" - } - }, - { - "name": "keyVaultText", - "type": "Microsoft.Common.TextBlock", - "visible": "true", - "options": { - "text": "Enabling a HTTPS (Secure) port for the Administration Console requires you to obtain a valid TLS/SSL Certificate. The template will look for the certificate and other configuration items in the Azure Key Vault specified here.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-key-vault" - } - } - }, - { - "name": "adminSSLKeyVaultResourceGroup", - "type": "Microsoft.Common.TextBox", - "visible": "true", - "label": "Resource group name in current subscription containing the Key Vault", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", - "validationMessage": "[if(greater(length(steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.adminSSLKeyVaultResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" - } - }, - { - "name": "adminSSLKeyVaultName", - "type": "Microsoft.Common.TextBox", - "visible": "true", - "label": "Name of the Azure Key Vault containing secrets for the TLS/SSL Certificate", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^(?=.{3,24}$)[a-zA-Z](([a-z0-9A-Z]*|(?:\\-[^\\-][a-z0-9A-Z]*))*)$", - "validationMessage": "[if(or(greater(length(steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.adminSSLKeyVaultName), 24), less(length(steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.adminSSLKeyVaultName), 3)),'Vault name must be between 3-24 alphanumeric characters. The name must begin with a letter, end with a letter or digit, and not contain consecutive hyphens.','Vault name must only contain alphanumeric characters and dashes and cannot start with a number')]" - } - }, - { - "name": "keyVaultCustomIdentityKeyStoreDataSecretName", - "type": "Microsoft.Common.TextBox", - "visible": "true", - "label": "The name of the secret in the specified Key Vault whose value is the Identity KeyStore Data", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - } - }, - { - "name": "keyVaultCustomIdentityKeyStorePassPhraseSecretName", - "type": "Microsoft.Common.TextBox", - "visible": "true", - "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Identity KeyStore", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - } - }, - { - "name": "keyVaultCustomIdentityKeyStoreType", - "type": "Microsoft.Common.DropDown", - "visible": "true", - "label": "The Identity KeyStore type (JKS,PKCS12)", - "defaultValue": "JKS", - "toolTip": "One of the supported KeyStore types", - "constraints": { - "allowedValues": [ - { - "label": "JKS", - "value": "JKS" - }, - { - "label": "PKCS12", - "value": "PKCS12" - } - ], - "required": true - } - }, - { - "name": "keyVaultPrivateKeyAliasSecretName", - "type": "Microsoft.Common.TextBox", - "visible": "true", - "label": "The name of the secret in the specified Key Vault whose value is the Private Key Alias", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - } - }, - { - "name": "keyVaultPrivateKeyPassPhraseSecretName", - "type": "Microsoft.Common.TextBox", - "visible": "true", - "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Private Key", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - } - }, - { - "name": "keyVaultCustomTrustKeyStoreDataSecretName", - "type": "Microsoft.Common.TextBox", - "visible": "true", - "label": "The name of the secret in the specified Key Vault whose value is the Trust KeyStore Data", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - } - }, - { - "name": "keyVaultCustomTrustKeyStorePassPhraseSecretName", - "type": "Microsoft.Common.TextBox", - "visible": "true", - "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Trust KeyStore", - "defaultValue": "", - "toolTip": "Use only letters and numbers", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." - } - }, - { - "name": "keyVaultCustomTrustKeyStoreType", - "type": "Microsoft.Common.DropDown", - "visible": "true", - "label": "The Trust KeyStore type (JKS,PKCS12)", - "defaultValue": "JKS", - "toolTip": "One of the supported KeyStore types", - "constraints": { - "allowedValues": [ - { - "label": "JKS", - "value": "JKS" - }, - { - "label": "PKCS12", - "value": "PKCS12" - } - ], - "required": true - } - } - ] - } - ] - }, - { - "name": "section_dnsConfiguration", - "type": "Microsoft.Common.Section", - "label": "DNS Configuration", - "elements": [ - { - "name": "dnsConfigurationText", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "Selecting 'Yes' here will cause the template to provision Oracle WebLogic Server Administration Console and Application Gateway using a custom DNS Name (for example: applications.contoso.com)", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-dns" - } - } - }, - { - "name": "enableCustomDNS", - "type": "Microsoft.Common.OptionsGroup", - "visible": true, - "label": "Configure Custom DNS Alias?", - "defaultValue": "No", - "toolTip": "Select 'Yes' to configure Custom DNS Alias.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": true - }, - { - "label": "No", - "value": false - } - ], - "required": false - } - }, - { - "name": "customDNSSettings", - "type": "Microsoft.Common.Section", - "label": "DNS Configuration Settings", - "visible": "[bool(steps('section_dnsConfiguration').enableCustomDNS)]", - "elements": [ - { - "name": "bringDNSZone", - "type": "Microsoft.Common.OptionsGroup", - "label": "Use an existing Azure DNS Zone", - "defaultValue": "No", - "toolTip": "Select 'Yes' to configure Custom DNS Alias based on an existing Azure DNS Zone. Select 'No' to create an Azure DNS Zone and Custom DNS Alias.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": true - }, - { - "label": "No", - "value": false - } - ] - } - }, - { - "name": "createDNSZoneText", - "type": "Microsoft.Common.InfoBox", - "visible": "[not(bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone))]", - "options": { - "icon": "Info", - "text": "You must perform DNS Domain Delegation at your DNS Registry after deployment.", - "uri": "https://aka.ms/dns-domain-delegation" - } - }, - { - "name": "deplymentScriptInfo", - "type": "Microsoft.Common.TextBlock", - "options": { - "text": "If you choose to use an existing Azure DNS Zone, you must select a user-assigned managed identity to enable the necessary configuration." - }, - "visible": "[steps('section_dnsConfiguration').customDNSSettings.bringDNSZone]" - }, - { - "name": "dnszoneName", - "type": "Microsoft.Common.TextBox", - "label": "DNS Zone Name", - "defaultValue": "", - "toolTip": "Use only letters and numbers and periods to separate Domains", - "constraints": { - "required": true, - "regex": "^([0-9a-zA-Z_-]{1,63}\\.){1,33}[0-9a-zA-Z_-]{1,63}$", - "validationMessage": "There must be between 2 and 34 labels. For example, \"contoso.com\" has 2 labels. Each label must contain between 1 and 63 characters. Each label must only contain letters, numbers, underscores, and dashes." - } - }, - { - "name": "dnsZoneResourceGroup", - "type": "Microsoft.Common.TextBox", - "label": "Name of the resource group contains the DNS Zone in current subscription", - "defaultValue": "", - "toolTip": "Name of the resource group which contains the DNS Zone in current subscription", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", - "validationMessage": "[if(greater(length(steps('section_dnsConfiguration').existingDNSZonesSettings.dnsZoneResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" - }, - "visible": "[steps('section_dnsConfiguration').customDNSSettings.bringDNSZone]" - }, - { - "name": "dnszoneAdminConsoleLabel", - "type": "Microsoft.Common.TextBox", - "label": "Label for Oracle WebLogic Administration Console", - "defaultValue": "admin", - "toolTip": "Specify a label to generate subdomain of Oracle WebLogic Administration Console", - "constraints": { - "required": true, - "validations": [ - { - "regex": "^([0-9a-zA-Z_-]{1,63}\\.){0,33}[0-9a-zA-Z_-]{1,63}$", - "message": "Each label must contain between 1 and 63 characters. Each label must only contain letters, numbers, underscores, and dashes." - }, - { - "isValid": "[less(sub(length(concat(steps('section_dnsConfiguration').customDNSSettings.dnszoneAdminConsoleLabel,'.',steps('section_dnsConfiguration').customDNSSettings.dnszoneName)),length(replace(concat(steps('section_dnsConfiguration').customDNSSettings.dnszoneAdminConsoleLabel,'.',steps('section_dnsConfiguration').customDNSSettings.dnszoneName), '.', ''))),34)]", - "message": "Subdomain must be between 2 and 34 labels. For example, \"admin.contoso.com\" has 3 labels." - } - ] - } - }, - { - "name": "dnszoneGatewayLabel", - "type": "Microsoft.Common.TextBox", - "label": "Label for Application Gateway", - "defaultValue": "www", - "toolTip": "Specify a label to generate subdomain of Application Gateway", - "constraints": { - "required": true, - "validations": [ - { - "regex": "^([0-9a-zA-Z_-]{1,63}\\.){0,33}[0-9a-zA-Z_-]{1,63}$", - "message": "Each label must contain between 1 and 63 characters. Each label must only contain letters, numbers, underscores, and dashes." - }, - { - "isValid": "[less(sub(length(concat(if(empty(steps('section_dnsConfiguration').customDNSSettings.dnszoneGatewayLabel), '', steps('section_dnsConfiguration').customDNSSettings.dnszoneGatewayLabel),'.',steps('section_dnsConfiguration').customDNSSettings.dnszoneName)),length(replace(concat(if(empty(steps('section_dnsConfiguration').customDNSSettings.dnszoneGatewayLabel), '', steps('section_dnsConfiguration').customDNSSettings.dnszoneGatewayLabel),'.',steps('section_dnsConfiguration').customDNSSettings.dnszoneName), '.', ''))),34)]", - "message": "Subdomain must be between 2 and 34 labels. For example, \"applications.contoso.com\" has 3 labels." - } - ] - }, - "visible": "[and(bool(steps('section_appGateway').enableAppGateway), bool(steps('section_dnsConfiguration').enableCustomDNS))]" - }, - { - "name": "reuseIdentity", - "type": "Microsoft.Common.OptionsGroup", - "label": "Use the Managed Identity seleted previousely?", - "defaultValue": "Yes", - "toolTip": "Select 'Yes' to reuse the Managed Identity seleted previousely, 'No' to input a new identity.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": true - }, - { - "label": "No", - "value": false - } - ] - }, - "visible": "[and(greater(length(steps('section_appGateway').gatewayIdentity.userAssignedIdentities),0), bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone))]" - }, - { - "name": "infoDNSIndentity", - "type": "Microsoft.Common.InfoBox", - "visible": "[and(bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone), less(length(steps('section_appGateway').gatewayIdentity.userAssignedIdentities),1), less(length(steps('section_dnsConfiguration').customDNSSettings.dnsIdentity.userAssignedIdentities), 1))]", - "options": { - "icon": "Error", - "text": "This option will use Azure deployment scripts to update records to your Azure DNS Zone. You have to add at least one user-assigned identity to access Azure resources.", - "uri": "https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/deployment-script-template" - } - }, - { - "name": "dnsIdentity", - "type": "Microsoft.ManagedIdentity.IdentitySelector", - "label": "Managed Identity Configuration", - "toolTip": { - "userAssignedIdentity": "Add user-assigned identities to grant the resource access to Azure resources." - }, - "defaultValue": { - "systemAssignedIdentity": "Off" - }, - "options": { - "hideSystemAssignedIdentity": true, - "hideUserAssignedIdentity": false - }, - "visible": "[if(greater(length(steps('section_appGateway').gatewayIdentity.userAssignedIdentities),0),and(equals(steps('section_dnsConfiguration').customDNSSettings.reuseIdentity, false), bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone)), bool(steps('section_dnsConfiguration').customDNSSettings.bringDNSZone))]" - } - ] - } - ] - }, - { - "name": "section_database", - "type": "Microsoft.Common.Section", - "label": "Database", - "subLabel": { - "preValidation": "Configure integrations to database", - "postValidation": "Done" - }, - "bladeTitle": "Database", - "elements": [ - { - "name": "aboutDatabase", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "Selecting 'Yes' here and providing the configuration will cause the template to configure the WebLogic Server to connect to the desired pre-existing database. The database must be network accessible to the VNET and subnets created by the template." - } - }, - { - "name": "enableDB", - "type": "Microsoft.Common.OptionsGroup", - "label": "Connect to database?", - "defaultValue": "No", - "toolTip": "Select 'Yes' and provide required info to configure the connection to a database.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": "true" - }, - { - "label": "No", - "value": "false" - } - ], - "required": true - } - }, - { - "name": "databaseConnectionInfo", - "type": "Microsoft.Common.Section", - "label": "Connection settings", - "elements": [ - { - "name": "databaseType", - "type": "Microsoft.Common.DropDown", - "label": "Choose database type", - "toolTip": "Choose database type", - "defaultValue": "Oracle database", - "constraints": { - "allowedValues": [ - { - "label": "Azure database for PostgreSQL", - "value": "postgresql" - }, - { - "label": "Oracle database", - "value": "oracle" - }, - { - "label": "Azure SQL", - "value": "sqlserver" - } - ], - "required": true - }, - "visible": true - }, - { - "name": "jdbcDataSourceName", - "type": "Microsoft.Common.TextBox", - "label": "JNDI Name", - "toolTip": "The JNDI name for the database JDBC connection", - "defaultValue": "", - "constraints": { - "required": "[bool(steps('section_database').enableDB)]", - "regex": "^[a-z0-9A-Z/]{1,30}$", - "validationMessage": "The value must be 1-30 characters long and must only contain letters, numbers, and slashes (/)." - }, - "visible": true - }, - { - "name": "dsConnectionURL", - "type": "Microsoft.Common.TextBox", - "label": "DataSource Connection String", - "toolTip": "The JDBC connection string for the database", - "defaultValue": "", - "constraints": { - "required": "[bool(steps('section_database').enableDB)]", - "regex": "[concat('^jdbc:', coalesce(steps('section_database').databaseConnectionInfo.databaseType, ''), '.*$')]", - "validationMessage": "A valid JDBC URL for the chosen database type must be provided" - }, - "visible": true - }, - { - "name": "dbUser", - "type": "Microsoft.Common.TextBox", - "label": "Database username", - "toolTip": "Use only letters and numbers", - "defaultValue": "", - "constraints": { - "required": "[bool(steps('section_database').enableDB)]", - "regex": "^(?!\\-)([a-z0-9A-Z@\\-]{1,128})([^\\-])$", - "validationMessage": "The value must be 1-128 characters long and must only contain letters, numbers, hyphen(-) and the at sign, no hyphen allowed at the beginning and the end of database username." - }, - "visible": true - }, - { - "name": "dbPassword", - "type": "Microsoft.Common.PasswordBox", - "label": { - "password": "Database Password", - "confirmPassword": "Confirm password" - }, - "toolTip": "Database Password", - "constraints": { - "required": "[bool(steps('section_database').enableDB)]", - "regex": "^((?=.*[0-9])(?=.*[a-zA-Z!@#$%^&*])).{5,128}$", - "validationMessage": "The password must be between five and 128 characters long and have at least one number." - }, - "options": { - "hideConfirmation": false - }, - "visible": true - } - ], - "visible": "[bool(steps('section_database').enableDB)]" - } - ] - }, - { - "name": "section_aad", - "label": "Azure Active Directory", - "subLabel": { - "preValidation": "Configure the connection to Azure Active Directory.", - "postValidation": "Done" - }, - "bladeTitle": "Azure Active Directory", - "elements": [ - { - "name": "aboutAad", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "icon": "None", - "text": "Selecting 'Yes' here and providing the configuration will cause the template to configure the connection to Azure Active Directory.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-aad-ldap" - } - } - }, - { - "name": "enableAAD", - "type": "Microsoft.Common.OptionsGroup", - "label": "Connect to Azure Active Directory?", - "defaultValue": "No", - "toolTip": "Select 'Yes' and provide required info to configure the connection to Azure Active Directory.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": "true" - }, - { - "label": "No", - "value": "false" - } - ], - "required": true - } - }, - { - "name": "aadInfo", - "type": "Microsoft.Common.Section", - "label": "Connection settings", - "elements": [ - { - "name": "aadsServerHost", - "type": "Microsoft.Common.TextBox", - "label": "Server Host", - "toolTip": "The LDAP server host.", - "defaultValue": "", - "constraints": { - "required": true, - "regex": "(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]", - "validationMessage": "The value must be a valid host name." - } - }, - { - "name": "aadsPublicIP", - "type": "Microsoft.Common.TextBox", - "label": "Secure LDAP external IP address", - "toolTip": "Secure LDAP external IP address.", - "constraints": { - "required": true, - "regex": "\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b", - "validationMessage": "The value must be a valid IP address." - } - }, - { - "name": "aadsPortNumber", - "type": "Microsoft.Common.TextBox", - "label": "Port", - "toolTip": "The port number of LDAP Server, default is 636.", - "defaultValue": "636", - "constraints": { - "required": true, - "regex": "^[0-9]+$", - "validationMessage": "The value must be numbers." - } - }, - { - "name": "wlsLDAPProviderName", - "type": "Microsoft.Common.TextBox", - "label": "Provider Name", - "defaultValue": "AzureActiveDirectoryProvider", - "toolTip": "The value used for creating authentication provider name of WebLogic Server.", - "constraints": { - "required": true, - "regex": "^[a-z0-9A-Z]{3,50}$", - "validationMessage": "The Provider Name must be between 3 and 50 characters long and contain letters, numbers only." - } - }, - { - "name": "wlsLDAPPrincipal", - "type": "Microsoft.Common.TextBox", - "label": "Principal", - "toolTip": "The Distinguished Name (DN) of the LDAP user that WebLogic Server should use to connect to the LDAP server.", - "constraints": { - "required": true, - "regex": ".+,dc|DC=([^,]+),dc|DC=([^,]+)$", - "validationMessage": "The value must be valid LDAP user distinguished name." - } - }, - { - "name": "wlsLDAPPrincipalPassword", - "type": "Microsoft.Common.PasswordBox", - "label": { - "password": "Password for Principal", - "confirmPassword": "Confirm password" - }, - "toolTip": "The credential (usually a password) used to connect to the LDAP server.", - "constraints": { - "required": true - } - }, - { - "name": "wlsLDAPUserBaseDN", - "type": "Microsoft.Common.TextBox", - "label": "User Base DN", - "toolTip": "The base distinguished name (DN) of the tree in the LDAP directory that contains users.", - "constraints": { - "required": true, - "regex": ".+,dc|DC=([^,]+),dc|DC=([^,]+)$", - "validationMessage": "The value must be valid LDAP user based distinguished name." - } - }, - { - "name": "wlsLDAPGroupBaseDN", - "type": "Microsoft.Common.TextBox", - "label": "Group Base DN", - "toolTip": "The base distinguished name (DN) of the tree in the LDAP directory that contains groups.", - "constraints": { - "required": true, - "regex": ".+,dc|DC=([^,]+),dc|DC=([^,]+)$", - "validationMessage": "The value must be valid LDAP group based distinguished name." - } - }, - { - "name": "wlsLDAPSSLCertificate", - "type": "Microsoft.Common.FileUpload", - "label": "Client certificate for TLS/SSL Configuration (.cer)", - "toolTip": "Client certificate of AAD LADP server, used to configure the Trust Keystore to enable TLS/SSL in Oracle WebLogic Server.", - "constraints": { - "required": true, - "accept": ".cer" - }, - "options": { - "multiple": false, - "uploadMode": "file", - "openMode": "binary" - } - } - ], - "visible": "[bool(steps('section_aad').enableAAD)]" - } - ] - }, - { - "name": "section_elk", - "label": "Elasticsearch and Kibana", - "subLabel": { - "preValidation": "Configure the connection to Elasticsearch and Kibana to store WLS logs.", - "postValidation": "Done" - }, - "bladeTitle": "Elasticsearch and Kibana", - "elements": [ - { - "name": "aboutelk", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "icon": "None", - "text": "Selecting 'Yes' here and providing the configuration will cause the template to configure Elasticsearch and Kibana to store WLS logs.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-elk-tutorial" - } - } - }, - { - "name": "enableELK", - "type": "Microsoft.Common.OptionsGroup", - "label": "Export logs to Elasticsearch?", - "defaultValue": "No", - "toolTip": "Select 'Yes' and provide required info to configure the connection to Elasticsearch.", - "constraints": { - "allowedValues": [ - { - "label": "Yes", - "value": "true" - }, - { - "label": "No", - "value": "false" - } - ], - "required": true - } - }, - { - "name": "elkInfo", - "type": "Microsoft.Common.Section", - "label": "Connection settings", - "elements": [ - { - "name": "elkMemoryRequiredText", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "icon": "None", - "text": "To ensure Logstash works correctly, the selected Virtual Machines have at least 2.5GB memory.", - "link": { - "label": "Learn more", - "uri": "https://aka.ms/arm-oraclelinux-wls-elk" - } - } - }, - { - "name": "elkMemoryRequired", - "type": "Microsoft.Common.InfoBox", - "options": { - "icon": "Error", - "text": "Your selected Virtual Machines have less than 2.5GB memory to set up Elasticsearch and Kibana, please go to Basics -> Virtual machine size to change it, recommended size is Standard_A2_v2." - }, - "visible": "[and(contains('Standard_A1,Basic_A1,Standard_B1ms,Standard_A1_v2,Standard_F1,Standard_F1s', basics('vmSizeSelect')),bool(steps('section_elk').enableELK))]" - }, - { - "name": "elasticsearchEndpoint", - "type": "Microsoft.Common.TextBox", - "label": "Elasticsearch endpoint URL", - "toolTip": "Elasticsearch endpoint.", - "defaultValue": "https://example.location.azure.elastic-cloud.com:9243", - "constraints": { - "required": true, - "validations": [ - { - "regex": "^https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*):[0-9]{1,6}$", - "message": "The value must be a valid endpoint." - }, - { - "isValid": "[not(contains('Standard_A1,Basic_A1,Standard_B1ms,Standard_A1_v2,Standard_F1,Standard_F1s', basics('vmSizeSelect')))]", - "message": "Your selected Virtual Machines have less than 2.5GB memory to set up Elasticsearch and Kibana, please go to Basics -> Virtual machine size to change it, recommended size is Standard_A2_v2." - } - ] - } - }, - { - "name": "elasticsearchUserName", - "type": "Microsoft.Common.TextBox", - "label": "Elasticsearch User Name", - "defaultValue": "elastic", - "toolTip": "User name of Elasticsearch account.", - "constraints": { - "required": true, - "regex": "^(?!\\-)([a-z0-9A-Z@\\-]{1,128})([^\\-])", - "validationMessage": "The value must be valid Elasticsearch user name." - } - }, - { - "name": "elasticsearchPassword", - "type": "Microsoft.Common.PasswordBox", - "label": { - "password": "Password for Elasticsearch account", - "confirmPassword": "Confirm password" - }, - "toolTip": "Password for Elasticsearch account.", - "constraints": { - "required": true - } - }, - { - "name": "domainLogsToIntegrate", - "type": "Microsoft.Common.DropDown", - "label": "WebLogic Domain logs to export", - "toolTip": "The logs selected will be exported to the Elasticsearch server.", - "defaultValue": [ - "Server Log" - ], - "multiselect": true, - "selectAll": true, - "multiLine": true, - "defaultDescription": "The logs selected will be exported to Elasticsearch", - "constraints": { - "allowedValues": [ - { - "label": "Data Source Log", - "description": "Export Datasource logs to Elasticsearch.", - "value": "DataSourceLog" - }, - { - "label": "Domain Log", - "description": "Export Domain logs to Elasticsearch.", - "value": "DomainLog" - }, - { - "label": "HTTP Access Log", - "description": "Export HTTP logs to Elasticsearch.", - "value": "HTTPAccessLog" - }, - { - "label": "Node Manager Logs", - "description": "Export Node Manager logs to Elasticsearch.", - "value": "NodeManagerLog" - }, - { - "label": "Server Log", - "description": "Export Server logs to Elasticsearch.", - "value": "ServerLog" - }, - { - "label": "Standard error and output", - "description": "Export standard error and output to Elasticsearch.", - "value": "StandardErrorAndOutput" - } - ], - "required": true - } - }, - { - "name": "operatorLog", - "type": "Microsoft.Common.CheckBox", - "label": "WebLogic Operator logs" - } - ], - "visible": "[bool(steps('section_elk').enableELK)]" - } - ] } ], "outputs": { "acrName": "[last(split(steps('section_aks').acrInfo.acrSelector.id, '/'))]", - "alertNameForScaleDown": "[steps('section_autoScaling').autoScalingInfo.alertNameForScaleDown]", - "alertNameForScaleUp": "[steps('section_autoScaling').autoScalingInfo.alertNameForScaleUp]", + "aksAgentPoolNodeCount": "[steps('section_aks').clusterInfo.aksNodeCount]", "aksClusterName": "[last(split(steps('section_aks').clusterInfo.aksClusterSelector.id, '/'))]", "aksClusterRGName": "[last(take(split(steps('section_aks').clusterInfo.aksClusterSelector.id, '/'), 5))]", - "appPackageUrl": "[steps('section_application').appPackageInfo.appPackageUrl]", - "appReplicas": "[int(steps('section_application').appReplicas)]", - "contextRoot": "[steps('section_application').appPackageInfo.contextRoot]", - "createACR": "[bool(steps('section_aks').createACR)]", - "createAKSCluster": "[bool(steps('section_aks').createAKSCluster)]", - "enableautoScaling": "[bool(steps('section_autoScaling').enableautoScaling)]", - "fromImage": "[bool(steps('section_application').fromImage)]", - "grafanaDashboards": "[steps('section_autoScaling').autoScalingInfo.alertNameForScaleDown]", + "appPackageUrls": "[steps('section_aks').jeeAppInfo.appPackageUrl]", + "appReplicas": "[int(steps('section_aks').jeeAppInfo.appReplicas)]", + "createACR": "[bool(steps('section_aks').acrInfo.createACR)]", + "createAKSCluster": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]", + "enableAzureMonitoring": "[bool(steps('section_aks').clusterInfo.enableAzureMonitoring)]", + "enableAzureFileShare": "[bool(steps('section_aks').clusterInfo.enableAzureFileShare)]", "identity": "[basics('basicsRequired').identity]", "location": "[location()]", "managedServerPrefix": "[basics('basicsOptional').managedServerPrefix]", - "uploadAppPackage": "[bool(steps('section_application').uploadAppPackage)]", - "prometheusAdditionalScrapeConfigs": "[steps('section_autoScaling').autoScalingInfo.prometheusAdditionalScrapeConfigs]", + "ocrSSOPSW": "[basics('basicsRequired').ocrSSOPassword]", + "ocrSSOUser": "[basics('basicsRequired').ocrSSOUserName]", + "uploadAppPackage": "[bool(steps('section_aks').jeeAppInfo.uploadAppPackage)]", "wdtRuntimePassword": "[basics('basicsRequired').wdtRuntimePassword]", + "wlsClusterSize": "[basics('basicsOptional').wlsClusterSize]", "wlsDomainName": "[basics('basicsOptional').wlsDomainName]", "wlsDomainUID": "[basics('basicsOptional').wlsDomainUID]", + "wlsImageTag": "[steps('section_aks').imageInfo.fromImage]", "wlsPassword": "[basics('basicsRequired').wlsPassword]", - "wlsPrometheusRules": "[steps('section_autoScaling').autoScalingInfo.wlsPrometheusRules]", "wlsUserName": "[basics('basicsRequired').wlsUserName]" } } diff --git a/src/main/arm/scripts/buildWLSDockerImage.sh b/src/main/arm/scripts/buildWLSDockerImage.sh new file mode 100644 index 000000000..e10e1540b --- /dev/null +++ b/src/main/arm/scripts/buildWLSDockerImage.sh @@ -0,0 +1,254 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +#Function to output message to StdErr +function echo_stderr() { + echo "$@" >&2 +} + +#Function to display usage message +function usage() { + echo_stdout "./buildWLSDockerImage.sh " + if [ $1 -eq 1 ]; then + exit 1 + fi +} + +# Validate teminal status with $?, exit if errors happen. +function validate_status() { + if [ $? == 1 ]; then + echo "$@" >&2 + echo "Errors happen, exit 1." + exit 1 + fi +} + +function validate_inputs() { + if [ -z "$wlsImagePath" ]; then + echo_stderr "wlsImagePath is required. " + usage 1 + fi + + if [ -z "$azureACRServer" ]; then + echo_stderr "azureACRServer is required. " + usage 1 + fi + + if [ -z "$azureACRUserName" ]; then + echo_stderr "azureACRUserName is required. " + usage 1 + fi + + if [ -z "$azureACRPassword" ]; then + echo_stderr "azureACRPassword is required. " + usage 1 + fi + + if [ -z "$imageTag" ]; then + echo_stderr "imageTag is required. " + usage 1 + fi + + if [ -z "$appPackageUrls" ]; then + echo_stderr "appPackageUrls is required. " + usage 1 + fi + + if [ -z "$ocrSSOUser" ]; then + echo_stderr "ocrSSOUser is required. " + usage 1 + fi + + if [ -z "$ocrSSOPSW" ]; then + echo_stderr "ocrSSOPSW is required. " + usage 1 + fi + + if [ -z "$wlsClusterSize" ]; then + echo_stderr "wlsClusterSize is required. " + usage 1 + fi +} + +function initialize() { + if [ -d "model-images" ]; then + rm model-images -f -r + fi + + mkdir model-images + cd model-images + + # Create Model directory + mkdir wlsdeploy + mkdir wlsdeploy/config + mkdir wlsdeploy/applications +} + +# Install docker, zip, unzip and java +# Download WebLogic Tools +function install_utilities() { + # Install docker + sudo apt-get -q update + sudo apt-get -y -q install apt-transport-https + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + echo \ + "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null + sudo apt-get -q update + sudo apt-get -y -q install docker-ce docker-ce-cli containerd.io + + echo "docker version" + sudo docker --version + validate_status "Check status of docker." + sudo systemctl start docker + + # Install Microsoft OpenJDK + wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + sudo dpkg -i packages-microsoft-prod.deb + sudo apt -q update + sudo apt -y -q install msopenjdk-11 + + echo "java version" + java -version + validate_status "Check status of Zulu JDK 8." + + export JAVA_HOME=/usr/lib/jvm/msopenjdk-11-amd64 + if [ ! -d "${JAVA_HOME}" ]; then + echo "Java home ${JAVA_HOME} does not exist" + exit 1 + fi + + sudo apt -y -q install zip + zip --help + validate_status "Check status of zip." + + sudo apt -y -q install unzip + echo "unzip version" + unzip --help + validate_status "Check status of unzip." + + # Download weblogic tools + curl -m 120 -fL ${wdtDownloadURL} -o weblogic-deploy.zip + validate_status "Check status of weblogic-deploy.zip." + + curl -m 120 -fL ${witDownloadURL} -o imagetool.zip + validate_status "Check status of imagetool.zip." +} + +# Login in OCR +# Pull weblogic image +function get_wls_image_from_ocr() { + sudo docker logout + sudo docker login ${ocrLoginServer} -u ${ocrSSOUser} -p ${ocrSSOPSW} + echo "Start to pull image ${wlsImagePath}" + sudo docker pull -q ${wlsImagePath} + validate_status "Finish pulling image from OCR." +} + +# Generate model configurations +function prepare_wls_models() { + # Create configuration in model.properties + echo "Create configuration in properties file" + cat <>${scriptDir}/model.properties +CLUSTER_SIZE=${wlsClusterSize} +EOF + + # Generate application deployment model in model.yaml + # Known issue: no support for package name that has comma. + # remove [] + if [ "${appPackageUrls}" == "[]" ]; then + return + fi + + cat <>${scriptDir}/model.yaml +appDeployments: + Application: +EOF + appPackageUrls=$(echo "${appPackageUrls:1:${#appPackageUrls}-2}") + appUrlArray=$(echo $appPackageUrls | tr "," "\n") + + index=1 + for item in $appUrlArray; do + # e.g. https://wlsaksapp.blob.core.windows.net/japps/testwebapp.war?sp=r&se=2021-04-29T15:12:38Z&sv=2020-02-10&sr=b&sig=7grL4qP%2BcJ%2BLfDJgHXiDeQ2ZvlWosRLRQ1ciLk0Kl7M%3D + fileNamewithQueryString="${item##*/}" + fileName="${fileNamewithQueryString%\?*}" + fileExtension="${fileName##*.}" + curl -m 120 -fL "$item" -o wlsdeploy/applications/${fileName} + cat <>${scriptDir}/model.yaml + app${index}: + SourcePath: 'wlsdeploy/applications/${fileName}' + ModuleType: ${fileExtension} + Target: 'cluster-1' +EOF + index=$((index + 1)) + done +} + +# Build weblogic image +# Push the image to ACR +function build_wls_image() { + # Add WDT + unzip imagetool.zip + ./imagetool/bin/imagetool.sh cache addInstaller \ + --type wdt \ + --version latest \ + --path ${scriptDir}/model-images/weblogic-deploy.zip + + # Zip wls model and applications + zip -r ${scriptDir}/model-images/archive.zip wlsdeploy + + # Build image + echo "Start building WLS image." + ./imagetool/bin/imagetool.sh update \ + --tag model-in-image:WLS-v1 \ + --fromImage ${wlsImagePath} \ + --wdtModel ${scriptDir}/model.yaml \ + --wdtVariables ${scriptDir}/model.properties \ + --wdtArchive ${scriptDir}/model-images/archive.zip \ + --wdtModelOnly \ + --wdtDomainType WLS \ + --chown oracle:root + + validate_status "Check status of building WLS domain image." + + sudo docker tag model-in-image:WLS-v1 ${acrImagePath} + + # Push image to ACR + sudo docker logout + sudo docker login $azureACRServer -u ${azureACRUserName} -p ${azureACRPassword} + echo "Start pushing image ${acrImagePath} to $azureACRServer." + sudo docker push -q ${acrImagePath} + validate_status "Check status of pushing WLS domain image." + echo "Finish pushing image ${acrImagePath} to $azureACRServer." +} + +# Initialize +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +export wlsImagePath=$1 +export azureACRServer=$2 +export azureACRUserName=$3 +export azureACRPassword=$4 +export imageTag=$5 +export appPackageUrls=$6 +export ocrSSOUser=$7 +export ocrSSOPSW=$8 +export wlsClusterSize=$9 + +export acrImagePath="$azureACRServer/aks-wls-images:${imageTag}" +export ocrLoginServer="container-registry.oracle.com" +export wdtDownloadURL="https://github.com/oracle/weblogic-deploy-tooling/releases/download/release-1.9.7/weblogic-deploy.zip" +export witDownloadURL="https://github.com/oracle/weblogic-image-tool/releases/download/release-1.9.11/imagetool.zip" + +validate_inputs + +initialize + +install_utilities + +get_wls_image_from_ocr + +prepare_wls_models + +build_wls_image diff --git a/src/main/arm/scripts/domain.yaml.template b/src/main/arm/scripts/domain.yaml.template new file mode 100644 index 000000000..c4bcf700f --- /dev/null +++ b/src/main/arm/scripts/domain.yaml.template @@ -0,0 +1,141 @@ +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +# Based on ./kubernetes/samples/scripts/create-weblogic-domain/model-in-image/domain-resources/WLS/mii-initial-d1-WLS-v1.yaml +# in https://github.com/oracle/weblogic-kubernetes-operator. +# This is an example of how to define a Domain resource. +# +apiVersion: "weblogic.oracle/v8" +kind: Domain +metadata: + name: "@WLS_DOMAIN_UID@" + namespace: "@WLS_DOMAIN_UID@-ns" + labels: + weblogic.domainUID: "@WLS_DOMAIN_UID@" + +spec: + # Set to 'FromModel' to indicate 'Model in Image'. + domainHomeSourceType: FromModel + + # The WebLogic Domain Home, this must be a location within + # the image for 'Model in Image' domains. + domainHome: /u01/domains/@WLS_DOMAIN_UID@ + + # The WebLogic Server Docker image that the Operator uses to start the domain + image: "@WLS_IMAGE_PATH_ACR@" + + # Defaults to "Always" if image tag (version) is ':latest' + imagePullPolicy: "IfNotPresent" + + # Identify which Secret contains the credentials for pulling an image + imagePullSecrets: + - name: regsecret + + # Identify which Secret contains the WebLogic Admin credentials, + # the secret must contain 'username' and 'password' fields. + webLogicCredentialsSecret: + name: "@WLS_DOMAIN_UID@-weblogic-credentials" + + # Whether to include the WebLogic Server stdout in the pod's stdout, default is true + includeServerOutInPodLog: true + + # Whether to enable overriding your log file location, see also 'logHome' + #logHomeEnabled: false + + # The location for domain log, server logs, server out, introspector out, and Node Manager log files + # see also 'logHomeEnabled', 'volumes', and 'volumeMounts'. + #logHome: /shared/logs/@WLS_DOMAIN_UID@ + + # Set which WebLogic Servers the Operator will start + # - "NEVER" will not start any server in the domain + # - "ADMIN_ONLY" will start up only the administration server (no managed servers will be started) + # - "IF_NEEDED" will start all non-clustered servers, including the administration server, and clustered servers up to their replica count. + serverStartPolicy: "IF_NEEDED" + + # Settings for all server pods in the domain including the introspector job pod + serverPod: + # Optional new or overridden environment variables for the domain's pods + # - This sample uses CUSTOM_DOMAIN_NAME in its image model file + # to set the Weblogic domain name + env: + - name: CUSTOM_DOMAIN_NAME + value: "@DOMAIN_NAME@" + - name: JAVA_OPTIONS + value: "-Dweblogic.StdoutDebugEnabled=false" + - name: USER_MEM_ARGS + value: "-Djava.security.egd=file:/dev/./urandom -Xms256m -Xmx512m " + - name: MANAGED_SERVER_PREFIX + value: "@MANAGED_SERVER_PREFIX@" + resources: + requests: + cpu: "@RESOURCE_CPU@" + memory: "@RESOURCE_MEMORY@" + + # Optional volumes and mounts for the domain's pods. See also 'logHome'. + #volumes: + #- name: weblogic-domain-storage-volume + # persistentVolumeClaim: + # claimName: model-azurefile + #volumeMounts: + #- mountPath: /shared + # name: weblogic-domain-storage-volume + + # The desired behavior for starting the domain's administration server. + adminServer: + # The serverStartState legal values are "RUNNING" or "ADMIN" + # "RUNNING" means the listed server will be started up to "RUNNING" mode + # "ADMIN" means the listed server will be start up to "ADMIN" mode + serverStartState: "RUNNING" + # Setup a Kubernetes node port for the administration server default channel + #adminService: + # channels: + # - channelName: default + # nodePort: 30701 + + # The number of admin servers to start for unlisted clusters + replicas: 1 + + # The desired behavior for starting a specific cluster's member servers + clusters: + - clusterName: cluster-1 + serverStartState: "RUNNING" + serverPod: + # Instructs Kubernetes scheduler to prefer nodes for new cluster members where there are not + # already members of the same cluster. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "weblogic.clusterName" + operator: In + values: + - $(CLUSTER_NAME) + topologyKey: "kubernetes.io/hostname" + # The number of managed servers to start for unlisted clusters + replicas: @WLS_CLUSTER_REPLICAS@ + + # Change the restartVersion to force the introspector job to rerun + # and apply any new model configuration, to also force a subsequent + # roll of your domain's WebLogic Server pods. + restartVersion: '1' + + configuration: + + # Settings for domainHomeSourceType 'FromModel' + model: + # Valid model domain types are 'WLS', 'JRF', and 'RestrictedJRF', default is 'WLS' + domainType: "WLS" + + # Optional configmap for additional models and variable files + #configMap: @WLS_DOMAIN_UID@-wdt-config-map + + # All 'FromModel' domains require a runtimeEncryptionSecret with a 'password' field + runtimeEncryptionSecret: "@WLS_DOMAIN_UID@-runtime-encryption-secret" + + # Secrets that are referenced by model yaml macros + # (the model yaml in the optional configMap or in the image) + #secrets: + #- @WLS_DOMAIN_UID@-datasource-secret diff --git a/src/main/arm/scripts/model.properties b/src/main/arm/scripts/model.properties new file mode 100644 index 000000000..b94145204 --- /dev/null +++ b/src/main/arm/scripts/model.properties @@ -0,0 +1,6 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# Based on ./kubernetes/samples/scripts/create-weblogic-domain/model-in-image/model-images/model-in-image__WLS-v1/model.10.properties +# in https://github.com/oracle/weblogic-kubernetes-operator. + diff --git a/src/main/arm/scripts/model.yaml b/src/main/arm/scripts/model.yaml new file mode 100644 index 000000000..e509b39ad --- /dev/null +++ b/src/main/arm/scripts/model.yaml @@ -0,0 +1,46 @@ +# Copyright (c) 2020, 2021, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# Based on ./kubernetes/samples/scripts/create-weblogic-domain/model-in-image/model-images/model-in-image__WLS-v1/model.10.yaml +# in https://github.com/oracle/weblogic-kubernetes-operator. + +domainInfo: + AdminUserName: "@@SECRET:__weblogic-credentials__:username@@" + AdminPassword: "@@SECRET:__weblogic-credentials__:password@@" + ServerStartMode: "prod" + +topology: + Name: "@@ENV:CUSTOM_DOMAIN_NAME@@" + AdminServerName: "admin-server" + Cluster: + "cluster-1": + DynamicServers: + ServerTemplate: "cluster-1-template" + ServerNamePrefix: "@@ENV:MANAGED_SERVER_PREFIX@@" + DynamicClusterSize: "@@PROP:CLUSTER_SIZE@@" + MaxDynamicClusterSize: "@@PROP:CLUSTER_SIZE@@" + MinDynamicClusterSize: "0" + CalculatedListenPorts: false + Server: + "admin-server": + ListenPort: 7001 + ServerTemplate: + "cluster-1-template": + Cluster: "cluster-1" + ListenPort: 8001 + +resources: + SelfTuning: + MinThreadsConstraint: + SampleMinThreads: + Target: "cluster-1" + Count: 1 + MaxThreadsConstraint: + SampleMaxThreads: + Target: "cluster-1" + Count: 10 + WorkManager: + SampleWM: + Target: "cluster-1" + MinThreadsConstraint: "SampleMinThreads" + MaxThreadsConstraint: "SampleMaxThreads" diff --git a/src/main/arm/scripts/pv.yaml.template b/src/main/arm/scripts/pv.yaml.template new file mode 100644 index 000000000..82450d8e5 --- /dev/null +++ b/src/main/arm/scripts/pv.yaml.template @@ -0,0 +1,28 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# Based on ./kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-file-pv-template.yaml +# in https://github.com/oracle/weblogic-kubernetes-operator. + +apiVersion: v1 +kind: PersistentVolume +metadata: + name: @PV_NME@ + namespace: @NAMESPACE@ +spec: + capacity: + storage: 5Gi + accessModes: + - ReadWriteMany + storageClassName: azurefile + azureFile: + secretName: azure-secret + shareName: weblogic + readOnly: false + mountOptions: + - dir_mode=0777 + - file_mode=0777 + - uid=1000 + - gid=1000 + - mfsymlinks + - nobrl \ No newline at end of file diff --git a/src/main/arm/scripts/pvc.yaml.template b/src/main/arm/scripts/pvc.yaml.template new file mode 100644 index 000000000..22fb22632 --- /dev/null +++ b/src/main/arm/scripts/pvc.yaml.template @@ -0,0 +1,18 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# Based on ./kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-file-pvc-template.yaml +# in https://github.com/oracle/weblogic-kubernetes-operator. + +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: @PVC_NAME@ + namespace: @NAMESPACE@ +spec: + accessModes: + - ReadWriteMany + storageClassName: azurefile + resources: + requests: + storage: 5Gi \ No newline at end of file diff --git a/src/main/arm/scripts/setupWLSDomain.sh b/src/main/arm/scripts/setupWLSDomain.sh new file mode 100644 index 000000000..61019a6ec --- /dev/null +++ b/src/main/arm/scripts/setupWLSDomain.sh @@ -0,0 +1,492 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +echo "Script starts" + +#Function to output message to stdout +function echo_stderr() { + echo "$@" >&2 + echo "$@" >>stdout +} + +function echo_stdout() { + echo "$@" >&2 + echo "$@" >>stdout +} + +#Function to display usage message +function usage() { + echo_stdout "./setupWLSDomain.sh " + if [ $1 -eq 1 ]; then + exit 1 + fi +} + +#Function to validate input +function validate_input() { + if [[ -z "$ocrSSOUser" || -z "${ocrSSOPSW}" ]]; then + echo_stderr "Oracle SSO account is required. " + usage 1 + fi + + if [[ -z "$aksClusterRGName" || -z "${aksClusterName}" ]]; then + echo_stderr "AKS cluster name and resource group name are required. " + usage 1 + fi + + if [ -z "$wlsImageTag" ]; then + echo_stderr "wlsImageTag is required. " + usage 1 + fi + + if [ -z "$acrName" ]; then + echo_stderr "acrName is required. " + usage 1 + fi + + if [ -z "$wlsDomainName" ]; then + echo_stderr "wlsDomainName is required. " + usage 1 + fi + + if [ -z "$wlsDomainUID" ]; then + echo_stderr "wlsDomainUID is required. " + usage 1 + fi + + if [ -z "$wlsUserName" ]; then + echo_stderr "wlsUserName is required. " + usage 1 + fi + + if [ -z "$wlsPassword" ]; then + echo_stderr "wlsPassword is required. " + usage 1 + fi + + if [ -z "$wdtRuntimePassword" ]; then + echo_stderr "wdtRuntimePassword is required. " + usage 1 + fi + + if [ -z "$wlsCPU" ]; then + echo_stderr "wlsCPU is required. " + usage 1 + fi + + if [ -z "$wlsMemory" ]; then + echo_stderr "wlsMemory is required. " + usage 1 + fi + + if [ -z "$managedServerPrefix" ]; then + echo_stderr "managedServerPrefix is required. " + usage 1 + fi + + if [ -z "$appReplicas" ]; then + echo_stderr "appReplicas is required. " + usage 1 + fi + + if [ -z "$appPackageUrls" ]; then + echo_stderr "appPackageUrls is required. " + usage 1 + fi + + if [ -z "$currentResourceGroup" ]; then + echo_stderr "currentResourceGroup is required. " + usage 1 + fi + + if [ -z "$scriptURL" ]; then + echo_stderr "scriptURL is required. " + usage 1 + fi + + if [ -z "$storageAccountName" ]; then + echo_stderr "storageAccountName is required. " + usage 1 + fi + + if [ -z "$wlsClusterSize" ]; then + echo_stderr "wlsClusterSize is required. " + usage 1 + fi +} + +# Validate teminal status with $?, exit with exception if errors happen. +function validate_status() { + if [ $? == 1 ]; then + echo_stderr "$@" + echo_stderr "Errors happen, exit 1." + exit 1 + else + echo_stdout "$@" + fi +} + +# Install latest kubectl and helm +function install_utilities() { + if [ -d "apps" ]; then + rm apps -f -r + fi + + mkdir apps + cd apps + + # Install kubectl + az aks install-cli + echo "kubectl version" + ret=$(kubectl --help) + validate_status ${ret} + + # Install helm + browserURL=$(curl -s https://api.github.com/repos/helm/helm/releases/latest \ + | grep "browser_download_url.*linux-amd64.tar.gz.asc" \ + | cut -d : -f 2,3 \ + | tr -d \") + helmLatestVersion=${browserURL#*download\/} + helmLatestVersion=${helmLatestVersion%%\/helm*} + helmPackageName=helm-${helmLatestVersion}-linux-amd64.tar.gz + curl -LO -s https://get.helm.sh/${helmPackageName} + tar -zxvf ${helmPackageName} + chmod +x linux-amd64/helm + mv linux-amd64/helm /usr/local/bin/helm + echo "helm version" + ret=$(helm version) + validate_status ${ret} + + echo "az cli version" + ret=$(az --version) + validate_status ${ret} + ret=$(az account show) + echo $ret >>stdout + if [ -n $(echo ${ret} | grep "systemAssignedIdentity") ]; then + echo_stderr "Make sure you are using user assigned identity." + exit 1 + fi +} + +# Connect to AKS cluster +function connect_aks_cluster() { + az aks get-credentials --resource-group ${aksClusterRGName} --name ${aksClusterName} --overwrite-existing +} + +# Install WebLogic operator using charts from GitHub Repo +# * Create namespace weblogic-operator-ns +# * Create service account +# * install operator +function install_wls_operator() { + kubectl create namespace ${wlsOptNameSpace} + kubectl -n ${wlsOptNameSpace} create serviceaccount ${wlsOptSA} + + helm repo add ${wlsOptRelease} ${wlsOptHelmChart} --force-update + ret=$(helm repo list) + validate_status ${ret} + helm install ${wlsOptRelease} weblogic-operator/weblogic-operator \ + --namespace ${wlsOptNameSpace} \ + --set serviceAccount=${wlsOptSA} \ + --set "enableClusterRoleBinding=true" \ + --set "domainNamespaceSelectionStrategy=LabelSelector" \ + --set "domainNamespaceLabelSelector=weblogic-operator\=enabled" \ + --wait + + validate_status "Installing WLS operator." + + # valiadate weblogic operator + ret=$(kubectl get pod -n ${wlsOptNameSpace} | grep "Running") + if [ -z "$ret" ];then + echo_stderr "Failed to install WebLogic operator." + exit 1 + fi +} + +# Query ACR login server, username, password +function query_acr_credentials() { + azureACRServer=$(az acr show -n $acrName --query 'loginServer' -o tsv) + validate_status ${azureACRServer} + azureACRUserName=$(az acr credential show -n $acrName --query 'username' -o tsv) + azureACRPassword=$(az acr credential show -n $acrName --query 'passwords[0].value' -o tsv) + validate_status "Query ACR credentials." +} + +# Build docker image +# * Create Ubuntu machine VM-UBUNTU +# * Running vm extension to run buildWLSDockerImage.sh, the script will: +# * build a docker image with domain model, applications based on specified WebLogic Standard image +# * push the image to ACR +function build_docker_image() { + # Create vm to build docker image + vmName="VM-UBUNTU" + + # MICROSOFT_INTERNAL + # Specify tag 'SkipASMAzSecPack' to skip policy 'linuxazuresecuritypackautodeployiaas_1.6' + # Specify tag 'SkipNRMS*' to skip Microsoft internal NRMS policy, which cause vm-redeployed issue + az vm create \ + --resource-group ${currentResourceGroup} \ + --name ${vmName} \ + --image "Canonical:UbuntuServer:18.04-LTS:latest" \ + --admin-username azureuser \ + --generate-ssh-keys \ + --nsg-rule NONE \ + --enable-agent true \ + --enable-auto-update false \ + --tags SkipASMAzSecPack=true SkipNRMSCorp=true SkipNRMSDatabricks=true SkipNRMSDB=true SkipNRMSHigh=true SkipNRMSMedium=true SkipNRMSRDPSSH=true SkipNRMSSAW=true SkipNRMSMgmt=true\ + --verbose + + validate_status "Check status of VM machine to build docker image." + + wlsImagePath="${ocrLoginServer}/middleware/weblogic:${wlsImageTag}" + az vm extension set --name CustomScript \ + --extension-instance-name wls-image-script \ + --resource-group ${currentResourceGroup} \ + --vm-name ${vmName} \ + --publisher Microsoft.Azure.Extensions \ + --version 2.0 \ + --settings "{ \"fileUris\": [\"${scriptURL}model.yaml\",\"${scriptURL}model.properties\",\"${scriptURL}buildWLSDockerImage.sh\"]}" \ + --protected-settings "{\"commandToExecute\":\"bash buildWLSDockerImage.sh ${wlsImagePath} ${azureACRServer} ${azureACRUserName} ${azureACRPassword} ${newImageTag} \\\"${appPackageUrls}\\\" ${ocrSSOUser} ${ocrSSOPSW} ${wlsClusterSize}\"}" + + # If error fires, keep vm resource and exit. + validate_status "Check status of buiding WLS domain image." + + #Validate image from ACR + az acr repository show -n ${acrName} --image aks-wls-images:${newImageTag} + validate_status "Check if new image aks-wls-images:${newImageTag} has been pushed to acr." + + cleanup_vm +} + +# Deploy WebLogic domain and cluster +# * Create namespace for domain +# * Create secret for weblogic +# * Create secret for Azure file +# * Create secret for ACR +# * Deploy WebLogic domain using image in ACR +# * Wait for the domain completed +function setup_wls_domain() { + kubectl create namespace ${wlsDomainNS} + kubectl label namespace ${wlsDomainNS} weblogic-operator=enabled + + kubectl -n ${wlsDomainNS} create secret generic ${kubectlWLSCredentials} \ + --from-literal=username=${wlsUserName} --from-literal=password=${wlsPassword} + kubectl -n ${wlsDomainNS} label secret ${kubectlWLSCredentials} weblogic.domainUID=${wlsDomainUID} + + kubectl -n ${wlsDomainNS} create secret generic ${wlsDomainUID}-runtime-encryption-secret \ + --from-literal=password=${wdtRuntimePassword} + kubectl -n ${wlsDomainNS} label secret ${wlsDomainUID}-runtime-encryption-secret weblogic.domainUID=${wlsDomainUID} + + kubectl create secret docker-registry ${kubectlSecretForACR} \ + --docker-server=${azureACRServer} \ + --docker-username=${azureACRUserName} \ + --docker-password=${azureACRPassword} \ + -n ${wlsDomainNS} + + if [[ "${storageAccountName}" != "null" ]]; then + create_pv + fi + + # generate domain yaml + customDomainYaml=${scriptDir}/custom-domain.yaml + cp ${scriptDir}/domain.yaml.template ${customDomainYaml} + sed -i -e "s:@WLS_DOMAIN_UID@:${wlsDomainUID}:g" ${customDomainYaml} + sed -i -e "s:@WLS_IMAGE_PATH_ACR@:${azureACRServer}/aks-wls-images\:${newImageTag}:g" ${customDomainYaml} + sed -i -e "s:@RESOURCE_CPU@:${wlsCPU}:g" ${customDomainYaml} + sed -i -e "s:@RESOURCE_MEMORY@:${wlsMemory}:g" ${customDomainYaml} + sed -i -e "s:@DOMAIN_NAME@:${wlsDomainName}:g" ${customDomainYaml} + sed -i -e "s:@MANAGED_SERVER_PREFIX@:${managedServerPrefix}:g" ${customDomainYaml} + sed -i -e "s:@WLS_CLUSTER_REPLICAS@:${appReplicas}:g" ${customDomainYaml} + + kubectl apply -f ${customDomainYaml} + + wait_for_domain_completed +} + +# Create storage for AKS cluster +# * Create secret for storage account +# * Create PV using Azure file share +# * Create PVC +function create_pv() { + export storageAccountKey=$(az storage account keys list --resource-group $currentResourceGroup --account-name $storageAccountName --query "[0].value" -o tsv) + export azureSecretName="azure-secret" + kubectl -n ${wlsDomainNS} create secret generic ${azureSecretName} \ + --from-literal=azurestorageaccountname=${storageAccountName} \ + --from-literal=azurestorageaccountkey=${storageAccountKey} + + # generate pv configurations + customPVYaml=${scriptDir}/pv.yaml + cp ${scriptDir}/pv.yaml.template ${customPVYaml} + pvName=${wlsDomainUID}-pv-azurefile + sed -i -e "s:@NAMESPACE@:${wlsDomainNS}:g" ${customPVYaml} + sed -i -e "s:@PV_NME@:${pvName}:g" ${customPVYaml} + + # generate pv configurations + customPVCYaml=${scriptDir}/pvc.yaml + cp ${scriptDir}/pvc.yaml.template ${customPVCYaml} + pvcName=${wlsDomainUID}-pvc-azurefile + sed -i -e "s:@NAMESPACE@:${wlsDomainNS}:g" ${customPVCYaml} + sed -i -e "s:@PVC_NAME@:${pvcName}:g" ${customPVCYaml} + + kubectl apply -f ${customPVYaml} + kubectl apply -f ${customPVCYaml} + + # validate PV PVC + ret=$(kubectl get pv | grep "${pvName}" | grep "${pvcName}") + if [ -z "$ret" ];then + echo_stderr "Failed to create pv/pvc." + fi +} + +function wait_for_domain_completed() { + attempts=0 + svcState="running" + while [ ! "$svcState" == "completed" ] && [ $attempts -lt 10 ]; do + svcState="completed" + attempts=$((attempts + 1)) + echo Waiting for job completed...${attempts} + sleep 2m + + # If the job is completed, there should have the following services created, + # ${domainUID}-${adminServerName}, e.g. domain1-admin-server + adminServiceCount=$(kubectl -n ${wlsDomainNS} get svc | grep -c "${wlsDomainUID}-${adminServerName}") + if [ ${adminServiceCount} -lt 1 ]; then svcState="running"; fi + + # If the job is completed, there should have the following services created, .assuming initialManagedServerReplicas=2 + # ${domainUID}-${managedServerNameBase}1, e.g. domain1-managed-server1 + # ${domainUID}-${managedServerNameBase}2, e.g. domain1-managed-server2 + managedServiceCount=$(kubectl -n ${wlsDomainNS} get svc | grep -c "${wlsDomainUID}-${managedServerPrefix}") + if [ ${managedServiceCount} -lt ${appReplicas} ]; then svcState="running"; fi + + # If the job is completed, there should have no service in pending status. + pendingCount=$(kubectl -n ${wlsDomainNS} get pod | grep -c "pending") + if [ ${pendingCount} -ne 0 ]; then svcState="running"; fi + + # If the job is completed, there should have the following pods running + # ${domainUID}-${adminServerName}, e.g. domain1-admin-server + # ${domainUID}-${managedServerNameBase}1, e.g. domain1-managed-server1 + # to + # ${domainUID}-${managedServerNameBase}n, e.g. domain1-managed-servern, n = initialManagedServerReplicas + runningPodCount=$(kubectl -n ${wlsDomainNS} get pods | grep "${wlsDomainUID}" | grep -c "Running") + if [[ $runningPodCount -le ${appReplicas} ]]; then svcState="running"; fi + done + + # If all the services are completed, print service details + # Otherwise, ask the user to refer to document for troubleshooting + if [ "$svcState" == "completed" ]; then + kubectl -n ${wlsDomainNS} get pods + kubectl -n ${wlsDomainNS} get svc + else + echo WARNING: WebLogic domain is not ready. It takes too long to create domain, please refer to http://oracle.github.io/weblogic-kubernetes-operator/samples/simple/azure-kubernetes-service/#troubleshooting + exitCode=1 + fi +} + +function cleanup_vm() { + #Remove VM resources + az extension add --name resource-graph + # query vm id + vmId=$(az graph query -q "Resources \ +| where type =~ 'microsoft.compute/virtualmachines' \ +| where name=~ '${vmName}' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| project vmid = id" -o tsv) + + # query nic id + nicId=$(az graph query -q "Resources \ +| where type =~ 'microsoft.compute/virtualmachines' \ +| where name=~ '${vmName}' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| extend nics=array_length(properties.networkProfile.networkInterfaces) \ +| mv-expand nic=properties.networkProfile.networkInterfaces \ +| where nics == 1 or nic.properties.primary =~ 'true' or isempty(nic) \ +| project nicId = tostring(nic.id)" -o tsv) + + # query ip id + ipId=$(az graph query -q "Resources \ +| where type =~ 'microsoft.network/networkinterfaces' \ +| where id=~ '${nicId}' \ +| extend ipConfigsCount=array_length(properties.ipConfigurations) \ +| mv-expand ipconfig=properties.ipConfigurations \ +| where ipConfigsCount == 1 or ipconfig.properties.primary =~ 'true' \ +| project publicIpId = tostring(ipconfig.properties.publicIPAddress.id)" -o tsv) + + # query os disk id + osDiskId=$(az graph query -q "Resources \ +| where type =~ 'microsoft.compute/virtualmachines' \ +| where name=~ '${vmName}' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| project osDiskId = tostring(properties.storageProfile.osDisk.managedDisk.id)" -o tsv) + + # query vnet id + vnetId=$(az graph query -q "Resources \ +| where type =~ 'Microsoft.Network/virtualNetworks' \ +| where name=~ '${vmName}VNET' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| project vNetId = id" -o tsv) + + # query nsg id + nsgId=$(az graph query -q "Resources \ +| where type =~ 'Microsoft.Network/networkSecurityGroups' \ +| where name=~ '${vmName}NSG' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| project nsgId = id" -o tsv) + + # Delete VM NIC IP VNET NSG resoruces + vmResourceIdS=$(echo ${vmId} ${nicId} ${ipId} ${osDiskId} ${vnetId} ${nsgId}) + echo ${vmResourceIdS} + az resource delete --verbose --ids ${vmResourceIdS} +} + +# Main script +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +export ocrSSOUser=$1 +export ocrSSOPSW=$2 +export aksClusterRGName=$3 +export aksClusterName=$4 +export wlsImageTag=$5 +export acrName=$6 +export wlsDomainName=$7 +export wlsDomainUID=$8 +export wlsUserName=$9 +export wlsPassword=${10} +export wdtRuntimePassword=${11} +export wlsCPU=${12} +export wlsMemory=${13} +export managedServerPrefix=${14} +export appReplicas=${15} +export appPackageUrls=${16} +export currentResourceGroup=${17} +export scriptURL=${18} +export storageAccountName=${19} +export wlsClusterSize=${20} + +export adminServerName="admin-server" +export exitCode=0 +export ocrLoginServer="container-registry.oracle.com" +export kubectlSecretForACR="regsecret" +export kubectlWLSCredentials="${wlsDomainUID}-weblogic-credentials" +export newImageTag=$(date +%s) +export storageFileShareName="weblogic" +export wlsDomainNS="${wlsDomainUID}-ns" +export wlsOptHelmChart="https://oracle.github.io/weblogic-kubernetes-operator/charts" +export wlsOptNameSpace="weblogic-operator-ns" +export wlsOptRelease="weblogic-operator" +export wlsOptSA="weblogic-operator-sa" + +validate_input + +install_utilities + +query_acr_credentials + +build_docker_image + +connect_aks_cluster + +install_wls_operator + +setup_wls_domain + +exit $exitCode diff --git a/src/main/bicep/mainTemplate.bicep b/src/main/bicep/mainTemplate.bicep index 5c403e215..88efdb5a1 100644 --- a/src/main/bicep/mainTemplate.bicep +++ b/src/main/bicep/mainTemplate.bicep @@ -1,7 +1,11 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + /* -* Terms +* Terms: * aci is short for Azure Container Insight * aks is short for Azure Kubernetes Service +* acr is short for Azure Container Registry * * Run the template: * $ bicep build mainTemplate.bicep @@ -11,91 +15,129 @@ * $ mvn -Pbicep -Ddev -Passembly clean install */ +param _artifactsLocation string = deployment().properties.templateLink.uri +@secure() +param _artifactsLocationSasToken string = '' @description('true to use resource or workspace permissions. false to require workspace permissions.') param aciResourcePermissions bool = true - @description('Number of days to retain data in Azure Monitor workspace.') param aciRetentionInDays int = 120 - @description('Pricing tier: PerGB2018 or legacy tiers (Free, Standalone, PerNode, Standard or Premium) which are not available to all customers.') param aciWorkspaceSku string = 'pergb2018' - +param acrName string = '' @maxLength(12) @minLength(1) @description('The name for this node pool. Node pool must contain only lowercase letters and numbers. For Linux node pools the name cannot be longer than 12 characters.') param aksAgentPoolName string = 'agentpool' - @maxValue(10000) @minValue(1) @description('The number of nodes that should be created along with the cluster. You will be able to resize the cluster later.') param aksAgentPoolNodeCount int = 3 - @description('The size of the virtual machines that will form the nodes in the cluster. This cannot be changed after creating the cluster') param aksAgentPoolVMSize string = 'Standard_DS2_v2' - @description('Prefix for cluster name. Only The name can contain only letters, numbers, underscores and hyphens. The name must start with letter or number.') param aksClusterNamePrefix string = 'wlsonaks' - +@description('Resource group name of an existing AKS cluster.') +param aksClusterRGName string = '' +@description('Name of an existing AKS cluster.') +param aksClusterName string = '' +@description('The AKS version.') param aksVersion string = 'default' - +@description('Urls of Java EE application packages.') +param appPackageUrls array = [] +@description('The number of managed server to start.') +param appReplicas int = 2 +@description('true to create a new Azure Container Registry.') +param createACR bool = false @description('true to create a new AKS cluster.') param createAKSCluster bool = true - @description('In addition to the CPU and memory metrics included in AKS by default, you can enable Container Insights for more comprehensive data on the overall performance and health of your cluster. Billing is based on data ingestion and retention settings.') param enableAzureMonitoring bool = false - +@description('true to create persistent volume using file share.') +param enableAzureFileShare bool = false +@description('An user assigned managed identity. Make sure the identity has permission to create/update/delete/list Azure resources.') +param identity object param location string = 'eastus' - -var defaultPidDeploymentName = 'pid' +@description('Name prefix of managed server.') +param managedServerPrefix string = 'managed-server' +@secure() +@description('Password of Oracle SSO account.') +param ocrSSOPSW string +@description('User name of Oracle SSO account.') +param ocrSSOUser string +@description('ture to upload Java EE applications and deploy the applications to WebLogic domain.') +param uploadAppPackage bool = false +@secure() +@description('Password for model WebLogic Deploy Tooling runtime encrytion.') +param wdtRuntimePassword string +@description('Maximum cluster size.') +param wlsClusterSize int = 5 +@description('Requests for CPU resources for admin server and managed server.') +param wlsCPU string = '200m' +@description('Name of WebLogic domain to create.') +param wlsDomainName string = 'domain1' +@description('UID of WebLogic domain, used in WebLogic Operator.') +param wlsDomainUID string = 'sample-domain1' +@description('Docker tag that comes after "container-registry.oracle.com/middleware/weblogic:"') +param wlsImageTag string = '12.2.1.4' +@description('Memory requests for admin server and managed server.') +param wlsMemory string = '1.5Gi' +@secure() +param wlsPassword string +@description('User name for WebLogic Administrator.') +param wlsUserName string = 'weblogic' + +var name_defaultPidDeployment = 'pid' /* * Beginning of the offer deployment */ -module pids './modules/pids/pid.bicep' = { +module pids './modules/_pids/_pid.bicep' = { name: 'initialization' } -/* -* Deploy a pid to tract an offer deployment starts -*/ -module pidStart './modules/pids/pid.bicep' = { - name: 'wls-aks-start-pid-deployment' - params:{ - name: pids.outputs.wlsAKSStart == '' ? defaultPidDeploymentName: pids.outputs.wlsAKSStart - } -} - -/* -* Deploy AKS cluster -*/ -module AKSClusterDeployment './modules/aks.bicep' = if(createAKSCluster){ - name: 'aks-cluster-deployment' +module wlsDomainDeployment 'modules/setupWebLogicCluster.bicep' = { + name: 'setup-wls-cluster' params: { + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + _pidEnd: pids.outputs.wlsAKSEnd == '' ? name_defaultPidDeployment : pids.outputs.wlsAKSEnd + _pidStart: pids.outputs.wlsAKSStart == '' ? name_defaultPidDeployment : pids.outputs.wlsAKSStart aciResourcePermissions: aciResourcePermissions aciRetentionInDays: aciRetentionInDays aciWorkspaceSku: aciWorkspaceSku + acrName: acrName aksAgentPoolName: aksAgentPoolName aksAgentPoolNodeCount: aksAgentPoolNodeCount aksAgentPoolVMSize: aksAgentPoolVMSize aksClusterNamePrefix: aksClusterNamePrefix + aksClusterRGName: aksClusterRGName + aksClusterName: aksClusterName aksVersion: aksVersion + appPackageUrls: appPackageUrls + appReplicas: appReplicas + createACR: createACR + createAKSCluster: createAKSCluster enableAzureMonitoring: enableAzureMonitoring + enableAzureFileShare: enableAzureFileShare + identity: identity location: location + managedServerPrefix: managedServerPrefix + ocrSSOPSW: ocrSSOPSW + ocrSSOUser: ocrSSOUser + uploadAppPackage: uploadAppPackage + wdtRuntimePassword: wdtRuntimePassword + wlsClusterSize: wlsClusterSize + wlsCPU: wlsCPU + wlsDomainName: wlsDomainName + wlsDomainUID: wlsDomainUID + wlsImageTag: wlsImageTag + wlsMemory: wlsMemory + wlsPassword: wlsPassword + wlsUserName: wlsUserName } } -/* -* Deploy a pid to tract an offer deployment ends -* Make sure all the dependencies added to dependsOn -*/ -module pidEnd './modules/pids/pid.bicep' = { - name: 'wls-aks-end-pid-deployment' - params:{ - name: pids.outputs.wlsAKSEnd == '' ? defaultPidDeploymentName: pids.outputs.wlsAKSEnd - } - dependsOn:[ - AKSClusterDeployment - ] -} - -output aksClusterName string = AKSClusterDeployment.outputs.aksClusterName +output aksClusterName string = wlsDomainDeployment.outputs.aksClusterName +output adminServerUrl string = wlsDomainDeployment.outputs.adminServerUrl +output clusterSVCUrl string = wlsDomainDeployment.outputs.clusterSVCUrl diff --git a/src/main/bicep/modules/_azure-resoruces/_acr.bicep b/src/main/bicep/modules/_azure-resoruces/_acr.bicep new file mode 100644 index 000000000..f4c179f89 --- /dev/null +++ b/src/main/bicep/modules/_azure-resoruces/_acr.bicep @@ -0,0 +1,43 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +param acrNamePrefix string = 'wlsaksacr' +param location string = 'eastus' +param utcValue string = utcNow() + +var name_acr= '${acrNamePrefix}${uniqueString(utcValue)}' + +resource registries 'Microsoft.ContainerRegistry/registries@2020-11-01-preview' = { + name: name_acr + location: location + sku: { + name: 'Standard' + tier: 'Standard' + } + properties: { + adminUserEnabled: true + policies: { + quarantinePolicy: { + status: 'disabled' + } + trustPolicy: { + type: 'Notary' + status: 'disabled' + } + retentionPolicy: { + days: 7 + status: 'disabled' + } + } + encryption: { + status: 'disabled' + } + dataEndpointEnabled: false + publicNetworkAccess: 'Enabled' + networkRuleBypassOptions: 'AzureServices' + zoneRedundancy: 'Disabled' + anonymousPullEnabled: false + } +} + +output acrName string = name_acr diff --git a/src/main/bicep/modules/aks.bicep b/src/main/bicep/modules/_azure-resoruces/_aks.bicep similarity index 76% rename from src/main/bicep/modules/aks.bicep rename to src/main/bicep/modules/_azure-resoruces/_aks.bicep index 3cba1c250..c213599f2 100644 --- a/src/main/bicep/modules/aks.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_aks.bicep @@ -1,61 +1,54 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + @description('true to use resource or workspace permissions. false to require workspace permissions.') param aciResourcePermissions bool = true - @description('Number of days to retain data in Azure Monitor workspace.') param aciRetentionInDays int = 120 - @description('Pricing tier: PerGB2018 or legacy tiers (Free, Standalone, PerNode, Standard or Premium) which are not available to all customers.') param aciWorkspaceSku string = 'pergb2018' - @maxLength(12) @minLength(1) @description('The name for this node pool. Node pool must contain only lowercase letters and numbers. For Linux node pools the name cannot be longer than 12 characters.') param aksAgentPoolName string = 'agentpool' - @maxValue(10000) @minValue(1) @description('The number of nodes that should be created along with the cluster. You will be able to resize the cluster later.') param aksAgentPoolNodeCount int = 3 - @description('The size of the virtual machines that will form the nodes in the cluster. This cannot be changed after creating the cluster') param aksAgentPoolVMSize string = 'Standard_DS2_v2' - @description('Prefix for cluster name. Only The name can contain only letters, numbers, underscores and hyphens. The name must start with letter or number.') param aksClusterNamePrefix string = 'wlsonaks' - param aksVersion string = 'default' - @description('In addition to the CPU and memory metrics included in AKS by default, you can enable Container Insights for more comprehensive data on the overall performance and health of your cluster. Billing is based on data ingestion and retention settings.') param enableAzureMonitoring bool = false - param location string = 'eastus' - param utcValue string = utcNow() -var aciWorkspaceName = 'Workspace-${guid(utcValue)}-${location}' -var aciDisableOmsAgent = { +var const_aksAgentPoolOSDiskSizeGB = 128 +var const_aksAgentPoolMaxPods = 110 +var const_aksAvailabilityZones = [ + '1' + '2' + '3' +] +var name_aciWorkspace = 'Workspace-${guid(utcValue)}-${location}' +// Generate a unique AKS name scoped to subscription. +// Create different cluster name for different deployment to avoid template validation error. +var name_aksClusterNameDefault = '${aksClusterNamePrefix}0${uniqueString(utcValue)}' +var name_aksClusterNameForSV = '${aksClusterNamePrefix}1${uniqueString(utcValue)}' +var obj_aciDisableOmsAgent = { enabled: false } -var aciEnableOmsAgent = { +var obj_aciEnableOmsAgent = { enabled: true config: { logAnalyticsWorkspaceResourceID: azureMonitoringWorkspace.id } } -var aksAgentPoolOSDiskSizeGB = 128 -var aksAgentPoolMaxPods = 110 -var aksAvailabilityZones = [ - '1' - '2' - '3' -] -// Generate a unique AKS name scoped to subscription. -// Create different cluster name for different deployment to avoid template validation error. -var aksClusterNameDefault = '${aksClusterNamePrefix}0${uniqueString(utcValue)}' -var aksClusterNameForSV = '${aksClusterNamePrefix}1${uniqueString(utcValue)}' resource azureMonitoringWorkspace 'Microsoft.OperationalInsights/workspaces@2020-08-01' = if (enableAzureMonitoring) { - name: aciWorkspaceName + name: name_aciWorkspace location: location properties: { sku: { @@ -71,21 +64,21 @@ resource azureMonitoringWorkspace 'Microsoft.OperationalInsights/workspaces@2020 } resource aksClusterDefault 'Microsoft.ContainerService/managedClusters@2021-02-01' = if (contains(aksVersion, 'default')) { - name: aksClusterNameDefault + name: name_aksClusterNameDefault location: location properties: { - dnsPrefix: '${aksClusterNameDefault}-dns' + dnsPrefix: '${name_aksClusterNameDefault}-dns' agentPoolProfiles: [ { name: aksAgentPoolName count: aksAgentPoolNodeCount vmSize: aksAgentPoolVMSize - osDiskSizeGB: aksAgentPoolOSDiskSizeGB + osDiskSizeGB: const_aksAgentPoolOSDiskSizeGB osDiskType: 'Managed' kubeletDiskType: 'OS' - maxPods: aksAgentPoolMaxPods + maxPods: const_aksAgentPoolMaxPods type: 'VirtualMachineScaleSets' - availabilityZones: aksAvailabilityZones + availabilityZones: const_aksAvailabilityZones nodeLabels: {} mode: 'System' osType: 'Linux' @@ -101,7 +94,7 @@ resource aksClusterDefault 'Microsoft.ContainerService/managedClusters@2021-02-0 httpApplicationRouting: { enabled: false } - omsAgent: enableAzureMonitoring ? aciEnableOmsAgent : aciDisableOmsAgent + omsAgent: enableAzureMonitoring ? obj_aciEnableOmsAgent : obj_aciDisableOmsAgent } enableRBAC: true networkProfile: { @@ -116,22 +109,22 @@ resource aksClusterDefault 'Microsoft.ContainerService/managedClusters@2021-02-0 } resource aksCluster 'Microsoft.ContainerService/managedClusters@2021-02-01' = if (!contains(aksVersion, 'default')) { - name: aksClusterNameForSV + name: name_aksClusterNameForSV location: location properties: { kubernetesVersion: '${aksVersion}' - dnsPrefix: '${aksClusterNameForSV}-dns' + dnsPrefix: '${name_aksClusterNameForSV}-dns' agentPoolProfiles: [ { name: aksAgentPoolName count: aksAgentPoolNodeCount vmSize: aksAgentPoolVMSize - osDiskSizeGB: aksAgentPoolOSDiskSizeGB + osDiskSizeGB: const_aksAgentPoolOSDiskSizeGB osDiskType: 'Managed' kubeletDiskType: 'OS' - maxPods: aksAgentPoolMaxPods + maxPods: const_aksAgentPoolMaxPods type: 'VirtualMachineScaleSets' - availabilityZones: aksAvailabilityZones + availabilityZones: const_aksAvailabilityZones nodeLabels: {} mode: 'System' osType: 'Linux' @@ -163,4 +156,4 @@ resource aksCluster 'Microsoft.ContainerService/managedClusters@2021-02-01' = if } } -output aksClusterName string = '${aksVersion}' == 'default' ? aksClusterNameDefault : aksClusterNameForSV +output aksClusterName string = '${aksVersion}' == 'default' ? name_aksClusterNameDefault : name_aksClusterNameForSV diff --git a/src/main/bicep/modules/_azure-resoruces/_storage.bicep b/src/main/bicep/modules/_azure-resoruces/_storage.bicep new file mode 100644 index 000000000..8713b77f3 --- /dev/null +++ b/src/main/bicep/modules/_azure-resoruces/_storage.bicep @@ -0,0 +1,53 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +param location string = 'eastus' +param utcValue string = utcNow() + +var const_shareQuota = 5120 +var const_sku = 'Standard_LRS' +var name_fileShare = 'weblogic' +var name_storageAccount = 'stg${uniqueString(utcValue)}' + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = { + name: name_storageAccount + location: location + kind: 'StorageV2' + sku: { + name: const_sku + tier: 'Standard' + } + properties: { + networkAcls: { + bypass: 'AzureServices' + virtualNetworkRules: [] + ipRules: [] + defaultAction: 'Allow' + } + supportsHttpsTrafficOnly: true + encryption: { + services: { + file: { + keyType: 'Account' + enabled: true + } + } + keySource: 'Microsoft.Storage' + } + accessTier: 'Hot' + } +} + +resource fileService 'Microsoft.Storage/storageAccounts/fileServices/shares@2021-02-01' = { + name: '${storageAccount.name}/default/${name_fileShare}' + properties: { + accessTier: 'TransactionOptimized' + shareQuota: const_shareQuota + enabledProtocols: 'SMB' + } + dependsOn: [ + storageAccount + ] +} + +output storageAccountName string = name_storageAccount diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep new file mode 100644 index 000000000..bf08dc0e8 --- /dev/null +++ b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep @@ -0,0 +1,57 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +param _artifactsLocation string +@secure() +param _artifactsLocationSasToken string = '' + +param aksClusterRGName string = '' +param aksClusterName string = '' +param acrName string = '' +param appPackageUrls array = [] +param appReplicas int = 2 +param identity object +param location string = 'eastus' +param managedServerPrefix string = 'managed-server' +@secure() +param ocrSSOPSW string +param ocrSSOUser string +param storageAccountName string = 'null' +param utcValue string = utcNow() +param wdtRuntimePassword string = 'welcome1' +param wlsClusterSize int = 5 +param wlsCPU string = '200m' +param wlsDomainName string = 'domain1' +param wlsDomainUID string = 'sample-domain1' +param wlsImageTag string = '12.2.1.4' +param wlsMemory string = '1.5Gi' +@secure() +param wlsPassword string +param wlsUserName string = 'weblogic' + +var const_arguments = '${ocrSSOUser} ${ocrSSOPSW} ${aksClusterRGName} ${aksClusterName} ${wlsImageTag} ${acrName} ${wlsDomainName} ${wlsDomainUID} ${wlsUserName} ${wlsPassword} ${wdtRuntimePassword} ${wlsCPU} ${wlsMemory} ${managedServerPrefix} ${appReplicas} ${string(appPackageUrls)} ${resourceGroup().name} ${const_scriptLocation} ${storageAccountName} ${wlsClusterSize}' +var const_domainTemplate = 'domain.yaml.template' +var const_pvTempalte = 'pv.yaml.template' +var const_pvcTempalte = 'pvc.yaml.template' +var const_scriptLocation = uri(_artifactsLocation, 'scripts/') +var const_setUpDomainScript = 'setupWLSDomain.sh' + +resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: 'ds-wls-cluster-creation' + location: location + kind: 'AzureCLI' + identity: identity + properties: { + azCliVersion: '2.15.0' + arguments: const_arguments + primaryScriptUri: uri(const_scriptLocation, '${const_setUpDomainScript}${_artifactsLocationSasToken}') + supportingScriptUris: [ + uri(const_scriptLocation, '${const_domainTemplate}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_pvTempalte}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_pvcTempalte}${_artifactsLocationSasToken}') + ] + cleanupPreference: 'OnSuccess' + retentionInterval: 'P1D' + forceUpdateTag: utcValue + } +} diff --git a/src/main/bicep/modules/_pids/_empty.bicep b/src/main/bicep/modules/_pids/_empty.bicep new file mode 100644 index 000000000..deb7ea7f4 --- /dev/null +++ b/src/main/bicep/modules/_pids/_empty.bicep @@ -0,0 +1,10 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +/* +* Used to create an empty deployment +* Example: +* module emptyDeployment './empty.bicep' = { +* name: name +* } +*/ diff --git a/src/main/bicep/modules/pids/pid-dev.bicep b/src/main/bicep/modules/_pids/_pid-dev.bicep similarity index 52% rename from src/main/bicep/modules/pids/pid-dev.bicep rename to src/main/bicep/modules/_pids/_pid-dev.bicep index 01aa0f9bd..c3b82e31d 100644 --- a/src/main/bicep/modules/pids/pid-dev.bicep +++ b/src/main/bicep/modules/_pids/_pid-dev.bicep @@ -1,9 +1,12 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + // Deployment for pids. param name string = 'pid' // create a pid deployment if there is a specified name -module pidStart './empty.bicep' = if (name != 'pid'){ +module pidStart './_empty.bicep' = if (name != 'pid'){ name: name } diff --git a/src/main/bicep/modules/pids/pid.bicep b/src/main/bicep/modules/_pids/_pid.bicep similarity index 52% rename from src/main/bicep/modules/pids/pid.bicep rename to src/main/bicep/modules/_pids/_pid.bicep index 8313a6462..fdf7d82f0 100644 --- a/src/main/bicep/modules/pids/pid.bicep +++ b/src/main/bicep/modules/_pids/_pid.bicep @@ -1,9 +1,12 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + // Deployment for pids. param name string = 'pid' // create a pid deployment if there is a specified name -module pidStart './empty.bicep' = if (name != 'pid'){ +module pidStart './_empty.bicep' = if (name != 'pid'){ name: name } diff --git a/src/main/bicep/modules/pids/empty.bicep b/src/main/bicep/modules/pids/empty.bicep deleted file mode 100644 index 8b9f99e11..000000000 --- a/src/main/bicep/modules/pids/empty.bicep +++ /dev/null @@ -1,7 +0,0 @@ -/* -* Used to create an empty deployment -* Example: -* module emptyDeployment './empty.bicep' = { -* name: name -* } -*/ diff --git a/src/main/bicep/modules/setupWebLogicCluster.bicep b/src/main/bicep/modules/setupWebLogicCluster.bicep new file mode 100644 index 000000000..cdf734ba7 --- /dev/null +++ b/src/main/bicep/modules/setupWebLogicCluster.bicep @@ -0,0 +1,197 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +/* +* The script is to create a simple WLS cluster, including: +* Create Azure resources: +* - Azure Kubenetes Cluster Service instance +* - Azure Container Registry instance +* - Azure Storage Account and file share +* - Azure Container Insight +* Initialize WebLogic cluster: +* - Build WebLogic domain image and push to ACR. +* - Install WebLogic Operator +* - Create WebLogic cluster and make sure the servers are running +*/ + +param _artifactsLocation string = deployment().properties.templateLink.uri +@secure() +param _artifactsLocationSasToken string = '' +param _pidEnd string +param _pidStart string +@description('true to use resource or workspace permissions. false to require workspace permissions.') +param aciResourcePermissions bool = true +@description('Number of days to retain data in Azure Monitor workspace.') +param aciRetentionInDays int = 120 +@description('Pricing tier: PerGB2018 or legacy tiers (Free, Standalone, PerNode, Standard or Premium) which are not available to all customers.') +param aciWorkspaceSku string = 'pergb2018' +param acrName string = '' +@maxLength(12) +@minLength(1) +@description('The name for this node pool. Node pool must contain only lowercase letters and numbers. For Linux node pools the name cannot be longer than 12 characters.') +param aksAgentPoolName string = 'agentpool' +@maxValue(10000) +@minValue(1) +@description('The number of nodes that should be created along with the cluster. You will be able to resize the cluster later.') +param aksAgentPoolNodeCount int = 3 +@description('The size of the virtual machines that will form the nodes in the cluster. This cannot be changed after creating the cluster') +param aksAgentPoolVMSize string = 'Standard_DS2_v2' +@description('Prefix for cluster name. Only The name can contain only letters, numbers, underscores and hyphens. The name must start with letter or number.') +param aksClusterNamePrefix string = 'wlsonaks' +@description('Resource group name of an existing AKS cluster.') +param aksClusterRGName string = '' +@description('Name of an existing AKS cluster.') +param aksClusterName string = '' +@description('The AKS version.') +param aksVersion string = 'default' +@description('Urls of Java EE application packages.') +param appPackageUrls array = [] +@description('The number of managed server to start.') +param appReplicas int = 2 +@description('true to create a new Azure Container Registry.') +param createACR bool = false +@description('true to create a new AKS cluster.') +param createAKSCluster bool = true +@description('In addition to the CPU and memory metrics included in AKS by default, you can enable Container Insights for more comprehensive data on the overall performance and health of your cluster. Billing is based on data ingestion and retention settings.') +param enableAzureMonitoring bool = false +@description('true to create persistent volume using file share.') +param enableAzureFileShare bool = false +@description('An user assigned managed identity. Make sure the identity has permission to create/update/delete/list Azure resources.') +param identity object +param location string = 'eastus' +@description('Name prefix of managed server.') +param managedServerPrefix string = 'managed-server' +@secure() +@description('Password of Oracle SSO account.') +param ocrSSOPSW string +@description('User name of Oracle SSO account.') +param ocrSSOUser string +@description('ture to upload Java EE applications and deploy the applications to WebLogic domain.') +param uploadAppPackage bool = false +@secure() +@description('Password for model WebLogic Deploy Tooling runtime encrytion.') +param wdtRuntimePassword string +@description('Maximum cluster size.') +param wlsClusterSize int = 5 +@description('Requests for CPU resources for admin server and managed server.') +param wlsCPU string = '200m' +@description('Name of WebLogic domain to create.') +param wlsDomainName string = 'domain1' +@description('UID of WebLogic domain, used in WebLogic Operator.') +param wlsDomainUID string = 'sample-domain1' +@description('Docker tag that comes after "container-registry.oracle.com/middleware/weblogic:"') +param wlsImageTag string = '12.2.1.4' +@description('Memory requests for admin server and managed server.') +param wlsMemory string = '1.5Gi' +@secure() +param wlsPassword string +@description('User name for WebLogic Administrator.') +param wlsUserName string = 'weblogic' + +/* +* Deploy a pid to tract an offer deployment starts +*/ +module pidStart './_pids/_pid.bicep'= { + name: 'wls-aks-start-pid-deployment' + params: { + name: _pidStart + } +} + +/* +* Deploy AKS cluster +*/ +module aksClusterDeployment './_azure-resoruces/_aks.bicep' = if (createAKSCluster) { + name: 'aks-cluster-deployment' + params: { + aciResourcePermissions: aciResourcePermissions + aciRetentionInDays: aciRetentionInDays + aciWorkspaceSku: aciWorkspaceSku + aksAgentPoolName: aksAgentPoolName + aksAgentPoolNodeCount: aksAgentPoolNodeCount + aksAgentPoolVMSize: aksAgentPoolVMSize + aksClusterNamePrefix: aksClusterNamePrefix + aksVersion: aksVersion + enableAzureMonitoring: enableAzureMonitoring + location: location + } + dependsOn: [ + pidStart + ] +} + +/* +* Deploy ACR +*/ +module acrDeployment './_azure-resoruces/_acr.bicep' = if (createACR) { + name: 'acr-deployment' + params: { + location: location + } + dependsOn: [ + pidStart + ] +} + +module storageDeployment './_azure-resoruces/_storage.bicep' = if (enableAzureFileShare) { + name: 'storage-deployment' + params: { + location: location + } + dependsOn: [ + pidStart + ] +} + +/* +* Deploy WLS domain +*/ +module wlsDomainDeployment './_deployment-scripts/_ds-create-wls-cluster.bicep' = { + name: 'wls-domain-deployment' + params: { + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + aksClusterRGName: createAKSCluster ? resourceGroup().name : aksClusterRGName + aksClusterName: createAKSCluster ? aksClusterDeployment.outputs.aksClusterName : aksClusterName + acrName: createACR ? acrDeployment.outputs.acrName : acrName + appPackageUrls: appPackageUrls + appReplicas: appReplicas + identity: identity + location: location + managedServerPrefix: managedServerPrefix + storageAccountName: enableAzureFileShare ? storageDeployment.outputs.storageAccountName : 'null' + ocrSSOUser: ocrSSOUser + ocrSSOPSW: ocrSSOPSW + wdtRuntimePassword: wdtRuntimePassword + wlsCPU: wlsCPU + wlsDomainName: wlsDomainName + wlsDomainUID: wlsDomainUID + wlsImageTag: wlsImageTag + wlsMemory: wlsMemory + wlsPassword: wlsPassword + wlsUserName: wlsUserName + } + dependsOn: [ + aksClusterDeployment + acrDeployment + storageDeployment + ] +} + +/* +* Deploy a pid to tract an offer deployment ends +* Make sure all the dependencies added to dependsOn array +*/ +module pidEnd './_pids/_pid.bicep' = { + name: 'wls-aks-end-pid-deployment' + params: { + name: _pidEnd + } + dependsOn: [ + wlsDomainDeployment + ] +} + +output aksClusterName string = createAKSCluster ? aksClusterDeployment.outputs.aksClusterName : aksClusterName +output adminServerUrl string = format('http://{0}-admin-server.{0}-ns.svc.cluster.local:7001/console',wlsDomainUID) +output clusterSVCUrl string = format('http://{0}-cluster-cluster-1.{0}-ns.svc.cluster.local:8001/', wlsDomainUID) From 0051021c85cdc08f543b241623302d62d1ddce13 Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Mon, 7 Jun 2021 21:34:32 +0000 Subject: [PATCH 21/37] Merged PR 328523: Setup networking and resolve comment from pr-323147 - Allow to set up public lv svc for admin server and cluster - Allow to set up Application Gateway ingress for admin server and cluster - Allow to create custom DNS - Resolve comment - apply meaningful script starting - use a unique ubuntu machine name - Known issue - HTTPS access is not available now. Related work items: #1326035, #1331317, #1333328, #1335576, #1336513 --- pom.xml | 2 +- src/main/arm/createUiDefinition.json | 481 ++++++++++++++- .../scripts/appgw-helm-config.yaml.template | 52 ++ ...ppgw-ingress-clusterAdmin-roleBinding.yaml | 15 + .../azure-ingress-appgateway.yaml.template | 21 + src/main/arm/scripts/buildWLSDockerImage.sh | 2 + src/main/arm/scripts/setupNetworking.sh | 553 ++++++++++++++++++ src/main/arm/scripts/setupWLSDomain.sh | 15 +- src/main/bicep/mainTemplate.bicep | 117 +++- .../_azure-resoruces/_appgateway.bicep | 247 ++++++++ .../modules/_azure-resoruces/_dnsZones.bicep | 13 + .../_keyvault/_keyvaultWithExistingCert.bicep | 61 ++ .../_keyvault/_keyvaultWithNewCert.bicep | 77 +++ .../_azure-resoruces/_keyvaultAdapter.bicep | 67 +++ .../_keyvaultAppGatewayConnector.bicep | 78 +++ .../_ds-create-networking.bicep | 58 ++ src/main/bicep/modules/_pids/_pid-dev.bicep | 4 + src/main/bicep/modules/_pids/_pid.bicep | 4 + src/main/bicep/modules/networking.bicep | 159 +++++ .../bicep/modules/setupWebLogicCluster.bicep | 1 + 20 files changed, 2011 insertions(+), 16 deletions(-) create mode 100644 src/main/arm/scripts/appgw-helm-config.yaml.template create mode 100644 src/main/arm/scripts/appgw-ingress-clusterAdmin-roleBinding.yaml create mode 100644 src/main/arm/scripts/azure-ingress-appgateway.yaml.template create mode 100644 src/main/arm/scripts/setupNetworking.sh create mode 100644 src/main/bicep/modules/_azure-resoruces/_appgateway.bicep create mode 100644 src/main/bicep/modules/_azure-resoruces/_dnsZones.bicep create mode 100644 src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep create mode 100644 src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep create mode 100644 src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep create mode 100644 src/main/bicep/modules/_azure-resoruces/_keyvaultAppGatewayConnector.bicep create mode 100644 src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep create mode 100644 src/main/bicep/modules/networking.bicep diff --git a/pom.xml b/pom.xml index 1bf4faa96..e889d14f0 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ com.oracle.weblogic.azure wls-on-aks-azure-marketplace - 1.0.1 + 1.0.4 com.microsoft.azure.iaas diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index 46b800699..f9c4a93c9 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -80,7 +80,7 @@ "name": "ocrSSOUserName", "type": "Microsoft.Common.TextBox", "label": "Username for Oracle Single Sign-On authentication", - "defaultValue": "example@foo.com", + "defaultValue": "example@contoso.com", "toolTip": "Username for Oracle Single Sign-On authentication to login the Oracle Container Registry.", "constraints": { "required": true, @@ -512,6 +512,468 @@ "visible": true } ] + }, + { + "name": "section_appGateway", + "type": "Microsoft.Common.Section", + "label": "Networking", + "subLabel": { + "preValidation": "Provide required info for networking", + "postValidation": "Done" + }, + "bladeTitle": "Networking", + "elements": [ + { + "name": "connectToAGText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here will cause the template to provision Load Balancer service or Ingress service for WebLogic Administration Console and WebLogic cluster.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-overview" + } + } + }, + { + "name": "lbSVCInfo", + "type": "Microsoft.Common.Section", + "label": "Standard Load Balancer service", + "elements": [ + { + "name": "enableLBSVC", + "type": "Microsoft.Common.OptionsGroup", + "label": "Create Standard Load Balancer services for the cluster?", + "defaultValue": "No", + "toolTip": "Select 'Yes' to create Standard Load Balancer services for the cluster.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ], + "required": false + } + }, + { + "name": "enableInternalLB", + "type": "Microsoft.Common.CheckBox", + "label": "Use Internal Load Balancer", + "visible": "[steps('section_appGateway').lbSVCInfo.enableLBSVC]" + }, + { + "name": "lbSVC", + "type": "Microsoft.Common.EditableGrid", + "ariaLabel": "Enter information", + "label": "Standard Load Balancer service", + "visible": "[steps('section_appGateway').lbSVCInfo.enableLBSVC]", + "constraints": { + "width": "Full", + "rows": { + "count": { + "min": 1, + "max": 10 + } + }, + "columns": [ + { + "id": "colName", + "header": "Service name prefix", + "width": "2fr", + "element": { + "type": "Microsoft.Common.TextBox", + "placeholder": "domain1-admin-server", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^[a-z0-9A-Z-]{1,30}$", + "message": "Only alphanumeric characters are allowed, and the value must be 1-30 characters long." + } + ] + } + } + }, + { + "id": "colTarget", + "header": "Target", + "width": "2fr", + "element": { + "name": "dropDownTargets", + "type": "Microsoft.Common.DropDown", + "placeholder": "admin-server", + "constraints": { + "allowedValues": [ + { + "label": "admin-server", + "value": "adminServer" + }, + { + "label": "cluster-1", + "value": "cluster1" + } + ], + "required": true + } + } + }, + { + "id": "colPort", + "header": "Port", + "width": "1fr", + "element": { + "type": "Microsoft.Common.TextBox", + "placeholder": "7001", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^[0-9]{1,5}$", + "message": "Only numbers are allowed, and the value must be 1-65535." + } + ] + } + } + } + ] + } + } + ], + "visible": true + }, + { + "name": "appgwIngress", + "type": "Microsoft.Common.Section", + "label": "Application Gateway Ingress", + "elements": [ + { + "name": "enableAppGateway", + "type": "Microsoft.Common.OptionsGroup", + "label": "Connect to Azure Application Gateway?", + "defaultValue": "No", + "toolTip": "Select 'Yes' to create an Azure Application Gateway Ingress as the load balancer for the cluster and admin server.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ], + "required": false + } + }, + { + "name": "keyVaultText00", + "type": "Microsoft.Common.TextBlock", + "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]", + "options": { + "text": "Choose an option for providing the TLS/SSL certificate:" + } + }, + { + "name": "keyVaultText01", + "type": "Microsoft.Common.TextBlock", + "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]", + "options": { + "text": "    ⁃ Upload a TLS/SSL certificate: Upload the pre-signed certificate now." + } + }, + { + "name": "keyVaultText02", + "type": "Microsoft.Common.TextBlock", + "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]", + "options": { + "text": "    ⁃ Identify an Azure Key Vault: The Key Vault must already contain the certificate and its password stored as secrets." + } + }, + { + "name": "keyVaultText03", + "type": "Microsoft.Common.TextBlock", + "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]", + "options": { + "text": "    ⁃ Generate a self-signed certificate: generate a self-signed certificate and apply it during deployment.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-key-vault" + } + } + }, + { + "name": "certificateOption", + "type": "Microsoft.Common.OptionsGroup", + "label": "Select desired TLS/SSL certificate option", + "defaultValue": "Generate a self-signed certificate", + "toolTip": "Select desired TLS/SSL certificate option", + "constraints": { + "allowedValues": [ + { + "label": "Upload a TLS/SSL certificate", + "value": "haveCert" + }, + { + "label": "Identify an Azure Key Vault", + "value": "haveKeyVault" + }, + { + "label": "Generate a self-signed certificate", + "value": "generateCert" + } + ], + "required": true + }, + "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]" + }, + { + "name": "keyVaultSSLCertData", + "type": "Microsoft.Common.FileUpload", + "label": "TLS/SSL certificate(.pfx)", + "toolTip": "TLS/SSL certificate used for App Gateway", + "constraints": { + "required": true, + "accept": ".pfx" + }, + "options": { + "multiple": false, + "uploadMode": "file", + "openMode": "binary" + }, + "visible": "[equals(steps('section_appGateway').appgwIngress.certificateOption, 'haveCert')]" + }, + { + "name": "appGatewaySSLCertPassword", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": "TLS/SSL certificate password", + "constraints": { + "required": "[equals(steps('section_appGateway').appgwIngress.certificateOption, 'haveCert')]", + "regex": "^((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])|(?=.*[0-9])(?=.*[a-z])(?=.*[!@#$%^&*])|(?=.*[0-9])(?=.*[A-Z])(?=.*[!@#$%^&*])|(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*])).{6,128}$", + "validationMessage": "The password must contain at least 6 characters, with at least 1 uppercase letter, 1 lowercase letter and 1 number." + }, + "options": { + "hideConfirmation": false + }, + "visible": "[equals(steps('section_appGateway').appgwIngress.certificateOption, 'haveCert')]" + }, + { + "name": "keyVaultResourceGroup", + "type": "Microsoft.Common.TextBox", + "label": "Resource group name in current subscription containing the Key Vault", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", + "validationMessage": "[if(greater(length(steps('section_appGateway').appgwIngress.keyVaultResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" + }, + "visible": "[equals(steps('section_appGateway').appgwIngress.certificateOption, 'haveKeyVault')]" + }, + { + "name": "keyVaultName", + "type": "Microsoft.Common.TextBox", + "label": "Name of the Azure Key Vault containing secrets for the certificate for TLS/SSL Termination", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^(?=.{3,24}$)[a-zA-Z](([a-z0-9A-Z]*|(?:\\-[^\\-][a-z0-9A-Z]*))*)$", + "validationMessage": "[if(or(greater(length(steps('section_appGateway').appgwIngress.keyVaultName), 24), less(length(steps('section_appGateway').appgwIngress.keyVaultName), 3)),'Vault name must be between 3-24 alphanumeric characters. The name must begin with a letter, end with a letter or digit, and not contain consecutive hyphens.','Vault name must only contain alphanumeric characters and dashes and cannot start with a number')]" + }, + "visible": "[equals(steps('section_appGateway').appgwIngress.certificateOption, 'haveKeyVault')]" + }, + { + "name": "keyVaultSSLCertDataSecretName", + "type": "Microsoft.Common.TextBox", + "label": "The name of the secret in the specified Key Vault whose value is the TLS/SSL certificate data", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + }, + "visible": "[equals(steps('section_appGateway').appgwIngress.certificateOption, 'haveKeyVault')]" + }, + { + "name": "keyVaultSSLCertPasswordSecretName", + "type": "Microsoft.Common.TextBox", + "label": "The name of the secret in the specified Key Vault whose value is the password for the TLS/SSL certificate", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + }, + "visible": "[equals(steps('section_appGateway').appgwIngress.certificateOption, 'haveKeyVault')]" + }, + { + "name": "servicePrincipal", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Service Principal", + "confirmPassword": "Confirm password" + }, + "toolTip": "Base64 encoded JSON blob of the service principal. You can generate one with command 'az ad sp create-for-rbac --sdk-auth | base64 -w0'", + "constraints": { + "required": true + }, + "options": { + "hideConfirmation": true + }, + "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]" + }, + { + "name": "appgwForAdminServer", + "type": "Microsoft.Common.OptionsGroup", + "label": "Create ingress for Administration Console. Make sure no application with path /console*, it will cause conflict with Administration Console path.", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to Create ingress for Administration Console. Make sure no application with path /console*, it will cause conflict with Administration Console path.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ] + }, + "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]" + } + ], + "visible": true + }, + { + "name": "dnsConfiguration", + "type": "Microsoft.Common.Section", + "label": "Custom DNS Configuration", + "elements": [ + { + "name": "enableDNSConfiguration", + "type": "Microsoft.Common.OptionsGroup", + "label": "Configure Custom DNS Alias", + "defaultValue": "No", + "toolTip": "Select 'Yes' to configure Custom DNS Alias.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ], + "required": false + } + }, + { + "name": "bringDNSZone", + "type": "Microsoft.Common.OptionsGroup", + "label": "Use an existing Azure DNS Zone", + "defaultValue": "No", + "toolTip": "Select 'Yes' to configure Custom DNS Alias based on an existing Azure DNS Zone. Select 'No' to create an Azure DNS Zone and Custom DNS Alias.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ] + }, + "visible": "[steps('section_appGateway').dnsConfiguration.enableDNSConfiguration]" + }, + { + "name": "dnszoneName", + "type": "Microsoft.Common.TextBox", + "label": "DNS Zone Name", + "defaultValue": "", + "toolTip": "Use only letters and numbers and periods to separate Domains", + "constraints": { + "required": true, + "regex": "^([0-9a-zA-Z_-]{1,63}\\.){1,33}[0-9a-zA-Z_-]{1,63}$", + "validationMessage": "There must be between 2 and 34 labels. For example, \"contoso.com\" has 2 labels. Each label must contain between 1 and 63 characters. Each label must only contain letters, numbers, underscores, and dashes." + }, + "visible": "[steps('section_appGateway').dnsConfiguration.enableDNSConfiguration]" + }, + { + "name": "dnsZoneResourceGroup", + "type": "Microsoft.Common.TextBox", + "label": "Name of the resource group contains the DNS Zone in current subscription", + "defaultValue": "", + "toolTip": "Name of the resource group which contains the DNS Zone in current subscription", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", + "validationMessage": "[if(greater(length(steps('section_appGateway').dnsConfiguration.dnsZoneResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" + }, + "visible": "[and(steps('section_appGateway').dnsConfiguration.enableDNSConfiguration,steps('section_appGateway').dnsConfiguration.bringDNSZone)]" + }, + { + "name": "dnszoneAdminConsoleLabel", + "type": "Microsoft.Common.TextBox", + "label": "Label for Oracle WebLogic Administration Console", + "defaultValue": "admin", + "toolTip": "Specify a label to generate subdomain of Oracle WebLogic Administration Console", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^([0-9a-zA-Z_-]{1,63}\\.){0,33}[0-9a-zA-Z_-]{1,63}$", + "message": "Each label must contain between 1 and 63 characters. Each label must only contain letters, numbers, underscores, and dashes." + }, + { + "isValid": "[less(sub(length(concat(steps('section_appGateway').dnsConfiguration.dnszoneAdminConsoleLabel,'.',steps('section_appGateway').dnsConfiguration.dnszoneName)),length(replace(concat(steps('section_appGateway').dnsConfiguration.dnszoneAdminConsoleLabel,'.',steps('section_appGateway').dnsConfiguration.dnszoneName), '.', ''))),34)]", + "message": "Subdomain must be between 2 and 34 labels. For example, \"admin.contoso.com\" has 3 labels." + } + ] + }, + "visible": "[steps('section_appGateway').dnsConfiguration.enableDNSConfiguration]" + }, + { + "name": "dnszoneGatewayLabel", + "type": "Microsoft.Common.TextBox", + "label": "Label for WebLogic Cluster", + "defaultValue": "www", + "toolTip": "Specify a label to generate subdomain of WebLogic Cluster", + "constraints": { + "required": true, + "validations": [ + { + "regex": "^([0-9a-zA-Z_-]{1,63}\\.){0,33}[0-9a-zA-Z_-]{1,63}$", + "message": "Each label must contain between 1 and 63 characters. Each label must only contain letters, numbers, underscores, and dashes." + }, + { + "isValid": "[less(sub(length(concat(if(empty(steps('section_appGateway').dnsConfiguration.dnszoneGatewayLabel), '', steps('section_appGateway').dnsConfiguration.dnszoneGatewayLabel),'.',steps('section_appGateway').dnsConfiguration.dnszoneName)),length(replace(concat(if(empty(steps('section_appGateway').dnsConfiguration.dnszoneGatewayLabel), '', steps('section_appGateway').dnsConfiguration.dnszoneGatewayLabel),'.',steps('section_appGateway').dnsConfiguration.dnszoneName), '.', ''))),34)]", + "message": "Subdomain must be between 2 and 34 labels. For example, \"applications.contoso.com\" has 3 labels." + } + ] + }, + "visible": "[steps('section_appGateway').dnsConfiguration.enableDNSConfiguration]" + } + ], + "visible": true + } + ] } ], "outputs": { @@ -519,17 +981,34 @@ "aksAgentPoolNodeCount": "[steps('section_aks').clusterInfo.aksNodeCount]", "aksClusterName": "[last(split(steps('section_aks').clusterInfo.aksClusterSelector.id, '/'))]", "aksClusterRGName": "[last(take(split(steps('section_aks').clusterInfo.aksClusterSelector.id, '/'), 5))]", + "appGatewayCertificateOption": "[steps('section_appGateway').appgwIngress.certificateOption]", + "appGatewaySSLCertData": "[steps('section_appGateway').appgwIngress.keyVaultSSLCertData]", + "appGatewaySSLCertPassword": "[steps('section_appGateway').appgwIngress.appGatewaySSLCertPassword]", + "appgwForAdminServer": "[steps('section_appGateway').appgwIngress.appgwForAdminServer]", "appPackageUrls": "[steps('section_aks').jeeAppInfo.appPackageUrl]", "appReplicas": "[int(steps('section_aks').jeeAppInfo.appReplicas)]", "createACR": "[bool(steps('section_aks').acrInfo.createACR)]", "createAKSCluster": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]", + "createDNSZone": "[not(bool(steps('section_appGateway').dnsConfiguration.bringDNSZone))]", + "dnszoneAdminConsoleLabel": "[steps('section_appGateway').dnsConfiguration.dnszoneAdminConsoleLabel]", + "dnszoneAppGatewayLabel": "[steps('section_appGateway').dnsConfiguration.dnszoneGatewayLabel]", + "dnszoneName": "[steps('section_appGateway').dnsConfiguration.dnszoneName]", + "dnszoneRGName": "[steps('section_appGateway').dnsConfiguration.dnsZoneResourceGroup]", + "enableAppGWIngress": "[steps('section_appGateway').appgwIngress.enableAppGateway]", "enableAzureMonitoring": "[bool(steps('section_aks').clusterInfo.enableAzureMonitoring)]", "enableAzureFileShare": "[bool(steps('section_aks').clusterInfo.enableAzureFileShare)]", + "enableDNSConfiguration": "[bool(steps('section_appGateway').dnsConfiguration.enableDNSConfiguration)]", "identity": "[basics('basicsRequired').identity]", + "lbSvcValues": "[steps('section_appGateway').lbSVCInfo.lbSVC]", "location": "[location()]", + "keyVaultName": "[steps('section_appGateway').appgwIngress.keyVaultName]", + "keyVaultResourceGroup": "[steps('section_appGateway').appgwIngress.keyVaultResourceGroup]", + "keyVaultSSLCertDataSecretName": "[steps('section_appGateway').appgwIngress.keyVaultSSLCertDataSecretName]", + "keyVaultSSLCertPasswordSecretName": "[steps('section_appGateway').appgwIngress.keyVaultSSLCertPasswordSecretName]", "managedServerPrefix": "[basics('basicsOptional').managedServerPrefix]", "ocrSSOPSW": "[basics('basicsRequired').ocrSSOPassword]", "ocrSSOUser": "[basics('basicsRequired').ocrSSOUserName]", + "servicePrincipal": "[steps('section_appGateway').appgwIngress.servicePrincipal]", "uploadAppPackage": "[bool(steps('section_aks').jeeAppInfo.uploadAppPackage)]", "wdtRuntimePassword": "[basics('basicsRequired').wdtRuntimePassword]", "wlsClusterSize": "[basics('basicsOptional').wlsClusterSize]", diff --git a/src/main/arm/scripts/appgw-helm-config.yaml.template b/src/main/arm/scripts/appgw-helm-config.yaml.template new file mode 100644 index 000000000..93f50fdb9 --- /dev/null +++ b/src/main/arm/scripts/appgw-helm-config.yaml.template @@ -0,0 +1,52 @@ +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +# Based on https://raw.githubusercontent.com/Azure/application-gateway-kubernetes-ingress/master/docs/examples/sample-helm-config.yaml + +# This file contains the essential configs for the ingress controller helm chart + +# Verbosity level of the App Gateway Ingress Controller +verbosityLevel: 3 + +################################################################################ +# Specify which application gateway the ingress controller will manage +# +appgw: + subscriptionId: @SUB_ID@ + resourceGroup: @APPGW_RG_NAME@ + name: @APPGW_NAME@ + usePrivateIP: false + + # Setting appgw.shared to "true" will create an AzureIngressProhibitedTarget CRD. + # This prohibits AGIC from applying config for any host/path. + # Use "kubectl get AzureIngressProhibitedTargets" to view and change this. + shared: false + +################################################################################ +# Specify which kubernetes namespace the ingress controller will watch +# Default value is "default" +# Leaving this variable out or setting it to blank or empty string would +# result in Ingress Controller observing all acessible namespaces. +# +kubernetes: + watchNamespace: @WATCH_NAMESPACE@ + +################################################################################ +# Specify the authentication with Azure Resource Manager +# +# Two authentication methods are available: +# - Option 1: AAD-Pod-Identity (https://github.com/Azure/aad-pod-identity) +# armAuth: +# type: aadPodIdentity +# identityResourceID: +# identityClientID: + +armAuth: + type: servicePrincipal + secretJSON: @SP_ENCODING_CREDENTIALS@ + +################################################################################ +# Specify if the cluster is RBAC enabled or not +rbac: + # Specifies whether RBAC resources should be created + create: true diff --git a/src/main/arm/scripts/appgw-ingress-clusterAdmin-roleBinding.yaml b/src/main/arm/scripts/appgw-ingress-clusterAdmin-roleBinding.yaml new file mode 100644 index 000000000..6bf39ce8e --- /dev/null +++ b/src/main/arm/scripts/appgw-ingress-clusterAdmin-roleBinding.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: ingress-azure-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: ingress-azure + namespace: default \ No newline at end of file diff --git a/src/main/arm/scripts/azure-ingress-appgateway.yaml.template b/src/main/arm/scripts/azure-ingress-appgateway.yaml.template new file mode 100644 index 000000000..354a07cd8 --- /dev/null +++ b/src/main/arm/scripts/azure-ingress-appgateway.yaml.template @@ -0,0 +1,21 @@ +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: @INGRESS_NAME@ + namespace: @NAMESPACE@ + annotations: + kubernetes.io/ingress.class: azure/application-gateway +spec: + rules: + - http: + paths: + - path: @PATH@ + pathType: Prefix + backend: + service: + name: @CLUSTER_SERVICE_NAME@ + port: + number: @TARGET_PORT@ diff --git a/src/main/arm/scripts/buildWLSDockerImage.sh b/src/main/arm/scripts/buildWLSDockerImage.sh index e10e1540b..d38a2e6b9 100644 --- a/src/main/arm/scripts/buildWLSDockerImage.sh +++ b/src/main/arm/scripts/buildWLSDockerImage.sh @@ -1,6 +1,8 @@ # Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +echo "Script ${0} starts" + #Function to output message to StdErr function echo_stderr() { echo "$@" >&2 diff --git a/src/main/arm/scripts/setupNetworking.sh b/src/main/arm/scripts/setupNetworking.sh new file mode 100644 index 000000000..a59c97eb5 --- /dev/null +++ b/src/main/arm/scripts/setupNetworking.sh @@ -0,0 +1,553 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +echo "Script ${0} starts" + +#Function to output message to stdout +function echo_stderr() { + echo "$@" >&2 + echo "$@" >>stdout +} + +function echo_stdout() { + echo "$@" >&2 + echo "$@" >>stdout +} + +function install_helm() { + # Install helm + browserURL=$(curl -s https://api.github.com/repos/helm/helm/releases/latest | + grep "browser_download_url.*linux-amd64.tar.gz.asc" | + cut -d : -f 2,3 | + tr -d \") + helmLatestVersion=${browserURL#*download\/} + helmLatestVersion=${helmLatestVersion%%\/helm*} + helmPackageName=helm-${helmLatestVersion}-linux-amd64.tar.gz + curl -m 120 -fL https://get.helm.sh/${helmPackageName} -o /tmp/${helmPackageName} + tar -zxvf /tmp/${helmPackageName} -C /tmp + mv /tmp/linux-amd64/helm /usr/local/bin/helm + echo "helm version" + helm version + validate_status "Finished installing helm." +} + +# Install latest kubectl and helm +function install_utilities() { + if [ -d "apps" ]; then + rm apps -f -r + fi + + mkdir apps + cd apps + + # Install kubectl + az aks install-cli + echo "kubectl version" + ret=$(kubectl --help) + validate_status ${ret} +} + +#Output value to deployment scripts +function output_result() { + echo ${adminConsoleEndpoint} + echo ${clusterEndpoint} + + result=$(jq -n -c \ + --arg adminEndpoint $adminConsoleEndpoint \ + --arg clusterEndpoint $clusterEndpoint \ + '{adminConsoleEndpoint: $adminEndpoint, clusterEndpoint: $clusterEndpoint}') + echo "result is: $result" + echo $result >$AZ_SCRIPTS_OUTPUT_PATH +} + +#Function to display usage message +function usage() { + echo_stdout "./setupNetworking.sh " + if [ $1 -eq 1 ]; then + exit 1 + fi +} + +#Validate teminal status with $?, exit with exception if errors happen. +function validate_status() { + if [ $? == 1 ]; then + echo_stderr "$@" + echo_stderr "Errors happen, exit 1." + exit 1 + else + echo_stdout "$@" + fi +} + +function waitfor_svc_completed() { + svcName=$1 + + attempts=0 + svcState="running" + while [ ! "$svcState" == "completed" ] && [ $attempts -lt ${perfSVCAttemps} ]; do + svcState="completed" + attempts=$((attempts + 1)) + echo Waiting for job completed...${attempts} + sleep ${perfRetryInterval} + + ret=$(kubectl get svc ${svcName} -n ${wlsDomainNS} | + grep -c "Running") + if [ -z "${ret}" ]; then + svcState="running" + fi + done +} + +#Function to validate input +function validate_input() { + if [[ -z "$aksClusterRGName" || -z "${aksClusterName}" ]]; then + echo_stderr "AKS cluster name and resource group name are required. " + usage 1 + fi + + if [[ -z "$wlsDomainName" || -z "${wlsDomainUID}" ]]; then + echo_stderr "WebLogic domain name and WebLogic domain UID are required. " + usage 1 + fi + + if [ -z "$lbSvcValues" ]; then + echo_stderr "lbSvcValues is required. " + usage 1 + fi + + if [ -z "$enableAppGWIngress" ]; then + echo_stderr "enableAppGWIngress is required. " + usage 1 + fi + + if [ -z "$subID" ]; then + echo_stderr "subID is required. " + usage 1 + fi + + if [ -z "$curRGName" ]; then + echo_stderr "curRGName is required. " + usage 1 + fi + + if [ -z "$appgwName" ]; then + echo_stderr "appgwName is required. " + usage 1 + fi + + if [ -z "$vnetName" ]; then + echo_stderr "vnetName is required. " + usage 1 + fi + + if [ -z "$spBase64String" ]; then + echo_stderr "spBase64String is required. " + usage 1 + fi + + if [ -z "$appgwForAdminServer" ]; then + echo_stderr "appgwForAdminServer is required. " + usage 1 + fi + + if [ -z "$enableCustomDNSAlias" ]; then + echo_stderr "enableCustomDNSAlias is required. " + usage 1 + fi + + if [[ -z "$dnsRGName" || -z "${dnsZoneName}" ]]; then + echo_stderr "dnsZoneName and dnsRGName are required. " + usage 1 + fi + + if [ -z "$dnsAdminLabel" ]; then + echo_stderr "dnsAdminLabel is required. " + usage 1 + fi + + if [ -z "$dnsClusterLabel" ]; then + echo_stderr "dnsClusterLabel is required. " + usage 1 + fi + + if [ -z "$appgwAlias" ]; then + echo_stderr "appgwAlias is required. " + usage 1 + fi +} + +function generate_admin_lb_definicion() { + cat <${scriptDir}/admin-server-lb.yaml +apiVersion: v1 +kind: Service +metadata: + name: ${adminServerLBSVCName} + namespace: ${wlsDomainNS} +EOF + + # to create internal load balancer service + if [[ "${enableInternalLB,,}" == "true" ]]; then + cat <>${scriptDir}/admin-server-lb.yaml + annotations: + service.beta.kubernetes.io/azure-load-balancer-internal: "true" +EOF + fi + + cat <>${scriptDir}/admin-server-lb.yaml +spec: + ports: + - name: default + port: ${adminLBPort} + protocol: TCP + targetPort: ${adminTargetPort} + selector: + weblogic.domainUID: ${wlsDomainUID} + weblogic.serverName: ${adminServerName} + sessionAffinity: None + type: LoadBalancer +EOF +} + +function generate_cluster_lb_definicion() { + cat <${scriptDir}/cluster-lb.yaml +apiVersion: v1 +kind: Service +metadata: + name: ${clusterLBSVCName} + namespace: ${wlsDomainNS} +EOF + + # to create internal load balancer service + if [[ "${enableInternalLB,,}" == "true" ]]; then + cat <>${scriptDir}/cluster-lb.yaml + annotations: + service.beta.kubernetes.io/azure-load-balancer-internal: "true" +EOF + fi + + cat <>${scriptDir}/cluster-lb.yaml +spec: + ports: + - name: default + port: ${clusterLBPort} + protocol: TCP + targetPort: ${clusterTargetPort} + selector: + weblogic.domainUID: ${wlsDomainUID} + weblogic.clusterName: ${clusterName} + sessionAffinity: None + type: LoadBalancer +EOF +} + +function query_admin_target_port() { + adminTargetPort=$(kubectl describe service ${svcAdminServer} -n ${wlsDomainNS} | grep 'TargetPort:' | tr -d -c 0-9) + validate_status "Query admin target port." + echo "Target port of ${adminServerName}: ${adminTargetPort}" +} + +function query_cluster_target_port() { + clusterTargetPort=$(kubectl describe service ${svcCluster} -n ${wlsDomainNS} | grep 'TargetPort:' | tr -d -c 0-9) + validate_status "Query cluster 1 target port." + echo "Target port of ${clusterName}: ${clusterTargetPort}" +} + +# Connect to AKS cluster +function connect_aks_cluster() { + az aks get-credentials --resource-group ${aksClusterRGName} --name ${aksClusterName} --overwrite-existing +} + +# create dns alias for lb service +function create_dns_A_record() { + if [ "${enableCustomDNSAlias,,}" == "true" ]; then + ipv4Addr=$1 + label=$2 + az network dns record-set a add-record --ipv4-address ${ipv4Addr} \ + --record-set-name ${label} \ + --resource-group ${dnsRGName} \ + --zone-name ${dnsZoneName} + fi +} + +# create dns alias for app gateway +function create_dns_CNAME_record() { + if [ "${enableCustomDNSAlias,,}" == "true" ]; then + + az network dns record-set cname create \ + -g ${dnsRGName} \ + -z ${dnsZoneName} \ + -n ${dnsClusterLabel} + + az network dns record-set cname set-record \ + -g ${dnsRGName} \ + -z ${dnsZoneName} \ + --cname ${appgwAlias} \ + --record-set-name ${dnsClusterLabel} + + if [[ ${appgwForAdminServer,,} == "true" ]]; then + az network dns record-set cname create \ + -g ${dnsRGName} \ + -z ${dnsZoneName} \ + -n ${dnsAdminLabel} + + az network dns record-set cname set-record \ + -g ${dnsRGName} \ + -z ${dnsZoneName} \ + --cname ${appgwAlias} \ + --record-set-name ${dnsAdminLabel} + fi + fi +} + +function create_svc_lb() { + # No lb svc inputs + if [[ "${lbSvcValues}" == "[]" ]]; then + return + fi + + query_admin_target_port + query_cluster_target_port + + # Parse lb svc input values + # Generate valid json + ret=$(echo $lbSvcValues | sed "s/\:/\\\"\:\\\"/g" | + sed "s/{/{\"/g" | + sed "s/}/\"}/g" | + sed "s/,/\",\"/g" | + sed "s/}\",\"{/},{/g" | + tr -d \(\)) + + cat <${scriptDir}/lbConfiguration.json +${ret} +EOF + + array=$(jq -r '.[] | "\(.colName),\(.colTarget),\(.colPort)"' ${scriptDir}/lbConfiguration.json) + for item in $array; do + # LB config for admin-server + target=$(cut -d',' -f2 <<<$item) + if [[ "${target}" == "adminServer" ]]; then + adminServerLBSVCNamePrefix=$(cut -d',' -f1 <<<$item) + adminServerLBSVCName="${adminServerLBSVCNamePrefix}-svc-lb-admin" + adminLBPort=$(cut -d',' -f3 <<<$item) + + generate_admin_lb_definicion + + kubectl apply -f ${scriptDir}/admin-server-lb.yaml + waitfor_svc_completed ${adminServerLBSVCName} + + adminServerEndpoint=$(kubectl get svc ${adminServerLBSVCName} -n ${wlsDomainNS} -o=jsonpath='{.status.loadBalancer.ingress[0].ip}:{.spec.ports[0].port}') + adminConsoleEndpoint="${adminServerEndpoint}/console" + + create_dns_A_record "${adminServerEndpoint%%:*}" ${dnsAdminLabel} + + if [ "${enableCustomDNSAlias,,}" == "true" ]; then + adminConsoleEndpoint="${dnsAdminLabel}.${dnsZoneName}:${adminServerEndpoint#*:}/console" + fi + else + clusterLBSVCNamePrefix=$(cut -d',' -f1 <<<$item) + clusterLBSVCName="${clusterLBSVCNamePrefix}-svc-lb-cluster" + clusterLBPort=$(cut -d',' -f3 <<<$item) + + generate_cluster_lb_definicion + + kubectl apply -f ${scriptDir}/cluster-lb.yaml + waitfor_svc_completed ${clusterLBSVCName} + + clusterEndpoint=$(kubectl get svc ${clusterLBSVCName} -n ${wlsDomainNS} -o=jsonpath='{.status.loadBalancer.ingress[0].ip}:{.spec.ports[0].port}') + + create_dns_A_record "${clusterEndpoint%%:*}" ${dnsClusterLabel} + + if [ "${enableCustomDNSAlias,,}" == "true" ]; then + clusterEndpoint="${dnsClusterLabel}.${dnsZoneName}:${clusterEndpoint#*:}/" + fi + fi + done +} + +# Create network peers for aks and appgw +function network_peers_aks_appgw() { + # To successfully peer two virtual networks command 'az network vnet peering create' must be called twice with the values + # for --vnet-name and --remote-vnet reversed. + aksLocation=$(az aks show --name ${aksClusterName} -g ${aksClusterRGName} -o tsv --query "location") + aksMCRGName="MC_${aksClusterRGName}_${aksClusterName}_${aksLocation}" + ret=$(az group exists ${aksMCRGName}) + if [ "${ret,,}" == "false" ]; then + echo_stderr "AKS namaged resource group ${aksMCRGName} does not exist." + exit 1 + fi + + aksNetWorkId=$(az resource list -g ${aksMCRGName} --resource-type Microsoft.Network/virtualNetworks -o tsv --query '[*].id') + aksNetworkName=$(az resource list -g ${aksMCRGName} --resource-type Microsoft.Network/virtualNetworks -o tsv --query '[*].name') + az network vnet peering create \ + --name aks-appgw-peer \ + --remote-vnet ${aksNetWorkId} \ + --resource-group ${curRGName} \ + --vnet-name ${vnetName} \ + --allow-vnet-access + validate_status "Create network peers for $aksNetWorkId and ${vnetName}." + + appgwNetworkId=$(az resource list -g ${curRGName} --name ${vnetName} -o tsv --query '[*].id') + az network vnet peering create \ + --name aks-appgw-peer \ + --remote-vnet ${appgwNetworkId} \ + --resource-group ${aksMCRGName} \ + --vnet-name ${aksNetworkName} \ + --allow-vnet-access + + validate_status "Create network peers for $aksNetWorkId and ${vnetName}." +} + +function create_appgw_ingress() { + if [[ "${enableAppGWIngress,,}" != "true" ]]; then + return + fi + + query_admin_target_port + query_cluster_target_port + network_peers_aks_appgw + + # create sa and bind cluster-admin role + kubectl apply -f ${scriptDir}/appgw-ingress-clusterAdmin-roleBinding.yaml + + # Keep the aad pod identity controller installation, may be used for CNI network usage + # Install aad pod identity controller + # https://github.com/Azure/aad-pod-identity + # latestAADPodIdentity=$(curl -s https://api.github.com/repos/Azure/aad-pod-identity/releases/latest \ + # | grep "browser_download_url.*deployment-rbac.yaml" \ + # | cut -d : -f 2,3 \ + # | tr -d \") + + # kubectl apply -f https://raw.githubusercontent.com/Azure/aad-pod-identity/v1.8.0/deploy/infra/deployment-rbac.yaml + + install_helm + + helm repo add application-gateway-kubernetes-ingress ${appgwIngressHelmRepo} + helm repo update + + # Keep the identity parsing, may be used for CNI network usage + # {type:UserAssigned,userAssignedIdentities:{/subscriptions/05887623-95c5-4e50-a71c-6e1c738794e2/resourceGroups/haiche-identity/providers/Microsoft.ManagedIdentity/userAssignedIdentities/wls-aks-mvp:{}}} + # identityId=${identity#*userAssignedIdentities:\{} + # identityId=${identityId%%:\{\}*} + # query identity client id + # identityClientId=$(az identity show --ids ${identityId} -o tsv --query "clientId") + + # generate helm config + customAppgwHelmConfig=${scriptDir}/appgw-helm-config.yaml + cp ${scriptDir}/appgw-helm-config.yaml.template ${customAppgwHelmConfig} + subID=${subID#*\/subscriptions\/} + sed -i -e "s:@SUB_ID@:${subID}:g" ${customAppgwHelmConfig} + sed -i -e "s:@APPGW_RG_NAME@:${curRGName}:g" ${customAppgwHelmConfig} + sed -i -e "s:@APPGW_NAME@:${appgwName}:g" ${customAppgwHelmConfig} + sed -i -e "s:@WATCH_NAMESPACE@:${wlsDomainNS}:g" ${customAppgwHelmConfig} + # sed -i -e "s:@INDENTITY_ID@:${identityId}:g" ${customAppgwHelmConfig} + # sed -i -e "s:@IDENTITY_CLIENT_ID@:${identityClientId}:g" ${customAppgwHelmConfig} + sed -i -e "s:@SP_ENCODING_CREDENTIALS@:${spBase64String}:g" ${customAppgwHelmConfig} + + helm install ingress-azure \ + -f ${customAppgwHelmConfig} \ + application-gateway-kubernetes-ingress/ingress-azure \ + --version ${azureAppgwIngressVersion} + + validate_status "Install app gateway ingress controller." + + attempts=0 + podState="running" + while [ ! "$podState" == "completed" ] && [ $attempts -lt ${perfPodAttemps} ]; do + podState="completed" + attempts=$((attempts + 1)) + echo Waiting for Pod running...${attempts} + sleep ${perfRetryInterval} + + ret=$(kubectl get pod | grep "ingress-azure") + if [ -z "${ret}" ]; then + podState="running" + + if [ $attempts -ge ${perfPodAttemps} ]; then + echo_stderr "Failed to install app gateway ingress controller." + exit 1 + fi + fi + done + + # generate ingress svc config for cluster + appgwIngressSvcConfig=${scriptDir}/azure-ingress-appgateway-cluster.yaml + cp ${scriptDir}/azure-ingress-appgateway.yaml.template ${appgwIngressSvcConfig} + ingressSvcName="${wlsDomainUID}-cluster-appgw-ingress-svc" + sed -i -e "s:@PATH@:\/:g" ${appgwIngressSvcConfig} + sed -i -e "s:@INGRESS_NAME@:${ingressSvcName}:g" ${appgwIngressSvcConfig} + sed -i -e "s:@NAMESPACE@:${wlsDomainNS}:g" ${appgwIngressSvcConfig} + sed -i -e "s:@CLUSTER_SERVICE_NAME@:${svcCluster}:g" ${appgwIngressSvcConfig} + sed -i -e "s:@TARGET_PORT@:${clusterTargetPort}:g" ${appgwIngressSvcConfig} + + kubectl apply -f ${appgwIngressSvcConfig} + validate_status "Create appgw ingress svc." + waitfor_svc_completed ${ingressSvcName} + + if [[ ${appgwForAdminServer,,} == "true" ]]; then + # generate ingress svc config for admin server + appgwIngressSvcConfig=${scriptDir}/azure-ingress-appgateway-admin.yaml + cp ${scriptDir}/azure-ingress-appgateway.yaml.template ${appgwIngressSvcConfig} + ingressSvcName="${wlsDomainUID}-admin-appgw-ingress-svc" + sed -i -e "s:@PATH@:\/console*:g" ${appgwIngressSvcConfig} + sed -i -e "s:@INGRESS_NAME@:${ingressSvcName}:g" ${appgwIngressSvcConfig} + sed -i -e "s:@NAMESPACE@:${wlsDomainNS}:g" ${appgwIngressSvcConfig} + sed -i -e "s:@CLUSTER_SERVICE_NAME@:${svcAdminServer}:g" ${appgwIngressSvcConfig} + sed -i -e "s:@TARGET_PORT@:${adminTargetPort}:g" ${appgwIngressSvcConfig} + + kubectl apply -f ${appgwIngressSvcConfig} + validate_status "Create appgw ingress svc." + waitfor_svc_completed ${ingressSvcName} + fi + + create_dns_CNAME_record +} + +# Main script +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +export aksClusterRGName=$1 +export aksClusterName=$2 +export wlsDomainName=$3 +export wlsDomainUID=$4 +export lbSvcValues=$5 +export enableAppGWIngress=$6 +export subID=$7 +export curRGName=${8} +export appgwName=${9} +export vnetName=${10} +export spBase64String=${11} +export appgwForAdminServer=${12} +export enableCustomDNSAlias=${13} +export dnsRGName=${14} +export dnsZoneName=${15} +export dnsAdminLabel=${16} +export dnsClusterLabel=${17} +export appgwAlias=${18} + +export adminServerName="admin-server" +export adminConsoleEndpoint="null" +export appgwIngressHelmRepo="https://appgwingress.blob.core.windows.net/ingress-azure-helm-package/" +export clusterName="cluster-1" +export clusterEndpoint="null" +export azureAppgwIngressVersion="1.4.0" +export perfRetryInterval=30 # seconds +export perfPodAttemps=5 +export perfSVCAttemps=10 +export svcAdminServer="${wlsDomainUID}-${adminServerName}" +export svcCluster="${wlsDomainUID}-cluster-${clusterName}" +export wlsDomainNS="${wlsDomainUID}-ns" + +echo $lbSvcValues + +validate_input + +install_utilities + +connect_aks_cluster + +create_svc_lb + +create_appgw_ingress + +output_result diff --git a/src/main/arm/scripts/setupWLSDomain.sh b/src/main/arm/scripts/setupWLSDomain.sh index 61019a6ec..170aef4ef 100644 --- a/src/main/arm/scripts/setupWLSDomain.sh +++ b/src/main/arm/scripts/setupWLSDomain.sh @@ -1,7 +1,7 @@ # Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -echo "Script starts" +echo "Script ${0} starts" #Function to output message to stdout function echo_stderr() { @@ -149,13 +149,12 @@ function install_utilities() { helmLatestVersion=${browserURL#*download\/} helmLatestVersion=${helmLatestVersion%%\/helm*} helmPackageName=helm-${helmLatestVersion}-linux-amd64.tar.gz - curl -LO -s https://get.helm.sh/${helmPackageName} - tar -zxvf ${helmPackageName} - chmod +x linux-amd64/helm - mv linux-amd64/helm /usr/local/bin/helm + curl -m 120 -fL https://get.helm.sh/${helmPackageName} -o /tmp/${helmPackageName} + tar -zxvf /tmp/${helmPackageName} -C /tmp + mv /tmp/linux-amd64/helm /usr/local/bin/helm echo "helm version" - ret=$(helm version) - validate_status ${ret} + helm version + validate_status "Finished installing helm." echo "az cli version" ret=$(az --version) @@ -218,7 +217,7 @@ function query_acr_credentials() { # * push the image to ACR function build_docker_image() { # Create vm to build docker image - vmName="VM-UBUNTU" + vmName="VM-UBUNTU-WLS-AKS-$(date +%s)" # MICROSOFT_INTERNAL # Specify tag 'SkipASMAzSecPack' to skip policy 'linuxazuresecuritypackautodeployiaas_1.6' diff --git a/src/main/bicep/mainTemplate.bicep b/src/main/bicep/mainTemplate.bicep index 88efdb5a1..64672aa3e 100644 --- a/src/main/bicep/mainTemplate.bicep +++ b/src/main/bicep/mainTemplate.bicep @@ -24,7 +24,7 @@ param aciResourcePermissions bool = true param aciRetentionInDays int = 120 @description('Pricing tier: PerGB2018 or legacy tiers (Free, Standalone, PerNode, Standard or Premium) which are not available to all customers.') param aciWorkspaceSku string = 'pergb2018' -param acrName string = '' +param acrName string = 'acr-contoso' @maxLength(12) @minLength(1) @description('The name for this node pool. Node pool must contain only lowercase letters and numbers. For Linux node pools the name cannot be longer than 12 characters.') @@ -38,11 +38,27 @@ param aksAgentPoolVMSize string = 'Standard_DS2_v2' @description('Prefix for cluster name. Only The name can contain only letters, numbers, underscores and hyphens. The name must start with letter or number.') param aksClusterNamePrefix string = 'wlsonaks' @description('Resource group name of an existing AKS cluster.') -param aksClusterRGName string = '' +param aksClusterRGName string = 'aks-contoso-rg' @description('Name of an existing AKS cluster.') -param aksClusterName string = '' +param aksClusterName string = 'aks-contoso' @description('The AKS version.') param aksVersion string = 'default' +@allowed([ + 'haveCert' + 'haveKeyVault' + 'generateCert' +]) +@description('Three scenarios we support for deploying app gateway') +param appGatewayCertificateOption string = 'haveCert' +@description('Public IP Name for the Application Gateway') +param appGatewayPublicIPAddressName string = 'gwip' +@description('The one-line, base64 string of the SSL certificate data.') +param appGatewaySSLCertData string = 'appgw-ssl-data' +@secure() +@description('The value of the password for the SSL Certificate') +param appGatewaySSLCertPassword string = newGuid() +@description('Create Application Gateway ingress for admin console.') +param appgwForAdminServer bool = true @description('Urls of Java EE application packages.') param appPackageUrls array = [] @description('The number of managed server to start.') @@ -51,13 +67,38 @@ param appReplicas int = 2 param createACR bool = false @description('true to create a new AKS cluster.') param createAKSCluster bool = true +@description('If true, the template will update records to the existing DNS Zone. If false, the template will create a new DNS Zone.') +param createDNSZone bool = false +@description('DNS prefix for ApplicationGateway') +param dnsNameforApplicationGateway string = 'wlsgw' +@description('Azure DNS Zone name.') +param dnszoneAdminConsoleLabel string = 'admin' +@description('Specify a label used to generate subdomain of Application Gateway. The final subdomain name will be label.dnszoneName, e.g. applications.contoso.xyz') +param dnszoneAppGatewayLabel string = 'www' +param dnszoneName string = 'contoso.xyz' +param dnszoneRGName string = 'dns-contoso-rg' +@description('true to set up Application Gateway ingress.') +param enableAppGWIngress bool = false @description('In addition to the CPU and memory metrics included in AKS by default, you can enable Container Insights for more comprehensive data on the overall performance and health of your cluster. Billing is based on data ingestion and retention settings.') param enableAzureMonitoring bool = false @description('true to create persistent volume using file share.') param enableAzureFileShare bool = false +param enableDNSConfiguration bool = false @description('An user assigned managed identity. Make sure the identity has permission to create/update/delete/list Azure resources.') param identity object +@description('Existing Key Vault Name') +param keyVaultName string = 'kv-contoso' +@description('Resource group name in current subscription containing the KeyVault') +param keyVaultResourceGroup string = 'kv-contoso-rg' +@description('Price tier for Key Vault.') +param keyVaultSku string = 'Standard' +@description('The name of the secret in the specified KeyVault whose value is the SSL Certificate Data') +param keyVaultSSLCertDataSecretName string = 'kv-ssl-data' +@description('The name of the secret in the specified KeyVault whose value is the password for the SSL Certificate') +param keyVaultSSLCertPasswordSecretName string = 'kv-ssl-psw' param location string = 'eastus' +@description('Object array to define Load Balancer service, each object must include service name, service target[admin-server or cluster-1], port.') +param lbSvcValues array = [] @description('Name prefix of managed server.') param managedServerPrefix string = 'managed-server' @secure() @@ -65,8 +106,12 @@ param managedServerPrefix string = 'managed-server' param ocrSSOPSW string @description('User name of Oracle SSO account.') param ocrSSOUser string +@secure() +@description('Base64 string of service principal. use the command to generate a testing string: az ad sp create-for-rbac --sdk-auth | base64 -w0') +param servicePrincipal string = newGuid() @description('ture to upload Java EE applications and deploy the applications to WebLogic domain.') param uploadAppPackage bool = false +param utcValue string = utcNow() @secure() @description('Password for model WebLogic Deploy Tooling runtime encrytion.') param wdtRuntimePassword string @@ -87,8 +132,13 @@ param wlsPassword string @description('User name for WebLogic Administrator.') param wlsUserName string = 'weblogic' +var const_appGatewaySSLCertOptionHaveCert = 'haveCert' +var const_appGatewaySSLCertOptionHaveKeyVault = 'haveKeyVault' +var const_azureSubjectName = '${format('{0}.{1}.{2}', name_domainLabelforApplicationGateway, location, '.cloudapp.azure.com')}' var name_defaultPidDeployment = 'pid' - +var name_dnsNameforApplicationGateway = '${concat(dnsNameforApplicationGateway, take(utcValue, 6))}' +var name_domainLabelforApplicationGateway = '${take(concat(name_dnsNameforApplicationGateway, '-', toLower(resourceGroup().name), '-', toLower(wlsDomainName)), 63)}' +var name_keyVaultName = '${take(concat('wls-kv', uniqueString(utcValue)), 24)}' /* * Beginning of the offer deployment */ @@ -138,6 +188,61 @@ module wlsDomainDeployment 'modules/setupWebLogicCluster.bicep' = { } } +module keyvaultDeployment 'modules/_azure-resoruces/_keyvaultAdapter.bicep' = if (enableAppGWIngress && (appGatewayCertificateOption != const_appGatewaySSLCertOptionHaveKeyVault)) { + name: 'keyvault-deployment' + params: { + certificateDataValue: appGatewaySSLCertData + certificatePasswordValue: appGatewaySSLCertPassword + identity: identity + sku: keyVaultSku + subjectName: format('CN={0}', enableDNSConfiguration ? format('{0}.{1}', dnsNameforApplicationGateway, dnszoneName) : const_azureSubjectName) + useExistingAppGatewaySSLCertificate: (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveCert) ? true : false + keyVaultName: name_keyVaultName + } + dependsOn: [ + wlsDomainDeployment + ] +} + +module networkingDeployment 'modules/networking.bicep' = { + name: 'networking-deployment' + params: { + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + _pidNetworkingEnd: pids.outputs.networkingEnd == '' ? name_defaultPidDeployment : pids.outputs.networkingEnd + _pidNetworkingStart: pids.outputs.networkingStart == '' ? name_defaultPidDeployment : pids.outputs.networkingStart + _pidAppgwEnd: pids.outputs.appgwEnd == '' ? name_defaultPidDeployment : pids.outputs.appgwEnd + _pidAppgwStart: pids.outputs.appgwStart == '' ? name_defaultPidDeployment : pids.outputs.appgwStart + aksClusterRGName: wlsDomainDeployment.outputs.aksClusterRGName + aksClusterName: wlsDomainDeployment.outputs.aksClusterName + appGatewayCertificateOption: appGatewayCertificateOption + appGatewayPublicIPAddressName: appGatewayPublicIPAddressName + appgwForAdminServer: appgwForAdminServer + createDNSZone: createDNSZone + dnsNameforApplicationGateway: dnsNameforApplicationGateway + dnszoneAdminConsoleLabel: dnszoneAdminConsoleLabel + dnszoneAppGatewayLabel: dnszoneAppGatewayLabel + dnszoneName: dnszoneName + dnszoneRGName: dnszoneRGName + enableAppGWIngress: enableAppGWIngress + enableDNSConfiguration: enableDNSConfiguration + identity: identity + keyVaultName: (! enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultName : keyvaultDeployment.outputs.keyVaultName + keyVaultResourceGroup: (! enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultResourceGroup : resourceGroup().name + keyVaultSSLCertDataSecretName: (! enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultSSLCertDataSecretName : keyvaultDeployment.outputs.sslCertDataSecretName + keyVaultSSLCertPasswordSecretName: (! enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultSSLCertPasswordSecretName : keyvaultDeployment.outputs.sslCertPwdSecretName + location: location + lbSvcValues: lbSvcValues + servicePrincipal: servicePrincipal + wlsDomainName: wlsDomainName + wlsDomainUID: wlsDomainUID + } +} + output aksClusterName string = wlsDomainDeployment.outputs.aksClusterName -output adminServerUrl string = wlsDomainDeployment.outputs.adminServerUrl -output clusterSVCUrl string = wlsDomainDeployment.outputs.clusterSVCUrl +output adminConsoleInternalUrl string = wlsDomainDeployment.outputs.adminServerUrl +output adminConsoleExternalUrl string = networkingDeployment.outputs.adminConsoleExternalUrl +output adminConsoleExternalSecuredUrl string = networkingDeployment.outputs.adminConsoleExternalSecuredUrl +output clusterInternalUrl string = wlsDomainDeployment.outputs.clusterSVCUrl +output clusterExternalUrl string = networkingDeployment.outputs.clusterExternalUrl +output clusterExternalSecuredURL string = networkingDeployment.outputs.clusterExternalSecuredURL diff --git a/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep b/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep new file mode 100644 index 000000000..b4c3db6c0 --- /dev/null +++ b/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep @@ -0,0 +1,247 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +@secure() +param appGatewaySSLCertificateData string = 'MIIKQQIBAzCCCgcGCSqGSIb3DQEHAaCCCfgEggn0MIIJ8DCCBKcGCSqGSIb3DQEHBqCCBJgwggSUAgEAMIIEjQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI6AB+FBLZ6zMCAggAgIIEYN6eVnBIdbhS89C6P7zd76at+tOhNXIAIdjdmpXxtS9MhAGTlH1iq4mlHNmSwgFUtHWi+QkUXr00Xi/+t8LZzCQPh4vAUVZVRJ2Yj0gA0VdIB+bBGA1wou93VJ1zr6PQpzhHRiiJ0eNFR0rZwmNPX44KMwZcbDVt+7qqgwItP6tIy3G6a+DoqUjRtBbgY9XQKwDVV/NcQ88tkGkDEWLVhTPgFOV1H47qugqKYzNDiegqd7osdDKY/f4vXz1t5HLnEfm2UxvPzHZD/xiMlZ/cnk7R40c4JXhjKTR3DQ0J+TZKW94pvDYz+HixiUV5/6Yw3O6SKhTduEhzhVO0yYh5msfBC86nz4bvt35Dy/KcaqPOFPbJ51uftO5lDHLMXX3ICypNQrIkFSfUafgFRhRnCMg+CBc/yaapY8if/ZPtWW530bk/uKL6CZXOqgGnGUsRvveRfum3rbLyduMgDGBsXM/dLmDVqCSECKSzxneraEaX5VOtQokk8vRx+clv0XR0LTG9iSN9Tez/MfnS6Ammh0iXqQWhYdDWLtGoyIEq2U4DMlIpvUyi5r1KrZwG0mJsyTnlFxNPXcgvA1LPlsvD5EBcpQTBtUL04Apg0W0xmXj8kfCzrBGUR8YTBCL0A6mH/a63t+1OBIZeYBCuWKDGr/6FXkkfS1XHM8WGGoor+m/rc9iThAj7c3KoA8i/G7hYrsXP16ranBb9kVNmKgQ0uRKPvx1jNCaewp7cEFXs7L/+TqtTOc/UMExeGu3kUafbL+4jTO0+/kF/F1aYMzPgO0T9Fg5XNDdwwfuoXfM21Qnn5JFH8oEfGFxzZeDot9PgBOj0ekp7dd1KI72uIgDVn0S/BNTw+DJpR2UhKR3TGzWL/TeDe1cDf5BBHZ66d5HIg7g5oNcGIIW5YTAhbDKkNdP5+ACRxD5KShSXsKDcAqQzqoJObrN6v0tr2FCu14F01RZAI3C6ROwwLsX4g+BcLlU+Gj8lvZIe9U+XNsdP8HMOhRklzuVYe/ifCGCJuRc8Jikm09jqUUJwacjvWeTP/VYkpGTWoi9nwCSDdsWicM5ouoc2eJWubFGjWHqsCoCJoXlHEMxBVV5KTatpU6cUW4CMrbqrCNXshnD/7kSN442ZfNcWgC8rsM8rloCS9feTA3mE9s9XrUelrMj7FhaOX7krJYaE8w22F+p8wBc41JY5tggMETpi5KyDzt+SDgmC5hOLEr+LEExYF7GCAUJRDJB7qJfjCA2ctqwJzfEejUjqt5HpHtcC7Qf7qACCgLmHrHSX6o9/urLVZsGnxMhadm9MNuSSW0z3od5b1b6XW3PfOSXqUZykv4ooCmPgkCIVAYoKeG2rwHAhKJ/QhZYXQ2zF34SYEO//hztGSzQKjivWhR2tRa/dCxKR/jrKnBUbedwtRD5LCWTef8rznmdH2wOCkS4KDGYRHqCWS8qr3TywkR8RsYMeZSBba9yoRiC2+jyush4DGyV+mBYXe9LpzuswggVBBgkqhkiG9w0BBwGgggUyBIIFLjCCBSowggUmBgsqhkiG9w0BDAoBAqCCBO4wggTqMBwGCiqGSIb3DQEMAQMwDgQIgPb003LlbnACAggABIIEyMsnCKRj/B1Rx87OT3EYs7IDM81qf8lqVMPAn2Z9m/ARMu+pOBzB5uY9+8FtL+XKd67WnCk/YxErB+fG/1WJHhOAj/DrnFYObz/FQU8ynkrshlDZvhj3IWBQoC2dO8aC13jPy8lyexony3tJMBNZblpLFJF4xsucDa8P3ROsLU7HhZel0LbiYUNIBC5ZRkyVPgG3R+H8iJTR5zTNR3d8gAwmOnlZAi16YOAnYdHrQZ4z29I8l15pY3I3dHo1A62T8jF+YT3+4EyekmD/FkcnxC0CdZ0OrndB+qnrOAnSmCNZ0oozwhvo/S4TT0pOaPBlAZtXE4WRtN0p12L4Dj7Kjbp1hq4CpxjaOq2Q2Y8D+RRgBb18JybYJ85NjfBAMMyVw3QJ09PNG56aYKAGyvrdKYcod5/ycPuLrMQKJmx5AlBzY0aR2MXxOqNBQ/cJDRyirLOAQIN6/7PH0CIlWp76u3EL8OO0fRFhrfbBsuKUoioR6AS518SprrJ3BXQv4cJz+8TsvlZWfM5XkdbFYfCqiCInLlNc7OkYC+H1vch2ResjdEodwqrFimogF0CuxQycgYf83H0aMWpb7kSa3LpcSSE/A9ogK9Rx3e/HmLvbZGzmcyA01D8dDhvqhJscnVBiPPCue6PAmM+RHoU7ma0+m7zk1NdfAtr0MMweqsLAN9U2Z9SCG+H9zMqiWmT6xsg+WhPMwc18W75WOlc6CJ6wCM0clx1bzFIu0jRKab78NLCjTODfRH0p0Sv/SIuJai4xJZCIFRWvMQaQL0Fc0b5x0GFD1ljJM15SMU3cv9/T+rzFgMweIU9gIi9CEZHnzTnp0zQXx3OTv+7ptQ+uqKpKvTyeR4FbDhn8hMX6LMeAnsyB9ZWX+TnKBQrYwjjmmbxcWOQtF9qYWR5dDQTFtY/DFn8r3rnU2DgO5Xe/n7pwDV6oBJ3DO6vhjpZZpsC2r9TTVLJQeK7LWzH2TNvC6vQbGFNLKMRiq5b8kdm2Kq1kiY+kzloy+uRiUf7JNxWDi0uSUUzEQlWP59a+QQ87clrFV4604wny/tHGZCoh6efuZipqT79bPoCVoy4GNylNjcmgrcq6oXJq7vnqbQl2H3/ECRlRg3KRv8lN5WJVKMLhogCy0q0BCoAOCxzaW5qip3n3Pz5OEOEC6WQAUH6U4ceSr+K3ZdcofAcOoRHVNwHcMp1HwflMB6JBo08yx4RrVPrYrkoCdZPRSpC7KSdWhSPhH4+jhgGgaYc90qFxJwRX6TQemfRf7s3EnEk4FGGzU1FYbItRTAJbPzEJIe58ndfzSn/NfoqJQWLv7K4BBYBKUKW0ArJ9Oe4OmPlp/be/FqTM4npZab7zQoeV7pvZmaFg7/dJUBxcTVZBX5eIwebK+zZSSinoT0jDVQgiXF8aV+/rXsCWpJDlTGZGgMsp9bZThHR/kYC1LdVw7qhr0bbnvVjwMn/EDHKVFRhspEF1plt9sTJFY0wsZG2984NPdL+9DfUF2n6xPgkqRg/qipa0NNIODzFNnnx6F1a4fw0U2geELx6rgPJ79rtvwz6kT3KsoV33E+9PMDmTDooKrYwk2Sf95OgLMCGCvJAHtH+0Ts2fDYu7p+EijoleJH7LdOFhgr3qqhYlYP2HHTElMCMGCSqGSIb3DQEJFTEWBBQ35ys3avr+k99lD2b1RqD5mQieXjAxMCEwCQYFKw4DAhoFAAQUEfKwNxwomTOTg32dc3hh5Qj4GFYECFd/2NISLDEkAgIIAA==' +@secure() +param appGatewaySSLCertificatePassword string = 'wlsEng@aug2019' +@description('DNS for ApplicationGateway') +param dnsNameforApplicationGateway string = take('wlsgw${uniqueString(utcValue)}-${toLower(resourceGroup().name)}', 63) +@description('Public IP Name for the Application Gateway') +param gatewayPublicIPAddressName string = 'gwip' +param utcValue string = utcNow() + +var const_subnetAddressPrefix = '172.16.0.0/28' +var const_virtualNetworkAddressPrefix = '172.16.0.0/24' +var name_appGateway = 'appgw${uniqueString(utcValue)}' +var name_appGatewayCertificate = 'appGwSslCertificate' +var name_appGatewaySubnet = 'appGatewaySubnet' +var name_backendAddressPool = 'myGatewayBackendPool' +var name_frontEndIPConfig = 'appGwPublicFrontendIp' +var name_httpListener = 'HTTPListener' +var name_httpPort = 'httpport' +var name_httpSetting = 'myHTTPSetting' +var name_httpsListener = 'HTTPSListener' +var name_httpsPort = 'httpsport' +var name_nsg = 'nsg${uniqueString(utcValue)}' +var name_virtualNetwork = 'vnet${uniqueString(utcValue)}' +var ref_appGatewaySubnet = resourceId('Microsoft.Network/virtualNetworks/subnets', name_virtualNetwork, name_appGatewaySubnet) +var ref_backendAddressPool = resourceId('Microsoft.Network/applicationGateways/backendAddressPools', name_appGateway, name_backendAddressPool) +var ref_backendHttpSettings = resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', name_appGateway, name_httpSetting) +var ref_frontendHTTPPort = resourceId('Microsoft.Network/applicationGateways/frontendPorts', name_appGateway, name_httpPort) +var ref_frontendHTTPSPort = resourceId('Microsoft.Network/applicationGateways/frontendPorts', name_appGateway, name_httpsPort) +var ref_frontendIPConfiguration = resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', name_appGateway, name_frontEndIPConfig) +var ref_httpListener = resourceId('Microsoft.Network/applicationGateways/httpListeners', name_appGateway, name_httpListener) +var ref_httpsListener = resourceId('Microsoft.Network/applicationGateways/httpListeners', name_appGateway, name_httpsListener) +var ref_sslCertificate = resourceId('Microsoft.Network/applicationGateways/sslCertificates', name_appGateway, name_appGatewayCertificate) + +resource nsg 'Microsoft.Network/networkSecurityGroups@2020-07-01' = { + name: name_nsg + location: resourceGroup().location + properties: { + securityRules: [ + { + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '65200-65535' + sourceAddressPrefix: 'GatewayManager' + destinationAddressPrefix: '*' + access: 'Allow' + priority: 500 + direction: 'Inbound' + } + name: 'ALLOW_APPGW' + } + ] + } +} + +resource vnet 'Microsoft.Network/virtualNetworks@2020-07-01' = { + name: name_virtualNetwork + location: resourceGroup().location + properties: { + addressSpace: { + addressPrefixes: [ + const_virtualNetworkAddressPrefix + ] + } + subnets: [ + { + name: name_appGatewaySubnet + properties: { + addressPrefix: const_subnetAddressPrefix + networkSecurityGroup: { + id: nsg.id + } + } + } + ] + } + dependsOn: [ + nsg + ] +} + +resource gatewayPublicIP 'Microsoft.Network/publicIPAddresses@2020-07-01' = { + name: gatewayPublicIPAddressName + sku: { + name: 'Standard' + } + location: resourceGroup().location + properties: { + publicIPAllocationMethod: 'Static' + dnsSettings: { + domainNameLabel: dnsNameforApplicationGateway + } + } +} + +resource appGateway 'Microsoft.Network/applicationGateways@2020-07-01' = { + name: name_appGateway + location: resourceGroup().location + tags: { + 'managed-by-k8s-ingress': 'true' + } + properties: { + sku: { + name: 'WAF_v2' + tier: 'WAF_v2' + } + gatewayIPConfigurations: [ + { + name: 'appGatewayIpConfig' + properties: { + subnet: { + id: ref_appGatewaySubnet + } + } + } + ] + frontendIPConfigurations: [ + { + name: name_frontEndIPConfig + properties: { + publicIPAddress: { + id: gatewayPublicIP.id + } + } + } + ] + frontendPorts: [ + { + name: name_httpPort + properties: { + port: 80 + } + } + { + name: name_httpsPort + properties: { + port: 443 + } + } + ] + sslCertificates: [ + { + name: 'appGwSslCertificate' + properties: { + data: appGatewaySSLCertificateData + password: appGatewaySSLCertificatePassword + } + } + ] + backendAddressPools: [ + { + name: 'myGatewayBackendPool' + properties: { + backendAddresses: [] + } + } + ] + httpListeners: [ + { + name: name_httpListener + properties: { + protocol: 'Http' + frontendIPConfiguration: { + id: ref_frontendIPConfiguration + } + frontendPort: { + id: ref_frontendHTTPPort + } + } + } + { + name: name_httpsListener + properties: { + frontendIPConfiguration: { + id: ref_frontendIPConfiguration + } + frontendPort: { + id: ref_frontendHTTPSPort + } + protocol: 'Https' + requireServerNameIndication: false + sslCertificate: { + id: ref_sslCertificate + } + } + } + ] + backendHttpSettingsCollection: [ + { + name: name_httpSetting + properties: { + port: 80 + protocol: 'Http' + } + } + ] + requestRoutingRules: [ + { + name: 'HTTPRoutingRule' + properties: { + httpListener: { + id: ref_httpListener + } + backendAddressPool: { + id: ref_backendAddressPool + } + backendHttpSettings: { + id: ref_backendHttpSettings + } + } + } + { + name: 'HTTPSRoutingRule' + properties: { + ruleType: 'Basic' + httpListener: { + id: ref_httpsListener + } + backendAddressPool: { + id: ref_backendAddressPool + } + backendHttpSettings: { + id: ref_backendHttpSettings + } + } + } + ] + enableHttp2: false + autoscaleConfiguration: { + minCapacity: 2 + maxCapacity: 3 + } + } + dependsOn: [ + vnet + ] +} + +output appGatewayAlias string = reference(gatewayPublicIP.id).dnsSettings.fqdn +output appGatewayName string = name_appGateway +output appGatewayURL string = 'http://${reference(gatewayPublicIP.id).dnsSettings.fqdn}/' +output appGatewaySecuredURL string = 'https://${reference(gatewayPublicIP.id).dnsSettings.fqdn}/' +output vnetName string = name_virtualNetwork diff --git a/src/main/bicep/modules/_azure-resoruces/_dnsZones.bicep b/src/main/bicep/modules/_azure-resoruces/_dnsZones.bicep new file mode 100644 index 000000000..ae220178f --- /dev/null +++ b/src/main/bicep/modules/_azure-resoruces/_dnsZones.bicep @@ -0,0 +1,13 @@ +@description('Azure DNS Zone name.') +param dnszoneName string + +@description('Location for all resources.') +param location string + +resource dnszoneName_resource 'Microsoft.Network/dnszones@2018-05-01' = { + name: dnszoneName + location: location + properties: { + zoneType: 'Public' + } +} diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep new file mode 100644 index 000000000..619cfa4c5 --- /dev/null +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep @@ -0,0 +1,61 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +@description('Secret name of certificate data.') +param certificateDataName string + +@description('Certificate data to store in the secret') +param certificateDataValue string + +@description('Secret name of certificate password.') +param certificatePasswordName string + +@description('Certificate password to store in the secret') +param certificatePasswordValue string + +@description('Property to specify whether Azure Resource Manager is permitted to retrieve secrets from the key vault.') +param enabledForTemplateDeployment bool = true + +@description('Name of the vault') +param name string + +@description('Price tier for Key Vault.') +param sku string + +resource keyvault 'Microsoft.KeyVault/vaults@2019-09-01' = { + name: name + location: resourceGroup().location + properties: { + enabledForTemplateDeployment: enabledForTemplateDeployment + sku: { + name: sku + family: 'A' + } + accessPolicies: [] + tenantId: subscription().tenantId + } +} + +resource secretForCertificate 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { + name: '${name}/${certificateDataName}' + properties: { + value: certificateDataValue + } + dependsOn: [ + keyvault + ] +} + +resource secretForCertPassword 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { + name: '${name}/${certificatePasswordName}' + properties: { + value: certificatePasswordValue + } + dependsOn: [ + keyvault + ] +} + +output keyVaultName string = name +output sslCertDataSecretName string = certificateDataName +output sslCertPwdSecretName string = certificatePasswordName diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep new file mode 100644 index 000000000..49a6f94d0 --- /dev/null +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep @@ -0,0 +1,77 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +@description('Managed identity to be used for the deployment script. Currently, only user-assigned MSI is supported.') +param identity object + +@description('Used to name the new Azure Key Vault resoure.') +param keyVaultName string = 'wls-kv-${uniqueString(utcValue)}' + +@description('Access permission of the key vault, will applied to all access policies.') +param permission object = { + certificates: [ + 'get' + 'list' + 'update' + 'create' + ] +} + +@description('Used to name the new certificate resource.') +param secretName string = 'mySelfSignedCertificate' + +@description('Price tier for Key Vault.') +param sku string = 'Standard' + +@description('Subject name to create a new certificate, example: \'CN=contoso.com\'.') +param subjectName string +param utcValue string = utcNow() + +var const_identityId = '${substring(string(identity.userAssignedIdentities), indexOf(string(identity.userAssignedIdentities), '"') + 1, lastIndexOf(string(identity.userAssignedIdentities), '"') - (indexOf(string(identity.userAssignedIdentities), '"') + 1))}' + +resource keyvault 'Microsoft.KeyVault/vaults@2019-09-01' = { + name: keyVaultName + location: resourceGroup().location + properties: { + sku: { + family: 'A' + name: sku + } + tenantId: subscription().tenantId + accessPolicies: [ + { + // Must specify API version of identity. + objectId: reference(const_identityId, '2018-11-30').principalId + tenantId: subscription().tenantId + permissions: permission + } + ] + enabledForDeployment: false + enabledForDiskEncryption: false + enabledForTemplateDeployment: true + enableSoftDelete: true + } +} + +resource createAddCertificate 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: 'ds-create-add-appgw-certificate' + location: resourceGroup().location + identity: identity + kind: 'AzurePowerShell' + properties: { + forceUpdateTag: utcValue + azPowerShellVersion: '5.0' + timeout: 'PT30M' + arguments: ' -vaultName ${keyVaultName} -certificateName ${secretName} -subjectName ${subjectName}' + scriptContent: '\n param(\n [string] [Parameter(Mandatory=$true)] $vaultName,\n [string] [Parameter(Mandatory=$true)] $certificateName,\n [string] [Parameter(Mandatory=$true)] $subjectName\n )\n\n $ErrorActionPreference = \'Stop\'\n $DeploymentScriptOutputs = @{}\n\n $existingCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName\n\n if ($existingCert -and $existingCert.Certificate.Subject -eq $subjectName) {\n\n Write-Host \'Certificate $certificateName in vault $vaultName is already present.\'\n\n $DeploymentScriptOutputs[\'certThumbprint\'] = $existingCert.Thumbprint\n $existingCert | Out-String\n }\n else {\n $policy = New-AzKeyVaultCertificatePolicy -SubjectName $subjectName -IssuerName Self -ValidityInMonths 12 -Verbose\n\n # private key is added as a secret that can be retrieved in the ARM template\n Add-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName -CertificatePolicy $policy -Verbose\n\n $newCert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certificateName\n\n # it takes a few seconds for KeyVault to finish\n $tries = 0\n do {\n Write-Host \'Waiting for certificate creation completion...\'\n Start-Sleep -Seconds 10\n $operation = Get-AzKeyVaultCertificateOperation -VaultName $vaultName -Name $certificateName\n $tries++\n\n if ($operation.Status -eq \'failed\')\n {\n throw \'Creating certificate $certificateName in vault $vaultName failed with error $($operation.ErrorMessage)\'\n }\n\n if ($tries -gt 120)\n {\n throw \'Timed out waiting for creation of certificate $certificateName in vault $vaultName\'\n }\n } while ($operation.Status -ne \'completed\')\n\n $DeploymentScriptOutputs[\'certThumbprint\'] = $newCert.Thumbprint\n $newCert | Out-String\n }\n ' + cleanupPreference: 'OnSuccess' + retentionInterval: 'P1D' + } + dependsOn: [ + keyvault + ] +} + +output keyVaultName string = keyVaultName +output secretName string = secretName +output identityId string = const_identityId diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep new file mode 100644 index 000000000..e71fe6f24 --- /dev/null +++ b/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep @@ -0,0 +1,67 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +@description('Certificate data to store in the secret') +param certificateDataValue string + +@description('Certificate password to store in the secret') +param certificatePasswordValue string + +@description('Property to specify whether Azure Resource Manager is permitted to retrieve secrets from the key vault.') +param enabledForTemplateDeployment bool = true + +param identity object +param permission object = { + certificates: [ + 'get' + 'list' + 'update' + 'create' + ] +} + +@description('Price tier for Key Vault.') +param sku string = 'Standard' + +@description('Subject name to create a certificate.') +param subjectName string + +@description('If false, will create a certificate.') +param useExistingAppGatewaySSLCertificate bool = false + +@description('Current deployment time. Used as a tag in deployment script.') +param keyVaultName string = 'GEN_UNIQUE' + +var name_kvWithExistingCertTemplateName = '_keyvaultWithExistingCertTemplate.json' +var name_kvWithNewCertTemplateName = '_keyvaultWithNewCertTemplate.json' +var name_kvTempaltesFolder = '_keyvault' +var name_sslCertSecretName = 'myAppGatewaySSLCert' +var name_sslCertPasswordSecretName = 'myAppGatewaySSLCertPassword' + +module keyVaultwithSelfSignedAppGatewaySSLCert '_keyvault/_keyvaultWithNewCert.bicep' = if (!useExistingAppGatewaySSLCertificate) { + name: 'kv-appgw-selfsigned-certificate-deployment' + params: { + identity: identity + keyVaultName: keyVaultName + permission: permission + subjectName: subjectName + sku: sku + } +} + +module keyVaultwithExistingAppGatewaySSLCert '_keyvault/_keyvaultWithExistingCert.bicep' = if (useExistingAppGatewaySSLCertificate) { + name: 'kv-appgw-existing-certificate-deployment' + params: { + certificateDataName: name_sslCertSecretName + certificateDataValue: certificateDataValue + certificatePasswordName: name_sslCertPasswordSecretName + certificatePasswordValue: certificatePasswordValue + enabledForTemplateDeployment: enabledForTemplateDeployment + name: keyVaultName + sku: sku + } +} + +output keyVaultName string = (useExistingAppGatewaySSLCertificate ? keyVaultwithSelfSignedAppGatewaySSLCert.outputs.keyVaultName : keyVaultwithSelfSignedAppGatewaySSLCert.outputs.keyVaultName) +output sslCertDataSecretName string = (useExistingAppGatewaySSLCertificate ? keyVaultwithExistingAppGatewaySSLCert.outputs.sslCertDataSecretName : keyVaultwithSelfSignedAppGatewaySSLCert.outputs.secretName) +output sslCertPwdSecretName string = (useExistingAppGatewaySSLCertificate ? keyVaultwithExistingAppGatewaySSLCert.outputs.sslCertPwdSecretName: '') diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvaultAppGatewayConnector.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvaultAppGatewayConnector.bicep new file mode 100644 index 000000000..1e8200b4b --- /dev/null +++ b/src/main/bicep/modules/_azure-resoruces/_keyvaultAppGatewayConnector.bicep @@ -0,0 +1,78 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +@allowed([ + 'haveKeyVault' + 'haveCert' + 'generateCert' +]) +@description('Three scenarios we support for deploying app gateway') +param appGatewayCertificateOption string = 'haveCert' + +@description('Custom DNS Zone domain name for the Application Gateway') +param customDomainNameforApplicationGateway string = 'application.contoso.xyz' + +@description('Azure DNS for Application Gateway') +param domainLabelforApplicationGateway string = 'wlsgw' + +@description('Public IP Name for the Application Gateway') +param gatewayPublicIPAddressName string = 'gwip' + +@description('Key Vault name') +param keyVaultName string = '' + +@description('Name of resource group in current subscription containing the Key Vault') +param keyVaultResourceGroup string = '' + +@description('The name of the secret in the specified Key Vault whose value is the SSL Certificate Data,') +param keyVaultSSLCertDataSecretName string = 'myCertSecretData' + +@description('The name of the secret in the specified Key Vault whose value is the password for the SSL Certificate') +param keyVaultSSLCertPasswordSecretName string = '' + +var const_appGatewaySSLCertOptionGenerateCert = 'generateCert' +var const_appGatewaySSLCertOptionHaveCert = 'haveCert' +var const_appGatewaySSLCertOptionHaveKeyVault = 'haveKeyVault' +var name_appgwDeployment = 'appgw-${appGatewayCertificateOption}-deployment' + +// get key vault object in a resource group +resource existingKeyvault 'Microsoft.KeyVault/vaults@2019-09-01' existing = { + name: keyVaultName + scope: resourceGroup(keyVaultResourceGroup) +} + +module appGatewaywithExistingKeyVault '_appgateway.bicep' = if (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault) { + name: 'appgw-${const_appGatewaySSLCertOptionHaveKeyVault}-deployment' + params: { + appGatewaySSLCertificateData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) + appGatewaySSLCertificatePassword: existingKeyvault.getSecret(keyVaultSSLCertPasswordSecretName) + dnsNameforApplicationGateway: domainLabelforApplicationGateway + gatewayPublicIPAddressName: gatewayPublicIPAddressName + } +} + +module appGatewaywithExistingSSLCert '_appgateway.bicep' = if (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveCert) { + name: 'appgw-${const_appGatewaySSLCertOptionHaveCert}-deployment' + params: { + appGatewaySSLCertificateData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) + appGatewaySSLCertificatePassword: existingKeyvault.getSecret(keyVaultSSLCertPasswordSecretName) + dnsNameforApplicationGateway: domainLabelforApplicationGateway + gatewayPublicIPAddressName: gatewayPublicIPAddressName + } +} + +module appGatewaywithSelfSignedCert '_appgateway.bicep' = if (appGatewayCertificateOption == const_appGatewaySSLCertOptionGenerateCert) { + name: 'appgw-${const_appGatewaySSLCertOptionGenerateCert}-deployment' + params: { + appGatewaySSLCertificateData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) + appGatewaySSLCertificatePassword: '' + dnsNameforApplicationGateway: domainLabelforApplicationGateway + gatewayPublicIPAddressName: gatewayPublicIPAddressName + } +} + +output appGatewayAlias string = reference(name_appgwDeployment).outputs.appGatewayAlias.value +output appGatewayName string = reference(name_appgwDeployment).outputs.appGatewayName.value +output appGatewayURL string = reference(name_appgwDeployment).outputs.appGatewayURL.value +output appGatewaySecuredURL string = reference(name_appgwDeployment).outputs.appGatewaySecuredURL.value +output vnetName string = reference(name_appgwDeployment).outputs.vnetName.value diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep new file mode 100644 index 000000000..95e917132 --- /dev/null +++ b/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep @@ -0,0 +1,58 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +param _artifactsLocation string +@secure() +param _artifactsLocationSasToken string = '' + +param appgwName string = 'appgw-contoso' +param appgwAlias string = 'appgw-contoso-alias' +param appgwForAdminServer bool = true +param aksClusterRGName string = 'aks-contoso-rg' +param aksClusterName string = 'aks-contoso' +param dnszoneAdminConsoleLabel string = 'admin' +param dnszoneAppGatewayLabel string = 'www' +param dnszoneName string = 'contoso.xyz' +param dnszoneRGName string = 'dns-contoso-rg' +param enableAppGWIngress bool = false +param enableDNSConfiguration bool = false +param identity object +param lbSvcValues array = [] +param location string = 'eastus' +@secure() +param servicePrincipal string = newGuid() +param utcValue string = utcNow() +param vnetName string = 'vnet-contoso' +param wlsDomainName string = 'domain1' +param wlsDomainUID string = 'sample-domain1' + +var const_appgwHelmConfigTemplate='appgw-helm-config.yaml.template' +var const_appgwIngressSVCTemplate='azure-ingress-appgateway.yaml.template' +var const_appgwSARoleBindingFile='appgw-ingress-clusterAdmin-roleBinding.yaml' +var const_arguments = '${aksClusterRGName} ${aksClusterName} ${wlsDomainName} ${wlsDomainUID} "${string(lbSvcValues)}" ${enableAppGWIngress} ${subscription().id} ${resourceGroup().name} ${appgwName} ${vnetName} ${string(servicePrincipal)} ${appgwForAdminServer} ${enableDNSConfiguration} ${dnszoneRGName} ${dnszoneName} ${dnszoneAdminConsoleLabel} ${dnszoneAppGatewayLabel} ${appgwAlias}' +var const_scriptLocation = uri(_artifactsLocation, 'scripts/') +var const_primaryScript = 'setupNetworking.sh' + +resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: 'ds-networking-deployment' + location: location + kind: 'AzureCLI' + identity: identity + properties: { + azCliVersion: '2.15.0' + arguments: const_arguments + primaryScriptUri: uri(const_scriptLocation, '${const_primaryScript}${_artifactsLocationSasToken}') + supportingScriptUris: [ + uri(const_scriptLocation, '${const_appgwHelmConfigTemplate}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_appgwIngressSVCTemplate}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_appgwSARoleBindingFile}${_artifactsLocationSasToken}') + ] + cleanupPreference: 'OnSuccess' + retentionInterval: 'P1D' + forceUpdateTag: utcValue + } +} + +output adminConsoleLBUrl string = length(lbSvcValues) > 0 && (reference('ds-networking-deployment').outputs.adminConsoleEndpoint != 'null') ? format('http://{0}/',reference('ds-networking-deployment').outputs.adminConsoleEndpoint): '' +output clusterLBUrl string = length(lbSvcValues) > 0 && (reference('ds-networking-deployment').outputs.clusterEndpoint != 'null') ? format('http://{0}/',reference('ds-networking-deployment').outputs.clusterEndpoint): '' + diff --git a/src/main/bicep/modules/_pids/_pid-dev.bicep b/src/main/bicep/modules/_pids/_pid-dev.bicep index c3b82e31d..48a4f5308 100644 --- a/src/main/bicep/modules/_pids/_pid-dev.bicep +++ b/src/main/bicep/modules/_pids/_pid-dev.bicep @@ -12,3 +12,7 @@ module pidStart './_empty.bicep' = if (name != 'pid'){ output wlsAKSEnd string = '17328b4d-841f-57b5-a9c5-861ad48f9d0d' output wlsAKSStart string = 'c46a11b1-e8d2-5053-9741-45294b2e15c9' +output networkingEnd string = '39d32fcd-1d02-50b6-9455-4b767a8e769e' +output networkingStart string = 'ed47756f-2475-56dd-b13a-26027749b6e1' +output appgwEnd string = '38647ff6-ea8d-59e5-832d-b036a4d29c73' +output appgwStart string = '8ba7beaa-96fd-576a-acd8-28f7a6efa83a' diff --git a/src/main/bicep/modules/_pids/_pid.bicep b/src/main/bicep/modules/_pids/_pid.bicep index fdf7d82f0..b1e5d11fa 100644 --- a/src/main/bicep/modules/_pids/_pid.bicep +++ b/src/main/bicep/modules/_pids/_pid.bicep @@ -12,3 +12,7 @@ module pidStart './_empty.bicep' = if (name != 'pid'){ output wlsAKSEnd string = '2571f846-2f66-5c22-9fe6-38ecea7889ac' output wlsAKSStart string = '3e6acde5-9a62-5488-9fd4-87c46f4105f4' +output networkingEnd string = '2798165c-49fa-5701-b608-b80ed3986176' +output networkingStart string = '0793308f-de9d-5f0d-92f9-d9fc4b413b8b' +output appgwEnd string = '47ea43a0-95cf-52c7-aee8-7ee6106fc1bf' +output appgwStart string = '01288010-2672-5831-a66b-7b8b45cace1b' diff --git a/src/main/bicep/modules/networking.bicep b/src/main/bicep/modules/networking.bicep new file mode 100644 index 000000000..a82a6d3d1 --- /dev/null +++ b/src/main/bicep/modules/networking.bicep @@ -0,0 +1,159 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +param _artifactsLocation string = deployment().properties.templateLink.uri +@secure() +param _artifactsLocationSasToken string = '' +param _pidNetworkingEnd string +param _pidNetworkingStart string +param _pidAppgwEnd string +param _pidAppgwStart string +@description('Resource group name of an existing AKS cluster.') +param aksClusterRGName string = 'aks-contoso-rg' +@description('Name of an existing AKS cluster.') +param aksClusterName string = 'aks-contoso' +@allowed([ + 'haveCert' + 'haveKeyVault' + 'generateCert' +]) +@description('Three scenarios we support for deploying app gateway') +param appGatewayCertificateOption string = 'haveCert' +@description('Public IP Name for the Application Gateway') +param appGatewayPublicIPAddressName string = 'gwip' +@description('Create Application Gateway ingress for admin console.') +param appgwForAdminServer bool = true +@description('If true, the template will update records to the existing DNS Zone. If false, the template will create a new DNS Zone.') +param createDNSZone bool = false +@description('DNS prefix for ApplicationGateway') +param dnsNameforApplicationGateway string = 'wlsgw' +@description('Azure DNS Zone name.') +param dnszoneName string = 'contoso.xyz' +param dnszoneAdminConsoleLabel string = 'admin' +@description('Specify a label used to generate subdomain of Application Gateway. The final subdomain name will be label.dnszoneName, e.g. applications.contoso.xyz') +param dnszoneAppGatewayLabel string = 'www' +param dnszoneRGName string = 'dns-contoso-rg' +@description('true to set up Application Gateway ingress.') +param enableAppGWIngress bool = false +param enableDNSConfiguration bool = false +param identity object +@description('Existing Key Vault Name') +param keyVaultName string = 'kv-contoso' +@description('Resource group name in current subscription containing the KeyVault') +param keyVaultResourceGroup string = 'kv-contoso-rg' +@description('The name of the secret in the specified KeyVault whose value is the SSL Certificate Data') +param keyVaultSSLCertDataSecretName string = 'kv-ssl-data' +@description('The name of the secret in the specified KeyVault whose value is the password for the SSL Certificate') +param keyVaultSSLCertPasswordSecretName string = 'kv-ssl-psw' +param location string = 'eastus' +@description('Object array to define Load Balancer service, each object must include service name, service target[admin-server or cluster-1], port.') +param lbSvcValues array = [] +@secure() +param servicePrincipal string = newGuid() +param utcValue string = utcNow() +@description('Name of WebLogic domain to create.') +param wlsDomainName string = 'domain1' +@description('UID of WebLogic domain, used in WebLogic Operator.') +param wlsDomainUID string = 'sample-domain1' + +var const_appgwCustomDNSAlias = format('{0}.{1}/', dnszoneAppGatewayLabel, dnszoneName) +var const_appgwAdminCustomDNSAlias = format('{0}.{1}/', dnszoneAdminConsoleLabel, dnszoneName) +var name_dnsNameforApplicationGateway = '${concat(dnsNameforApplicationGateway, take(utcValue, 6))}' +var name_domainLabelforApplicationGateway = '${take(concat(name_dnsNameforApplicationGateway, '-', toLower(resourceGroup().name), '-', toLower(wlsDomainName)), 63)}' + +module pidNetworkingStart './_pids/_pid.bicep' = { + name: 'pid-networking-start-deployment' + params: { + name: _pidNetworkingStart + } +} + +module pidAppgwStart './_pids/_pid.bicep' = if (enableAppGWIngress) { + name: 'pid-app-gateway-start-deployment' + params: { + name: _pidAppgwStart + } +} + +module appgwDeployment '_azure-resoruces/_keyvaultAppGatewayConnector.bicep' = if (enableAppGWIngress) { + name: 'app-gateway-deployment' + params: { + appGatewayCertificateOption: appGatewayCertificateOption + customDomainNameforApplicationGateway: format('{0}.{1}', dnszoneAppGatewayLabel, dnszoneName) + domainLabelforApplicationGateway: name_domainLabelforApplicationGateway + gatewayPublicIPAddressName: appGatewayPublicIPAddressName + keyVaultName: keyVaultName + keyVaultResourceGroup: keyVaultResourceGroup + keyVaultSSLCertDataSecretName: keyVaultSSLCertDataSecretName + keyVaultSSLCertPasswordSecretName: keyVaultSSLCertPasswordSecretName + } + dependsOn: [ + pidAppgwStart + ] +} + +module dnsZoneDeployment '_azure-resoruces/_dnsZones.bicep' = if (enableDNSConfiguration && createDNSZone) { + name: 'dnszone-deployment' + params: { + dnszoneName: dnszoneName + location: location + } + dependsOn: [ + pidNetworkingStart + ] +} + +module networkingDeployment '_deployment-scripts/_ds-create-networking.bicep' = { + name: 'ds-networking-deployment' + params: { + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + appgwName: enableAppGWIngress ? appgwDeployment.outputs.appGatewayName : 'null' + appgwAlias: enableAppGWIngress ? appgwDeployment.outputs.appGatewayAlias : 'null' + appgwForAdminServer: appgwForAdminServer + aksClusterRGName: aksClusterRGName + aksClusterName: aksClusterName + dnszoneAdminConsoleLabel: dnszoneAdminConsoleLabel + dnszoneAppGatewayLabel: dnszoneAppGatewayLabel + dnszoneName: dnszoneName + dnszoneRGName: createDNSZone ? resourceGroup().name : dnszoneRGName + enableAppGWIngress: enableAppGWIngress + enableDNSConfiguration: enableDNSConfiguration + identity: identity + lbSvcValues: lbSvcValues + location: location + servicePrincipal: servicePrincipal + vnetName: enableAppGWIngress ? appgwDeployment.outputs.vnetName : 'null' + wlsDomainName: wlsDomainName + wlsDomainUID: wlsDomainUID + } + dependsOn: [ + appgwDeployment + dnsZoneDeployment + ] +} + +module pidAppgwEnd './_pids/_pid.bicep' = if (enableAppGWIngress) { + name: 'pid-app-gateway-end-deployment' + params: { + name: _pidAppgwEnd + } + dependsOn: [ + networkingDeployment + ] +} + +module pidNetworkingEnd './_pids/_pid.bicep' = { + name: 'pid-networking-end-deployment' + params: { + name: _pidNetworkingEnd + } + dependsOn: [ + pidAppgwEnd + ] +} + +output adminConsoleExternalUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('http://{0}console', const_appgwAdminCustomDNSAlias) : format('http://{0}/console', appgwDeployment.outputs.appGatewayAlias)) : networkingDeployment.outputs.adminConsoleLBUrl +output adminConsoleExternalSecuredUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('https://{0}console', const_appgwAdminCustomDNSAlias) : format('https://{0}/console', appgwDeployment.outputs.appGatewayAlias)) : '' +output clusterExternalUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('http://{0}', const_appgwCustomDNSAlias) : appgwDeployment.outputs.appGatewayURL) : networkingDeployment.outputs.clusterLBUrl +output clusterExternalSecuredURL string = enableAppGWIngress ? (enableDNSConfiguration ? format('https://{0}', const_appgwCustomDNSAlias) : appgwDeployment.outputs.appGatewaySecuredURL) : '' diff --git a/src/main/bicep/modules/setupWebLogicCluster.bicep b/src/main/bicep/modules/setupWebLogicCluster.bicep index cdf734ba7..9798fa405 100644 --- a/src/main/bicep/modules/setupWebLogicCluster.bicep +++ b/src/main/bicep/modules/setupWebLogicCluster.bicep @@ -193,5 +193,6 @@ module pidEnd './_pids/_pid.bicep' = { } output aksClusterName string = createAKSCluster ? aksClusterDeployment.outputs.aksClusterName : aksClusterName +output aksClusterRGName string = createAKSCluster ? resourceGroup().name : aksClusterRGName output adminServerUrl string = format('http://{0}-admin-server.{0}-ns.svc.cluster.local:7001/console',wlsDomainUID) output clusterSVCUrl string = format('http://{0}-cluster-cluster-1.{0}-ns.svc.cluster.local:8001/', wlsDomainUID) From 007ec91af1ffbab4d4f4dff8d4ea5f4dee4b0316 Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Mon, 19 Jul 2021 19:55:08 +0000 Subject: [PATCH 22/37] Merged PR 337599: E2E SSL - Enable internal load balancer service - Update WDT version to 1.9.14 - Expose gateway HTTPS - E2E SSL to admin console and cluster Related work items: #1340864, #1355692, #1355738, #1355739 --- pom.xml | 2 +- src/main/arm/createUiDefinition.json | 426 +++++++++++++++++- .../azure-ingress-appgateway.yaml.template | 21 - src/main/arm/scripts/buildWLSDockerImage.sh | 66 ++- src/main/arm/scripts/common.sh | 1 + ...omain.yaml.template => genDomainConfig.sh} | 126 ++++-- src/main/arm/scripts/genImageModel.sh | 136 ++++++ src/main/arm/scripts/model.yaml | 46 -- src/main/arm/scripts/setupNetworking.sh | 398 ++++++++++++++-- src/main/arm/scripts/setupWLSDomain.sh | 355 +++++++++++---- src/main/arm/scripts/utility.sh | 14 + src/main/bicep/mainTemplate.bicep | 231 +++++++++- .../_azure-resoruces/_appgateway.bicep | 75 +-- .../_keyvault/_keyvaultForWLSSSLCert.bicep | 99 ++++ .../_keyvault/_keyvaultWithExistingCert.bicep | 10 +- .../_azure-resoruces/_keyvaultAdapter.bicep | 8 +- .../_keyvaultAppGatewayConnector.bicep | 78 ---- .../_ds-create-networking.bicep | 22 +- .../_ds-create-wls-cluster.bicep | 35 +- src/main/bicep/modules/networking.bicep | 113 ++++- .../bicep/modules/setupWebLogicCluster.bicep | 47 +- 21 files changed, 1873 insertions(+), 436 deletions(-) delete mode 100644 src/main/arm/scripts/azure-ingress-appgateway.yaml.template create mode 100644 src/main/arm/scripts/common.sh rename src/main/arm/scripts/{domain.yaml.template => genDomainConfig.sh} (59%) create mode 100644 src/main/arm/scripts/genImageModel.sh delete mode 100644 src/main/arm/scripts/model.yaml create mode 100644 src/main/arm/scripts/utility.sh create mode 100644 src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep delete mode 100644 src/main/bicep/modules/_azure-resoruces/_keyvaultAppGatewayConnector.bicep diff --git a/pom.xml b/pom.xml index e889d14f0..3bf67665b 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ com.oracle.weblogic.azure wls-on-aks-azure-marketplace - 1.0.4 + 1.0.5 com.microsoft.azure.iaas diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index f9c4a93c9..dd5644652 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -513,12 +513,407 @@ } ] }, + { + "name": "section_sslConfiguration", + "type": "Microsoft.Common.Section", + "label": "TLS/SSL Configuration", + "elements": [ + { + "name": "sslConfigurationText", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here will cause the template to provision WebLogic Administration Console on HTTPS (Secure) port, with your own TLS/SSL certificate.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-config" + } + } + }, + { + "name": "enableCustomSSL", + "type": "Microsoft.Common.OptionsGroup", + "label": "Configure end to end TLS/SSL to WebLogic Administration Console and cluster on HTTPS (Secure) port, with your own certificate?", + "defaultValue": "No", + "toolTip": "Select 'Yes' to configure end to end TLS/SSL to WebLogic Administration Console and cluster on HTTPS (Secure) port, with your own certificate.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ], + "required": false + } + }, + { + "name": "sslConfigurationAccessOption", + "type": "Microsoft.Common.OptionsGroup", + "visible": "[steps('section_sslConfiguration').enableCustomSSL]", + "label": "How would you like to provide required configuration", + "defaultValue": "Upload existing KeyStores", + "toolTip": "Select 'Upload existing KeyStores' to use local stored KeyStores.", + "constraints": { + "allowedValues": [ + { + "label": "Upload existing KeyStores", + "value": "uploadConfig" + }, + { + "label": "Use KeyStores stored in Azure Key Vault", + "value": "keyVaultStoredConfig" + } + ], + "required": false + } + }, + { + "name": "uploadedCustomSSLSettings", + "type": "Microsoft.Common.Section", + "visible": "[and(steps('section_sslConfiguration').enableCustomSSL, equals(steps('section_sslConfiguration').sslConfigurationAccessOption, 'uploadConfig'))]", + "label": "TLS/SSL Configuration Settings", + "elements": [ + { + "name": "sslKeystoreInfo0", + "type": "Microsoft.Common.InfoBox", + "visible": "true", + "options": { + "icon": "Info", + "text": "You must provide different files for identity and trust KeyStores. Select here for more details.", + "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-configuration" + } + }, + { + "name": "uploadedCustomIdentityKeyStoreData", + "type": "Microsoft.Common.FileUpload", + "label": "Identity KeyStore Data file(.jks,.p12)", + "toolTip": "Identity KeyStore for TLS/SSL configuration", + "constraints": { + "required": true, + "accept": ".jks,.p12" + }, + "options": { + "multiple": false, + "uploadMode": "file", + "openMode": "binary" + }, + "visible": true + }, + { + "name": "uploadedCustomIdentityKeyStorePassphrase", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": " The passphrase for the Identity KeyStore", + "constraints": { + "required": true, + "regex": "^.{6,}$", + "validationMessage": "Keypass must be at least 6 characters long." + }, + "options": { + "hideConfirmation": false + }, + "visible": true + }, + { + "name": "uploadedCustomIdentityKeyStoreType", + "type": "Microsoft.Common.DropDown", + "visible": "true", + "label": "The Identity KeyStore type (JKS,PKCS12)", + "defaultValue": "JKS", + "toolTip": "One of the supported KeyStore types", + "constraints": { + "allowedValues": [ + { + "label": "JKS", + "value": "JKS" + }, + { + "label": "PKCS12", + "value": "PKCS12" + } + ], + "required": true + } + }, + { + "name": "uploadedPrivateKeyAlias", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The alias of the server's private key within the Identity KeyStore", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "uploadedPrivateKeyPassPhrase", + "type": "Microsoft.Common.PasswordBox", + "visible": "true", + "label": { + "password": "The passphrase for the server's private key within the Identity KeyStore", + "confirmPassword": "Confirm passphrase" + }, + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^.{6,}$", + "validationMessage": "Keypass must be at least 6 characters long." + }, + "options": { + "hideConfirmation": false + } + }, + { + "name": "uploadedCustomTrustKeyStoreData", + "type": "Microsoft.Common.FileUpload", + "label": "Trust KeyStore Data file(.jks,.p12)", + "toolTip": "Trust KeyStore for TLS/SSL configuration.", + "constraints": { + "required": true, + "accept": ".jks,.p12" + }, + "options": { + "multiple": false, + "uploadMode": "file", + "openMode": "binary" + }, + "visible": true + }, + { + "name": "uploadedCustomTrustKeyStorePassPhrase", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Password", + "confirmPassword": "Confirm password" + }, + "toolTip": " The passphrase for the Trust KeyStore", + "constraints": { + "required": true, + "regex": "^.{6,}$", + "validationMessage": "Keypass must be at least 6 characters long." + }, + "options": { + "hideConfirmation": false + }, + "visible": true + }, + { + "name": "uploadedCustomTrustKeyStoreType", + "type": "Microsoft.Common.DropDown", + "visible": "true", + "label": "The Trust KeyStore type (JKS,PKCS12)", + "defaultValue": "JKS", + "toolTip": "One of the supported KeyStore types", + "constraints": { + "allowedValues": [ + { + "label": "JKS", + "value": "JKS" + }, + { + "label": "PKCS12", + "value": "PKCS12" + } + ], + "required": true + } + } + ] + }, + { + "name": "keyVaultStoredCustomSSLSettings", + "type": "Microsoft.Common.Section", + "visible": "[and(steps('section_sslConfiguration').enableCustomSSL, equals(steps('section_sslConfiguration').sslConfigurationAccessOption, 'keyVaultStoredConfig'))]", + "label": "TLS/SSL Configuration Settings", + "elements": [ + { + "name": "sslKeystoreInfo1", + "type": "Microsoft.Common.InfoBox", + "visible": "true", + "options": { + "icon": "Info", + "text": "You must provide different files for identity and trust KeyStores. Select here for more details.", + "uri": "https://aka.ms/arm-oraclelinux-wls-ssl-configuration" + } + }, + { + "name": "keyVaultText", + "type": "Microsoft.Common.TextBlock", + "visible": "true", + "options": { + "text": "Enabling a HTTPS (Secure) port for the Administration Console requires you to obtain a valid TLS/SSL certificate. The template will look for the certificate and other configuration items in the Azure Key Vault specified here.", + "link": { + "label": "Learn more", + "uri": "https://aka.ms/arm-oraclelinux-wls-cluster-app-gateway-key-vault" + } + } + }, + { + "name": "keyVaultResourceGroup", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "Resource group name in current subscription containing the Key Vault", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z.\\-_()]{0,89}([a-z0-9A-Z\\-_()]{1})$", + "validationMessage": "[if(greater(length(steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultResourceGroup), 90),'Resource group names only allow up to 90 characters.', 'Resource group names only allow alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period.')]" + } + }, + { + "name": "keyVaultName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "Name of the Azure Key Vault containing secrets for the TLS/SSL certificate", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^(?=.{3,24}$)[a-zA-Z](([a-z0-9A-Z]*|(?:\\-[^\\-][a-z0-9A-Z]*))*)$", + "validationMessage": "[if(or(greater(length(steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultName), 24), less(length(steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultName), 3)),'Vault name must be between 3-24 alphanumeric characters. The name must begin with a letter, end with a letter or digit, and not contain consecutive hyphens.','Vault name must only contain alphanumeric characters and dashes and cannot start with a number')]" + } + }, + { + "name": "keyVaultCustomIdentityKeyStoreDataSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the Identity KeyStore Data", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomIdentityKeyStorePassPhraseSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Identity KeyStore", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomIdentityKeyStoreType", + "type": "Microsoft.Common.DropDown", + "visible": "true", + "label": "The Identity KeyStore type (JKS,PKCS12)", + "defaultValue": "JKS", + "toolTip": "One of the supported KeyStore types", + "constraints": { + "allowedValues": [ + { + "label": "JKS", + "value": "JKS" + }, + { + "label": "PKCS12", + "value": "PKCS12" + } + ], + "required": true + } + }, + { + "name": "keyVaultPrivateKeyAliasSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the Private Key Alias", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultPrivateKeyPassPhraseSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Private Key", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomTrustKeyStoreDataSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the Trust KeyStore Data", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomTrustKeyStorePassPhraseSecretName", + "type": "Microsoft.Common.TextBox", + "visible": "true", + "label": "The name of the secret in the specified Key Vault whose value is the passphrase for the Trust KeyStore", + "defaultValue": "", + "toolTip": "Use only letters and numbers", + "constraints": { + "required": true, + "regex": "^[a-z0-9A-Z]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters and numbers." + } + }, + { + "name": "keyVaultCustomTrustKeyStoreType", + "type": "Microsoft.Common.DropDown", + "visible": "true", + "label": "The Trust KeyStore type (JKS,PKCS12)", + "defaultValue": "JKS", + "toolTip": "One of the supported KeyStore types", + "constraints": { + "allowedValues": [ + { + "label": "JKS", + "value": "JKS" + }, + { + "label": "PKCS12", + "value": "PKCS12" + } + ], + "required": true + } + } + ] + } + ] + }, { "name": "section_appGateway", "type": "Microsoft.Common.Section", "label": "Networking", "subLabel": { - "preValidation": "Provide required info for networking", + "preValidation": "Provide required information for networking", "postValidation": "Done" }, "bladeTitle": "Networking", @@ -834,6 +1229,12 @@ }, "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]" }, + { + "name": "enableCookieBasedAffinity", + "type": "Microsoft.Common.CheckBox", + "label": "Enable cookie based affinity", + "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]" + }, { "name": "appgwForAdminServer", "type": "Microsoft.Common.OptionsGroup", @@ -997,6 +1398,8 @@ "enableAppGWIngress": "[steps('section_appGateway').appgwIngress.enableAppGateway]", "enableAzureMonitoring": "[bool(steps('section_aks').clusterInfo.enableAzureMonitoring)]", "enableAzureFileShare": "[bool(steps('section_aks').clusterInfo.enableAzureFileShare)]", + "enableCookieBasedAffinity": "[bool(steps('section_appGateway').appgwIngress.enableCookieBasedAffinity)]", + "enableCustomSSL": "[bool(steps('section_sslConfiguration').enableCustomSSL)]", "enableDNSConfiguration": "[bool(steps('section_appGateway').dnsConfiguration.enableDNSConfiguration)]", "identity": "[basics('basicsRequired').identity]", "lbSvcValues": "[steps('section_appGateway').lbSVCInfo.lbSVC]", @@ -1009,7 +1412,26 @@ "ocrSSOPSW": "[basics('basicsRequired').ocrSSOPassword]", "ocrSSOUser": "[basics('basicsRequired').ocrSSOUserName]", "servicePrincipal": "[steps('section_appGateway').appgwIngress.servicePrincipal]", - "uploadAppPackage": "[bool(steps('section_aks').jeeAppInfo.uploadAppPackage)]", + "sslConfigurationAccessOption": "[steps('section_sslConfiguration').sslConfigurationAccessOption]", + "sslKeyVaultCustomIdentityKeyStoreDataSecretName": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultCustomIdentityKeyStoreDataSecretName]", + "sslKeyVaultCustomIdentityKeyStorePassPhraseSecretName": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultCustomIdentityKeyStorePassPhraseSecretName]", + "sslKeyVaultCustomIdentityKeyStoreType": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultCustomIdentityKeyStoreType]", + "sslKeyVaultCustomTrustKeyStoreDataSecretName": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultCustomTrustKeyStoreDataSecretName]", + "sslKeyVaultCustomTrustKeyStorePassPhraseSecretName": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultCustomTrustKeyStorePassPhraseSecretName]", + "sslKeyVaultCustomTrustKeyStoreType": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultCustomTrustKeyStoreType]", + "sslKeyVaultName": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultName]", + "sslKeyVaultPrivateKeyAliasSecretName": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultPrivateKeyAliasSecretName]", + "sslKeyVaultPrivateKeyPassPhraseSecretName": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultPrivateKeyPassPhraseSecretName]", + "sslKeyVaultResourceGroup": "[steps('section_sslConfiguration').keyVaultStoredCustomSSLSettings.keyVaultResourceGroup]", + "sslUploadedCustomIdentityKeyStoreData": "[steps('section_sslConfiguration').uploadedCustomSSLSettings.uploadedCustomIdentityKeyStoreData]", + "sslUploadedCustomIdentityKeyStorePassphrase": "[steps('section_sslConfiguration').uploadedCustomSSLSettings.uploadedCustomIdentityKeyStorePassphrase]", + "sslUploadedCustomIdentityKeyStoreType": "[steps('section_sslConfiguration').uploadedCustomSSLSettings.uploadedCustomIdentityKeyStoreType]", + "sslUploadedCustomTrustKeyStoreData": "[steps('section_sslConfiguration').uploadedCustomSSLSettings.uploadedCustomTrustKeyStoreData]", + "sslUploadedCustomTrustKeyStorePassPhrase": "[steps('section_sslConfiguration').uploadedCustomSSLSettings.uploadedCustomTrustKeyStorePassPhrase]", + "sslUploadedCustomTrustKeyStoreType": "[steps('section_sslConfiguration').uploadedCustomSSLSettings.uploadedCustomTrustKeyStoreType]", + "sslUploadedPrivateKeyAlias": "[steps('section_sslConfiguration').uploadedCustomSSLSettings.uploadedPrivateKeyAlias]", + "sslUploadedPrivateKeyPassPhrase": "[steps('section_sslConfiguration').uploadedCustomSSLSettings.uploadedPrivateKeyPassPhrase]", + "useInternalLB": "[bool(steps('section_appGateway').lbSVCInfo.enableInternalLB)]", "wdtRuntimePassword": "[basics('basicsRequired').wdtRuntimePassword]", "wlsClusterSize": "[basics('basicsOptional').wlsClusterSize]", "wlsDomainName": "[basics('basicsOptional').wlsDomainName]", diff --git a/src/main/arm/scripts/azure-ingress-appgateway.yaml.template b/src/main/arm/scripts/azure-ingress-appgateway.yaml.template deleted file mode 100644 index 354a07cd8..000000000 --- a/src/main/arm/scripts/azure-ingress-appgateway.yaml.template +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2021, Oracle Corporation and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. - -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: @INGRESS_NAME@ - namespace: @NAMESPACE@ - annotations: - kubernetes.io/ingress.class: azure/application-gateway -spec: - rules: - - http: - paths: - - path: @PATH@ - pathType: Prefix - backend: - service: - name: @CLUSTER_SERVICE_NAME@ - port: - number: @TARGET_PORT@ diff --git a/src/main/arm/scripts/buildWLSDockerImage.sh b/src/main/arm/scripts/buildWLSDockerImage.sh index d38a2e6b9..41709e7ff 100644 --- a/src/main/arm/scripts/buildWLSDockerImage.sh +++ b/src/main/arm/scripts/buildWLSDockerImage.sh @@ -10,7 +10,7 @@ function echo_stderr() { #Function to display usage message function usage() { - echo_stdout "./buildWLSDockerImage.sh " + echo_stdout "./buildWLSDockerImage.sh " if [ $1 -eq 1 ]; then exit 1 fi @@ -70,6 +70,11 @@ function validate_inputs() { echo_stderr "wlsClusterSize is required. " usage 1 fi + + if [ -z "$enableSSL" ]; then + echo_stderr "enableSSL is required. " + usage 1 + fi } function initialize() { @@ -84,6 +89,7 @@ function initialize() { mkdir wlsdeploy mkdir wlsdeploy/config mkdir wlsdeploy/applications + mkdir wlsdeploy/domainLibraries } # Install docker, zip, unzip and java @@ -92,7 +98,7 @@ function install_utilities() { # Install docker sudo apt-get -q update sudo apt-get -y -q install apt-transport-https - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + curl -m ${curlMaxTime} -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null @@ -130,11 +136,17 @@ function install_utilities() { validate_status "Check status of unzip." # Download weblogic tools - curl -m 120 -fL ${wdtDownloadURL} -o weblogic-deploy.zip + curl -m ${curlMaxTime} -fL ${wdtDownloadURL} -o weblogic-deploy.zip validate_status "Check status of weblogic-deploy.zip." - curl -m 120 -fL ${witDownloadURL} -o imagetool.zip + curl -m ${curlMaxTime} -fL ${witDownloadURL} -o imagetool.zip validate_status "Check status of imagetool.zip." + + curl -m ${curlMaxTime} -fL ${wlsPostgresqlDriverUrl} -o ${scriptDir}/model-images/wlsdeploy/domainLibraries/postgresql-42.2.8.jar + validate_status "Install postgresql driver." + + curl -m ${curlMaxTime} -fL ${wlsMSSQLDriverUrl} -o ${scriptDir}/model-images/wlsdeploy/domainLibraries/mssql-jdbc-7.4.1.jre8.jar + validate_status "Install mssql driver." } # Login in OCR @@ -155,35 +167,15 @@ function prepare_wls_models() { CLUSTER_SIZE=${wlsClusterSize} EOF - # Generate application deployment model in model.yaml - # Known issue: no support for package name that has comma. - # remove [] - if [ "${appPackageUrls}" == "[]" ]; then - return - fi + echo "Starting generating image model file..." + modelFilePath="$scriptDir/model.yaml" - cat <>${scriptDir}/model.yaml -appDeployments: - Application: -EOF - appPackageUrls=$(echo "${appPackageUrls:1:${#appPackageUrls}-2}") - appUrlArray=$(echo $appPackageUrls | tr "," "\n") - - index=1 - for item in $appUrlArray; do - # e.g. https://wlsaksapp.blob.core.windows.net/japps/testwebapp.war?sp=r&se=2021-04-29T15:12:38Z&sv=2020-02-10&sr=b&sig=7grL4qP%2BcJ%2BLfDJgHXiDeQ2ZvlWosRLRQ1ciLk0Kl7M%3D - fileNamewithQueryString="${item##*/}" - fileName="${fileNamewithQueryString%\?*}" - fileExtension="${fileName##*.}" - curl -m 120 -fL "$item" -o wlsdeploy/applications/${fileName} - cat <>${scriptDir}/model.yaml - app${index}: - SourcePath: 'wlsdeploy/applications/${fileName}' - ModuleType: ${fileExtension} - Target: 'cluster-1' -EOF - index=$((index + 1)) - done + chmod ugo+x $scriptDir/genImageModel.sh + bash $scriptDir/genImageModel.sh \ + ${modelFilePath} \ + ${appPackageUrls} \ + ${enableSSL} + validate_status "Generate image model file." } # Build weblogic image @@ -210,6 +202,7 @@ function build_wls_image() { --wdtModelOnly \ --wdtDomainType WLS \ --chown oracle:root + # --additionalBuildCommands ${scriptDir}/nodemanager.dockerfile validate_status "Check status of building WLS domain image." @@ -228,6 +221,8 @@ function build_wls_image() { export script="${BASH_SOURCE[0]}" export scriptDir="$(cd "$(dirname "${script}")" && pwd)" +source ${scriptDir}/common.sh + export wlsImagePath=$1 export azureACRServer=$2 export azureACRUserName=$3 @@ -237,11 +232,14 @@ export appPackageUrls=$6 export ocrSSOUser=$7 export ocrSSOPSW=$8 export wlsClusterSize=$9 +export enableSSL=${10} export acrImagePath="$azureACRServer/aks-wls-images:${imageTag}" export ocrLoginServer="container-registry.oracle.com" -export wdtDownloadURL="https://github.com/oracle/weblogic-deploy-tooling/releases/download/release-1.9.7/weblogic-deploy.zip" -export witDownloadURL="https://github.com/oracle/weblogic-image-tool/releases/download/release-1.9.11/imagetool.zip" +export wdtDownloadURL="https://github.com/oracle/weblogic-deploy-tooling/releases/download/release-1.9.14/weblogic-deploy.zip" +export witDownloadURL="https://github.com/oracle/weblogic-image-tool/releases/download/release-1.9.12/imagetool.zip" +export wlsPostgresqlDriverUrl="https://jdbc.postgresql.org/download/postgresql-42.2.8.jar" +export wlsMSSQLDriverUrl="https://repo.maven.apache.org/maven2/com/microsoft/sqlserver/mssql-jdbc/7.4.1.jre8/mssql-jdbc-7.4.1.jre8.jar" validate_inputs diff --git a/src/main/arm/scripts/common.sh b/src/main/arm/scripts/common.sh new file mode 100644 index 000000000..bb267ce87 --- /dev/null +++ b/src/main/arm/scripts/common.sh @@ -0,0 +1 @@ +export curlMaxTime=120 # seconds \ No newline at end of file diff --git a/src/main/arm/scripts/domain.yaml.template b/src/main/arm/scripts/genDomainConfig.sh similarity index 59% rename from src/main/arm/scripts/domain.yaml.template rename to src/main/arm/scripts/genDomainConfig.sh index c4bcf700f..ac7154e64 100644 --- a/src/main/arm/scripts/domain.yaml.template +++ b/src/main/arm/scripts/genDomainConfig.sh @@ -1,3 +1,22 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +export filePath=$1 +export replicas=$2 +export wlsCPU=$3 +export wlsDomainUID=$4 +export wlsDomainName=$5 +export wlsImagePath=$6 +export wlsMemory=$7 +export wlsManagedPrefix=$8 +export enableSSL=${9} +export enablePV=${10} +export javaOptions=${11} + +cat <$filePath # Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # @@ -8,10 +27,10 @@ apiVersion: "weblogic.oracle/v8" kind: Domain metadata: - name: "@WLS_DOMAIN_UID@" - namespace: "@WLS_DOMAIN_UID@-ns" + name: "${wlsDomainUID}" + namespace: "${wlsDomainUID}-ns" labels: - weblogic.domainUID: "@WLS_DOMAIN_UID@" + weblogic.domainUID: "${wlsDomainUID}" spec: # Set to 'FromModel' to indicate 'Model in Image'. @@ -19,10 +38,10 @@ spec: # The WebLogic Domain Home, this must be a location within # the image for 'Model in Image' domains. - domainHome: /u01/domains/@WLS_DOMAIN_UID@ + domainHome: /u01/domains/${wlsDomainUID} # The WebLogic Server Docker image that the Operator uses to start the domain - image: "@WLS_IMAGE_PATH_ACR@" + image: "${wlsImagePath}" # Defaults to "Always" if image tag (version) is ':latest' imagePullPolicy: "IfNotPresent" @@ -34,7 +53,7 @@ spec: # Identify which Secret contains the WebLogic Admin credentials, # the secret must contain 'username' and 'password' fields. webLogicCredentialsSecret: - name: "@WLS_DOMAIN_UID@-weblogic-credentials" + name: "${wlsDomainUID}-weblogic-credentials" # Whether to include the WebLogic Server stdout in the pod's stdout, default is true includeServerOutInPodLog: true @@ -44,7 +63,7 @@ spec: # The location for domain log, server logs, server out, introspector out, and Node Manager log files # see also 'logHomeEnabled', 'volumes', and 'volumeMounts'. - #logHome: /shared/logs/@WLS_DOMAIN_UID@ + #logHome: /shared/logs/${wlsDomainUID} # Set which WebLogic Servers the Operator will start # - "NEVER" will not start any server in the domain @@ -59,27 +78,81 @@ spec: # to set the Weblogic domain name env: - name: CUSTOM_DOMAIN_NAME - value: "@DOMAIN_NAME@" + value: "${wlsDomainName}" - name: JAVA_OPTIONS - value: "-Dweblogic.StdoutDebugEnabled=false" + value: "-Dweblogic.StdoutDebugEnabled=false ${javaOptions}" - name: USER_MEM_ARGS - value: "-Djava.security.egd=file:/dev/./urandom -Xms256m -Xmx512m " + value: "-Djava.security.egd=file:/dev/./urandom -Xms256m -Xmx512m -XX:MinRAMPercentage=25.0 -XX:MaxRAMPercentage=50.0 " - name: MANAGED_SERVER_PREFIX - value: "@MANAGED_SERVER_PREFIX@" + value: "${wlsManagedPrefix}" +EOF + if [[ "${enableSSL,,}" == "true" ]]; then + cat <>$filePath + - name: SSL_IDENTITY_PRIVATE_KEY_ALIAS + valueFrom: + secretKeyRef: + key: sslidentitykeyalias + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_IDENTITY_PRIVATE_KEY_PSW + valueFrom: + secretKeyRef: + key: sslidentitykeypassword + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_IDENTITY_PRIVATE_KEYSTORE_PATH + valueFrom: + secretKeyRef: + key: sslidentitystorepath + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_IDENTITY_PRIVATE_KEYSTORE_TYPE + valueFrom: + secretKeyRef: + key: sslidentitystoretype + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_IDENTITY_PRIVATE_KEYSTORE_PSW + valueFrom: + secretKeyRef: + key: sslidentitystorepassword + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_TRUST_KEYSTORE_PATH + valueFrom: + secretKeyRef: + key: ssltruststorepath + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_TRUST_KEYSTORE_TYPE + valueFrom: + secretKeyRef: + key: ssltruststoretype + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_TRUST_KEYSTORE_PSW + valueFrom: + secretKeyRef: + key: ssltruststorepassword + name: ${wlsDomainUID}-weblogic-ssl-credentials +EOF + fi + + # Resources + cat <>$filePath resources: requests: - cpu: "@RESOURCE_CPU@" - memory: "@RESOURCE_MEMORY@" + cpu: "${wlsCPU}" + memory: "${wlsMemory}" +EOF + if [[ "${enablePV,,}" == "true" ]]; then + cat <>$filePath # Optional volumes and mounts for the domain's pods. See also 'logHome'. - #volumes: - #- name: weblogic-domain-storage-volume - # persistentVolumeClaim: - # claimName: model-azurefile - #volumeMounts: - #- mountPath: /shared - # name: weblogic-domain-storage-volume - + volumes: + - name: ${wlsDomainUID}-pv-azurefile + persistentVolumeClaim: + claimName: ${wlsDomainUID}-pvc-azurefile + volumeMounts: + - mountPath: /shared + name: ${wlsDomainUID}-pv-azurefile +EOF + fi + + cat <>$filePath # The desired behavior for starting the domain's administration server. adminServer: # The serverStartState legal values are "RUNNING" or "ADMIN" @@ -112,10 +185,10 @@ spec: - key: "weblogic.clusterName" operator: In values: - - $(CLUSTER_NAME) + - \$(CLUSTER_NAME) topologyKey: "kubernetes.io/hostname" # The number of managed servers to start for unlisted clusters - replicas: @WLS_CLUSTER_REPLICAS@ + replicas: ${replicas} # Change the restartVersion to force the introspector job to rerun # and apply any new model configuration, to also force a subsequent @@ -130,12 +203,13 @@ spec: domainType: "WLS" # Optional configmap for additional models and variable files - #configMap: @WLS_DOMAIN_UID@-wdt-config-map + #configMap: ${wlsDomainUID}-wdt-config-map # All 'FromModel' domains require a runtimeEncryptionSecret with a 'password' field - runtimeEncryptionSecret: "@WLS_DOMAIN_UID@-runtime-encryption-secret" + runtimeEncryptionSecret: "${wlsDomainUID}-runtime-encryption-secret" # Secrets that are referenced by model yaml macros # (the model yaml in the optional configMap or in the image) #secrets: - #- @WLS_DOMAIN_UID@-datasource-secret + #- ${wlsDomainUID}-datasource-secret +EOF \ No newline at end of file diff --git a/src/main/arm/scripts/genImageModel.sh b/src/main/arm/scripts/genImageModel.sh new file mode 100644 index 000000000..3a8c4abe6 --- /dev/null +++ b/src/main/arm/scripts/genImageModel.sh @@ -0,0 +1,136 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# Initialize +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +export filePath=$1 +export appPackageUrls=$2 +export enableCustomSSL=$3 + +cat <${filePath} +# Copyright (c) 2020, 2021, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# Based on ./kubernetes/samples/scripts/create-weblogic-domain/model-in-image/model-images/model-in-image__WLS-v1/model.10.yaml +# in https://github.com/oracle/weblogic-kubernetes-operator. + +domainInfo: + AdminUserName: "@@SECRET:__weblogic-credentials__:username@@" + AdminPassword: "@@SECRET:__weblogic-credentials__:password@@" + ServerStartMode: "prod" + domainLibraries: [ 'wlsdeploy/domainLibraries/postgresql-42.2.8.jar', 'wlsdeploy/domainLibraries/mssql-jdbc-7.4.1.jre8.jar'] + +topology: + Name: "@@ENV:CUSTOM_DOMAIN_NAME@@" + ProductionModeEnabled: true + AdminServerName: "admin-server" + Cluster: + "cluster-1": + DynamicServers: + ServerTemplate: "cluster-1-template" + ServerNamePrefix: "@@ENV:MANAGED_SERVER_PREFIX@@" + DynamicClusterSize: "@@PROP:CLUSTER_SIZE@@" + MaxDynamicClusterSize: "@@PROP:CLUSTER_SIZE@@" + MinDynamicClusterSize: "0" + CalculatedListenPorts: false + Server: + "admin-server": + ListenPort: 7001 +EOF + +if [[ "${enableCustomSSL,,}" == "true" ]];then + cat <>${filePath} + SSL: + HostnameVerificationIgnored: true + ListenPort: 7002 + Enabled: true + HostnameVerifier: 'None' + ServerPrivateKeyAlias: "@@ENV:SSL_IDENTITY_PRIVATE_KEY_ALIAS@@" + ServerPrivateKeyPassPhraseEncrypted: "@@ENV:SSL_IDENTITY_PRIVATE_KEY_PSW@@" + KeyStores: 'CustomIdentityAndCustomTrust' + CustomIdentityKeyStoreFileName: "@@ENV:SSL_IDENTITY_PRIVATE_KEYSTORE_PATH@@" + CustomIdentityKeyStoreType: "@@ENV:SSL_IDENTITY_PRIVATE_KEYSTORE_TYPE@@" + CustomIdentityKeyStorePassPhraseEncrypted: "@@ENV:SSL_IDENTITY_PRIVATE_KEYSTORE_PSW@@" + CustomTrustKeyStoreFileName: "@@ENV:SSL_TRUST_KEYSTORE_PATH@@" + CustomTrustKeyStoreType: "@@ENV:SSL_TRUST_KEYSTORE_TYPE@@" + CustomTrustKeyStorePassPhraseEncrypted: "@@ENV:SSL_TRUST_KEYSTORE_PSW@@" +EOF +fi + +cat <>${filePath} + ServerTemplate: + "cluster-1-template": + Cluster: "cluster-1" + ListenPort: 8001 +EOF + +if [[ "${enableCustomSSL,,}" == "true" ]];then + cat <>${filePath} + SSL: + HostnameVerificationIgnored: true + ListenPort: 8002 + Enabled: true + HostnameVerifier: 'None' + ServerPrivateKeyAlias: "@@ENV:SSL_IDENTITY_PRIVATE_KEY_ALIAS@@" + ServerPrivateKeyPassPhraseEncrypted: "@@ENV:SSL_IDENTITY_PRIVATE_KEY_PSW@@" + KeyStores: 'CustomIdentityAndCustomTrust' + CustomIdentityKeyStoreFileName: "@@ENV:SSL_IDENTITY_PRIVATE_KEYSTORE_PATH@@" + CustomIdentityKeyStoreType: "@@ENV:SSL_IDENTITY_PRIVATE_KEYSTORE_TYPE@@" + CustomIdentityKeyStorePassPhraseEncrypted: "@@ENV:SSL_IDENTITY_PRIVATE_KEYSTORE_PSW@@" + CustomTrustKeyStoreFileName: "@@ENV:SSL_TRUST_KEYSTORE_PATH@@" + CustomTrustKeyStoreType: "@@ENV:SSL_TRUST_KEYSTORE_TYPE@@" + CustomTrustKeyStorePassPhraseEncrypted: "@@ENV:SSL_TRUST_KEYSTORE_PSW@@" +EOF +fi + +cat <>${filePath} + SecurityConfiguration: + NodeManagerUsername: "@@SECRET:__weblogic-credentials__:username@@" + NodeManagerPasswordEncrypted: "@@SECRET:__weblogic-credentials__:password@@" + +resources: + SelfTuning: + MinThreadsConstraint: + SampleMinThreads: + Target: "cluster-1" + Count: 1 + MaxThreadsConstraint: + SampleMaxThreads: + Target: "cluster-1" + Count: 10 + WorkManager: + SampleWM: + Target: "cluster-1" + MinThreadsConstraint: "SampleMinThreads" + MaxThreadsConstraint: "SampleMaxThreads" + +EOF + +if [ "${appPackageUrls}" == "[]" ]; then + exit 0 +fi + + cat <>${filePath} +appDeployments: + Application: +EOF + appPackageUrls=$(echo "${appPackageUrls:1:${#appPackageUrls}-2}") + appUrlArray=$(echo $appPackageUrls | tr "," "\n") + + index=1 + for item in $appUrlArray; do + # e.g. https://wlsaksapp.blob.core.windows.net/japps/testwebapp.war?sp=r&se=2021-04-29T15:12:38Z&sv=2020-02-10&sr=b&sig=7grL4qP%2BcJ%2BLfDJgHXiDeQ2ZvlWosRLRQ1ciLk0Kl7M%3D + fileNamewithQueryString="${item##*/}" + fileName="${fileNamewithQueryString%\?*}" + fileExtension="${fileName##*.}" + curl -m 120 -fL "$item" -o ${scriptDir}/model-images/wlsdeploy/applications/${fileName} + cat <>${filePath} + app${index}: + SourcePath: 'wlsdeploy/applications/${fileName}' + ModuleType: ${fileExtension} + Target: 'cluster-1' +EOF + index=$((index + 1)) + done \ No newline at end of file diff --git a/src/main/arm/scripts/model.yaml b/src/main/arm/scripts/model.yaml deleted file mode 100644 index e509b39ad..000000000 --- a/src/main/arm/scripts/model.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. - -# Based on ./kubernetes/samples/scripts/create-weblogic-domain/model-in-image/model-images/model-in-image__WLS-v1/model.10.yaml -# in https://github.com/oracle/weblogic-kubernetes-operator. - -domainInfo: - AdminUserName: "@@SECRET:__weblogic-credentials__:username@@" - AdminPassword: "@@SECRET:__weblogic-credentials__:password@@" - ServerStartMode: "prod" - -topology: - Name: "@@ENV:CUSTOM_DOMAIN_NAME@@" - AdminServerName: "admin-server" - Cluster: - "cluster-1": - DynamicServers: - ServerTemplate: "cluster-1-template" - ServerNamePrefix: "@@ENV:MANAGED_SERVER_PREFIX@@" - DynamicClusterSize: "@@PROP:CLUSTER_SIZE@@" - MaxDynamicClusterSize: "@@PROP:CLUSTER_SIZE@@" - MinDynamicClusterSize: "0" - CalculatedListenPorts: false - Server: - "admin-server": - ListenPort: 7001 - ServerTemplate: - "cluster-1-template": - Cluster: "cluster-1" - ListenPort: 8001 - -resources: - SelfTuning: - MinThreadsConstraint: - SampleMinThreads: - Target: "cluster-1" - Count: 1 - MaxThreadsConstraint: - SampleMaxThreads: - Target: "cluster-1" - Count: 10 - WorkManager: - SampleWM: - Target: "cluster-1" - MinThreadsConstraint: "SampleMinThreads" - MaxThreadsConstraint: "SampleMaxThreads" diff --git a/src/main/arm/scripts/setupNetworking.sh b/src/main/arm/scripts/setupNetworking.sh index a59c97eb5..0a8b4e8f9 100644 --- a/src/main/arm/scripts/setupNetworking.sh +++ b/src/main/arm/scripts/setupNetworking.sh @@ -16,14 +16,14 @@ function echo_stdout() { function install_helm() { # Install helm - browserURL=$(curl -s https://api.github.com/repos/helm/helm/releases/latest | + browserURL=$(curl -m ${curlMaxTime} -s https://api.github.com/repos/helm/helm/releases/latest | grep "browser_download_url.*linux-amd64.tar.gz.asc" | cut -d : -f 2,3 | tr -d \") helmLatestVersion=${browserURL#*download\/} helmLatestVersion=${helmLatestVersion%%\/helm*} helmPackageName=helm-${helmLatestVersion}-linux-amd64.tar.gz - curl -m 120 -fL https://get.helm.sh/${helmPackageName} -o /tmp/${helmPackageName} + curl -m ${curlMaxTime} -fL https://get.helm.sh/${helmPackageName} -o /tmp/${helmPackageName} tar -zxvf /tmp/${helmPackageName} -C /tmp mv /tmp/linux-amd64/helm /usr/local/bin/helm echo "helm version" @@ -62,7 +62,7 @@ function output_result() { #Function to display usage message function usage() { - echo_stdout "./setupNetworking.sh " + echo_stdout "./setupNetworking.sh " if [ $1 -eq 1 ]; then exit 1 fi @@ -174,6 +174,26 @@ function validate_input() { echo_stderr "appgwAlias is required. " usage 1 fi + + if [ -z "$enableInternalLB" ]; then + echo_stderr "enableInternalLB is required. " + usage 1 + fi + + if [[ -z "$appgwFrontendSSLCertData" || -z "${appgwFrontendSSLCertPsw}" ]]; then + echo_stderr "appgwFrontendSSLCertData and appgwFrontendSSLCertPsw are required. " + usage 1 + fi + + if [ -z "$enableCustomSSL" ]; then + echo_stderr "enableCustomSSL is required. " + usage 1 + fi + + if [ -z "$enableCookieBasedAffinity" ]; then + echo_stderr "enableCookieBasedAffinity is required. " + usage 1 + fi } function generate_admin_lb_definicion() { @@ -240,14 +260,288 @@ spec: EOF } +function generate_appgw_cluster_config_file_expose_https() +{ + export clusterIngressHttpsName=${wlsDomainUID}-cluster-appgw-ingress-https-svc + export clusterAppgwIngressHttpsYamlPath=${scriptDir}/appgw-cluster-ingress-https-svc.yaml + cat <${clusterAppgwIngressHttpsYamlPath} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${clusterIngressHttpsName} + namespace: ${wlsDomainNS} + annotations: + kubernetes.io/ingress.class: azure/application-gateway +EOF + +if [[ "${enableCookieBasedAffinity,,}" == "true" ]];then + cat <>${clusterAppgwIngressHttpsYamlPath} + appgw.ingress.kubernetes.io/cookie-based-affinity: "true" +EOF +fi + +cat <>${clusterAppgwIngressHttpsYamlPath} +spec: + tls: + - secretName: ${appgwFrontendSecretName} + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: ${svcCluster} + port: + number: ${clusterTargetPort} +EOF +} + +function generate_appgw_cluster_config_file_nossl() +{ + export clusterIngressName=${wlsDomainUID}-cluster-appgw-ingress-svc + export clusterAppgwIngressYamlPath=${scriptDir}/appgw-cluster-ingress-svc.yaml + cat <${clusterAppgwIngressYamlPath} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${clusterIngressName} + namespace: ${wlsDomainNS} + annotations: + kubernetes.io/ingress.class: azure/application-gateway +EOF + +if [[ "${enableCookieBasedAffinity,,}" == "true" ]];then + cat <>${clusterAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/cookie-based-affinity: "true" +EOF +fi + +cat <>${clusterAppgwIngressYamlPath} +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: ${svcCluster} + port: + number: ${clusterTargetPort} +EOF +} + +function generate_appgw_cluster_config_file_ssl() +{ + export clusterIngressName=${wlsDomainUID}-cluster-appgw-ingress-svc + export clusterAppgwIngressYamlPath=${scriptDir}/appgw-cluster-ingress-svc.yaml + cat <${clusterAppgwIngressYamlPath} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${clusterIngressName} + namespace: ${wlsDomainNS} + annotations: + kubernetes.io/ingress.class: azure/application-gateway + appgw.ingress.kubernetes.io/ssl-redirect: "true" + appgw.ingress.kubernetes.io/backend-protocol: "https" +EOF + if [[ "${enableCustomDNSAlias,,}" == "true" ]];then + cat <>${clusterAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/backend-hostname: "${dnsClusterLabel}.${dnsZoneName}" +EOF + else + cat <>${clusterAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/backend-hostname: "${appgwAlias}" +EOF + fi + + if [[ "${enableCookieBasedAffinity,,}" == "true" ]];then + cat <>${clusterAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/cookie-based-affinity: "true" +EOF +fi + +cat <>${clusterAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/appgw-trusted-root-certificate: "${appgwBackendSecretName}" + +spec: + tls: + - secretName: ${appgwFrontendSecretName} + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: ${svcCluster} + port: + number: ${clusterTargetPort} +EOF +} + +function generate_appgw_admin_config_file_nossl() +{ + export adminIngressName=${wlsDomainUID}-admin-appgw-ingress-svc + export adminAppgwIngressYamlPath=${scriptDir}/appgw-admin-ingress-svc.yaml + cat <${adminAppgwIngressYamlPath} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${adminIngressName} + namespace: ${wlsDomainNS} + annotations: + kubernetes.io/ingress.class: azure/application-gateway +EOF + +if [[ "${enableCookieBasedAffinity,,}" == "true" ]];then + cat <>${adminAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/cookie-based-affinity: "true" +EOF +fi + +cat <>${adminAppgwIngressYamlPath} +spec: + rules: + - http: + paths: + - path: /console* + pathType: Prefix + backend: + service: + name: ${svcAdminServer} + port: + number: ${adminTargetPort} +EOF +} + +function generate_appgw_admin_config_file_ssl() +{ + export adminIngressName=${wlsDomainUID}-admin-appgw-ingress-svc + export adminAppgwIngressYamlPath=${scriptDir}/appgw-admin-ingress-svc.yaml + cat <${adminAppgwIngressYamlPath} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${adminIngressName} + namespace: ${wlsDomainNS} + annotations: + kubernetes.io/ingress.class: azure/application-gateway + appgw.ingress.kubernetes.io/ssl-redirect: "true" + appgw.ingress.kubernetes.io/backend-protocol: "https" +EOF + + if [[ "${enableCustomDNSAlias,,}" == "true" ]];then + cat <>${adminAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/backend-hostname: "${dnsAdminLabel}.${dnsZoneName}" +EOF + else + cat <>${adminAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/backend-hostname: "${appgwAlias}" +EOF + fi + + if [[ "${enableCookieBasedAffinity,,}" == "true" ]];then + cat <>${adminAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/cookie-based-affinity: "true" +EOF +fi + + cat <>${adminAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/appgw-trusted-root-certificate: "${appgwBackendSecretName}" + +spec: + tls: + - secretName: ${appgwFrontendSecretName} + rules: + - http: + paths: + - path: /console* + pathType: Prefix + backend: + service: + name: ${svcAdminServer} + port: + number: ${adminTargetPort} +EOF +} + +function generate_appgw_cluster_config_file() { + if [[ "${enableCustomSSL,,}" == "true" ]];then + generate_appgw_cluster_config_file_ssl + else + generate_appgw_cluster_config_file_nossl + generate_appgw_cluster_config_file_expose_https + fi +} + +function generate_appgw_admin_config_file() { + if [[ "${enableCustomSSL,,}" == "true" ]];then + generate_appgw_admin_config_file_ssl + else + generate_appgw_admin_config_file_nossl + fi +} + +function output_create_gateway_ssl_k8s_secret(){ + echo "export gateway frontend certificates" + echo "$appgwFrontendSSLCertData" | base64 -d >${scriptDir}/$appgwFrontCertFileName + + appgwFrontendSSLCertPassin=${appgwFrontendSSLCertPsw} + if [[ "$appgwCertificateOption" == "${appgwSelfsignedCert}" ]];then + appgwFrontendSSLCertPassin="" # empty password + fi + + openssl pkcs12 \ + -in ${scriptDir}/$appgwFrontCertFileName \ + -nocerts \ + -out ${scriptDir}/$appgwFrontCertKeyFileName \ + -passin pass:${appgwFrontendSSLCertPassin} \ + -passout pass:${appgwFrontendSSLCertPsw} + + validate_status "Export key from frontend certificate." + + openssl rsa -in ${scriptDir}/$appgwFrontCertKeyFileName \ + -out ${scriptDir}/$appgwFrontCertKeyDecrytedFileName \ + -passin pass:${appgwFrontendSSLCertPsw} + + validate_status "Decryte private key." + + openssl pkcs12 \ + -in ${scriptDir}/$appgwFrontCertFileName \ + -clcerts \ + -nokeys \ + -out ${scriptDir}/$appgwFrontPublicCertFileName \ + -passin pass:${appgwFrontendSSLCertPassin} \ + + validate_status "Export cert from frontend certificate." + + echo "create k8s tsl secret for app gateway frontend ssl certificate" + kubectl -n ${wlsDomainNS} create secret tls ${appgwFrontendSecretName} \ + --key="${scriptDir}/$appgwFrontCertKeyDecrytedFileName" \ + --cert="${scriptDir}/$appgwFrontPublicCertFileName" +} + function query_admin_target_port() { - adminTargetPort=$(kubectl describe service ${svcAdminServer} -n ${wlsDomainNS} | grep 'TargetPort:' | tr -d -c 0-9) + if [[ "${enableCustomSSL,,}" == "true" ]];then + adminTargetPort=$(kubectl describe service ${svcAdminServer} -n ${wlsDomainNS} | grep 'default-secure' | tr -d -c 0-9) + else + adminTargetPort=$(kubectl describe service ${svcAdminServer} -n ${wlsDomainNS} | grep 'default' | tr -d -c 0-9) + fi + validate_status "Query admin target port." echo "Target port of ${adminServerName}: ${adminTargetPort}" } function query_cluster_target_port() { - clusterTargetPort=$(kubectl describe service ${svcCluster} -n ${wlsDomainNS} | grep 'TargetPort:' | tr -d -c 0-9) + if [[ "${enableCustomSSL,,}" == "true" ]];then + clusterTargetPort=$(kubectl describe service ${svcCluster} -n ${wlsDomainNS} | grep 'default-secure' | tr -d -c 0-9) + else + clusterTargetPort=$(kubectl describe service ${svcCluster} -n ${wlsDomainNS} | grep 'default' | tr -d -c 0-9) + fi + validate_status "Query cluster 1 target port." echo "Target port of ${clusterName}: ${clusterTargetPort}" } @@ -368,8 +662,7 @@ EOF function network_peers_aks_appgw() { # To successfully peer two virtual networks command 'az network vnet peering create' must be called twice with the values # for --vnet-name and --remote-vnet reversed. - aksLocation=$(az aks show --name ${aksClusterName} -g ${aksClusterRGName} -o tsv --query "location") - aksMCRGName="MC_${aksClusterRGName}_${aksClusterName}_${aksLocation}" + aksMCRGName=$(az aks show -n $aksClusterName -g $aksClusterRGName -o tsv --query "nodeResourceGroup") ret=$(az group exists ${aksMCRGName}) if [ "${ret,,}" == "false" ]; then echo_stderr "AKS namaged resource group ${aksMCRGName} does not exist." @@ -395,8 +688,24 @@ function network_peers_aks_appgw() { --allow-vnet-access validate_status "Create network peers for $aksNetWorkId and ${vnetName}." + + # For Kbectl network plugin: https://azure.github.io/application-gateway-kubernetes-ingress/how-tos/networking/#with-kubenet + # find route table used by aks cluster + routeTableId=$(az network route-table list -g $aksMCRGName --query "[].id | [0]" -o tsv) + + # get the application gateway's subnet + appGatewaySubnetId=$(az network application-gateway show -n $appgwName -g $curRGName -o tsv --query "gatewayIpConfigurations[0].subnet.id") + + # associate the route table to Application Gateway's subnet + az network vnet subnet update \ + --ids $appGatewaySubnetId \ + --route-table $routeTableId + + validate_status "Associate the route table ${routeTableId} to Application Gateway's subnet ${appGatewaySubnetId}" } + + function create_appgw_ingress() { if [[ "${enableAppGWIngress,,}" != "true" ]]; then return @@ -469,34 +778,40 @@ function create_appgw_ingress() { fi done - # generate ingress svc config for cluster - appgwIngressSvcConfig=${scriptDir}/azure-ingress-appgateway-cluster.yaml - cp ${scriptDir}/azure-ingress-appgateway.yaml.template ${appgwIngressSvcConfig} - ingressSvcName="${wlsDomainUID}-cluster-appgw-ingress-svc" - sed -i -e "s:@PATH@:\/:g" ${appgwIngressSvcConfig} - sed -i -e "s:@INGRESS_NAME@:${ingressSvcName}:g" ${appgwIngressSvcConfig} - sed -i -e "s:@NAMESPACE@:${wlsDomainNS}:g" ${appgwIngressSvcConfig} - sed -i -e "s:@CLUSTER_SERVICE_NAME@:${svcCluster}:g" ${appgwIngressSvcConfig} - sed -i -e "s:@TARGET_PORT@:${clusterTargetPort}:g" ${appgwIngressSvcConfig} - - kubectl apply -f ${appgwIngressSvcConfig} + # create tsl secret + output_create_gateway_ssl_k8s_secret + + + if [[ "${enableCustomSSL,,}" == "true" ]];then + # create backend tls secret + rootcertPath=${scriptDir}/root.cert + kubectl cp -n ${wlsDomainNS} ${wlsDomainUID}-${adminServerName}:${appgwBackendCertPath} ${rootcertPath} + validate_status "Copy public key from fileshare." + + az network application-gateway root-cert create \ + --gateway-name $appgwName \ + --resource-group $curRGName \ + --name ${appgwBackendSecretName} \ + --cert-file ${rootcertPath} + fi + + # generate ingress svc config for cluster + generate_appgw_cluster_config_file + kubectl apply -f ${clusterAppgwIngressYamlPath} validate_status "Create appgw ingress svc." - waitfor_svc_completed ${ingressSvcName} + waitfor_svc_completed ${clusterIngressName} + # expose https if e2e ssl is not set up. + if [[ "${enableCustomSSL,,}" != "true" ]];then + kubectl apply -f ${clusterAppgwIngressHttpsYamlPath} + validate_status "Create appgw ingress https svc." + waitfor_svc_completed ${clusterIngressHttpsName} + fi if [[ ${appgwForAdminServer,,} == "true" ]]; then - # generate ingress svc config for admin server - appgwIngressSvcConfig=${scriptDir}/azure-ingress-appgateway-admin.yaml - cp ${scriptDir}/azure-ingress-appgateway.yaml.template ${appgwIngressSvcConfig} - ingressSvcName="${wlsDomainUID}-admin-appgw-ingress-svc" - sed -i -e "s:@PATH@:\/console*:g" ${appgwIngressSvcConfig} - sed -i -e "s:@INGRESS_NAME@:${ingressSvcName}:g" ${appgwIngressSvcConfig} - sed -i -e "s:@NAMESPACE@:${wlsDomainNS}:g" ${appgwIngressSvcConfig} - sed -i -e "s:@CLUSTER_SERVICE_NAME@:${svcAdminServer}:g" ${appgwIngressSvcConfig} - sed -i -e "s:@TARGET_PORT@:${adminTargetPort}:g" ${appgwIngressSvcConfig} - - kubectl apply -f ${appgwIngressSvcConfig} + generate_appgw_admin_config_file + kubectl apply -f ${adminAppgwIngressYamlPath} validate_status "Create appgw ingress svc." - waitfor_svc_completed ${ingressSvcName} + waitfor_svc_completed ${adminIngressName} fi create_dns_CNAME_record @@ -506,6 +821,8 @@ function create_appgw_ingress() { export script="${BASH_SOURCE[0]}" export scriptDir="$(cd "$(dirname "${script}")" && pwd)" +source ${scriptDir}/common.sh + export aksClusterRGName=$1 export aksClusterName=$2 export wlsDomainName=$3 @@ -524,19 +841,36 @@ export dnsZoneName=${15} export dnsAdminLabel=${16} export dnsClusterLabel=${17} export appgwAlias=${18} +export enableInternalLB=${19} +export appgwFrontendSSLCertData=${20} +export appgwFrontendSSLCertPsw=${21} +export appgwCertificateOption=${22} +export enableCustomSSL=${23} +export enableCookieBasedAffinity=${24} export adminServerName="admin-server" export adminConsoleEndpoint="null" export appgwIngressHelmRepo="https://appgwingress.blob.core.windows.net/ingress-azure-helm-package/" +export appgwFrontCertFileName="appgw-frontend-cert.pfx" +export appgwFrontCertKeyDecrytedFileName="appgw-frontend-cert.key" +export appgwFrontCertKeyFileName="appgw-frontend-cert-decryted.key" +export appgwFrontPublicCertFileName="appgw-frontend-cert.crt" +export appgwFrontendSecretName="frontend-tls" +export appgwBackendSecretName="backend-tls" +export appgwSelfsignedCert="generateCert" +export azureAppgwIngressVersion="1.4.0" export clusterName="cluster-1" export clusterEndpoint="null" -export azureAppgwIngressVersion="1.4.0" +export httpsListenerName="myHttpsListenerName$(date +%s)" +export httpsRuleName="myHttpsRule$(date +%s)" export perfRetryInterval=30 # seconds export perfPodAttemps=5 export perfSVCAttemps=10 +export sharedPath="/shared" export svcAdminServer="${wlsDomainUID}-${adminServerName}" export svcCluster="${wlsDomainUID}-cluster-${clusterName}" export wlsDomainNS="${wlsDomainUID}-ns" +export appgwBackendCertPath="${sharedPath}/security/root.cert" echo $lbSvcValues diff --git a/src/main/arm/scripts/setupWLSDomain.sh b/src/main/arm/scripts/setupWLSDomain.sh index 170aef4ef..9d7ad7f3f 100644 --- a/src/main/arm/scripts/setupWLSDomain.sh +++ b/src/main/arm/scripts/setupWLSDomain.sh @@ -16,7 +16,7 @@ function echo_stdout() { #Function to display usage message function usage() { - echo_stdout "./setupWLSDomain.sh " + echo_stdout "./setupWLSDomain.sh " if [ $1 -eq 1 ]; then exit 1 fi @@ -113,6 +113,46 @@ function validate_input() { echo_stderr "wlsClusterSize is required. " usage 1 fi + + if [ -z "$enableCustomSSL" ]; then + echo_stderr "enableCustomSSL is required. " + usage 1 + fi + + if [[ -z "$wlsIdentityData" || -z "${wlsIdentityPsw}" ]]; then + echo_stderr "wlsIdentityPsw and wlsIdentityData are required. " + usage 1 + fi + + if [ -z "$wlsIdentityType" ]; then + echo_stderr "wlsIdentityType is required. " + usage 1 + fi + + if [[ -z "$wlsIdentityAlias" || -z "${wlsIdentityKeyPsw}" ]]; then + echo_stderr "wlsIdentityAlias and wlsIdentityKeyPsw are required. " + usage 1 + fi + + if [[ -z "$wlsTrustData" || -z "${wlsTrustPsw}" ]]; then + echo_stderr "wlsIdentityAlias and wlsIdentityKeyPsw are required. " + usage 1 + fi + + if [ -z "$wlsTrustType" ]; then + echo_stderr "wlsTrustType is required. " + usage 1 + fi + + if [ -z "$gatewayAlias" ]; then + echo_stderr "gatewayAlias is required. " + usage 1 + fi + + if [ -z "$enablePV" ]; then + echo_stderr "enablePV is required. " + usage 1 + fi } # Validate teminal status with $?, exit with exception if errors happen. @@ -142,14 +182,14 @@ function install_utilities() { validate_status ${ret} # Install helm - browserURL=$(curl -s https://api.github.com/repos/helm/helm/releases/latest \ - | grep "browser_download_url.*linux-amd64.tar.gz.asc" \ - | cut -d : -f 2,3 \ - | tr -d \") + browserURL=$(curl -m ${curlMaxTime} -s https://api.github.com/repos/helm/helm/releases/latest | + grep "browser_download_url.*linux-amd64.tar.gz.asc" | + cut -d : -f 2,3 | + tr -d \") helmLatestVersion=${browserURL#*download\/} helmLatestVersion=${helmLatestVersion%%\/helm*} helmPackageName=helm-${helmLatestVersion}-linux-amd64.tar.gz - curl -m 120 -fL https://get.helm.sh/${helmPackageName} -o /tmp/${helmPackageName} + curl -m ${curlMaxTime} -fL https://get.helm.sh/${helmPackageName} -o /tmp/${helmPackageName} tar -zxvf /tmp/${helmPackageName} -C /tmp mv /tmp/linux-amd64/helm /usr/local/bin/helm echo "helm version" @@ -159,12 +199,6 @@ function install_utilities() { echo "az cli version" ret=$(az --version) validate_status ${ret} - ret=$(az account show) - echo $ret >>stdout - if [ -n $(echo ${ret} | grep "systemAssignedIdentity") ]; then - echo_stderr "Make sure you are using user assigned identity." - exit 1 - fi } # Connect to AKS cluster @@ -184,18 +218,18 @@ function install_wls_operator() { ret=$(helm repo list) validate_status ${ret} helm install ${wlsOptRelease} weblogic-operator/weblogic-operator \ - --namespace ${wlsOptNameSpace} \ - --set serviceAccount=${wlsOptSA} \ - --set "enableClusterRoleBinding=true" \ - --set "domainNamespaceSelectionStrategy=LabelSelector" \ - --set "domainNamespaceLabelSelector=weblogic-operator\=enabled" \ - --wait + --namespace ${wlsOptNameSpace} \ + --set serviceAccount=${wlsOptSA} \ + --set "enableClusterRoleBinding=true" \ + --set "domainNamespaceSelectionStrategy=LabelSelector" \ + --set "domainNamespaceLabelSelector=weblogic-operator\=enabled" \ + --wait validate_status "Installing WLS operator." # valiadate weblogic operator ret=$(kubectl get pod -n ${wlsOptNameSpace} | grep "Running") - if [ -z "$ret" ];then + if [ -z "$ret" ]; then echo_stderr "Failed to install WebLogic operator." exit 1 fi @@ -223,28 +257,27 @@ function build_docker_image() { # Specify tag 'SkipASMAzSecPack' to skip policy 'linuxazuresecuritypackautodeployiaas_1.6' # Specify tag 'SkipNRMS*' to skip Microsoft internal NRMS policy, which cause vm-redeployed issue az vm create \ - --resource-group ${currentResourceGroup} \ - --name ${vmName} \ - --image "Canonical:UbuntuServer:18.04-LTS:latest" \ - --admin-username azureuser \ - --generate-ssh-keys \ - --nsg-rule NONE \ - --enable-agent true \ - --enable-auto-update false \ - --tags SkipASMAzSecPack=true SkipNRMSCorp=true SkipNRMSDatabricks=true SkipNRMSDB=true SkipNRMSHigh=true SkipNRMSMedium=true SkipNRMSRDPSSH=true SkipNRMSSAW=true SkipNRMSMgmt=true\ - --verbose + --resource-group ${currentResourceGroup} \ + --name ${vmName} \ + --image "Canonical:UbuntuServer:18.04-LTS:latest" \ + --admin-username azureuser \ + --generate-ssh-keys \ + --nsg-rule NONE \ + --enable-agent true \ + --enable-auto-update false \ + --tags SkipASMAzSecPack=true SkipNRMSCorp=true SkipNRMSDatabricks=true SkipNRMSDB=true SkipNRMSHigh=true SkipNRMSMedium=true SkipNRMSRDPSSH=true SkipNRMSSAW=true SkipNRMSMgmt=true --verbose validate_status "Check status of VM machine to build docker image." wlsImagePath="${ocrLoginServer}/middleware/weblogic:${wlsImageTag}" az vm extension set --name CustomScript \ - --extension-instance-name wls-image-script \ - --resource-group ${currentResourceGroup} \ - --vm-name ${vmName} \ - --publisher Microsoft.Azure.Extensions \ - --version 2.0 \ - --settings "{ \"fileUris\": [\"${scriptURL}model.yaml\",\"${scriptURL}model.properties\",\"${scriptURL}buildWLSDockerImage.sh\"]}" \ - --protected-settings "{\"commandToExecute\":\"bash buildWLSDockerImage.sh ${wlsImagePath} ${azureACRServer} ${azureACRUserName} ${azureACRPassword} ${newImageTag} \\\"${appPackageUrls}\\\" ${ocrSSOUser} ${ocrSSOPSW} ${wlsClusterSize}\"}" + --extension-instance-name wls-image-script \ + --resource-group ${currentResourceGroup} \ + --vm-name ${vmName} \ + --publisher Microsoft.Azure.Extensions \ + --version 2.0 \ + --settings "{ \"fileUris\": [\"${scriptURL}model.properties\",\"${scriptURL}genImageModel.sh\",\"${scriptURL}buildWLSDockerImage.sh\",\"${scriptURL}common.sh\"]}" \ + --protected-settings "{\"commandToExecute\":\"bash buildWLSDockerImage.sh ${wlsImagePath} ${azureACRServer} ${azureACRUserName} ${azureACRPassword} ${newImageTag} \\\"${appPackageUrls}\\\" ${ocrSSOUser} ${ocrSSOPSW} ${wlsClusterSize} ${enableCustomSSL} \"}" # If error fires, keep vm resource and exit. validate_status "Check status of buiding WLS domain image." @@ -256,49 +289,116 @@ function build_docker_image() { cleanup_vm } -# Deploy WebLogic domain and cluster -# * Create namespace for domain -# * Create secret for weblogic -# * Create secret for Azure file -# * Create secret for ACR -# * Deploy WebLogic domain using image in ACR -# * Wait for the domain completed -function setup_wls_domain() { - kubectl create namespace ${wlsDomainNS} - kubectl label namespace ${wlsDomainNS} weblogic-operator=enabled +function mount_fileshare() { + resourceGroupName="${currentResourceGroup}" + fileShareName="${azFileShareName}" - kubectl -n ${wlsDomainNS} create secret generic ${kubectlWLSCredentials} \ - --from-literal=username=${wlsUserName} --from-literal=password=${wlsPassword} - kubectl -n ${wlsDomainNS} label secret ${kubectlWLSCredentials} weblogic.domainUID=${wlsDomainUID} + # Disable https-only + az storage account update --name ${storageAccountName} --resource-group ${resourceGroupName} --https-only false - kubectl -n ${wlsDomainNS} create secret generic ${wlsDomainUID}-runtime-encryption-secret \ - --from-literal=password=${wdtRuntimePassword} - kubectl -n ${wlsDomainNS} label secret ${wlsDomainUID}-runtime-encryption-secret weblogic.domainUID=${wlsDomainUID} + mntRoot="/wls" + mntPath="$mntRoot/$storageAccountName/$fileShareName" - kubectl create secret docker-registry ${kubectlSecretForACR} \ - --docker-server=${azureACRServer} \ - --docker-username=${azureACRUserName} \ - --docker-password=${azureACRPassword} \ - -n ${wlsDomainNS} + mkdir -p $mntPath - if [[ "${storageAccountName}" != "null" ]]; then - create_pv - fi + httpEndpoint=$(az storage account show \ + --resource-group $resourceGroupName \ + --name $storageAccountName \ + --query "primaryEndpoints.file" | tr -d '"') + smbPath=$(echo $httpEndpoint | cut -c7-$(expr length $httpEndpoint))$fileShareName - # generate domain yaml - customDomainYaml=${scriptDir}/custom-domain.yaml - cp ${scriptDir}/domain.yaml.template ${customDomainYaml} - sed -i -e "s:@WLS_DOMAIN_UID@:${wlsDomainUID}:g" ${customDomainYaml} - sed -i -e "s:@WLS_IMAGE_PATH_ACR@:${azureACRServer}/aks-wls-images\:${newImageTag}:g" ${customDomainYaml} - sed -i -e "s:@RESOURCE_CPU@:${wlsCPU}:g" ${customDomainYaml} - sed -i -e "s:@RESOURCE_MEMORY@:${wlsMemory}:g" ${customDomainYaml} - sed -i -e "s:@DOMAIN_NAME@:${wlsDomainName}:g" ${customDomainYaml} - sed -i -e "s:@MANAGED_SERVER_PREFIX@:${managedServerPrefix}:g" ${customDomainYaml} - sed -i -e "s:@WLS_CLUSTER_REPLICAS@:${appReplicas}:g" ${customDomainYaml} + mount -t cifs $smbPath $mntPath -o username=$storageAccountName,password=$storageAccountKey,serverino,vers=3.0,file_mode=0777,dir_mode=0777 + validate_status "Mounting path." +} - kubectl apply -f ${customDomainYaml} +function unmount_fileshare() { + echo "unmount fileshare." + umount ${mntPath} + # Disable https-only + az storage account update --name ${storageAccountName} --resource-group ${currentResourceGroup} --https-only true +} - wait_for_domain_completed +function validate_ssl_keystores() { + #validate if identity keystore has entry + ${JAVA_HOME}/bin/keytool -list -v \ + -keystore ${mntPath}/$wlsIdentityKeyStoreFileName \ + -storepass $wlsIdentityPsw \ + -storetype $wlsIdentityType | + grep 'Entry type:' | + grep 'PrivateKeyEntry' + + validate_status "Validate Identity Keystore." + + #validate if trust keystore has entry + ${JAVA_HOME}/bin/keytool -list -v \ + -keystore ${mntPath}/${wlsTrustKeyStoreFileName} \ + -storepass $wlsTrustPsw \ + -storetype $wlsTrustType | + grep 'Entry type:' | + grep 'trustedCertEntry' + + validate_status "Validate Trust Keystore." + + #validate if trust keystore has entry + ${JAVA_HOME}/bin/keytool -list -v \ + -keystore ${mntPath}/${wlsTrustKeyStoreFileName} \ + -storepass $wlsTrustPsw \ + -storetype jks | + grep 'Entry type:' | + grep 'trustedCertEntry' + + ${JAVA_HOME}/bin/keytool -list -v \ + -keystore ${mntPath}/${wlsTrustKeyStoreJKSFileName} \ + -storepass $wlsTrustPsw \ + -storetype jks | + grep 'Entry type:' | + grep 'trustedCertEntry' + + validate_status "Validate Trust Keystore." + + echo "Validate SSL key stores successfull !!" +} + +function output_ssl_keystore() { + echo "Custom SSL is enabled. Storing CertInfo as files..." + # Create a folder for certificates + securityDir=${mntPath}/security + if [ ! -d "${securityDir}" ]; then + mkdir ${mntPath}/security + else + rm -f ${mntPath}/$wlsIdentityKeyStoreFileName + rm -f ${mntPath}/$wlsTrustKeyStoreFileName + rm -f ${mntPath}/${wlsIdentityRootCertFileName} + rm -f ${mntPath}/${wlsTrustKeyStoreJKSFileName} + fi + + #decode cert data once again as it would got base64 encoded + echo "$wlsIdentityData" | base64 -d >${mntPath}/$wlsIdentityKeyStoreFileName + echo "$wlsTrustData" | base64 -d >${mntPath}/$wlsTrustKeyStoreFileName + # export root cert. Used as gateway backend certificate + ${JAVA_HOME}/bin/keytool -export \ + -alias ${wlsIdentityAlias} \ + -noprompt \ + -file ${mntPath}/${wlsIdentityRootCertFileName} \ + -keystore ${mntPath}/$wlsIdentityKeyStoreFileName \ + -storepass ${wlsIdentityPsw} + + # export jks file + # -Dweblogic.security.SSL.trustedCAKeyStorePassPhrase for PKCS12 is not working correctly + # we neet to convert PKCS12 file to JKS file and specify in domain.yaml via -Dweblogic.security.SSL.trustedCAKeyStore + if [[ "${wlsTrustType,,}" != "jks" ]]; then + ${JAVA_HOME}/bin/keytool -importkeystore \ + -srckeystore ${mntPath}/${wlsTrustKeyStoreFileName} \ + -srcstoretype ${wlsTrustType} \ + -srcstorepass ${wlsTrustPsw} \ + -destkeystore ${mntPath}/${wlsTrustKeyStoreJKSFileName} \ + -deststoretype jks \ + -deststorepass ${wlsTrustPsw} + + validate_status "Export trust JKS file." + else + echo "$wlsTrustData" | base64 -d >${mntPath}/${wlsTrustKeyStoreJKSFileName} + fi } # Create storage for AKS cluster @@ -309,8 +409,8 @@ function create_pv() { export storageAccountKey=$(az storage account keys list --resource-group $currentResourceGroup --account-name $storageAccountName --query "[0].value" -o tsv) export azureSecretName="azure-secret" kubectl -n ${wlsDomainNS} create secret generic ${azureSecretName} \ - --from-literal=azurestorageaccountname=${storageAccountName} \ - --from-literal=azurestorageaccountkey=${storageAccountKey} + --from-literal=azurestorageaccountname=${storageAccountName} \ + --from-literal=azurestorageaccountkey=${storageAccountKey} # generate pv configurations customPVYaml=${scriptDir}/pv.yaml @@ -331,9 +431,91 @@ function create_pv() { # validate PV PVC ret=$(kubectl get pv | grep "${pvName}" | grep "${pvcName}") - if [ -z "$ret" ];then + if [ -z "$ret" ]; then echo_stderr "Failed to create pv/pvc." - fi + fi +} + +# Deploy WebLogic domain and cluster +# * Create namespace for domain +# * Create secret for weblogic +# * Create secret for Azure file +# * Create secret for ACR +# * Deploy WebLogic domain using image in ACR +# * Wait for the domain completed +function setup_wls_domain() { + kubectl create namespace ${wlsDomainNS} + kubectl label namespace ${wlsDomainNS} weblogic-operator=enabled + + kubectl -n ${wlsDomainNS} create secret generic \ + ${kubectlWLSCredentials} \ + --from-literal=username=${wlsUserName} \ + --from-literal=password=${wlsPassword} + + kubectl -n ${wlsDomainNS} label secret ${kubectlWLSCredentials} weblogic.domainUID=${wlsDomainUID} + + kubectl -n ${wlsDomainNS} create secret generic ${wlsDomainUID}-runtime-encryption-secret \ + --from-literal=password=${wdtRuntimePassword} + kubectl -n ${wlsDomainNS} label secret ${wlsDomainUID}-runtime-encryption-secret weblogic.domainUID=${wlsDomainUID} + + kubectl create secret docker-registry ${kubectlSecretForACR} \ + --docker-server=${azureACRServer} \ + --docker-username=${azureACRUserName} \ + --docker-password=${azureACRPassword} \ + -n ${wlsDomainNS} + + if [[ "${enablePV,,}" == "true" ]]; then + create_pv + fi + + export javaOptions="" + if [[ "${enableCustomSSL,,}" == "true" ]]; then + # use default Java, if no, install open jdk 11. + # why not Microsoft open jdk? No apk installation package! + export JAVA_HOME=/usr/lib/jvm/default-jvm/ + if [ ! -d "${JAVA_HOME}" ]; then + install_jdk + JAVA_HOME=/usr/lib/jvm/java-11-openjdk + fi + + mount_fileshare + output_ssl_keystore + validate_ssl_keystores + unmount_fileshare + + kubectl -n ${wlsDomainNS} create secret generic ${kubectlWLSSSLCredentials} \ + --from-literal=sslidentitykeyalias=${wlsIdentityAlias} \ + --from-literal=sslidentitykeypassword=${wlsIdentityKeyPsw} \ + --from-literal=sslidentitystorepath=${sharedPath}/$wlsIdentityKeyStoreFileName \ + --from-literal=sslidentitystorepassword=${wlsIdentityPsw} \ + --from-literal=sslidentitystoretype=${wlsIdentityType} \ + --from-literal=ssltruststorepath=${sharedPath}/${wlsTrustKeyStoreFileName} \ + --from-literal=ssltruststoretype=${wlsTrustType} \ + --from-literal=ssltruststorepassword=${wlsTrustPsw} + + kubectl -n ${wlsDomainNS} label secret ${kubectlWLSSSLCredentials} weblogic.domainUID=${wlsDomainUID} + javaOptions="-Dweblogic.security.SSL.ignoreHostnameVerification=true -Dweblogic.security.SSL.trustedCAKeyStore=${sharedPath}/${wlsTrustKeyStoreJKSFileName}" + fi + + # generate domain yaml + customDomainYaml=${scriptDir}/custom-domain.yaml + chmod ugo+x $scriptDir/genDomainConfig.sh + bash $scriptDir/genDomainConfig.sh \ + ${customDomainYaml} \ + ${appReplicas} \ + ${wlsCPU} \ + ${wlsDomainUID} \ + ${wlsDomainName} \ + "${azureACRServer}/aks-wls-images:${newImageTag}" \ + ${wlsMemory} \ + ${managedServerPrefix} \ + ${enableCustomSSL} \ + ${enablePV} \ + "${javaOptions}" + + kubectl apply -f ${customDomainYaml} + + wait_for_domain_completed } function wait_for_domain_completed() { @@ -440,6 +622,9 @@ function cleanup_vm() { export script="${BASH_SOURCE[0]}" export scriptDir="$(cd "$(dirname "${script}")" && pwd)" +source ${scriptDir}/common.sh +source ${scriptDir}/utility.sh + export ocrSSOUser=$1 export ocrSSOPSW=$2 export aksClusterRGName=$3 @@ -460,19 +645,37 @@ export currentResourceGroup=${17} export scriptURL=${18} export storageAccountName=${19} export wlsClusterSize=${20} +export enableCustomSSL=${21} +export wlsIdentityData=${22} +export wlsIdentityPsw=${23} +export wlsIdentityType=${24} +export wlsIdentityAlias=${25} +export wlsIdentityKeyPsw=${26} +export wlsTrustData=${27} +export wlsTrustPsw=${28} +export wlsTrustType=${29} +export gatewayAlias=${30} +export enablePV=${31} export adminServerName="admin-server" +export azFileShareName="weblogic" export exitCode=0 export ocrLoginServer="container-registry.oracle.com" export kubectlSecretForACR="regsecret" export kubectlWLSCredentials="${wlsDomainUID}-weblogic-credentials" +export kubectlWLSSSLCredentials="${wlsDomainUID}-weblogic-ssl-credentials" export newImageTag=$(date +%s) export storageFileShareName="weblogic" +export sharedPath="/shared" export wlsDomainNS="${wlsDomainUID}-ns" export wlsOptHelmChart="https://oracle.github.io/weblogic-kubernetes-operator/charts" export wlsOptNameSpace="weblogic-operator-ns" export wlsOptRelease="weblogic-operator" export wlsOptSA="weblogic-operator-sa" +export wlsIdentityKeyStoreFileName="security/identity.keystore" +export wlsTrustKeyStoreFileName="security/trust.keystore" +export wlsTrustKeyStoreJKSFileName="security/trust.jks" +export wlsIdentityRootCertFileName="security/root.cert" validate_input diff --git a/src/main/arm/scripts/utility.sh b/src/main/arm/scripts/utility.sh new file mode 100644 index 000000000..9fe96e172 --- /dev/null +++ b/src/main/arm/scripts/utility.sh @@ -0,0 +1,14 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +function install_jdk() { + # Install Microsoft OpenJDK + apk --no-cache add openjdk11 --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community + + echo "java version" + java -version + if [ $? -eq 1 ]; then + exit 1 + fi + # JAVA_HOME=/usr/lib/jvm/java-11-openjdk +} \ No newline at end of file diff --git a/src/main/bicep/mainTemplate.bicep b/src/main/bicep/mainTemplate.bicep index 64672aa3e..8215dbfb9 100644 --- a/src/main/bicep/mainTemplate.bicep +++ b/src/main/bicep/mainTemplate.bicep @@ -83,6 +83,9 @@ param enableAppGWIngress bool = false param enableAzureMonitoring bool = false @description('true to create persistent volume using file share.') param enableAzureFileShare bool = false +@description('true to enable cookie based affinity.') +param enableCookieBasedAffinity bool = false +param enableCustomSSL bool = false param enableDNSConfiguration bool = false @description('An user assigned managed identity. Make sure the identity has permission to create/update/delete/list Azure resources.') param identity object @@ -92,9 +95,9 @@ param keyVaultName string = 'kv-contoso' param keyVaultResourceGroup string = 'kv-contoso-rg' @description('Price tier for Key Vault.') param keyVaultSku string = 'Standard' -@description('The name of the secret in the specified KeyVault whose value is the SSL Certificate Data') +@description('The name of the secret in the specified KeyVault whose value is the SSL Certificate Data for Appliation Gateway frontend TLS/SSL.') param keyVaultSSLCertDataSecretName string = 'kv-ssl-data' -@description('The name of the secret in the specified KeyVault whose value is the password for the SSL Certificate') +@description('The name of the secret in the specified KeyVault whose value is the password for the SSL Certificate of Appliation Gateway frontend TLS/SSL') param keyVaultSSLCertPasswordSecretName string = 'kv-ssl-psw' param location string = 'eastus' @description('Object array to define Load Balancer service, each object must include service name, service target[admin-server or cluster-1], port.') @@ -109,8 +112,70 @@ param ocrSSOUser string @secure() @description('Base64 string of service principal. use the command to generate a testing string: az ad sp create-for-rbac --sdk-auth | base64 -w0') param servicePrincipal string = newGuid() +@allowed([ + 'uploadConfig' + 'keyVaultStoredConfig' +]) +@description('Two scenarios to refer to WebLogic Server TLS/SSL certificates.') +param sslConfigurationAccessOption string = 'uploadConfig' +@description('Secret name in KeyVault containing Weblogic Custom Identity Keystore Data') +param sslKeyVaultCustomIdentityKeyStoreDataSecretName string = 'kv-wls-identity-data' +@description('Secret name in KeyVault containing Weblogic Custom Identity Keystore Passphrase') +param sslKeyVaultCustomIdentityKeyStorePassPhraseSecretName string = 'kv-wls-identity-psw' +@description('Weblogic Custom Identity Keystore type') +@allowed([ + 'JKS' + 'PKCS12' +]) +param sslKeyVaultCustomIdentityKeyStoreType string = 'PKCS12' +@description('Secret name in KeyVault containing Weblogic Custom Trust Store Data') +param sslKeyVaultCustomTrustKeyStoreDataSecretName string = 'kv-wls-trust-data' +@description('Secret name in KeyVault containing Weblogic Custom Trust Store Passphrase') +param sslKeyVaultCustomTrustKeyStorePassPhraseSecretName string = 'kv-wls-trust-psw' +@description('WWeblogic Custom Trust Store type') +@allowed([ + 'JKS' + 'PKCS12' +]) +param sslKeyVaultCustomTrustKeyStoreType string = 'PKCS12' +@description('Resource group containing Weblogic SSL certificates') +param sslKeyVaultName string = 'kv-wls-ssl-name' +@description('Secret name in KeyVault containing Weblogic Server private key alias') +param sslKeyVaultPrivateKeyAliasSecretName string = 'contoso' +@description('Secret name in KeyVault containing Weblogic Server private key passphrase') +param sslKeyVaultPrivateKeyPassPhraseSecretName string = 'kv-wls-ssl-alias' +@description('Keyvault name containing Weblogic SSL certificates') +param sslKeyVaultResourceGroup string = 'rg-kv-wls-ssl-name' +@description('Custom Identity Store Data') +param sslUploadedCustomIdentityKeyStoreData string = 'null' +@secure() +@description('Custom Identity Store passphrase') +param sslUploadedCustomIdentityKeyStorePassphrase string = newGuid() +@description('Weblogic Custom Identity Store Type') +@allowed([ + 'JKS' + 'PKCS12' +]) +param sslUploadedCustomIdentityKeyStoreType string = 'PKCS12' +@description('Custom Trust Store data') +param sslUploadedCustomTrustKeyStoreData string = 'null' +@secure() +@description('Custom Trust Store passphrase') +param sslUploadedCustomTrustKeyStorePassPhrase string = newGuid() +@description('Weblogic Custom Trust Store Type') +@allowed([ + 'JKS' + 'PKCS12' +]) +param sslUploadedCustomTrustKeyStoreType string = 'PKCS12' +@description('Alias of the private key') +param sslUploadedPrivateKeyAlias string = 'contoso' +@secure() +@description('Password of the private key') +param sslUploadedPrivateKeyPassPhrase string = newGuid() +@description('True to set up internal load balancer service.') +param useInternalLB bool = false @description('ture to upload Java EE applications and deploy the applications to WebLogic domain.') -param uploadAppPackage bool = false param utcValue string = utcNow() @secure() @description('Password for model WebLogic Deploy Tooling runtime encrytion.') @@ -134,11 +199,25 @@ param wlsUserName string = 'weblogic' var const_appGatewaySSLCertOptionHaveCert = 'haveCert' var const_appGatewaySSLCertOptionHaveKeyVault = 'haveKeyVault' -var const_azureSubjectName = '${format('{0}.{1}.{2}', name_domainLabelforApplicationGateway, location, '.cloudapp.azure.com')}' +var const_azureSubjectName = '${format('{0}.{1}.{2}', name_domainLabelforApplicationGateway, location, 'cloudapp.azure.com')}' +var const_defaultKeystoreType = 'PKCS12' +var const_enableNetworking = (length(lbSvcValues) > 0) || enableAppGWIngress +var const_enablePV = enableCustomSSL || enableAzureFileShare +var const_identityKeyStoreType = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomIdentityKeyStoreType : sslUploadedCustomIdentityKeyStoreType +var const_trustKeyStoreType = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomTrustKeyStoreType : sslUploadedCustomTrustKeyStoreType +var const_wlsSSLCertOptionKeyVault = 'keyVaultStoredConfig' var name_defaultPidDeployment = 'pid' var name_dnsNameforApplicationGateway = '${concat(dnsNameforApplicationGateway, take(utcValue, 6))}' var name_domainLabelforApplicationGateway = '${take(concat(name_dnsNameforApplicationGateway, '-', toLower(resourceGroup().name), '-', toLower(wlsDomainName)), 63)}' +var name_identityKeyStoreDataSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomIdentityKeyStoreDataSecretName : 'myIdentityKeyStoreData${uniqueString(utcValue)}' +var name_identityKeyStorePswSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomIdentityKeyStorePassPhraseSecretName : 'myIdentityKeyStorePsw${uniqueString(utcValue)}' var name_keyVaultName = '${take(concat('wls-kv', uniqueString(utcValue)), 24)}' +var name_privateKeyAliasSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultPrivateKeyAliasSecretName : 'privateKeyAlias${uniqueString(utcValue)}' +var name_privateKeyPswSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultPrivateKeyPassPhraseSecretName : 'privateKeyPsw${uniqueString(utcValue)}' +var name_rgKeyvaultForWLSSSL = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultResourceGroup : resourceGroup().name +var name_trustKeyStoreDataSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomTrustKeyStoreDataSecretName : 'myTrustKeyStoreData${uniqueString(utcValue)}' +var name_trustKeyStorePswSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomTrustKeyStorePassPhraseSecretName : 'myTrustKeyStorePsw${uniqueString(utcValue)}' +var ref_wlsDomainDeployment = reference(resourceId('Microsoft.Resources/deployments', (enableCustomSSL)? 'setup-wls-cluster-with-custom-ssl-enabled' : 'setup-wls-cluster')) /* * Beginning of the offer deployment */ @@ -146,7 +225,36 @@ module pids './modules/_pids/_pid.bicep' = { name: 'initialization' } -module wlsDomainDeployment 'modules/setupWebLogicCluster.bicep' = { +module wlsSSLCertSecretsDeployment 'modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep' = if (enableCustomSSL && sslConfigurationAccessOption != const_wlsSSLCertOptionKeyVault) { + name: 'upload-wls-ssl-cert-to-keyvault' + params: { + keyVaultName: name_keyVaultName + sku: keyVaultSku + wlsIdentityKeyStoreData: sslUploadedCustomIdentityKeyStoreData + wlsIdentityKeyStoreDataSecretName: name_identityKeyStoreDataSecret + wlsIdentityKeyStorePassphrase: sslUploadedCustomIdentityKeyStorePassphrase + wlsIdentityKeyStorePassphraseSecretName: name_identityKeyStorePswSecret + wlsPrivateKeyAlias: sslUploadedPrivateKeyAlias + wlsPrivateKeyAliasSecretName: name_privateKeyAliasSecret + wlsPrivateKeyPassPhrase: sslUploadedPrivateKeyPassPhrase + wlsPrivateKeyPassPhraseSecretName: name_privateKeyPswSecret + wlsTrustKeyStoreData: sslUploadedCustomTrustKeyStoreData + wlsTrustKeyStoreDataSecretName: name_trustKeyStoreDataSecret + wlsTrustKeyStorePassPhrase: sslUploadedCustomTrustKeyStorePassPhrase + wlsTrustKeyStorePassPhraseSecretName: name_trustKeyStorePswSecret + } + dependsOn: [ + pids + ] +} + +// get key vault object in a resource group +resource sslKeyvault 'Microsoft.KeyVault/vaults@2019-09-01' existing = if (enableCustomSSL) { + name: (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultName : name_keyVaultName + scope: resourceGroup(name_rgKeyvaultForWLSSSL) +} + +module wlsDomainDeployment 'modules/setupWebLogicCluster.bicep' = if (!enableCustomSSL) { name: 'setup-wls-cluster' params: { _artifactsLocation: _artifactsLocation @@ -164,32 +272,100 @@ module wlsDomainDeployment 'modules/setupWebLogicCluster.bicep' = { aksClusterRGName: aksClusterRGName aksClusterName: aksClusterName aksVersion: aksVersion + appgwAlias: const_azureSubjectName + appPackageUrls: appPackageUrls + appReplicas: appReplicas + createACR: createACR + createAKSCluster: createAKSCluster + enableAzureMonitoring: enableAzureMonitoring + enableAzureFileShare: const_enablePV + enableCustomSSL: enableCustomSSL + enablePV: const_enablePV + identity: identity + location: location + managedServerPrefix: managedServerPrefix + ocrSSOPSW: ocrSSOPSW + ocrSSOUser: ocrSSOUser + wdtRuntimePassword: wdtRuntimePassword + wlsClusterSize: wlsClusterSize + wlsCPU: wlsCPU + wlsDomainName: wlsDomainName + wlsDomainUID: wlsDomainUID + wlsIdentityKeyStoreData: sslUploadedCustomIdentityKeyStoreData + wlsIdentityKeyStorePassphrase: sslUploadedCustomIdentityKeyStorePassphrase + wlsIdentityKeyStoreType: const_defaultKeystoreType + wlsImageTag: wlsImageTag + wlsMemory: wlsMemory + wlsPassword: wlsPassword + wlsPrivateKeyAlias: sslUploadedPrivateKeyAlias + wlsPrivateKeyPassPhrase: sslUploadedPrivateKeyPassPhrase + wlsTrustKeyStoreData: sslUploadedCustomTrustKeyStoreData + wlsTrustKeyStorePassPhrase: sslUploadedCustomTrustKeyStorePassPhrase + wlsTrustKeyStoreType: const_defaultKeystoreType + wlsUserName: wlsUserName + } + dependsOn: [ + pids + ] +} + +module wlsDomainWithCustomSSLDeployment 'modules/setupWebLogicCluster.bicep' = if (enableCustomSSL) { + name: 'setup-wls-cluster-with-custom-ssl-enabled' + params: { + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + _pidEnd: pids.outputs.wlsAKSEnd == '' ? name_defaultPidDeployment : pids.outputs.wlsAKSEnd + _pidStart: pids.outputs.wlsAKSStart == '' ? name_defaultPidDeployment : pids.outputs.wlsAKSStart + aciResourcePermissions: aciResourcePermissions + aciRetentionInDays: aciRetentionInDays + aciWorkspaceSku: aciWorkspaceSku + acrName: acrName + aksAgentPoolName: aksAgentPoolName + aksAgentPoolNodeCount: aksAgentPoolNodeCount + aksAgentPoolVMSize: aksAgentPoolVMSize + aksClusterNamePrefix: aksClusterNamePrefix + aksClusterRGName: aksClusterRGName + aksClusterName: aksClusterName + aksVersion: aksVersion + appgwAlias: const_azureSubjectName appPackageUrls: appPackageUrls appReplicas: appReplicas createACR: createACR createAKSCluster: createAKSCluster enableAzureMonitoring: enableAzureMonitoring - enableAzureFileShare: enableAzureFileShare + enableAzureFileShare: const_enablePV + enableCustomSSL: enableCustomSSL + enablePV: const_enablePV identity: identity location: location managedServerPrefix: managedServerPrefix ocrSSOPSW: ocrSSOPSW ocrSSOUser: ocrSSOUser - uploadAppPackage: uploadAppPackage wdtRuntimePassword: wdtRuntimePassword wlsClusterSize: wlsClusterSize wlsCPU: wlsCPU wlsDomainName: wlsDomainName wlsDomainUID: wlsDomainUID + wlsIdentityKeyStoreData: sslKeyvault.getSecret(name_identityKeyStoreDataSecret) + wlsIdentityKeyStorePassphrase: sslKeyvault.getSecret(name_identityKeyStorePswSecret) + wlsIdentityKeyStoreType: const_identityKeyStoreType wlsImageTag: wlsImageTag wlsMemory: wlsMemory wlsPassword: wlsPassword + wlsPrivateKeyAlias: sslKeyvault.getSecret(name_privateKeyAliasSecret) + wlsPrivateKeyPassPhrase: sslKeyvault.getSecret(name_privateKeyPswSecret) + wlsTrustKeyStoreData: sslKeyvault.getSecret(name_trustKeyStoreDataSecret) + wlsTrustKeyStorePassPhrase: sslKeyvault.getSecret(name_trustKeyStorePswSecret) + wlsTrustKeyStoreType: const_trustKeyStoreType wlsUserName: wlsUserName } + dependsOn: [ + wlsSSLCertSecretsDeployment + ] } -module keyvaultDeployment 'modules/_azure-resoruces/_keyvaultAdapter.bicep' = if (enableAppGWIngress && (appGatewayCertificateOption != const_appGatewaySSLCertOptionHaveKeyVault)) { - name: 'keyvault-deployment' +module appgwSecretDeployment 'modules/_azure-resoruces/_keyvaultAdapter.bicep' = if (enableAppGWIngress && (appGatewayCertificateOption != const_appGatewaySSLCertOptionHaveKeyVault)) { + name: 'appgateway-certificates-secrets-deployment' params: { certificateDataValue: appGatewaySSLCertData certificatePasswordValue: appGatewaySSLCertPassword @@ -201,10 +377,11 @@ module keyvaultDeployment 'modules/_azure-resoruces/_keyvaultAdapter.bicep' = if } dependsOn: [ wlsDomainDeployment + wlsDomainWithCustomSSLDeployment ] } -module networkingDeployment 'modules/networking.bicep' = { +module networkingDeployment 'modules/networking.bicep' = if (const_enableNetworking) { name: 'networking-deployment' params: { _artifactsLocation: _artifactsLocation @@ -213,36 +390,42 @@ module networkingDeployment 'modules/networking.bicep' = { _pidNetworkingStart: pids.outputs.networkingStart == '' ? name_defaultPidDeployment : pids.outputs.networkingStart _pidAppgwEnd: pids.outputs.appgwEnd == '' ? name_defaultPidDeployment : pids.outputs.appgwEnd _pidAppgwStart: pids.outputs.appgwStart == '' ? name_defaultPidDeployment : pids.outputs.appgwStart - aksClusterRGName: wlsDomainDeployment.outputs.aksClusterRGName - aksClusterName: wlsDomainDeployment.outputs.aksClusterName + aksClusterRGName: ref_wlsDomainDeployment.outputs.aksClusterRGName.value + aksClusterName: ref_wlsDomainDeployment.outputs.aksClusterName.value appGatewayCertificateOption: appGatewayCertificateOption appGatewayPublicIPAddressName: appGatewayPublicIPAddressName appgwForAdminServer: appgwForAdminServer createDNSZone: createDNSZone - dnsNameforApplicationGateway: dnsNameforApplicationGateway + dnsNameforApplicationGateway: name_domainLabelforApplicationGateway dnszoneAdminConsoleLabel: dnszoneAdminConsoleLabel dnszoneAppGatewayLabel: dnszoneAppGatewayLabel dnszoneName: dnszoneName dnszoneRGName: dnszoneRGName enableAppGWIngress: enableAppGWIngress + enableCookieBasedAffinity: enableCookieBasedAffinity + enableCustomSSL: enableCustomSSL enableDNSConfiguration: enableDNSConfiguration identity: identity - keyVaultName: (! enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultName : keyvaultDeployment.outputs.keyVaultName - keyVaultResourceGroup: (! enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultResourceGroup : resourceGroup().name - keyVaultSSLCertDataSecretName: (! enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultSSLCertDataSecretName : keyvaultDeployment.outputs.sslCertDataSecretName - keyVaultSSLCertPasswordSecretName: (! enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultSSLCertPasswordSecretName : keyvaultDeployment.outputs.sslCertPwdSecretName + keyVaultName: (!enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultName : appgwSecretDeployment.outputs.keyVaultName + keyVaultResourceGroup: (!enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultResourceGroup : resourceGroup().name + keyVaultSSLCertDataSecretName: (!enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultSSLCertDataSecretName : appgwSecretDeployment.outputs.sslCertDataSecretName + keyVaultSSLCertPasswordSecretName: (!enableAppGWIngress || (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault)) ? keyVaultSSLCertPasswordSecretName : appgwSecretDeployment.outputs.sslCertPwdSecretName location: location lbSvcValues: lbSvcValues servicePrincipal: servicePrincipal + useInternalLB: useInternalLB wlsDomainName: wlsDomainName wlsDomainUID: wlsDomainUID } + dependsOn:[ + appgwSecretDeployment + ] } -output aksClusterName string = wlsDomainDeployment.outputs.aksClusterName -output adminConsoleInternalUrl string = wlsDomainDeployment.outputs.adminServerUrl -output adminConsoleExternalUrl string = networkingDeployment.outputs.adminConsoleExternalUrl -output adminConsoleExternalSecuredUrl string = networkingDeployment.outputs.adminConsoleExternalSecuredUrl -output clusterInternalUrl string = wlsDomainDeployment.outputs.clusterSVCUrl -output clusterExternalUrl string = networkingDeployment.outputs.clusterExternalUrl -output clusterExternalSecuredURL string = networkingDeployment.outputs.clusterExternalSecuredURL +output aksClusterName string = ref_wlsDomainDeployment.outputs.aksClusterName.value +output adminConsoleInternalUrl string = ref_wlsDomainDeployment.outputs.adminServerUrl.value +output adminConsoleExternalUrl string = const_enableNetworking ? networkingDeployment.outputs.adminConsoleExternalUrl : '' +output adminConsoleExternalSecuredUrl string = const_enableNetworking ? networkingDeployment.outputs.adminConsoleExternalSecuredUrl : '' +output clusterInternalUrl string = ref_wlsDomainDeployment.outputs.clusterSVCUrl.value +output clusterExternalUrl string = const_enableNetworking ? networkingDeployment.outputs.clusterExternalUrl : '' +output clusterExternalSecuredUrl string = const_enableNetworking ? networkingDeployment.outputs.clusterExternalSecuredUrl : '' diff --git a/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep b/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep index b4c3db6c0..1c19943da 100644 --- a/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep @@ -1,10 +1,6 @@ // Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -@secure() -param appGatewaySSLCertificateData string = 'MIIKQQIBAzCCCgcGCSqGSIb3DQEHAaCCCfgEggn0MIIJ8DCCBKcGCSqGSIb3DQEHBqCCBJgwggSUAgEAMIIEjQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI6AB+FBLZ6zMCAggAgIIEYN6eVnBIdbhS89C6P7zd76at+tOhNXIAIdjdmpXxtS9MhAGTlH1iq4mlHNmSwgFUtHWi+QkUXr00Xi/+t8LZzCQPh4vAUVZVRJ2Yj0gA0VdIB+bBGA1wou93VJ1zr6PQpzhHRiiJ0eNFR0rZwmNPX44KMwZcbDVt+7qqgwItP6tIy3G6a+DoqUjRtBbgY9XQKwDVV/NcQ88tkGkDEWLVhTPgFOV1H47qugqKYzNDiegqd7osdDKY/f4vXz1t5HLnEfm2UxvPzHZD/xiMlZ/cnk7R40c4JXhjKTR3DQ0J+TZKW94pvDYz+HixiUV5/6Yw3O6SKhTduEhzhVO0yYh5msfBC86nz4bvt35Dy/KcaqPOFPbJ51uftO5lDHLMXX3ICypNQrIkFSfUafgFRhRnCMg+CBc/yaapY8if/ZPtWW530bk/uKL6CZXOqgGnGUsRvveRfum3rbLyduMgDGBsXM/dLmDVqCSECKSzxneraEaX5VOtQokk8vRx+clv0XR0LTG9iSN9Tez/MfnS6Ammh0iXqQWhYdDWLtGoyIEq2U4DMlIpvUyi5r1KrZwG0mJsyTnlFxNPXcgvA1LPlsvD5EBcpQTBtUL04Apg0W0xmXj8kfCzrBGUR8YTBCL0A6mH/a63t+1OBIZeYBCuWKDGr/6FXkkfS1XHM8WGGoor+m/rc9iThAj7c3KoA8i/G7hYrsXP16ranBb9kVNmKgQ0uRKPvx1jNCaewp7cEFXs7L/+TqtTOc/UMExeGu3kUafbL+4jTO0+/kF/F1aYMzPgO0T9Fg5XNDdwwfuoXfM21Qnn5JFH8oEfGFxzZeDot9PgBOj0ekp7dd1KI72uIgDVn0S/BNTw+DJpR2UhKR3TGzWL/TeDe1cDf5BBHZ66d5HIg7g5oNcGIIW5YTAhbDKkNdP5+ACRxD5KShSXsKDcAqQzqoJObrN6v0tr2FCu14F01RZAI3C6ROwwLsX4g+BcLlU+Gj8lvZIe9U+XNsdP8HMOhRklzuVYe/ifCGCJuRc8Jikm09jqUUJwacjvWeTP/VYkpGTWoi9nwCSDdsWicM5ouoc2eJWubFGjWHqsCoCJoXlHEMxBVV5KTatpU6cUW4CMrbqrCNXshnD/7kSN442ZfNcWgC8rsM8rloCS9feTA3mE9s9XrUelrMj7FhaOX7krJYaE8w22F+p8wBc41JY5tggMETpi5KyDzt+SDgmC5hOLEr+LEExYF7GCAUJRDJB7qJfjCA2ctqwJzfEejUjqt5HpHtcC7Qf7qACCgLmHrHSX6o9/urLVZsGnxMhadm9MNuSSW0z3od5b1b6XW3PfOSXqUZykv4ooCmPgkCIVAYoKeG2rwHAhKJ/QhZYXQ2zF34SYEO//hztGSzQKjivWhR2tRa/dCxKR/jrKnBUbedwtRD5LCWTef8rznmdH2wOCkS4KDGYRHqCWS8qr3TywkR8RsYMeZSBba9yoRiC2+jyush4DGyV+mBYXe9LpzuswggVBBgkqhkiG9w0BBwGgggUyBIIFLjCCBSowggUmBgsqhkiG9w0BDAoBAqCCBO4wggTqMBwGCiqGSIb3DQEMAQMwDgQIgPb003LlbnACAggABIIEyMsnCKRj/B1Rx87OT3EYs7IDM81qf8lqVMPAn2Z9m/ARMu+pOBzB5uY9+8FtL+XKd67WnCk/YxErB+fG/1WJHhOAj/DrnFYObz/FQU8ynkrshlDZvhj3IWBQoC2dO8aC13jPy8lyexony3tJMBNZblpLFJF4xsucDa8P3ROsLU7HhZel0LbiYUNIBC5ZRkyVPgG3R+H8iJTR5zTNR3d8gAwmOnlZAi16YOAnYdHrQZ4z29I8l15pY3I3dHo1A62T8jF+YT3+4EyekmD/FkcnxC0CdZ0OrndB+qnrOAnSmCNZ0oozwhvo/S4TT0pOaPBlAZtXE4WRtN0p12L4Dj7Kjbp1hq4CpxjaOq2Q2Y8D+RRgBb18JybYJ85NjfBAMMyVw3QJ09PNG56aYKAGyvrdKYcod5/ycPuLrMQKJmx5AlBzY0aR2MXxOqNBQ/cJDRyirLOAQIN6/7PH0CIlWp76u3EL8OO0fRFhrfbBsuKUoioR6AS518SprrJ3BXQv4cJz+8TsvlZWfM5XkdbFYfCqiCInLlNc7OkYC+H1vch2ResjdEodwqrFimogF0CuxQycgYf83H0aMWpb7kSa3LpcSSE/A9ogK9Rx3e/HmLvbZGzmcyA01D8dDhvqhJscnVBiPPCue6PAmM+RHoU7ma0+m7zk1NdfAtr0MMweqsLAN9U2Z9SCG+H9zMqiWmT6xsg+WhPMwc18W75WOlc6CJ6wCM0clx1bzFIu0jRKab78NLCjTODfRH0p0Sv/SIuJai4xJZCIFRWvMQaQL0Fc0b5x0GFD1ljJM15SMU3cv9/T+rzFgMweIU9gIi9CEZHnzTnp0zQXx3OTv+7ptQ+uqKpKvTyeR4FbDhn8hMX6LMeAnsyB9ZWX+TnKBQrYwjjmmbxcWOQtF9qYWR5dDQTFtY/DFn8r3rnU2DgO5Xe/n7pwDV6oBJ3DO6vhjpZZpsC2r9TTVLJQeK7LWzH2TNvC6vQbGFNLKMRiq5b8kdm2Kq1kiY+kzloy+uRiUf7JNxWDi0uSUUzEQlWP59a+QQ87clrFV4604wny/tHGZCoh6efuZipqT79bPoCVoy4GNylNjcmgrcq6oXJq7vnqbQl2H3/ECRlRg3KRv8lN5WJVKMLhogCy0q0BCoAOCxzaW5qip3n3Pz5OEOEC6WQAUH6U4ceSr+K3ZdcofAcOoRHVNwHcMp1HwflMB6JBo08yx4RrVPrYrkoCdZPRSpC7KSdWhSPhH4+jhgGgaYc90qFxJwRX6TQemfRf7s3EnEk4FGGzU1FYbItRTAJbPzEJIe58ndfzSn/NfoqJQWLv7K4BBYBKUKW0ArJ9Oe4OmPlp/be/FqTM4npZab7zQoeV7pvZmaFg7/dJUBxcTVZBX5eIwebK+zZSSinoT0jDVQgiXF8aV+/rXsCWpJDlTGZGgMsp9bZThHR/kYC1LdVw7qhr0bbnvVjwMn/EDHKVFRhspEF1plt9sTJFY0wsZG2984NPdL+9DfUF2n6xPgkqRg/qipa0NNIODzFNnnx6F1a4fw0U2geELx6rgPJ79rtvwz6kT3KsoV33E+9PMDmTDooKrYwk2Sf95OgLMCGCvJAHtH+0Ts2fDYu7p+EijoleJH7LdOFhgr3qqhYlYP2HHTElMCMGCSqGSIb3DQEJFTEWBBQ35ys3avr+k99lD2b1RqD5mQieXjAxMCEwCQYFKw4DAhoFAAQUEfKwNxwomTOTg32dc3hh5Qj4GFYECFd/2NISLDEkAgIIAA==' -@secure() -param appGatewaySSLCertificatePassword string = 'wlsEng@aug2019' @description('DNS for ApplicationGateway') param dnsNameforApplicationGateway string = take('wlsgw${uniqueString(utcValue)}-${toLower(resourceGroup().name)}', 63) @description('Public IP Name for the Application Gateway') @@ -14,26 +10,20 @@ param utcValue string = utcNow() var const_subnetAddressPrefix = '172.16.0.0/28' var const_virtualNetworkAddressPrefix = '172.16.0.0/24' var name_appGateway = 'appgw${uniqueString(utcValue)}' -var name_appGatewayCertificate = 'appGwSslCertificate' var name_appGatewaySubnet = 'appGatewaySubnet' var name_backendAddressPool = 'myGatewayBackendPool' var name_frontEndIPConfig = 'appGwPublicFrontendIp' var name_httpListener = 'HTTPListener' var name_httpPort = 'httpport' var name_httpSetting = 'myHTTPSetting' -var name_httpsListener = 'HTTPSListener' -var name_httpsPort = 'httpsport' var name_nsg = 'nsg${uniqueString(utcValue)}' var name_virtualNetwork = 'vnet${uniqueString(utcValue)}' var ref_appGatewaySubnet = resourceId('Microsoft.Network/virtualNetworks/subnets', name_virtualNetwork, name_appGatewaySubnet) var ref_backendAddressPool = resourceId('Microsoft.Network/applicationGateways/backendAddressPools', name_appGateway, name_backendAddressPool) var ref_backendHttpSettings = resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', name_appGateway, name_httpSetting) var ref_frontendHTTPPort = resourceId('Microsoft.Network/applicationGateways/frontendPorts', name_appGateway, name_httpPort) -var ref_frontendHTTPSPort = resourceId('Microsoft.Network/applicationGateways/frontendPorts', name_appGateway, name_httpsPort) var ref_frontendIPConfiguration = resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', name_appGateway, name_frontEndIPConfig) var ref_httpListener = resourceId('Microsoft.Network/applicationGateways/httpListeners', name_appGateway, name_httpListener) -var ref_httpsListener = resourceId('Microsoft.Network/applicationGateways/httpListeners', name_appGateway, name_httpsListener) -var ref_sslCertificate = resourceId('Microsoft.Network/applicationGateways/sslCertificates', name_appGateway, name_appGatewayCertificate) resource nsg 'Microsoft.Network/networkSecurityGroups@2020-07-01' = { name: name_nsg @@ -53,6 +43,25 @@ resource nsg 'Microsoft.Network/networkSecurityGroups@2020-07-01' = { } name: 'ALLOW_APPGW' } + { + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'Internet' + destinationAddressPrefix: '*' + access: 'Allow' + priority: 510 + direction: 'Inbound' + sourcePortRanges: [] + destinationPortRanges: [ + '80' + '443' + ] + sourceAddressPrefixes: [] + destinationAddressPrefixes: [] + } + name: 'ALLOW_HTTP_ACCESS' + } ] } } @@ -135,21 +144,6 @@ resource appGateway 'Microsoft.Network/applicationGateways@2020-07-01' = { port: 80 } } - { - name: name_httpsPort - properties: { - port: 443 - } - } - ] - sslCertificates: [ - { - name: 'appGwSslCertificate' - properties: { - data: appGatewaySSLCertificateData - password: appGatewaySSLCertificatePassword - } - } ] backendAddressPools: [ { @@ -172,22 +166,6 @@ resource appGateway 'Microsoft.Network/applicationGateways@2020-07-01' = { } } } - { - name: name_httpsListener - properties: { - frontendIPConfiguration: { - id: ref_frontendIPConfiguration - } - frontendPort: { - id: ref_frontendHTTPSPort - } - protocol: 'Https' - requireServerNameIndication: false - sslCertificate: { - id: ref_sslCertificate - } - } - } ] backendHttpSettingsCollection: [ { @@ -213,21 +191,6 @@ resource appGateway 'Microsoft.Network/applicationGateways@2020-07-01' = { } } } - { - name: 'HTTPSRoutingRule' - properties: { - ruleType: 'Basic' - httpListener: { - id: ref_httpsListener - } - backendAddressPool: { - id: ref_backendAddressPool - } - backendHttpSettings: { - id: ref_backendHttpSettings - } - } - } ] enableHttp2: false autoscaleConfiguration: { diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep new file mode 100644 index 000000000..7b8116154 --- /dev/null +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep @@ -0,0 +1,99 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +@description('Property to specify whether Azure Resource Manager is permitted to retrieve secrets from the key vault.') +param enabledForTemplateDeployment bool = true +@description('Name of the vault') +param keyVaultName string +@description('Price tier for Key Vault.') +param sku string +param wlsIdentityKeyStoreData string +param wlsIdentityKeyStoreDataSecretName string +@secure() +param wlsIdentityKeyStorePassphrase string +param wlsIdentityKeyStorePassphraseSecretName string +param wlsPrivateKeyAlias string +param wlsPrivateKeyAliasSecretName string +@secure() +param wlsPrivateKeyPassPhrase string +param wlsPrivateKeyPassPhraseSecretName string +param wlsTrustKeyStoreData string +param wlsTrustKeyStoreDataSecretName string +@secure() +param wlsTrustKeyStorePassPhrase string +param wlsTrustKeyStorePassPhraseSecretName string + +resource keyvault 'Microsoft.KeyVault/vaults@2019-09-01' = { + name: keyVaultName + location: resourceGroup().location + properties: { + enabledForTemplateDeployment: enabledForTemplateDeployment + sku: { + name: sku + family: 'A' + } + accessPolicies: [] + tenantId: subscription().tenantId + } +} + +resource identityKeyStoreDataSecret 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { + name: '${keyVaultName}/${wlsIdentityKeyStoreDataSecretName}' + properties: { + value: wlsIdentityKeyStoreData + } + dependsOn: [ + keyvault + ] +} + +resource identityKeyStorePswSecret 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { + name: '${keyVaultName}/${wlsIdentityKeyStorePassphraseSecretName}' + properties: { + value: wlsIdentityKeyStorePassphrase + } + dependsOn: [ + keyvault + ] +} + +resource privateKeyAliasSecret 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { + name: '${keyVaultName}/${wlsPrivateKeyAliasSecretName}' + properties: { + value: wlsPrivateKeyAlias + } + dependsOn: [ + keyvault + ] +} + +resource privateKeyPswSecret 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { + name: '${keyVaultName}/${wlsPrivateKeyPassPhraseSecretName}' + properties: { + value: wlsPrivateKeyPassPhrase + } + dependsOn: [ + keyvault + ] +} + +resource trustKeyStoreDataSecret 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { + name: '${keyVaultName}/${wlsTrustKeyStoreDataSecretName}' + properties: { + value: wlsTrustKeyStoreData + } + dependsOn: [ + keyvault + ] +} + +resource trustKeyStorePswSecret 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { + name: '${keyVaultName}/${wlsTrustKeyStorePassPhraseSecretName}' + properties: { + value: wlsTrustKeyStorePassPhrase + } + dependsOn: [ + keyvault + ] +} + diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep index 619cfa4c5..99d5e353a 100644 --- a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep @@ -17,13 +17,13 @@ param certificatePasswordValue string param enabledForTemplateDeployment bool = true @description('Name of the vault') -param name string +param keyVaultName string @description('Price tier for Key Vault.') param sku string resource keyvault 'Microsoft.KeyVault/vaults@2019-09-01' = { - name: name + name: keyVaultName location: resourceGroup().location properties: { enabledForTemplateDeployment: enabledForTemplateDeployment @@ -37,7 +37,7 @@ resource keyvault 'Microsoft.KeyVault/vaults@2019-09-01' = { } resource secretForCertificate 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { - name: '${name}/${certificateDataName}' + name: '${keyVaultName}/${certificateDataName}' properties: { value: certificateDataValue } @@ -47,7 +47,7 @@ resource secretForCertificate 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { } resource secretForCertPassword 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { - name: '${name}/${certificatePasswordName}' + name: '${keyVaultName}/${certificatePasswordName}' properties: { value: certificatePasswordValue } @@ -56,6 +56,6 @@ resource secretForCertPassword 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = ] } -output keyVaultName string = name +output keyVaultName string = keyVaultName output sslCertDataSecretName string = certificateDataName output sslCertPwdSecretName string = certificatePasswordName diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep index e71fe6f24..df69d23a1 100644 --- a/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep @@ -1,5 +1,6 @@ // Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +// Deploy Application Gateway certificate secrets. @description('Certificate data to store in the secret') param certificateDataValue string @@ -32,9 +33,6 @@ param useExistingAppGatewaySSLCertificate bool = false @description('Current deployment time. Used as a tag in deployment script.') param keyVaultName string = 'GEN_UNIQUE' -var name_kvWithExistingCertTemplateName = '_keyvaultWithExistingCertTemplate.json' -var name_kvWithNewCertTemplateName = '_keyvaultWithNewCertTemplate.json' -var name_kvTempaltesFolder = '_keyvault' var name_sslCertSecretName = 'myAppGatewaySSLCert' var name_sslCertPasswordSecretName = 'myAppGatewaySSLCertPassword' @@ -57,11 +55,11 @@ module keyVaultwithExistingAppGatewaySSLCert '_keyvault/_keyvaultWithExistingCer certificatePasswordName: name_sslCertPasswordSecretName certificatePasswordValue: certificatePasswordValue enabledForTemplateDeployment: enabledForTemplateDeployment - name: keyVaultName + keyVaultName: keyVaultName sku: sku } } -output keyVaultName string = (useExistingAppGatewaySSLCertificate ? keyVaultwithSelfSignedAppGatewaySSLCert.outputs.keyVaultName : keyVaultwithSelfSignedAppGatewaySSLCert.outputs.keyVaultName) +output keyVaultName string = (useExistingAppGatewaySSLCertificate ? keyVaultwithExistingAppGatewaySSLCert.outputs.keyVaultName : keyVaultwithSelfSignedAppGatewaySSLCert.outputs.keyVaultName) output sslCertDataSecretName string = (useExistingAppGatewaySSLCertificate ? keyVaultwithExistingAppGatewaySSLCert.outputs.sslCertDataSecretName : keyVaultwithSelfSignedAppGatewaySSLCert.outputs.secretName) output sslCertPwdSecretName string = (useExistingAppGatewaySSLCertificate ? keyVaultwithExistingAppGatewaySSLCert.outputs.sslCertPwdSecretName: '') diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvaultAppGatewayConnector.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvaultAppGatewayConnector.bicep deleted file mode 100644 index 1e8200b4b..000000000 --- a/src/main/bicep/modules/_azure-resoruces/_keyvaultAppGatewayConnector.bicep +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. -// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. - -@allowed([ - 'haveKeyVault' - 'haveCert' - 'generateCert' -]) -@description('Three scenarios we support for deploying app gateway') -param appGatewayCertificateOption string = 'haveCert' - -@description('Custom DNS Zone domain name for the Application Gateway') -param customDomainNameforApplicationGateway string = 'application.contoso.xyz' - -@description('Azure DNS for Application Gateway') -param domainLabelforApplicationGateway string = 'wlsgw' - -@description('Public IP Name for the Application Gateway') -param gatewayPublicIPAddressName string = 'gwip' - -@description('Key Vault name') -param keyVaultName string = '' - -@description('Name of resource group in current subscription containing the Key Vault') -param keyVaultResourceGroup string = '' - -@description('The name of the secret in the specified Key Vault whose value is the SSL Certificate Data,') -param keyVaultSSLCertDataSecretName string = 'myCertSecretData' - -@description('The name of the secret in the specified Key Vault whose value is the password for the SSL Certificate') -param keyVaultSSLCertPasswordSecretName string = '' - -var const_appGatewaySSLCertOptionGenerateCert = 'generateCert' -var const_appGatewaySSLCertOptionHaveCert = 'haveCert' -var const_appGatewaySSLCertOptionHaveKeyVault = 'haveKeyVault' -var name_appgwDeployment = 'appgw-${appGatewayCertificateOption}-deployment' - -// get key vault object in a resource group -resource existingKeyvault 'Microsoft.KeyVault/vaults@2019-09-01' existing = { - name: keyVaultName - scope: resourceGroup(keyVaultResourceGroup) -} - -module appGatewaywithExistingKeyVault '_appgateway.bicep' = if (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveKeyVault) { - name: 'appgw-${const_appGatewaySSLCertOptionHaveKeyVault}-deployment' - params: { - appGatewaySSLCertificateData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) - appGatewaySSLCertificatePassword: existingKeyvault.getSecret(keyVaultSSLCertPasswordSecretName) - dnsNameforApplicationGateway: domainLabelforApplicationGateway - gatewayPublicIPAddressName: gatewayPublicIPAddressName - } -} - -module appGatewaywithExistingSSLCert '_appgateway.bicep' = if (appGatewayCertificateOption == const_appGatewaySSLCertOptionHaveCert) { - name: 'appgw-${const_appGatewaySSLCertOptionHaveCert}-deployment' - params: { - appGatewaySSLCertificateData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) - appGatewaySSLCertificatePassword: existingKeyvault.getSecret(keyVaultSSLCertPasswordSecretName) - dnsNameforApplicationGateway: domainLabelforApplicationGateway - gatewayPublicIPAddressName: gatewayPublicIPAddressName - } -} - -module appGatewaywithSelfSignedCert '_appgateway.bicep' = if (appGatewayCertificateOption == const_appGatewaySSLCertOptionGenerateCert) { - name: 'appgw-${const_appGatewaySSLCertOptionGenerateCert}-deployment' - params: { - appGatewaySSLCertificateData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) - appGatewaySSLCertificatePassword: '' - dnsNameforApplicationGateway: domainLabelforApplicationGateway - gatewayPublicIPAddressName: gatewayPublicIPAddressName - } -} - -output appGatewayAlias string = reference(name_appgwDeployment).outputs.appGatewayAlias.value -output appGatewayName string = reference(name_appgwDeployment).outputs.appGatewayName.value -output appGatewayURL string = reference(name_appgwDeployment).outputs.appGatewayURL.value -output appGatewaySecuredURL string = reference(name_appgwDeployment).outputs.appGatewaySecuredURL.value -output vnetName string = reference(name_appgwDeployment).outputs.vnetName.value diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep index 95e917132..ef900e55d 100644 --- a/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep +++ b/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep @@ -5,9 +5,20 @@ param _artifactsLocation string @secure() param _artifactsLocationSasToken string = '' -param appgwName string = 'appgw-contoso' param appgwAlias string = 'appgw-contoso-alias' +param appgwName string = 'appgw-contoso' +@allowed([ + 'haveCert' + 'haveKeyVault' + 'generateCert' +]) +@description('Three scenarios we support for deploying app gateway') +param appgwCertificateOption string = 'haveCert' param appgwForAdminServer bool = true +@secure() +param appgwFrontendSSLCertData string = newGuid() +@secure() +param appgwFrontendSSLCertPsw string = newGuid() param aksClusterRGName string = 'aks-contoso-rg' param aksClusterName string = 'aks-contoso' param dnszoneAdminConsoleLabel string = 'admin' @@ -15,21 +26,24 @@ param dnszoneAppGatewayLabel string = 'www' param dnszoneName string = 'contoso.xyz' param dnszoneRGName string = 'dns-contoso-rg' param enableAppGWIngress bool = false +param enableCookieBasedAffinity bool = false +param enableCustomSSL bool = false param enableDNSConfiguration bool = false param identity object param lbSvcValues array = [] param location string = 'eastus' @secure() param servicePrincipal string = newGuid() +param useInternalLB bool = false param utcValue string = utcNow() param vnetName string = 'vnet-contoso' param wlsDomainName string = 'domain1' param wlsDomainUID string = 'sample-domain1' var const_appgwHelmConfigTemplate='appgw-helm-config.yaml.template' -var const_appgwIngressSVCTemplate='azure-ingress-appgateway.yaml.template' var const_appgwSARoleBindingFile='appgw-ingress-clusterAdmin-roleBinding.yaml' -var const_arguments = '${aksClusterRGName} ${aksClusterName} ${wlsDomainName} ${wlsDomainUID} "${string(lbSvcValues)}" ${enableAppGWIngress} ${subscription().id} ${resourceGroup().name} ${appgwName} ${vnetName} ${string(servicePrincipal)} ${appgwForAdminServer} ${enableDNSConfiguration} ${dnszoneRGName} ${dnszoneName} ${dnszoneAdminConsoleLabel} ${dnszoneAppGatewayLabel} ${appgwAlias}' +var const_arguments = '${aksClusterRGName} ${aksClusterName} ${wlsDomainName} ${wlsDomainUID} "${string(lbSvcValues)}" ${enableAppGWIngress} ${subscription().id} ${resourceGroup().name} ${appgwName} ${vnetName} ${string(servicePrincipal)} ${appgwForAdminServer} ${enableDNSConfiguration} ${dnszoneRGName} ${dnszoneName} ${dnszoneAdminConsoleLabel} ${dnszoneAppGatewayLabel} ${appgwAlias} ${useInternalLB} ${appgwFrontendSSLCertData} ${appgwFrontendSSLCertPsw} ${appgwCertificateOption} ${enableCustomSSL} ${enableCookieBasedAffinity}' +var const_commonScript = 'common.sh' var const_scriptLocation = uri(_artifactsLocation, 'scripts/') var const_primaryScript = 'setupNetworking.sh' @@ -44,8 +58,8 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { primaryScriptUri: uri(const_scriptLocation, '${const_primaryScript}${_artifactsLocationSasToken}') supportingScriptUris: [ uri(const_scriptLocation, '${const_appgwHelmConfigTemplate}${_artifactsLocationSasToken}') - uri(const_scriptLocation, '${const_appgwIngressSVCTemplate}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_appgwSARoleBindingFile}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_commonScript}${_artifactsLocationSasToken}') ] cleanupPreference: 'OnSuccess' retentionInterval: 'P1D' diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep index bf08dc0e8..c1cc64eaf 100644 --- a/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep +++ b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep @@ -8,8 +8,11 @@ param _artifactsLocationSasToken string = '' param aksClusterRGName string = '' param aksClusterName string = '' param acrName string = '' +param appgwAlias string = 'contoso' param appPackageUrls array = [] param appReplicas int = 2 +param enableCustomSSL bool = false +param enablePV bool = false param identity object param location string = 'eastus' param managedServerPrefix string = 'managed-server' @@ -18,23 +21,45 @@ param ocrSSOPSW string param ocrSSOUser string param storageAccountName string = 'null' param utcValue string = utcNow() -param wdtRuntimePassword string = 'welcome1' +@secure() +param wdtRuntimePassword string param wlsClusterSize int = 5 param wlsCPU string = '200m' param wlsDomainName string = 'domain1' param wlsDomainUID string = 'sample-domain1' +param wlsIdentityKeyStoreData string ='null' +@secure() +param wlsIdentityKeyStorePassphrase string = newGuid() +@allowed([ + 'JKS' + 'PKCS12' +]) +param wlsIdentityKeyStoreType string = 'PKCS12' param wlsImageTag string = '12.2.1.4' param wlsMemory string = '1.5Gi' @secure() param wlsPassword string +param wlsPrivateKeyAlias string ='contoso' +@secure() +param wlsPrivateKeyPassPhrase string = newGuid() +param wlsTrustKeyStoreData string = 'null' +@secure() +param wlsTrustKeyStorePassPhrase string = newGuid() +@allowed([ + 'JKS' + 'PKCS12' +]) +param wlsTrustKeyStoreType string = 'PKCS12' param wlsUserName string = 'weblogic' -var const_arguments = '${ocrSSOUser} ${ocrSSOPSW} ${aksClusterRGName} ${aksClusterName} ${wlsImageTag} ${acrName} ${wlsDomainName} ${wlsDomainUID} ${wlsUserName} ${wlsPassword} ${wdtRuntimePassword} ${wlsCPU} ${wlsMemory} ${managedServerPrefix} ${appReplicas} ${string(appPackageUrls)} ${resourceGroup().name} ${const_scriptLocation} ${storageAccountName} ${wlsClusterSize}' -var const_domainTemplate = 'domain.yaml.template' +var const_arguments = '${ocrSSOUser} ${ocrSSOPSW} ${aksClusterRGName} ${aksClusterName} ${wlsImageTag} ${acrName} ${wlsDomainName} ${wlsDomainUID} ${wlsUserName} ${wlsPassword} ${wdtRuntimePassword} ${wlsCPU} ${wlsMemory} ${managedServerPrefix} ${appReplicas} ${string(appPackageUrls)} ${resourceGroup().name} ${const_scriptLocation} ${storageAccountName} ${wlsClusterSize} ${enableCustomSSL} ${wlsIdentityKeyStoreData} ${wlsIdentityKeyStorePassphrase} ${wlsIdentityKeyStoreType} ${wlsPrivateKeyAlias} ${wlsPrivateKeyPassPhrase} ${wlsTrustKeyStoreData} ${wlsTrustKeyStorePassPhrase} ${wlsTrustKeyStoreType} ${appgwAlias} ${enablePV} ' +var const_commonScript = 'common.sh' var const_pvTempalte = 'pv.yaml.template' var const_pvcTempalte = 'pvc.yaml.template' var const_scriptLocation = uri(_artifactsLocation, 'scripts/') +var const_genDomainConfigScript= 'genDomainConfig.sh' var const_setUpDomainScript = 'setupWLSDomain.sh' +var const_utilityScript= 'utility.sh' resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { name: 'ds-wls-cluster-creation' @@ -46,9 +71,11 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { arguments: const_arguments primaryScriptUri: uri(const_scriptLocation, '${const_setUpDomainScript}${_artifactsLocationSasToken}') supportingScriptUris: [ - uri(const_scriptLocation, '${const_domainTemplate}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_genDomainConfigScript}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_utilityScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_pvTempalte}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_pvcTempalte}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_commonScript}${_artifactsLocationSasToken}') ] cleanupPreference: 'OnSuccess' retentionInterval: 'P1D' diff --git a/src/main/bicep/modules/networking.bicep b/src/main/bicep/modules/networking.bicep index a82a6d3d1..47b1a428b 100644 --- a/src/main/bicep/modules/networking.bicep +++ b/src/main/bicep/modules/networking.bicep @@ -35,6 +35,8 @@ param dnszoneAppGatewayLabel string = 'www' param dnszoneRGName string = 'dns-contoso-rg' @description('true to set up Application Gateway ingress.') param enableAppGWIngress bool = false +param enableCookieBasedAffinity bool = false +param enableCustomSSL bool = false param enableDNSConfiguration bool = false param identity object @description('Existing Key Vault Name') @@ -50,7 +52,8 @@ param location string = 'eastus' param lbSvcValues array = [] @secure() param servicePrincipal string = newGuid() -param utcValue string = utcNow() +@description('True to set up internal load balancer service.') +param useInternalLB bool = false @description('Name of WebLogic domain to create.') param wlsDomainName string = 'domain1' @description('UID of WebLogic domain, used in WebLogic Operator.') @@ -58,8 +61,7 @@ param wlsDomainUID string = 'sample-domain1' var const_appgwCustomDNSAlias = format('{0}.{1}/', dnszoneAppGatewayLabel, dnszoneName) var const_appgwAdminCustomDNSAlias = format('{0}.{1}/', dnszoneAdminConsoleLabel, dnszoneName) -var name_dnsNameforApplicationGateway = '${concat(dnsNameforApplicationGateway, take(utcValue, 6))}' -var name_domainLabelforApplicationGateway = '${take(concat(name_dnsNameforApplicationGateway, '-', toLower(resourceGroup().name), '-', toLower(wlsDomainName)), 63)}' +var const_appgwSSLCertOptionGenerateCert = 'generateCert' module pidNetworkingStart './_pids/_pid.bicep' = { name: 'pid-networking-start-deployment' @@ -75,23 +77,23 @@ module pidAppgwStart './_pids/_pid.bicep' = if (enableAppGWIngress) { } } -module appgwDeployment '_azure-resoruces/_keyvaultAppGatewayConnector.bicep' = if (enableAppGWIngress) { +module appgwDeployment '_azure-resoruces/_appgateway.bicep' = if (enableAppGWIngress) { name: 'app-gateway-deployment' params: { - appGatewayCertificateOption: appGatewayCertificateOption - customDomainNameforApplicationGateway: format('{0}.{1}', dnszoneAppGatewayLabel, dnszoneName) - domainLabelforApplicationGateway: name_domainLabelforApplicationGateway + dnsNameforApplicationGateway: dnsNameforApplicationGateway gatewayPublicIPAddressName: appGatewayPublicIPAddressName - keyVaultName: keyVaultName - keyVaultResourceGroup: keyVaultResourceGroup - keyVaultSSLCertDataSecretName: keyVaultSSLCertDataSecretName - keyVaultSSLCertPasswordSecretName: keyVaultSSLCertPasswordSecretName } dependsOn: [ pidAppgwStart ] } +// get key vault object in a resource group +resource existingKeyvault 'Microsoft.KeyVault/vaults@2019-09-01' existing = if (enableAppGWIngress) { + name: keyVaultName + scope: resourceGroup(keyVaultResourceGroup) +} + module dnsZoneDeployment '_azure-resoruces/_dnsZones.bicep' = if (enableDNSConfiguration && createDNSZone) { name: 'dnszone-deployment' params: { @@ -103,14 +105,90 @@ module dnsZoneDeployment '_azure-resoruces/_dnsZones.bicep' = if (enableDNSConfi ] } -module networkingDeployment '_deployment-scripts/_ds-create-networking.bicep' = { +module networkingDeployment '_deployment-scripts/_ds-create-networking.bicep' = if (enableAppGWIngress && appGatewayCertificateOption != const_appgwSSLCertOptionGenerateCert) { name: 'ds-networking-deployment' params: { _artifactsLocation: _artifactsLocation _artifactsLocationSasToken: _artifactsLocationSasToken appgwName: enableAppGWIngress ? appgwDeployment.outputs.appGatewayName : 'null' appgwAlias: enableAppGWIngress ? appgwDeployment.outputs.appGatewayAlias : 'null' + appgwCertificateOption: appGatewayCertificateOption + appgwForAdminServer: appgwForAdminServer + appgwFrontendSSLCertData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) + appgwFrontendSSLCertPsw: existingKeyvault.getSecret(keyVaultSSLCertPasswordSecretName) + aksClusterRGName: aksClusterRGName + aksClusterName: aksClusterName + dnszoneAdminConsoleLabel: dnszoneAdminConsoleLabel + dnszoneAppGatewayLabel: dnszoneAppGatewayLabel + dnszoneName: dnszoneName + dnszoneRGName: createDNSZone ? resourceGroup().name : dnszoneRGName + enableAppGWIngress: enableAppGWIngress + enableCookieBasedAffinity: enableCookieBasedAffinity + enableCustomSSL: enableCustomSSL + enableDNSConfiguration: enableDNSConfiguration + identity: identity + lbSvcValues: lbSvcValues + location: location + servicePrincipal: servicePrincipal + useInternalLB: useInternalLB + vnetName: enableAppGWIngress ? appgwDeployment.outputs.vnetName : 'null' + wlsDomainName: wlsDomainName + wlsDomainUID: wlsDomainUID + } + dependsOn: [ + appgwDeployment + dnsZoneDeployment + ] +} + +// Wrokaround for "Error BCP180: Function "getSecret" is not valid at this location. It can only be used when directly assigning to a module parameter with a secure decorator." +module networkingDeployment2 '_deployment-scripts/_ds-create-networking.bicep' = if (enableAppGWIngress && appGatewayCertificateOption == const_appgwSSLCertOptionGenerateCert) { + name: 'ds-networking-deployment-1' + params: { + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + appgwName: enableAppGWIngress ? appgwDeployment.outputs.appGatewayName : 'null' + appgwAlias: enableAppGWIngress ? appgwDeployment.outputs.appGatewayAlias : 'null' + appgwCertificateOption: appGatewayCertificateOption + appgwForAdminServer: appgwForAdminServer + appgwFrontendSSLCertData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) + appgwFrontendSSLCertPsw: 'null' + aksClusterRGName: aksClusterRGName + aksClusterName: aksClusterName + dnszoneAdminConsoleLabel: dnszoneAdminConsoleLabel + dnszoneAppGatewayLabel: dnszoneAppGatewayLabel + dnszoneName: dnszoneName + dnszoneRGName: createDNSZone ? resourceGroup().name : dnszoneRGName + enableAppGWIngress: enableAppGWIngress + enableCustomSSL: enableCustomSSL + enableCookieBasedAffinity: enableCookieBasedAffinity + enableDNSConfiguration: enableDNSConfiguration + identity: identity + lbSvcValues: lbSvcValues + location: location + servicePrincipal: servicePrincipal + useInternalLB: useInternalLB + vnetName: enableAppGWIngress ? appgwDeployment.outputs.vnetName : 'null' + wlsDomainName: wlsDomainName + wlsDomainUID: wlsDomainUID + } + dependsOn: [ + appgwDeployment + dnsZoneDeployment + ] +} + +module networkingDeployment3 '_deployment-scripts/_ds-create-networking.bicep' = if (!enableAppGWIngress) { + name: 'ds-networking-deployment-2' + params: { + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + appgwName: enableAppGWIngress ? appgwDeployment.outputs.appGatewayName : 'null' + appgwAlias: enableAppGWIngress ? appgwDeployment.outputs.appGatewayAlias : 'null' + appgwCertificateOption: appGatewayCertificateOption appgwForAdminServer: appgwForAdminServer + appgwFrontendSSLCertData: 'null' + appgwFrontendSSLCertPsw: 'null' aksClusterRGName: aksClusterRGName aksClusterName: aksClusterName dnszoneAdminConsoleLabel: dnszoneAdminConsoleLabel @@ -118,11 +196,14 @@ module networkingDeployment '_deployment-scripts/_ds-create-networking.bicep' = dnszoneName: dnszoneName dnszoneRGName: createDNSZone ? resourceGroup().name : dnszoneRGName enableAppGWIngress: enableAppGWIngress + enableCookieBasedAffinity: enableCookieBasedAffinity + enableCustomSSL: enableCustomSSL enableDNSConfiguration: enableDNSConfiguration identity: identity lbSvcValues: lbSvcValues location: location servicePrincipal: servicePrincipal + useInternalLB: useInternalLB vnetName: enableAppGWIngress ? appgwDeployment.outputs.vnetName : 'null' wlsDomainName: wlsDomainName wlsDomainUID: wlsDomainUID @@ -153,7 +234,7 @@ module pidNetworkingEnd './_pids/_pid.bicep' = { ] } -output adminConsoleExternalUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('http://{0}console', const_appgwAdminCustomDNSAlias) : format('http://{0}/console', appgwDeployment.outputs.appGatewayAlias)) : networkingDeployment.outputs.adminConsoleLBUrl -output adminConsoleExternalSecuredUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('https://{0}console', const_appgwAdminCustomDNSAlias) : format('https://{0}/console', appgwDeployment.outputs.appGatewayAlias)) : '' -output clusterExternalUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('http://{0}', const_appgwCustomDNSAlias) : appgwDeployment.outputs.appGatewayURL) : networkingDeployment.outputs.clusterLBUrl -output clusterExternalSecuredURL string = enableAppGWIngress ? (enableDNSConfiguration ? format('https://{0}', const_appgwCustomDNSAlias) : appgwDeployment.outputs.appGatewaySecuredURL) : '' +output adminConsoleExternalUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('http://{0}console', const_appgwAdminCustomDNSAlias) : format('http://{0}/console', appgwDeployment.outputs.appGatewayAlias)) : networkingDeployment3.outputs.adminConsoleLBUrl +output adminConsoleExternalSecuredUrl string = enableAppGWIngress && enableCustomSSL ? (enableDNSConfiguration ? format('https://{0}console', const_appgwAdminCustomDNSAlias) : format('https://{0}/console', appgwDeployment.outputs.appGatewayAlias)) : '' +output clusterExternalUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('http://{0}', const_appgwCustomDNSAlias) : appgwDeployment.outputs.appGatewayURL) : networkingDeployment3.outputs.clusterLBUrl +output clusterExternalSecuredUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('https://{0}', const_appgwCustomDNSAlias) : appgwDeployment.outputs.appGatewaySecuredURL) : '' diff --git a/src/main/bicep/modules/setupWebLogicCluster.bicep b/src/main/bicep/modules/setupWebLogicCluster.bicep index 9798fa405..ebab9dad9 100644 --- a/src/main/bicep/modules/setupWebLogicCluster.bicep +++ b/src/main/bicep/modules/setupWebLogicCluster.bicep @@ -44,6 +44,7 @@ param aksClusterRGName string = '' param aksClusterName string = '' @description('The AKS version.') param aksVersion string = 'default' +param appgwAlias string = 'contoso' @description('Urls of Java EE application packages.') param appPackageUrls array = [] @description('The number of managed server to start.') @@ -56,6 +57,8 @@ param createAKSCluster bool = true param enableAzureMonitoring bool = false @description('true to create persistent volume using file share.') param enableAzureFileShare bool = false +param enableCustomSSL bool = false +param enablePV bool = false @description('An user assigned managed identity. Make sure the identity has permission to create/update/delete/list Azure resources.') param identity object param location string = 'eastus' @@ -66,8 +69,6 @@ param managedServerPrefix string = 'managed-server' param ocrSSOPSW string @description('User name of Oracle SSO account.') param ocrSSOUser string -@description('ture to upload Java EE applications and deploy the applications to WebLogic domain.') -param uploadAppPackage bool = false @secure() @description('Password for model WebLogic Deploy Tooling runtime encrytion.') param wdtRuntimePassword string @@ -79,19 +80,40 @@ param wlsCPU string = '200m' param wlsDomainName string = 'domain1' @description('UID of WebLogic domain, used in WebLogic Operator.') param wlsDomainUID string = 'sample-domain1' +@secure() +param wlsIdentityKeyStoreData string = newGuid() +@secure() +param wlsIdentityKeyStorePassphrase string = newGuid() +@allowed([ + 'JKS' + 'PKCS12' +]) +param wlsIdentityKeyStoreType string = 'PKCS12' @description('Docker tag that comes after "container-registry.oracle.com/middleware/weblogic:"') param wlsImageTag string = '12.2.1.4' @description('Memory requests for admin server and managed server.') param wlsMemory string = '1.5Gi' @secure() param wlsPassword string +@secure() +param wlsPrivateKeyAlias string = newGuid() +@secure() +param wlsPrivateKeyPassPhrase string = newGuid() +@secure() +param wlsTrustKeyStoreData string = newGuid() +@secure() +param wlsTrustKeyStorePassPhrase string = newGuid() +@allowed([ + 'JKS' + 'PKCS12' +]) +param wlsTrustKeyStoreType string = 'PKCS12' @description('User name for WebLogic Administrator.') param wlsUserName string = 'weblogic' - /* * Deploy a pid to tract an offer deployment starts */ -module pidStart './_pids/_pid.bicep'= { +module pidStart './_pids/_pid.bicep' = { name: 'wls-aks-start-pid-deployment' params: { name: _pidStart @@ -133,7 +155,8 @@ module acrDeployment './_azure-resoruces/_acr.bicep' = if (createACR) { ] } -module storageDeployment './_azure-resoruces/_storage.bicep' = if (enableAzureFileShare) { +// enableAppGWIngress: if true, will create storage for certificates. +module storageDeployment './_azure-resoruces/_storage.bicep' = if (enablePV) { name: 'storage-deployment' params: { location: location @@ -154,8 +177,11 @@ module wlsDomainDeployment './_deployment-scripts/_ds-create-wls-cluster.bicep' aksClusterRGName: createAKSCluster ? resourceGroup().name : aksClusterRGName aksClusterName: createAKSCluster ? aksClusterDeployment.outputs.aksClusterName : aksClusterName acrName: createACR ? acrDeployment.outputs.acrName : acrName + appgwAlias: appgwAlias appPackageUrls: appPackageUrls appReplicas: appReplicas + enableCustomSSL: enableCustomSSL + enablePV: enablePV identity: identity location: location managedServerPrefix: managedServerPrefix @@ -163,12 +189,21 @@ module wlsDomainDeployment './_deployment-scripts/_ds-create-wls-cluster.bicep' ocrSSOUser: ocrSSOUser ocrSSOPSW: ocrSSOPSW wdtRuntimePassword: wdtRuntimePassword + wlsClusterSize: wlsClusterSize wlsCPU: wlsCPU wlsDomainName: wlsDomainName wlsDomainUID: wlsDomainUID + wlsIdentityKeyStoreData: wlsIdentityKeyStoreData + wlsIdentityKeyStorePassphrase: wlsIdentityKeyStorePassphrase + wlsIdentityKeyStoreType: wlsIdentityKeyStoreType wlsImageTag: wlsImageTag wlsMemory: wlsMemory wlsPassword: wlsPassword + wlsPrivateKeyAlias: wlsPrivateKeyAlias + wlsPrivateKeyPassPhrase: wlsPrivateKeyPassPhrase + wlsTrustKeyStoreData: wlsTrustKeyStoreData + wlsTrustKeyStorePassPhrase: wlsTrustKeyStorePassPhrase + wlsTrustKeyStoreType: wlsTrustKeyStoreType wlsUserName: wlsUserName } dependsOn: [ @@ -194,5 +229,5 @@ module pidEnd './_pids/_pid.bicep' = { output aksClusterName string = createAKSCluster ? aksClusterDeployment.outputs.aksClusterName : aksClusterName output aksClusterRGName string = createAKSCluster ? resourceGroup().name : aksClusterRGName -output adminServerUrl string = format('http://{0}-admin-server.{0}-ns.svc.cluster.local:7001/console',wlsDomainUID) +output adminServerUrl string = format('http://{0}-admin-server.{0}-ns.svc.cluster.local:7001/console', wlsDomainUID) output clusterSVCUrl string = format('http://{0}-cluster-cluster-1.{0}-ns.svc.cluster.local:8001/', wlsDomainUID) From f21a260041734cb933ba09f5925f9d1a52bae4ee Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Thu, 22 Jul 2021 22:20:28 +0000 Subject: [PATCH 23/37] Merged PR 338855: Update application using template. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Post deployment: update applications. - use arm template, az cli to update applications - allow to use a new weblogic standard image - applications input via: - sas url - storage blob name Performance: - It took 15min to update 1 war (3.47KiB) and 1 ear (19.63KiB) Related work items: #1356173 --- src/main/arm/scripts/common.sh | 11 +- src/main/arm/scripts/createVMAndBuildImage.sh | 121 +++++++++ src/main/arm/scripts/setupWLSDomain.sh | 108 ++------ src/main/arm/scripts/updateApplications.sh | 234 ++++++++++++++++++ src/main/arm/scripts/utility.sh | 11 + .../_ds-create-wls-cluster.bicep | 2 + .../_ds_update-applications.bicep | 55 ++++ src/main/bicep/modules/_pids/_pid-dev.bicep | 10 +- src/main/bicep/modules/_pids/_pid.bicep | 10 +- .../modules/updateWebLogicApplications.bicep | 115 +++++++++ 10 files changed, 575 insertions(+), 102 deletions(-) create mode 100644 src/main/arm/scripts/createVMAndBuildImage.sh create mode 100644 src/main/arm/scripts/updateApplications.sh create mode 100644 src/main/bicep/modules/_deployment-scripts/_ds_update-applications.bicep create mode 100644 src/main/bicep/modules/updateWebLogicApplications.bicep diff --git a/src/main/arm/scripts/common.sh b/src/main/arm/scripts/common.sh index bb267ce87..901c93361 100644 --- a/src/main/arm/scripts/common.sh +++ b/src/main/arm/scripts/common.sh @@ -1 +1,10 @@ -export curlMaxTime=120 # seconds \ No newline at end of file +export checkPodStatusInterval=1m # interval to check pod status. +export checkPodStatusMaxAttemps=10 # interval to check pod status. + +export constFalse="false" +export constTrue="true" + +export curlMaxTime=120 # seconds +export ocrLoginServer="container-registry.oracle.com" +export optUninstallMaxTry=5 # Max try number to wait for the operator uninstalled +export optUninstallInterval=10 diff --git a/src/main/arm/scripts/createVMAndBuildImage.sh b/src/main/arm/scripts/createVMAndBuildImage.sh new file mode 100644 index 000000000..b1273f577 --- /dev/null +++ b/src/main/arm/scripts/createVMAndBuildImage.sh @@ -0,0 +1,121 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +function cleanup_vm() { + #Remove VM resources + az extension add --name resource-graph + # query vm id + vmId=$(az graph query -q "Resources \ +| where type =~ 'microsoft.compute/virtualmachines' \ +| where name=~ '${vmName}' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| project vmid = id" -o tsv) + + # query nic id + nicId=$(az graph query -q "Resources \ +| where type =~ 'microsoft.compute/virtualmachines' \ +| where name=~ '${vmName}' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| extend nics=array_length(properties.networkProfile.networkInterfaces) \ +| mv-expand nic=properties.networkProfile.networkInterfaces \ +| where nics == 1 or nic.properties.primary =~ 'true' or isempty(nic) \ +| project nicId = tostring(nic.id)" -o tsv) + + # query ip id + ipId=$(az graph query -q "Resources \ +| where type =~ 'microsoft.network/networkinterfaces' \ +| where id=~ '${nicId}' \ +| extend ipConfigsCount=array_length(properties.ipConfigurations) \ +| mv-expand ipconfig=properties.ipConfigurations \ +| where ipConfigsCount == 1 or ipconfig.properties.primary =~ 'true' \ +| project publicIpId = tostring(ipconfig.properties.publicIPAddress.id)" -o tsv) + + # query os disk id + osDiskId=$(az graph query -q "Resources \ +| where type =~ 'microsoft.compute/virtualmachines' \ +| where name=~ '${vmName}' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| project osDiskId = tostring(properties.storageProfile.osDisk.managedDisk.id)" -o tsv) + + # query vnet id + vnetId=$(az graph query -q "Resources \ +| where type =~ 'Microsoft.Network/virtualNetworks' \ +| where name=~ '${vmName}VNET' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| project vNetId = id" -o tsv) + + # query nsg id + nsgId=$(az graph query -q "Resources \ +| where type =~ 'Microsoft.Network/networkSecurityGroups' \ +| where name=~ '${vmName}NSG' \ +| where resourceGroup =~ '${currentResourceGroup}' \ +| project nsgId = id" -o tsv) + + # Delete VM NIC IP VNET NSG resoruces + vmResourceIdS=$(echo ${vmId} ${nicId} ${ipId} ${osDiskId} ${vnetId} ${nsgId}) + echo ${vmResourceIdS} + az resource delete --verbose --ids ${vmResourceIdS} +} + +# Build docker image +# * Create Ubuntu machine VM-UBUNTU +# * Running vm extension to run buildWLSDockerImage.sh, the script will: +# * build a docker image with domain model, applications based on specified WebLogic Standard image +# * push the image to ACR +function build_docker_image() { + # Create vm to build docker image + vmName="VM-UBUNTU-WLS-AKS-$(date +%s)" + + # MICROSOFT_INTERNAL + # Specify tag 'SkipASMAzSecPack' to skip policy 'linuxazuresecuritypackautodeployiaas_1.6' + # Specify tag 'SkipNRMS*' to skip Microsoft internal NRMS policy, which cause vm-redeployed issue + az vm create \ + --resource-group ${currentResourceGroup} \ + --name ${vmName} \ + --image "Canonical:UbuntuServer:18.04-LTS:latest" \ + --admin-username azureuser \ + --generate-ssh-keys \ + --nsg-rule NONE \ + --enable-agent true \ + --vnet-name ${vmName}VNET \ + --enable-auto-update false \ + --tags SkipASMAzSecPack=true SkipNRMSCorp=true SkipNRMSDatabricks=true SkipNRMSDB=true SkipNRMSHigh=true SkipNRMSMedium=true SkipNRMSRDPSSH=true SkipNRMSSAW=true SkipNRMSMgmt=true --verbose + + wlsImagePath="${ocrLoginServer}/middleware/weblogic:${wlsImageTag}" + az vm extension set --name CustomScript \ + --extension-instance-name wls-image-script \ + --resource-group ${currentResourceGroup} \ + --vm-name ${vmName} \ + --publisher Microsoft.Azure.Extensions \ + --version 2.0 \ + --settings "{ \"fileUris\": [\"${scriptURL}model.properties\",\"${scriptURL}genImageModel.sh\",\"${scriptURL}buildWLSDockerImage.sh\",\"${scriptURL}common.sh\"]}" \ + --protected-settings "{\"commandToExecute\":\"bash buildWLSDockerImage.sh ${wlsImagePath} ${azureACRServer} ${azureACRUserName} ${azureACRPassword} ${newImageTag} \\\"${appPackageUrls}\\\" ${ocrSSOUser} ${ocrSSOPSW} ${wlsClusterSize} ${enableCustomSSL} \"}" + + #Validate image from ACR + az acr repository show -n ${acrName} --image aks-wls-images:${newImageTag} + + cleanup_vm +} + +# Shell Global settings +set -e #Exit immediately if a command exits with a non-zero status. + +export currentResourceGroup=$1 +export wlsImageTag=$2 +export azureACRServer=$3 +export azureACRUserName=$4 +export azureACRPassword=$5 +export newImageTag=$6 +export appPackageUrls=$7 +export ocrSSOUser=$8 +export ocrSSOPSW=$9 +export wlsClusterSize=${10} +export enableCustomSSL=${11} +export scriptURL=${12} + +echo ${scriptURL} + +build_docker_image + + + diff --git a/src/main/arm/scripts/setupWLSDomain.sh b/src/main/arm/scripts/setupWLSDomain.sh index 9d7ad7f3f..d491c31ff 100644 --- a/src/main/arm/scripts/setupWLSDomain.sh +++ b/src/main/arm/scripts/setupWLSDomain.sh @@ -250,43 +250,21 @@ function query_acr_credentials() { # * build a docker image with domain model, applications based on specified WebLogic Standard image # * push the image to ACR function build_docker_image() { - # Create vm to build docker image - vmName="VM-UBUNTU-WLS-AKS-$(date +%s)" - - # MICROSOFT_INTERNAL - # Specify tag 'SkipASMAzSecPack' to skip policy 'linuxazuresecuritypackautodeployiaas_1.6' - # Specify tag 'SkipNRMS*' to skip Microsoft internal NRMS policy, which cause vm-redeployed issue - az vm create \ - --resource-group ${currentResourceGroup} \ - --name ${vmName} \ - --image "Canonical:UbuntuServer:18.04-LTS:latest" \ - --admin-username azureuser \ - --generate-ssh-keys \ - --nsg-rule NONE \ - --enable-agent true \ - --enable-auto-update false \ - --tags SkipASMAzSecPack=true SkipNRMSCorp=true SkipNRMSDatabricks=true SkipNRMSDB=true SkipNRMSHigh=true SkipNRMSMedium=true SkipNRMSRDPSSH=true SkipNRMSSAW=true SkipNRMSMgmt=true --verbose - - validate_status "Check status of VM machine to build docker image." - - wlsImagePath="${ocrLoginServer}/middleware/weblogic:${wlsImageTag}" - az vm extension set --name CustomScript \ - --extension-instance-name wls-image-script \ - --resource-group ${currentResourceGroup} \ - --vm-name ${vmName} \ - --publisher Microsoft.Azure.Extensions \ - --version 2.0 \ - --settings "{ \"fileUris\": [\"${scriptURL}model.properties\",\"${scriptURL}genImageModel.sh\",\"${scriptURL}buildWLSDockerImage.sh\",\"${scriptURL}common.sh\"]}" \ - --protected-settings "{\"commandToExecute\":\"bash buildWLSDockerImage.sh ${wlsImagePath} ${azureACRServer} ${azureACRUserName} ${azureACRPassword} ${newImageTag} \\\"${appPackageUrls}\\\" ${ocrSSOUser} ${ocrSSOPSW} ${wlsClusterSize} ${enableCustomSSL} \"}" - - # If error fires, keep vm resource and exit. - validate_status "Check status of buiding WLS domain image." - - #Validate image from ACR - az acr repository show -n ${acrName} --image aks-wls-images:${newImageTag} - validate_status "Check if new image aks-wls-images:${newImageTag} has been pushed to acr." - - cleanup_vm + echo "build a new image including the new applications" + chmod ugo+x $scriptDir/createVMAndBuildImage.sh + bash $scriptDir/createVMAndBuildImage.sh \ + $currentResourceGroup \ + $wlsImageTag \ + $azureACRServer \ + $azureACRUserName \ + $azureACRPassword \ + $newImageTag \ + "$appPackageUrls" \ + $ocrSSOUser \ + $ocrSSOPSW \ + $wlsClusterSize \ + $enableCustomSSL \ + "$scriptURL" } function mount_fileshare() { @@ -562,62 +540,6 @@ function wait_for_domain_completed() { fi } -function cleanup_vm() { - #Remove VM resources - az extension add --name resource-graph - # query vm id - vmId=$(az graph query -q "Resources \ -| where type =~ 'microsoft.compute/virtualmachines' \ -| where name=~ '${vmName}' \ -| where resourceGroup =~ '${currentResourceGroup}' \ -| project vmid = id" -o tsv) - - # query nic id - nicId=$(az graph query -q "Resources \ -| where type =~ 'microsoft.compute/virtualmachines' \ -| where name=~ '${vmName}' \ -| where resourceGroup =~ '${currentResourceGroup}' \ -| extend nics=array_length(properties.networkProfile.networkInterfaces) \ -| mv-expand nic=properties.networkProfile.networkInterfaces \ -| where nics == 1 or nic.properties.primary =~ 'true' or isempty(nic) \ -| project nicId = tostring(nic.id)" -o tsv) - - # query ip id - ipId=$(az graph query -q "Resources \ -| where type =~ 'microsoft.network/networkinterfaces' \ -| where id=~ '${nicId}' \ -| extend ipConfigsCount=array_length(properties.ipConfigurations) \ -| mv-expand ipconfig=properties.ipConfigurations \ -| where ipConfigsCount == 1 or ipconfig.properties.primary =~ 'true' \ -| project publicIpId = tostring(ipconfig.properties.publicIPAddress.id)" -o tsv) - - # query os disk id - osDiskId=$(az graph query -q "Resources \ -| where type =~ 'microsoft.compute/virtualmachines' \ -| where name=~ '${vmName}' \ -| where resourceGroup =~ '${currentResourceGroup}' \ -| project osDiskId = tostring(properties.storageProfile.osDisk.managedDisk.id)" -o tsv) - - # query vnet id - vnetId=$(az graph query -q "Resources \ -| where type =~ 'Microsoft.Network/virtualNetworks' \ -| where name=~ '${vmName}VNET' \ -| where resourceGroup =~ '${currentResourceGroup}' \ -| project vNetId = id" -o tsv) - - # query nsg id - nsgId=$(az graph query -q "Resources \ -| where type =~ 'Microsoft.Network/networkSecurityGroups' \ -| where name=~ '${vmName}NSG' \ -| where resourceGroup =~ '${currentResourceGroup}' \ -| project nsgId = id" -o tsv) - - # Delete VM NIC IP VNET NSG resoruces - vmResourceIdS=$(echo ${vmId} ${nicId} ${ipId} ${osDiskId} ${vnetId} ${nsgId}) - echo ${vmResourceIdS} - az resource delete --verbose --ids ${vmResourceIdS} -} - # Main script export script="${BASH_SOURCE[0]}" export scriptDir="$(cd "$(dirname "${script}")" && pwd)" diff --git a/src/main/arm/scripts/updateApplications.sh b/src/main/arm/scripts/updateApplications.sh new file mode 100644 index 000000000..8c456f08d --- /dev/null +++ b/src/main/arm/scripts/updateApplications.sh @@ -0,0 +1,234 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +echo "Script ${0} starts" + +# Connect to AKS cluster +function connect_aks_cluster() { + az aks get-credentials \ + --resource-group ${aksClusterRGName} \ + --name ${aksClusterName} \ + --overwrite-existing +} + +function query_wls_cluster_info(){ + wlsClusterSize=$(kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json \ + | jq '. | .status.clusters[] | select(.clusterName == "'${wlsClusterName}'") | .maximumReplicas') + echo "cluster size: ${wlsClusterSize}" + + enableCustomSSL=${constFalse} + sslIdentityEnv=$(kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json \ + | jq '. | .spec.serverPod.env[] | select(.name=="'${sslIdentityEnvName}'")') + if [ -n "${sslIdentityEnv}" ]; then + enableCustomSSL=${constTrue} + fi +} + +# Query ACR login server, username, password +function query_acr_credentials() { + echo "query credentials of ACR ${acrName}" + azureACRServer=$(az acr show -n $acrName --query 'loginServer' -o tsv) + azureACRUserName=$(az acr credential show -n $acrName --query 'username' -o tsv) + azureACRPassword=$(az acr credential show -n $acrName --query 'passwords[0].value' -o tsv) +} + +function get_app_sas_url() { + args=("$@") + appNumber=$# + index=0 + while [ $index -lt $appNumber ]; do + appName=${args[${index}]} + echo "app package file name: ${appName}" + if [[ "$appName" == *".war" || "$appName" == *".ear" ]]; then + appSaSUrl=$(az storage blob url --container-name ${appContainerName} \ + --name ${appName} \ + --account-name ${appStorageAccountName} \ + --sas-token ${sasToken}) + echo ${appSaSUrl} + appPackageUrls=$(echo "${appPackageUrls}" | jq ". |= [${appSaSUrl}] + .") # append url + fi + + index=$((index+1)) + done +} + +function query_app_urls() { + echo "check if the storage account exists." + ret=$(az storage account check-name --name ${appStorageAccountName} \ + | grep "AlreadyExists") + if [ -z "$ret" ]; then + echo "${appStorageAccountName} does not exist." + return + fi + + appList=$(az storage blob list --container-name ${appContainerName} \ + --account-name ${appStorageAccountName} \ + | jq '.[] | .name' \ + | tr -d "\"") + + if [ $? == 1 ]; then + echo "Failed to query application from ${appContainerName}" + return + fi + + sasTokenEnd=`date -u -d "${sasTokenValidTime} minutes" '+%Y-%m-%dT%H:%MZ'` + sasToken=$(az storage account generate-sas \ + --permissions r \ + --account-name ${appStorageAccountName} \ + --services b \ + --resource-types c \ + --expiry $sasTokenEnd -o tsv) + + get_app_sas_url ${appList} +} + +function build_docker_image() { + echo "build a new image including the new applications" + chmod ugo+x $scriptDir/createVMAndBuildImage.sh + bash $scriptDir/createVMAndBuildImage.sh \ + $currentResourceGroup \ + $wlsImageTag \ + $azureACRServer \ + $azureACRUserName \ + $azureACRPassword \ + $newImageTag \ + "$appPackageUrls" \ + $ocrSSOUser \ + $ocrSSOPSW \ + $wlsClusterSize \ + $enableCustomSSL \ + "$scriptURL" +} + +function apply_new_image() { + acrImagePath="${azureACRServer}/aks-wls-images:${newImageTag}" + restartVersion=$(kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} '-o=jsonpath={.spec.restartVersion}') + # increase restart version + restartVersion=$((restartVersion + 1)) + kubectl -n ${wlsDomainNS} patch domain ${wlsDomainUID} \ + --type=json \ + '-p=[{"op": "replace", "path": "/spec/restartVersion", "value": "'${restartVersion}'" }, {"op": "replace", "path": "/spec/image", "value": "'${acrImagePath}'" }]' +} + +function wait_for_pod_completed() { + # Make sure all of the pods are running. + replicas=$(kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json \ + | jq '. | .spec.clusters[] | .replicas') + + echo "Waiting for $((replicas+1)) pods are running." + + readyPodNum=0 + attempt=0 + while [[ ${readyPodNum} -le ${replicas} && $attempt -le ${checkPodStatusMaxAttemps} ]];do + ret=$(kubectl get pods -n ${wlsDomainNS} -o json \ + | jq '.items[] | .status.phase' \ + | grep "Running") + if [ -z "${ret}" ];then + readyPodNum=0 + else + readyPodNum=$(kubectl get pods -n ${wlsDomainNS} -o json \ + | jq '.items[] | .status.phase' \ + | grep -c "Running") + fi + echo "Number of new running pod: ${readyPodNum}" + attempt=$((attempt+1)) + sleep ${checkPodStatusInterval} + done + + if [ ${attempt} -gt ${checkPodStatusMaxAttemps} ];then + echo "It takes too long to wait for all the pods are running, please refer to http://oracle.github.io/weblogic-kubernetes-operator/samples/simple/azure-kubernetes-service/#troubleshooting" + exit 1 + fi +} + +function wait_for_image_update_completed() { + # Make sure all of the pods are updated with new image. + # Assumption: we have only one cluster currently. + replicas=$(kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json \ + | jq '. | .spec.clusters[] | .replicas') + echo "Waiting for $((replicas+1)) new pods created with image ${acrImagePath}" + + updatedPodNum=0 + attempt=0 + while [ ${updatedPodNum} -le ${replicas} ] && [ $attempt -le ${checkPodStatusMaxAttemps} ];do + echo "attempts ${attempt}" + ret=$(kubectl get pods -n ${wlsDomainNS} -o json \ + | jq '.items[] | .spec | .containers[] | select(.name == "weblogic-server") | .image' \ + | grep "${acrImagePath}") + + if [ -z "${ret}" ];then + updatedPodNum=0 + else + updatedPodNum=$(kubectl get pods -n ${wlsDomainNS} -o json \ + | jq '.items[] | .spec | .containers[] | select(.name == "weblogic-server") | .image' \ + | grep -c "${acrImagePath}") + fi + echo "Number of new pod: ${updatedPodNum}" + + attempt=$((attempt+1)) + sleep ${checkPodStatusInterval} + done + + if [ ${attempt} -gt ${checkPodStatusMaxAttemps} ];then + echo "Failed to update with image ${acrImagePath} to all weblogic server pods. " + exit 1 + fi +} + +#Output value to deployment scripts +function output_image() { + echo ${acrImagePath} + + result=$(jq -n -c \ + --arg image $acrImagePath \ + '{image: $image}') + echo "output of deployment script: $result" + echo $result >$AZ_SCRIPTS_OUTPUT_PATH +} + +# Main script +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +source ${scriptDir}/common.sh +source ${scriptDir}/utility.sh + +export ocrSSOUser=$1 +export ocrSSOPSW=$2 +export aksClusterRGName=$3 +export aksClusterName=$4 +export wlsImageTag=$5 +export acrName=$6 +export wlsDomainName=$7 +export wlsDomainUID=$8 +export currentResourceGroup=$9 +export appPackageUrls=${10} +export scriptURL=${11} +export appStorageAccountName=${12} +export appContainerName=${13} + +export newImageTag=$(date +%s) +export sasTokenValidTime=40 #min +export sslIdentityEnvName="SSL_IDENTITY_PRIVATE_KEY_ALIAS" +export wlsClusterName="cluster-1" +export wlsDomainNS="${wlsDomainUID}-ns" + +install_kubectl + +connect_aks_cluster + +query_wls_cluster_info + +query_acr_credentials + +query_app_urls + +build_docker_image + +apply_new_image + +wait_for_image_update_completed + +wait_for_pod_completed + +output_image diff --git a/src/main/arm/scripts/utility.sh b/src/main/arm/scripts/utility.sh index 9fe96e172..a99c4ecd7 100644 --- a/src/main/arm/scripts/utility.sh +++ b/src/main/arm/scripts/utility.sh @@ -11,4 +11,15 @@ function install_jdk() { exit 1 fi # JAVA_HOME=/usr/lib/jvm/java-11-openjdk +} + +function install_kubectl() { + # Install kubectl + az aks install-cli + echo "validate kubectl" + kubectl --help + if [ $? -eq 1 ]; then + echo "Failed to install kubectl." + exit 1 + fi } \ No newline at end of file diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep index c1cc64eaf..f1eddf21a 100644 --- a/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep +++ b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep @@ -53,6 +53,7 @@ param wlsTrustKeyStoreType string = 'PKCS12' param wlsUserName string = 'weblogic' var const_arguments = '${ocrSSOUser} ${ocrSSOPSW} ${aksClusterRGName} ${aksClusterName} ${wlsImageTag} ${acrName} ${wlsDomainName} ${wlsDomainUID} ${wlsUserName} ${wlsPassword} ${wdtRuntimePassword} ${wlsCPU} ${wlsMemory} ${managedServerPrefix} ${appReplicas} ${string(appPackageUrls)} ${resourceGroup().name} ${const_scriptLocation} ${storageAccountName} ${wlsClusterSize} ${enableCustomSSL} ${wlsIdentityKeyStoreData} ${wlsIdentityKeyStorePassphrase} ${wlsIdentityKeyStoreType} ${wlsPrivateKeyAlias} ${wlsPrivateKeyPassPhrase} ${wlsTrustKeyStoreData} ${wlsTrustKeyStorePassPhrase} ${wlsTrustKeyStoreType} ${appgwAlias} ${enablePV} ' +var const_buildDockerImageScript='createVMAndBuildImage.sh' var const_commonScript = 'common.sh' var const_pvTempalte = 'pv.yaml.template' var const_pvcTempalte = 'pvc.yaml.template' @@ -76,6 +77,7 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { uri(const_scriptLocation, '${const_pvTempalte}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_pvcTempalte}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_commonScript}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_buildDockerImageScript}${_artifactsLocationSasToken}') ] cleanupPreference: 'OnSuccess' retentionInterval: 'P1D' diff --git a/src/main/bicep/modules/_deployment-scripts/_ds_update-applications.bicep b/src/main/bicep/modules/_deployment-scripts/_ds_update-applications.bicep new file mode 100644 index 000000000..3a756e414 --- /dev/null +++ b/src/main/bicep/modules/_deployment-scripts/_ds_update-applications.bicep @@ -0,0 +1,55 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +param _artifactsLocation string +@secure() +param _artifactsLocationSasToken string = '' + +param aksClusterRGName string = '' +param aksClusterName string = '' +param acrName string = '' +param appPackageUrls array = [] +param appPackageFromStorageBlob object = { + storageAccountName: 'stg-contoso' + containerName: 'container-contoso' +} +param identity object + +@secure() +param ocrSSOPSW string +param ocrSSOUser string + +param utcValue string = utcNow() +param wlsDomainName string = 'domain1' +param wlsDomainUID string = 'sample-domain1' +param wlsImageTag string = '12.2.1.4' + +var const_arguments = '${ocrSSOUser} ${ocrSSOPSW} ${aksClusterRGName} ${aksClusterName} ${wlsImageTag} ${acrName} ${wlsDomainName} ${wlsDomainUID} ${resourceGroup().name} ${string(appPackageUrls)} ${const_scriptLocation} ${appPackageFromStorageBlob.storageAccountName} ${appPackageFromStorageBlob.containerName} ' +var const_azcliVersion='2.15.0' +var const_buildDockerImageScript='createVMAndBuildImage.sh' +var const_commonScript = 'common.sh' +var const_scriptLocation = uri(_artifactsLocation, 'scripts/') +var const_updateAppScript= 'updateApplications.sh' +var const_utilityScript= 'utility.sh' + +resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: 'ds-wls-update-applications' + location: resourceGroup().location + kind: 'AzureCLI' + identity: identity + properties: { + azCliVersion: const_azcliVersion + arguments: const_arguments + primaryScriptUri: uri(const_scriptLocation, '${const_updateAppScript}${_artifactsLocationSasToken}') + supportingScriptUris: [ + uri(const_scriptLocation, '${const_commonScript}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_utilityScript}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_buildDockerImageScript}${_artifactsLocationSasToken}') + ] + cleanupPreference: 'OnSuccess' + retentionInterval: 'P1D' + forceUpdateTag: utcValue + } +} + +output image string = reference('ds-wls-update-applications').outputs.image diff --git a/src/main/bicep/modules/_pids/_pid-dev.bicep b/src/main/bicep/modules/_pids/_pid-dev.bicep index 48a4f5308..fe9951f5e 100644 --- a/src/main/bicep/modules/_pids/_pid-dev.bicep +++ b/src/main/bicep/modules/_pids/_pid-dev.bicep @@ -10,9 +10,11 @@ module pidStart './_empty.bicep' = if (name != 'pid'){ name: name } -output wlsAKSEnd string = '17328b4d-841f-57b5-a9c5-861ad48f9d0d' -output wlsAKSStart string = 'c46a11b1-e8d2-5053-9741-45294b2e15c9' -output networkingEnd string = '39d32fcd-1d02-50b6-9455-4b767a8e769e' -output networkingStart string = 'ed47756f-2475-56dd-b13a-26027749b6e1' output appgwEnd string = '38647ff6-ea8d-59e5-832d-b036a4d29c73' output appgwStart string = '8ba7beaa-96fd-576a-acd8-28f7a6efa83a' +output networkingEnd string = '39d32fcd-1d02-50b6-9455-4b767a8e769e' +output networkingStart string = 'ed47756f-2475-56dd-b13a-26027749b6e1' +output wlsAKSEnd string = '17328b4d-841f-57b5-a9c5-861ad48f9d0d' +output wlsAKSStart string = 'c46a11b1-e8d2-5053-9741-45294b2e15c9' +output wlsClusterAppEnd string = '18121d1c-4227-51ff-a9fa-ceb890d683e3' +output wlsClusterAppStart string = '4218fc54-4b9b-5e5c-b6a9-bc8736c25b68' diff --git a/src/main/bicep/modules/_pids/_pid.bicep b/src/main/bicep/modules/_pids/_pid.bicep index b1e5d11fa..99118a842 100644 --- a/src/main/bicep/modules/_pids/_pid.bicep +++ b/src/main/bicep/modules/_pids/_pid.bicep @@ -10,9 +10,11 @@ module pidStart './_empty.bicep' = if (name != 'pid'){ name: name } -output wlsAKSEnd string = '2571f846-2f66-5c22-9fe6-38ecea7889ac' -output wlsAKSStart string = '3e6acde5-9a62-5488-9fd4-87c46f4105f4' -output networkingEnd string = '2798165c-49fa-5701-b608-b80ed3986176' -output networkingStart string = '0793308f-de9d-5f0d-92f9-d9fc4b413b8b' output appgwEnd string = '47ea43a0-95cf-52c7-aee8-7ee6106fc1bf' output appgwStart string = '01288010-2672-5831-a66b-7b8b45cace1b' +output networkingEnd string = '2798165c-49fa-5701-b608-b80ed3986176' +output networkingStart string = '0793308f-de9d-5f0d-92f9-d9fc4b413b8b' +output wlsAKSEnd string = '2571f846-2f66-5c22-9fe6-38ecea7889ac' +output wlsAKSStart string = '3e6acde5-9a62-5488-9fd4-87c46f4105f4' +output wlsClusterAppEnd string = 'e6e33240-e5db-52fc-9154-7fc7b3b8b508' +output wlsClusterAppStart string = '4570a81a-3f3a-53d5-b178-7f985d9c5ecf' diff --git a/src/main/bicep/modules/updateWebLogicApplications.bicep b/src/main/bicep/modules/updateWebLogicApplications.bicep new file mode 100644 index 000000000..3daf88906 --- /dev/null +++ b/src/main/bicep/modules/updateWebLogicApplications.bicep @@ -0,0 +1,115 @@ +/* +Copyright (c) 2018, 2021, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +Description + - This script is to update applications running in an existing WebLogic Cluster. + - Application input can be customized using parameters appPackageUrls and appPackageFromStorageBlob. + +Pre-requisites + - There is at least one WebLogic cluster running on Azure Kubernetes Service (AKS), the cluster must be deployed using Azure WebLoigc on AKS marketplace offer. + - Azure CLI with bicep installed. + +Parameters + - _artifactsLocation: Script location. + - acrName: Name of Azure Container Registry that is used to managed the WebLogic domain images. + - aksClusterRGName: Name of resource group that contains the (AKS) instance, probably the resource group you are working on. It's recommended to run this sript with the same resource group that runs AKS. + - aksClusterName: Name of the AKS instance that runs the WebLogic cluster. + - appPackageUrls: String array of Java EE applciation location, which can be downloaded using "curl". Currently, only support urls of Azure Storage Account blob. + - appPackageFromStorageBlob: Storage blob that contains Java EE applciations, the script will download all the .war and .ear file from that blob. + - storageAccountName: Storage account name. + - containerName: container name. + - identity: Azure user managed identity used, make sure the identity has permission to create/update/delete Azure resources. It's recommended to assign "Contributor" role. + - ocrSSOPSW: Password of Oracle SSO account. The script will pull image from Oracle Container Registry (OCR), Oracle account is required. Make sure the account has checkout WebLogic images. + - ocrSSOUser: User name of Oracle SSO account. + - wlsDomainName: Name of the domain that you are going to update. Make sure it's the same with the initial cluster deployment. + - wlsDomainUID: UID of the domain that you are going to update. Make sure it's the same with the initial cluster deployment. + - wlsImageTag: The available WebLogic docker image tags that OCR provides. + +Build and run + - Run command `bicep build updateWebLogicApplications.bicep`, you will get built ARM template updateWebLogicApplications.json. + - Prepare parameters file parameters.json + - Run command `az deployment group create -f updateWebLogicApplications.json -p parameters.json -g ` +*/ + +param _artifactsLocation string = '' +@secure() +param _artifactsLocationSasToken string = '' + +param acrName string = '' +@description('Resource group name of an existing AKS cluster.') +param aksClusterRGName string = '' +@description('Name of an existing AKS cluster.') +param aksClusterName string = '' + +@description('Download all the .war and .ear packages from the specified storage blob. You can specify the applciation using "appPackageUrls" and "appPackageFromStorageBlob", please do not specify the same applciation in both parameters.') +param appPackageFromStorageBlob object = { + storageAccountName: 'stg-contoso' + containerName: 'container-contoso' +} +@description('Url array of Java EE application locations.') +param appPackageUrls array = [] + +param identity object + +@secure() +@description('Password of Oracle SSO account.') +param ocrSSOPSW string +@description('User name of Oracle SSO account.') +param ocrSSOUser string + +@description('Name of WebLogic domain to create.') +param wlsDomainName string = 'domain1' +@description('UID of WebLogic domain, used in WebLogic Operator.') +param wlsDomainUID string = 'sample-domain1' +@description('Docker tag that comes after "container-registry.oracle.com/middleware/weblogic:"') +param wlsImageTag string = '12.2.1.4' + +module pids './_pids/_pid.bicep' = { + name: 'initialization' +} + +module pidStart './_pids/_pid.bicep' = { + name: 'wls-aks-update-app-start-pid-deployment' + params: { + name: pids.outputs.wlsClusterAppStart + } + dependsOn:[ + pids + ] +} + +module updateWLSApplications '_deployment-scripts/_ds_update-applications.bicep' = { + name: 'update-wls-applications' + params:{ + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + aksClusterRGName: aksClusterRGName + aksClusterName: aksClusterName + acrName: acrName + appPackageUrls: appPackageUrls + appPackageFromStorageBlob: appPackageFromStorageBlob + identity: identity + ocrSSOPSW: ocrSSOPSW + ocrSSOUser: ocrSSOUser + wlsDomainName: wlsDomainName + wlsDomainUID: wlsDomainUID + wlsImageTag: wlsImageTag + } + dependsOn:[ + pidStart + ] +} + + +module pidEnd './_pids/_pid.bicep' = { + name: 'wls-aks-update-app-end-pid-deployment' + params: { + name: pids.outputs.wlsClusterAppEnd + } + dependsOn:[ + updateWLSApplications + ] +} + +output image string = updateWLSApplications.outputs.image From 44da2c4da8abf6b56838e24ca5610f7d7b3bb251 Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Wed, 28 Jul 2021 23:53:40 +0000 Subject: [PATCH 24/37] Merged PR 340400: Update app: parse sas url that has slash in query string. - Parse sas url that has slash in query string. - Use curl global timeout in `genImageModel.sh` - Enhance comments of parameter `appPackageFromStorageBlob` Related work items: #1356173, #1361385 --- src/main/arm/scripts/createVMAndBuildImage.sh | 3 -- src/main/arm/scripts/genImageModel.sh | 15 ++++++++-- src/main/arm/scripts/setupWLSDomain.sh | 6 ++++ src/main/arm/scripts/updateApplications.sh | 29 +++++++++++++++---- .../modules/updateWebLogicApplications.bicep | 2 +- 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/main/arm/scripts/createVMAndBuildImage.sh b/src/main/arm/scripts/createVMAndBuildImage.sh index b1273f577..5352ae9ec 100644 --- a/src/main/arm/scripts/createVMAndBuildImage.sh +++ b/src/main/arm/scripts/createVMAndBuildImage.sh @@ -91,9 +91,6 @@ function build_docker_image() { --settings "{ \"fileUris\": [\"${scriptURL}model.properties\",\"${scriptURL}genImageModel.sh\",\"${scriptURL}buildWLSDockerImage.sh\",\"${scriptURL}common.sh\"]}" \ --protected-settings "{\"commandToExecute\":\"bash buildWLSDockerImage.sh ${wlsImagePath} ${azureACRServer} ${azureACRUserName} ${azureACRPassword} ${newImageTag} \\\"${appPackageUrls}\\\" ${ocrSSOUser} ${ocrSSOPSW} ${wlsClusterSize} ${enableCustomSSL} \"}" - #Validate image from ACR - az acr repository show -n ${acrName} --image aks-wls-images:${newImageTag} - cleanup_vm } diff --git a/src/main/arm/scripts/genImageModel.sh b/src/main/arm/scripts/genImageModel.sh index 3a8c4abe6..96d33cd97 100644 --- a/src/main/arm/scripts/genImageModel.sh +++ b/src/main/arm/scripts/genImageModel.sh @@ -5,6 +5,8 @@ export script="${BASH_SOURCE[0]}" export scriptDir="$(cd "$(dirname "${script}")" && pwd)" +source ${scriptDir}/common.sh + export filePath=$1 export appPackageUrls=$2 export enableCustomSSL=$3 @@ -121,11 +123,18 @@ EOF index=1 for item in $appUrlArray; do + echo ${item} # e.g. https://wlsaksapp.blob.core.windows.net/japps/testwebapp.war?sp=r&se=2021-04-29T15:12:38Z&sv=2020-02-10&sr=b&sig=7grL4qP%2BcJ%2BLfDJgHXiDeQ2ZvlWosRLRQ1ciLk0Kl7M%3D - fileNamewithQueryString="${item##*/}" - fileName="${fileNamewithQueryString%\?*}" + urlWithoutQueryString="${item%\?*}" + echo $urlWithoutQueryString + fileName="${urlWithoutQueryString##*/}" + echo $fileName fileExtension="${fileName##*.}" - curl -m 120 -fL "$item" -o ${scriptDir}/model-images/wlsdeploy/applications/${fileName} + curl -m ${curlMaxTime} -fL "$item" -o ${scriptDir}/model-images/wlsdeploy/applications/${fileName} + if [ $? -ne 0 ];then + echo "Failed to download $item" + exit 1 + fi cat <>${filePath} app${index}: SourcePath: 'wlsdeploy/applications/${fileName}' diff --git a/src/main/arm/scripts/setupWLSDomain.sh b/src/main/arm/scripts/setupWLSDomain.sh index d491c31ff..41cd029de 100644 --- a/src/main/arm/scripts/setupWLSDomain.sh +++ b/src/main/arm/scripts/setupWLSDomain.sh @@ -265,6 +265,12 @@ function build_docker_image() { $wlsClusterSize \ $enableCustomSSL \ "$scriptURL" + + az acr repository show -n ${acrName} --image aks-wls-images:${newImageTag} + if [ $? -ne 0 ]; then + echo "Failed to create image ${azureACRServer}/aks-wls-images:${newImageTag}" + exit 1 + fi } function mount_fileshare() { diff --git a/src/main/arm/scripts/updateApplications.sh b/src/main/arm/scripts/updateApplications.sh index 8c456f08d..738575a47 100644 --- a/src/main/arm/scripts/updateApplications.sh +++ b/src/main/arm/scripts/updateApplications.sh @@ -36,6 +36,7 @@ function get_app_sas_url() { args=("$@") appNumber=$# index=0 + appSASUrlString="" while [ $index -lt $appNumber ]; do appName=${args[${index}]} echo "app package file name: ${appName}" @@ -43,13 +44,23 @@ function get_app_sas_url() { appSaSUrl=$(az storage blob url --container-name ${appContainerName} \ --name ${appName} \ --account-name ${appStorageAccountName} \ - --sas-token ${sasToken}) + --sas-token ${sasToken} -o tsv) echo ${appSaSUrl} - appPackageUrls=$(echo "${appPackageUrls}" | jq ". |= [${appSaSUrl}] + .") # append url + appSASUrlString="${appSASUrlString},${appSaSUrl}" fi index=$((index+1)) done + + # append urls + if [ "${appPackageUrls}" == "[]" ]; then + appPackageUrls="[${appSASUrlString:1:${#appSASUrlString}-1}]" # remove the beginning comma + else + appPackageUrls=$(echo "${appPackageUrls:1:${#appPackageUrls}-2}") # remove [] + appPackageUrls="[${appPackageUrls}${appSASUrlString}]" + fi + + echo $appPackageUrls } function query_app_urls() { @@ -71,12 +82,13 @@ function query_app_urls() { return fi - sasTokenEnd=`date -u -d "${sasTokenValidTime} minutes" '+%Y-%m-%dT%H:%MZ'` + expiryData=$(( `date +%s`+${sasTokenValidTime})) + sasTokenEnd=`date -d@"$expiryData" -u '+%Y-%m-%dT%H:%MZ'` sasToken=$(az storage account generate-sas \ --permissions r \ --account-name ${appStorageAccountName} \ --services b \ - --resource-types c \ + --resource-types sco \ --expiry $sasTokenEnd -o tsv) get_app_sas_url ${appList} @@ -98,6 +110,12 @@ function build_docker_image() { $wlsClusterSize \ $enableCustomSSL \ "$scriptURL" + + az acr repository show -n ${acrName} --image aks-wls-images:${newImageTag} + if [ $? -ne 0 ]; then + echo "Failed to create image ${azureACRServer}/aks-wls-images:${newImageTag}" + exit 1 + fi } function apply_new_image() { @@ -208,7 +226,8 @@ export appStorageAccountName=${12} export appContainerName=${13} export newImageTag=$(date +%s) -export sasTokenValidTime=40 #min +# seconds +export sasTokenValidTime=3600 export sslIdentityEnvName="SSL_IDENTITY_PRIVATE_KEY_ALIAS" export wlsClusterName="cluster-1" export wlsDomainNS="${wlsDomainUID}-ns" diff --git a/src/main/bicep/modules/updateWebLogicApplications.bicep b/src/main/bicep/modules/updateWebLogicApplications.bicep index 3daf88906..58944e360 100644 --- a/src/main/bicep/modules/updateWebLogicApplications.bicep +++ b/src/main/bicep/modules/updateWebLogicApplications.bicep @@ -16,7 +16,7 @@ Parameters - aksClusterRGName: Name of resource group that contains the (AKS) instance, probably the resource group you are working on. It's recommended to run this sript with the same resource group that runs AKS. - aksClusterName: Name of the AKS instance that runs the WebLogic cluster. - appPackageUrls: String array of Java EE applciation location, which can be downloaded using "curl". Currently, only support urls of Azure Storage Account blob. - - appPackageFromStorageBlob: Storage blob that contains Java EE applciations, the script will download all the .war and .ear file from that blob. + - appPackageFromStorageBlob: Storage blob that contains Java EE applciations, the script will download all the .war and .ear file from that blob. Do not include white space in the file name. - storageAccountName: Storage account name. - containerName: container name. - identity: Azure user managed identity used, make sure the identity has permission to create/update/delete Azure resources. It's recommended to assign "Contributor" role. From 830087227fccb7985c91db8e6ebc41a7a06bcccb Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Tue, 10 Aug 2021 18:43:59 +0000 Subject: [PATCH 25/37] Merged PR 341394: Allow updating weblogic cluster using maintemplate and marketplace offer ### Description This pr is to enhance the templates to allow updating weblogic cluster using maintemplate and marketplace offer. Including - update an existing domain using maintemplate and marketplace offer - create multiple domains using maintemplate and marketplace offer ### What those scripts will do? - build a new image according new inputs - apply the new image to current cluster if the target domain exists - create new domain with one cluster using the image if the target domain does not exist ### Difference between using maintemplate and marketplace offer - use maintemplate - invoke maintamplate using AZ CLI in the existing resource group - be able to re-use existing key vault and storage account - use marketplace offer - invoke the marketplace offer in a new resource group, it's not allowed to deploy the offer in a non-empty resource group - be able to re-use the storage account - will create a new key vault for certificates if there are certificate inputs, the previous key vault will not be used any more (it will not cause cost). Related work items: #1361284, #1363463 --- pom.xml | 2 +- src/main/arm/scripts/pv.yaml.template | 8 +- src/main/arm/scripts/pvc.yaml.template | 2 + src/main/arm/scripts/queryStorageAccount.sh | 49 +++ src/main/arm/scripts/setupWLSDomain.sh | 367 +++++++++++++----- src/main/arm/scripts/updateApplications.sh | 61 +-- src/main/arm/scripts/updateDomainConfig.sh | 269 +++++++++++++ src/main/arm/scripts/utility.sh | 90 +++++ src/main/bicep/mainTemplate.bicep | 75 +++- .../_keyvault/_keyvaultForWLSSSLCert.bicep | 4 + .../_keyvault/_keyvaultWithExistingCert.bicep | 5 + .../_keyvault/_keyvaultWithNewCert.bicep | 3 + .../modules/_azure-resoruces/_storage.bicep | 9 +- .../_ds-create-wls-cluster.bicep | 5 +- .../_ds-query-storage-account.bicep | 30 ++ .../bicep/modules/setupWebLogicCluster.bicep | 10 +- 16 files changed, 805 insertions(+), 184 deletions(-) create mode 100644 src/main/arm/scripts/queryStorageAccount.sh create mode 100644 src/main/arm/scripts/updateDomainConfig.sh create mode 100644 src/main/bicep/modules/_deployment-scripts/_ds-query-storage-account.bicep diff --git a/pom.xml b/pom.xml index 3bf67665b..a1613dc34 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ com.oracle.weblogic.azure wls-on-aks-azure-marketplace - 1.0.5 + 1.0.6 com.microsoft.azure.iaas diff --git a/src/main/arm/scripts/pv.yaml.template b/src/main/arm/scripts/pv.yaml.template index 82450d8e5..fde59f802 100644 --- a/src/main/arm/scripts/pv.yaml.template +++ b/src/main/arm/scripts/pv.yaml.template @@ -7,14 +7,18 @@ apiVersion: v1 kind: PersistentVolume metadata: - name: @PV_NME@ - namespace: @NAMESPACE@ + name: @PV_NAME@ + labels: + storageAccount: @STORAGE_ACCOUNT@ spec: capacity: storage: 5Gi accessModes: - ReadWriteMany storageClassName: azurefile + claimRef: + name: @PVC_NAME@ + namespace: @NAMESPACE@ azureFile: secretName: azure-secret shareName: weblogic diff --git a/src/main/arm/scripts/pvc.yaml.template b/src/main/arm/scripts/pvc.yaml.template index 22fb22632..baab56058 100644 --- a/src/main/arm/scripts/pvc.yaml.template +++ b/src/main/arm/scripts/pvc.yaml.template @@ -9,6 +9,8 @@ kind: PersistentVolumeClaim metadata: name: @PVC_NAME@ namespace: @NAMESPACE@ + labels: + storageAccount: @STORAGE_ACCOUNT@ spec: accessModes: - ReadWriteMany diff --git a/src/main/arm/scripts/queryStorageAccount.sh b/src/main/arm/scripts/queryStorageAccount.sh new file mode 100644 index 000000000..58c033a74 --- /dev/null +++ b/src/main/arm/scripts/queryStorageAccount.sh @@ -0,0 +1,49 @@ +export aksClusterRGName=$1 +export aksClusterName=$2 +export wlsDomainUID=$3 + +export wlsDomainNS="${wlsDomainUID}-ns" +export currentStorageAccount="null" + +# Connect to AKS cluster +function connect_aks_cluster() { + az aks get-credentials \ + --resource-group ${aksClusterRGName} \ + --name ${aksClusterName} \ + --overwrite-existing +} + +function query_storage_account() { + echo "install kubectl" + az aks install-cli + + echo "get pv, pvc" + pvcName=${wlsDomainUID}-pvc-azurefile + pvName=${wlsDomainUID}-pv-azurefile + + ret=$(kubectl -n ${wlsDomainNS} get pvc ${pvcName} | grep "Bound") + + if [ -n "$ret" ]; then + echo "pvc is bound to namespace ${wlsDomainNS}." + # this is a workaround for update domain using marketplace offer. + # the offer will create a new storage account in a new resource group. + # remove the new storage account. + currentStorageAccount=$(kubectl get pv ${pvName} -o json | jq '. | .metadata.labels.storageAccount' | tr -d "\"") + fi +} + +function output_result() { + echo ${currentStorageAccount} + + result=$(jq -n -c \ + --arg storageAccount $currentStorageAccount \ + '{storageAccount: $storageAccount}') + echo "result is: $result" + echo $result >$AZ_SCRIPTS_OUTPUT_PATH +} + +connect_aks_cluster + +query_storage_account + +output_result \ No newline at end of file diff --git a/src/main/arm/scripts/setupWLSDomain.sh b/src/main/arm/scripts/setupWLSDomain.sh index 41cd029de..be94147e1 100644 --- a/src/main/arm/scripts/setupWLSDomain.sh +++ b/src/main/arm/scripts/setupWLSDomain.sh @@ -10,13 +10,46 @@ function echo_stderr() { } function echo_stdout() { - echo "$@" >&2 + echo "$@" echo "$@" >>stdout } #Function to display usage message function usage() { - echo_stdout "./setupWLSDomain.sh " + cat< \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + +EOF if [ $1 -eq 1 ]; then exit 1 fi @@ -144,12 +177,7 @@ function validate_input() { usage 1 fi - if [ -z "$gatewayAlias" ]; then - echo_stderr "gatewayAlias is required. " - usage 1 - fi - - if [ -z "$enablePV" ]; then + if [ -z "$enablePV" ]; then echo_stderr "enablePV is required. " usage 1 fi @@ -206,31 +234,80 @@ function connect_aks_cluster() { az aks get-credentials --resource-group ${aksClusterRGName} --name ${aksClusterName} --overwrite-existing } +# remove the operator if it is not running. +function uninstall_operator() { + echo "remove operator" + helm uninstall ${operatorName} -n ${wlsOptNameSpace} + attempts=0 + ret=$(helm list -n ${wlsOptNameSpace} | grep "${operatorName}") + while [ -n "$ret" ] && [ $attempts -lt ${optUninstallMaxTry} ]; do + sleep ${optUninstallInterval} + attempts=$((attempts + 1)) + ret=$(helm list -n ${wlsOptNameSpace} | grep "${operatorName}") + done + + if [ $attempts -ge ${optUninstallMaxTry} ]; then + echo_stderr "Failed to remove an unvaliable operator." + exit 1 + fi +} + +function validate_existing_operator() { + ret=$(helm list -n ${wlsOptNameSpace} | grep "${operatorName}" | grep "deployed") + if [ -n "${ret}" ]; then + echo "the operator has been deployed" + echo "${ret}" + + ret=$(kubectl get pod -n ${wlsOptNameSpace} | grep "Running" | grep "1/1") + if [ -n "${ret}" ]; then + echo "the operator is ready to use." + operatorStatus=${constTrue} + else + echo "the operator is unavailable." + uninstall_operator + fi + fi +} + # Install WebLogic operator using charts from GitHub Repo # * Create namespace weblogic-operator-ns # * Create service account # * install operator function install_wls_operator() { - kubectl create namespace ${wlsOptNameSpace} - kubectl -n ${wlsOptNameSpace} create serviceaccount ${wlsOptSA} + echo "check if the operator is installed" + ret=$(kubectl get namespace | grep "${wlsOptNameSpace}") + if [ -z "${ret}" ]; then + echo "create namespace ${wlsOptNameSpace}" + kubectl create namespace ${wlsOptNameSpace} + kubectl -n ${wlsOptNameSpace} create serviceaccount ${wlsOptSA} + + helm repo add ${wlsOptRelease} ${wlsOptHelmChart} --force-update + ret=$(helm repo list) + validate_status ${ret} + else + export operatorStatus=${constFalse} + validate_existing_operator + if [[ "${operatorStatus}" == "${constTrue}" ]]; then + return + fi + fi - helm repo add ${wlsOptRelease} ${wlsOptHelmChart} --force-update - ret=$(helm repo list) - validate_status ${ret} + echo "install the operator" helm install ${wlsOptRelease} weblogic-operator/weblogic-operator \ - --namespace ${wlsOptNameSpace} \ - --set serviceAccount=${wlsOptSA} \ - --set "enableClusterRoleBinding=true" \ - --set "domainNamespaceSelectionStrategy=LabelSelector" \ - --set "domainNamespaceLabelSelector=weblogic-operator\=enabled" \ - --wait + --namespace ${wlsOptNameSpace} \ + --set serviceAccount=${wlsOptSA} \ + --set "enableClusterRoleBinding=true" \ + --set "domainNamespaceSelectionStrategy=LabelSelector" \ + --set "domainNamespaceLabelSelector=weblogic-operator\=enabled" \ + --version ${wlsOptVersion} \ + --wait validate_status "Installing WLS operator." # valiadate weblogic operator - ret=$(kubectl get pod -n ${wlsOptNameSpace} | grep "Running") + ret=$(kubectl get pod -n ${wlsOptNameSpace} | grep "Running" | grep "1/1") if [ -z "$ret" ]; then - echo_stderr "Failed to install WebLogic operator." + echo_stderr "No WebLogic operator is running." exit 1 fi } @@ -274,23 +351,29 @@ function build_docker_image() { } function mount_fileshare() { - resourceGroupName="${currentResourceGroup}" fileShareName="${azFileShareName}" # Disable https-only - az storage account update --name ${storageAccountName} --resource-group ${resourceGroupName} --https-only false + az storage account update --name ${storageAccountName} --resource-group ${storageResourceGroup} --https-only false mntRoot="/wls" mntPath="$mntRoot/$storageAccountName/$fileShareName" mkdir -p $mntPath - httpEndpoint=$(az storage account show \ - --resource-group $resourceGroupName \ + httpEndpoint=$( + az storage account show \ + --resource-group $storageResourceGroup \ --name $storageAccountName \ - --query "primaryEndpoints.file" | tr -d '"') + --query "primaryEndpoints.file" | tr -d '"' + ) smbPath=$(echo $httpEndpoint | cut -c7-$(expr length $httpEndpoint))$fileShareName + export storageAccountKey=$(az storage account keys list \ + --resource-group $storageResourceGroup \ + --account-name $storageAccountName \ + --query "[0].value" -o tsv) + mount -t cifs $smbPath $mntPath -o username=$storageAccountName,password=$storageAccountKey,serverino,vers=3.0,file_mode=0777,dir_mode=0777 validate_status "Mounting path." } @@ -299,7 +382,7 @@ function unmount_fileshare() { echo "unmount fileshare." umount ${mntPath} # Disable https-only - az storage account update --name ${storageAccountName} --resource-group ${currentResourceGroup} --https-only true + az storage account update --name ${storageAccountName} --resource-group ${storageResourceGroup} --https-only true } function validate_ssl_keystores() { @@ -390,25 +473,50 @@ function output_ssl_keystore() { # * Create PV using Azure file share # * Create PVC function create_pv() { - export storageAccountKey=$(az storage account keys list --resource-group $currentResourceGroup --account-name $storageAccountName --query "[0].value" -o tsv) + echo "check if pv/pvc have been created." + pvcName=${wlsDomainUID}-pvc-azurefile + pvName=${wlsDomainUID}-pv-azurefile + ret=$(kubectl -n ${wlsDomainNS} get pvc ${pvcName} | grep "Bound") + + if [ -n "$ret" ]; then + echo "pvc is bound to namespace ${wlsDomainNS}." + # this is a workaround for update domain using marketplace offer. + # the offer will create a new storage account in a new resource group. + # remove the new storage account. + currentStorageAccount=$(kubectl get pv ${pvName} -o json | jq '. | .metadata.labels.storageAccount' | tr -d "\"") + if [[ "${currentStorageAccount}" != "${storageAccountName}" ]]; then + echo "the cluster is bound to pv on storage account ${currentStorageAccount}" + az storage account delete -n ${storageAccountName} -g $currentResourceGroup -y + storageAccountName=${currentStorageAccount} # update storage account name + echo "query storage account resource group" + storageResourceGroup=$(az storage account show --name ${storageAccountName} | jq '.resourceGroup' | tr -d "\"") + echo "resource group that contains storage account ${storageAccountName} is ${storageResourceGroup}" + fi + + return + fi + + echo "create pv/pvc." + export storageAccountKey=$(az storage account keys list --resource-group $storageResourceGroup --account-name $storageAccountName --query "[0].value" -o tsv) export azureSecretName="azure-secret" kubectl -n ${wlsDomainNS} create secret generic ${azureSecretName} \ - --from-literal=azurestorageaccountname=${storageAccountName} \ - --from-literal=azurestorageaccountkey=${storageAccountKey} + --from-literal=azurestorageaccountname=${storageAccountName} \ + --from-literal=azurestorageaccountkey=${storageAccountKey} # generate pv configurations customPVYaml=${scriptDir}/pv.yaml cp ${scriptDir}/pv.yaml.template ${customPVYaml} - pvName=${wlsDomainUID}-pv-azurefile sed -i -e "s:@NAMESPACE@:${wlsDomainNS}:g" ${customPVYaml} - sed -i -e "s:@PV_NME@:${pvName}:g" ${customPVYaml} + sed -i -e "s:@PV_NAME@:${pvName}:g" ${customPVYaml} + sed -i -e "s:@PVC_NAME@:${pvcName}:g" ${customPVYaml} + sed -i -e "s:@STORAGE_ACCOUNT@:${storageAccountName}:g" ${customPVYaml} # generate pv configurations customPVCYaml=${scriptDir}/pvc.yaml cp ${scriptDir}/pvc.yaml.template ${customPVCYaml} - pvcName=${wlsDomainUID}-pvc-azurefile sed -i -e "s:@NAMESPACE@:${wlsDomainNS}:g" ${customPVCYaml} sed -i -e "s:@PVC_NAME@:${pvcName}:g" ${customPVCYaml} + sed -i -e "s:@STORAGE_ACCOUNT@:${storageAccountName}:g" ${customPVCYaml} kubectl apply -f ${customPVYaml} kubectl apply -f ${customPVCYaml} @@ -420,40 +528,70 @@ function create_pv() { fi } -# Deploy WebLogic domain and cluster -# * Create namespace for domain -# * Create secret for weblogic -# * Create secret for Azure file -# * Create secret for ACR -# * Deploy WebLogic domain using image in ACR -# * Wait for the domain completed -function setup_wls_domain() { - kubectl create namespace ${wlsDomainNS} - kubectl label namespace ${wlsDomainNS} weblogic-operator=enabled +function wait_for_pod_completed() { + echo "Waiting for $((appReplicas+1)) pods are running." + + utility_wait_for_pod_completed \ + ${appReplicas} \ + "${wlsDomainNS}" \ + ${checkPodStatusMaxAttemps} \ + ${checkPodStatusInterval} +} + +function wait_for_image_update_completed() { + # Make sure all of the pods are updated with new image. + # Assumption: we have only one cluster currently. + acrImagePath=${azureACRServer}/aks-wls-images:${newImageTag} + echo "Waiting for $((appReplicas+1)) new pods created with image ${acrImagePath}" + + utility_wait_for_image_update_completed \ + "${acrImagePath}" \ + ${appReplicas} \ + "${wlsDomainNS}" \ + ${checkPodStatusMaxAttemps} \ + ${checkPodStatusInterval} +} + +function create_domain_namespace() { + echo "check if namespace ${wlsDomainNS} exists?" + ret=$(kubectl get namespace | grep "${wlsDomainNS}") + + updateNamepace=${constFalse} + if [ -z "${ret}" ]; then + echo "create namespace ${wlsDomainNS}" + kubectl create namespace ${wlsDomainNS} + kubectl label namespace ${wlsDomainNS} weblogic-operator=enabled + else + updateNamepace=${constTrue} + echo "Remove existing secrets and replace with new values" + kubectl -n ${wlsDomainNS} delete secret ${kubectlWLSCredentials} + kubectl -n ${wlsDomainNS} delete secret ${kubectlWDTEncryptionSecret} + kubectl -n ${wlsDomainNS} delete secret ${kubectlSecretForACR} + fi kubectl -n ${wlsDomainNS} create secret generic \ - ${kubectlWLSCredentials} \ - --from-literal=username=${wlsUserName} \ - --from-literal=password=${wlsPassword} + ${kubectlWLSCredentials} \ + --from-literal=username=${wlsUserName} \ + --from-literal=password=${wlsPassword} kubectl -n ${wlsDomainNS} label secret ${kubectlWLSCredentials} weblogic.domainUID=${wlsDomainUID} - kubectl -n ${wlsDomainNS} create secret generic ${wlsDomainUID}-runtime-encryption-secret \ - --from-literal=password=${wdtRuntimePassword} - kubectl -n ${wlsDomainNS} label secret ${wlsDomainUID}-runtime-encryption-secret weblogic.domainUID=${wlsDomainUID} + kubectl -n ${wlsDomainNS} create secret generic ${kubectlWDTEncryptionSecret} \ + --from-literal=password=${wdtRuntimePassword} + kubectl -n ${wlsDomainNS} label secret ${kubectlWDTEncryptionSecret} weblogic.domainUID=${wlsDomainUID} kubectl create secret docker-registry ${kubectlSecretForACR} \ - --docker-server=${azureACRServer} \ - --docker-username=${azureACRUserName} \ - --docker-password=${azureACRPassword} \ - -n ${wlsDomainNS} + --docker-server=${azureACRServer} \ + --docker-username=${azureACRUserName} \ + --docker-password=${azureACRPassword} \ + -n ${wlsDomainNS} - if [[ "${enablePV,,}" == "true" ]]; then - create_pv - fi + kubectl -n ${wlsDomainNS} label secret ${kubectlSecretForACR} weblogic.domainUID=${wlsDomainUID} +} +function parsing_ssl_certs_and_create_ssl_secret() { export javaOptions="" - if [[ "${enableCustomSSL,,}" == "true" ]]; then + if [[ "${enableCustomSSL,,}" == "${constTrue}" ]]; then # use default Java, if no, install open jdk 11. # why not Microsoft open jdk? No apk installation package! export JAVA_HOME=/usr/lib/jvm/default-jvm/ @@ -467,6 +605,13 @@ function setup_wls_domain() { validate_ssl_keystores unmount_fileshare + echo "check if ${kubectlWLSSSLCredentials} exists." + ret=$(kubectl get secret -n ${wlsDomainNS} | grep "${kubectlWLSSSLCredentials}") + if [ -n "${ret}" ]; then + echo "delete secret ${kubectlWLSSSLCredentials}" + kubectl -n ${wlsDomainNS} delete secret ${kubectlWLSSSLCredentials} + fi + echo "create secret ${kubectlWLSSSLCredentials}" kubectl -n ${wlsDomainNS} create secret generic ${kubectlWLSSSLCredentials} \ --from-literal=sslidentitykeyalias=${wlsIdentityAlias} \ --from-literal=sslidentitykeypassword=${wlsIdentityKeyPsw} \ @@ -480,11 +625,42 @@ function setup_wls_domain() { kubectl -n ${wlsDomainNS} label secret ${kubectlWLSSSLCredentials} weblogic.domainUID=${wlsDomainUID} javaOptions="-Dweblogic.security.SSL.ignoreHostnameVerification=true -Dweblogic.security.SSL.trustedCAKeyStore=${sharedPath}/${wlsTrustKeyStoreJKSFileName}" fi +} + +# Deploy WebLogic domain and cluster +# * Create namespace for domain +# * Create secret for weblogic +# * Create secret for Azure file +# * Create secret for ACR +# * Deploy WebLogic domain using image in ACR +# * Wait for the domain completed +function setup_wls_domain() { + # create namespace + create_domain_namespace + + echo "constTrue": "${constTrue}" + if [[ "${enablePV,,}" == "${constTrue}" ]]; then + echo "start to create pv/pvc. " + create_pv + fi + + parsing_ssl_certs_and_create_ssl_secret + + # show resources + echo "print weblogic operator status" + kubectl -n ${wlsOptNameSpace} get pod -o wide + echo "print secrets that is ready to use" + kubectl -n ${wlsDomainNS} get secret -o wide + echo "print current configmap" + kubectl -n ${wlsDomainNS} get configmap -o wide + echo "print pvc info" + kubectl -n ${wlsDomainNS} get pvc -o wide - # generate domain yaml customDomainYaml=${scriptDir}/custom-domain.yaml - chmod ugo+x $scriptDir/genDomainConfig.sh - bash $scriptDir/genDomainConfig.sh \ + if [[ "${updateNamepace}" == "${constTrue}" ]]; then + echo "start to update domain ${wlsDomainUID}" + chmod ugo+x $scriptDir/updateDomainConfig.sh + bash $scriptDir/updateDomainConfig.sh \ ${customDomainYaml} \ ${appReplicas} \ ${wlsCPU} \ @@ -496,54 +672,29 @@ function setup_wls_domain() { ${enableCustomSSL} \ ${enablePV} \ "${javaOptions}" + else + echo "start to create domain ${wlsDomainUID}" + # generate domain yaml + chmod ugo+x $scriptDir/genDomainConfig.sh + bash $scriptDir/genDomainConfig.sh \ + ${customDomainYaml} \ + ${appReplicas} \ + ${wlsCPU} \ + ${wlsDomainUID} \ + ${wlsDomainName} \ + "${azureACRServer}/aks-wls-images:${newImageTag}" \ + ${wlsMemory} \ + ${managedServerPrefix} \ + ${enableCustomSSL} \ + ${enablePV} \ + "${javaOptions}" + fi kubectl apply -f ${customDomainYaml} - wait_for_domain_completed -} - -function wait_for_domain_completed() { - attempts=0 - svcState="running" - while [ ! "$svcState" == "completed" ] && [ $attempts -lt 10 ]; do - svcState="completed" - attempts=$((attempts + 1)) - echo Waiting for job completed...${attempts} - sleep 2m - - # If the job is completed, there should have the following services created, - # ${domainUID}-${adminServerName}, e.g. domain1-admin-server - adminServiceCount=$(kubectl -n ${wlsDomainNS} get svc | grep -c "${wlsDomainUID}-${adminServerName}") - if [ ${adminServiceCount} -lt 1 ]; then svcState="running"; fi - - # If the job is completed, there should have the following services created, .assuming initialManagedServerReplicas=2 - # ${domainUID}-${managedServerNameBase}1, e.g. domain1-managed-server1 - # ${domainUID}-${managedServerNameBase}2, e.g. domain1-managed-server2 - managedServiceCount=$(kubectl -n ${wlsDomainNS} get svc | grep -c "${wlsDomainUID}-${managedServerPrefix}") - if [ ${managedServiceCount} -lt ${appReplicas} ]; then svcState="running"; fi - - # If the job is completed, there should have no service in pending status. - pendingCount=$(kubectl -n ${wlsDomainNS} get pod | grep -c "pending") - if [ ${pendingCount} -ne 0 ]; then svcState="running"; fi - - # If the job is completed, there should have the following pods running - # ${domainUID}-${adminServerName}, e.g. domain1-admin-server - # ${domainUID}-${managedServerNameBase}1, e.g. domain1-managed-server1 - # to - # ${domainUID}-${managedServerNameBase}n, e.g. domain1-managed-servern, n = initialManagedServerReplicas - runningPodCount=$(kubectl -n ${wlsDomainNS} get pods | grep "${wlsDomainUID}" | grep -c "Running") - if [[ $runningPodCount -le ${appReplicas} ]]; then svcState="running"; fi - done + wait_for_image_update_completed - # If all the services are completed, print service details - # Otherwise, ask the user to refer to document for troubleshooting - if [ "$svcState" == "completed" ]; then - kubectl -n ${wlsDomainNS} get pods - kubectl -n ${wlsDomainNS} get svc - else - echo WARNING: WebLogic domain is not ready. It takes too long to create domain, please refer to http://oracle.github.io/weblogic-kubernetes-operator/samples/simple/azure-kubernetes-service/#troubleshooting - exitCode=1 - fi + wait_for_pod_completed } # Main script @@ -582,24 +733,26 @@ export wlsIdentityKeyPsw=${26} export wlsTrustData=${27} export wlsTrustPsw=${28} export wlsTrustType=${29} -export gatewayAlias=${30} -export enablePV=${31} +export enablePV=${30} export adminServerName="admin-server" export azFileShareName="weblogic" export exitCode=0 -export ocrLoginServer="container-registry.oracle.com" export kubectlSecretForACR="regsecret" export kubectlWLSCredentials="${wlsDomainUID}-weblogic-credentials" export kubectlWLSSSLCredentials="${wlsDomainUID}-weblogic-ssl-credentials" +export kubectlWDTEncryptionSecret="${wlsDomainUID}-runtime-encryption-secret" export newImageTag=$(date +%s) +export operatorName="weblogic-operator" export storageFileShareName="weblogic" +export storageResourceGroup=${currentResourceGroup} export sharedPath="/shared" export wlsDomainNS="${wlsDomainUID}-ns" export wlsOptHelmChart="https://oracle.github.io/weblogic-kubernetes-operator/charts" export wlsOptNameSpace="weblogic-operator-ns" export wlsOptRelease="weblogic-operator" export wlsOptSA="weblogic-operator-sa" +export wlsOptVersion="3.2.5" export wlsIdentityKeyStoreFileName="security/identity.keystore" export wlsTrustKeyStoreFileName="security/trust.keystore" export wlsTrustKeyStoreJKSFileName="security/trust.jks" diff --git a/src/main/arm/scripts/updateApplications.sh b/src/main/arm/scripts/updateApplications.sh index 738575a47..e08460261 100644 --- a/src/main/arm/scripts/updateApplications.sh +++ b/src/main/arm/scripts/updateApplications.sh @@ -133,30 +133,11 @@ function wait_for_pod_completed() { replicas=$(kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json \ | jq '. | .spec.clusters[] | .replicas') - echo "Waiting for $((replicas+1)) pods are running." - - readyPodNum=0 - attempt=0 - while [[ ${readyPodNum} -le ${replicas} && $attempt -le ${checkPodStatusMaxAttemps} ]];do - ret=$(kubectl get pods -n ${wlsDomainNS} -o json \ - | jq '.items[] | .status.phase' \ - | grep "Running") - if [ -z "${ret}" ];then - readyPodNum=0 - else - readyPodNum=$(kubectl get pods -n ${wlsDomainNS} -o json \ - | jq '.items[] | .status.phase' \ - | grep -c "Running") - fi - echo "Number of new running pod: ${readyPodNum}" - attempt=$((attempt+1)) - sleep ${checkPodStatusInterval} - done - - if [ ${attempt} -gt ${checkPodStatusMaxAttemps} ];then - echo "It takes too long to wait for all the pods are running, please refer to http://oracle.github.io/weblogic-kubernetes-operator/samples/simple/azure-kubernetes-service/#troubleshooting" - exit 1 - fi + utility_wait_for_pod_completed \ + ${replicas} \ + "${wlsDomainNS}" \ + ${checkPodStatusMaxAttemps} \ + ${checkPodStatusInterval} } function wait_for_image_update_completed() { @@ -164,33 +145,13 @@ function wait_for_image_update_completed() { # Assumption: we have only one cluster currently. replicas=$(kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json \ | jq '. | .spec.clusters[] | .replicas') - echo "Waiting for $((replicas+1)) new pods created with image ${acrImagePath}" - - updatedPodNum=0 - attempt=0 - while [ ${updatedPodNum} -le ${replicas} ] && [ $attempt -le ${checkPodStatusMaxAttemps} ];do - echo "attempts ${attempt}" - ret=$(kubectl get pods -n ${wlsDomainNS} -o json \ - | jq '.items[] | .spec | .containers[] | select(.name == "weblogic-server") | .image' \ - | grep "${acrImagePath}") - if [ -z "${ret}" ];then - updatedPodNum=0 - else - updatedPodNum=$(kubectl get pods -n ${wlsDomainNS} -o json \ - | jq '.items[] | .spec | .containers[] | select(.name == "weblogic-server") | .image' \ - | grep -c "${acrImagePath}") - fi - echo "Number of new pod: ${updatedPodNum}" - - attempt=$((attempt+1)) - sleep ${checkPodStatusInterval} - done - - if [ ${attempt} -gt ${checkPodStatusMaxAttemps} ];then - echo "Failed to update with image ${acrImagePath} to all weblogic server pods. " - exit 1 - fi + utility_wait_for_image_update_completed \ + "${acrImagePath}" \ + ${replicas} \ + "${wlsDomainNS}" \ + ${checkPodStatusMaxAttemps} \ + ${checkPodStatusInterval} } #Output value to deployment scripts diff --git a/src/main/arm/scripts/updateDomainConfig.sh b/src/main/arm/scripts/updateDomainConfig.sh new file mode 100644 index 000000000..e65c163d1 --- /dev/null +++ b/src/main/arm/scripts/updateDomainConfig.sh @@ -0,0 +1,269 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +export filePath=$1 +export replicas=$2 +export wlsCPU=$3 +export wlsDomainUID=$4 +export wlsDomainName=$5 +export wlsImagePath=$6 +export wlsMemory=$7 +export wlsManagedPrefix=$8 +export enableSSL=${9} +export enablePV=${10} +export javaOptions=${11} + +export wlsDomainNS="${wlsDomainUID}-ns" + +# output the existing domain configuration +export currentConfig=${scriptDir}/currentDomain.json +kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json >${currentConfig} + +# query logHomeEnabled +logHomeEnabled=$(cat ${currentConfig} | jq '. | .spec.logHomeEnabled') +logHome=$(cat ${currentConfig} | jq '. | .spec.logHome') +envList=$(cat ${currentConfig} | jq '. | .spec.serverPod.env') +envLength=$(cat ${currentConfig} | jq '. | .spec.serverPod.env | length') +restartVersion=$(cat ${currentConfig} | jq '. | .spec.restartVersion' | tr -d "\"") +configMap=$(cat ${currentConfig} | jq '. | .spec.configuration.model.configMap') +secretList=$(cat ${currentConfig} | jq '. | .spec.configuration.secrets') +restartVersion=$((restartVersion+1)) + +cat <$filePath +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. +# +# Based on ./kubernetes/samples/scripts/create-weblogic-domain/model-in-image/domain-resources/WLS/mii-initial-d1-WLS-v1.yaml +# in https://github.com/oracle/weblogic-kubernetes-operator. +# This is an example of how to define a Domain resource. +# +apiVersion: "weblogic.oracle/v8" +kind: Domain +metadata: + name: "${wlsDomainUID}" + namespace: "${wlsDomainNS}" + labels: + weblogic.domainUID: "${wlsDomainUID}" + +spec: + # Set to 'FromModel' to indicate 'Model in Image'. + domainHomeSourceType: FromModel + + # The WebLogic Domain Home, this must be a location within + # the image for 'Model in Image' domains. + domainHome: /u01/domains/${wlsDomainUID} + + # The WebLogic Server Docker image that the Operator uses to start the domain + image: "${wlsImagePath}" + + # Defaults to "Always" if image tag (version) is ':latest' + imagePullPolicy: "IfNotPresent" + + # Identify which Secret contains the credentials for pulling an image + imagePullSecrets: + - name: regsecret + + # Identify which Secret contains the WebLogic Admin credentials, + # the secret must contain 'username' and 'password' fields. + webLogicCredentialsSecret: + name: "${wlsDomainUID}-weblogic-credentials" + + # Whether to include the WebLogic Server stdout in the pod's stdout, default is true + includeServerOutInPodLog: true + # Set which WebLogic Servers the Operator will start + # - "NEVER" will not start any server in the domain + # - "ADMIN_ONLY" will start up only the administration server (no managed servers will be started) + # - "IF_NEEDED" will start all non-clustered servers, including the administration server, and clustered servers up to their replica count. + serverStartPolicy: "IF_NEEDED" +EOF + +if [[ "${logHomeEnabled}" == "true" ]];then + cat <>$filePath + # Whether to enable overriding your log file location, see also 'logHome' + logHomeEnabled: true + + # The location for domain log, server logs, server out, introspector out, and Node Manager log files + # see also 'logHomeEnabled', 'volumes', and 'volumeMounts'. + logHome: ${logHome} +EOF +fi + +# Resources +cat <>$filePath + # Settings for all server pods in the domain including the introspector job pod + serverPod: + resources: + requests: + cpu: "${wlsCPU}" + memory: "${wlsMemory}" + # Optional new or overridden environment variables for the domain's pods + # - This sample uses CUSTOM_DOMAIN_NAME in its image model file + # to set the Weblogic domain name + env: +EOF + +if [[ "${enableSSL,,}" == "true" ]]; then + cat <>$filePath + - name: SSL_IDENTITY_PRIVATE_KEY_ALIAS + valueFrom: + secretKeyRef: + key: sslidentitykeyalias + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_IDENTITY_PRIVATE_KEY_PSW + valueFrom: + secretKeyRef: + key: sslidentitykeypassword + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_IDENTITY_PRIVATE_KEYSTORE_PATH + valueFrom: + secretKeyRef: + key: sslidentitystorepath + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_IDENTITY_PRIVATE_KEYSTORE_TYPE + valueFrom: + secretKeyRef: + key: sslidentitystoretype + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_IDENTITY_PRIVATE_KEYSTORE_PSW + valueFrom: + secretKeyRef: + key: sslidentitystorepassword + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_TRUST_KEYSTORE_PATH + valueFrom: + secretKeyRef: + key: ssltruststorepath + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_TRUST_KEYSTORE_TYPE + valueFrom: + secretKeyRef: + key: ssltruststoretype + name: ${wlsDomainUID}-weblogic-ssl-credentials + - name: SSL_TRUST_KEYSTORE_PSW + valueFrom: + secretKeyRef: + key: ssltruststorepassword + name: ${wlsDomainUID}-weblogic-ssl-credentials +EOF +fi + +index=0 +while [ $index -lt ${envLength} ]; do + envItemName=$(cat ${currentConfig} | jq ". | .spec.serverPod.env[$index] | .name" | tr -d "\"") + envItemValue=$(cat ${currentConfig} | jq ". | .spec.serverPod.env[$index] | .value") + index=$((index+1)) + + if [[ "${envItemName}" == "JAVA_OPTIONS" ]];then + envItemValue="\"-Dweblogic.StdoutDebugEnabled=false ${javaOptions}\"" + fi + + # do not copy value from SSL_ env + if [[ "${envItemName}" == "SSL_IDENTITY_PRIVATE_KEY_ALIAS" ]] \ + || [[ "${envItemName}" == "SSL_IDENTITY_PRIVATE_KEY_PSW" ]] \ + || [[ "${envItemName}" == "SSL_IDENTITY_PRIVATE_KEYSTORE_PATH" ]] \ + || [[ "${envItemName}" == "SSL_IDENTITY_PRIVATE_KEYSTORE_TYPE" ]] \ + || [[ "${envItemName}" == "SSL_IDENTITY_PRIVATE_KEYSTORE_PSW" ]] \ + || [[ "${envItemName}" == "SSL_TRUST_KEYSTORE_PATH" ]] \ + || [[ "${envItemName}" == "SSL_TRUST_KEYSTORE_TYPE" ]] \ + || [[ "${envItemName}" == "SSL_TRUST_KEYSTORE_PSW" ]];then + continue + fi + + cat <>$filePath + - name: "${envItemName}" + value: ${envItemValue} +EOF +done + +if [[ "${enablePV,,}" == "true" ]]; then + cat <>$filePath + # Optional volumes and mounts for the domain's pods. See also 'logHome'. + volumes: + - name: ${wlsDomainUID}-pv-azurefile + persistentVolumeClaim: + claimName: ${wlsDomainUID}-pvc-azurefile + volumeMounts: + - mountPath: /shared + name: ${wlsDomainUID}-pv-azurefile +EOF +fi + +cat <>$filePath + # The desired behavior for starting the domain's administration server. + adminServer: + # The serverStartState legal values are "RUNNING" or "ADMIN" + # "RUNNING" means the listed server will be started up to "RUNNING" mode + # "ADMIN" means the listed server will be start up to "ADMIN" mode + serverStartState: "RUNNING" + # Setup a Kubernetes node port for the administration server default channel + #adminService: + # channels: + # - channelName: default + # nodePort: 30701 + + # The number of admin servers to start for unlisted clusters + replicas: 1 + + # The desired behavior for starting a specific cluster's member servers + clusters: + - clusterName: cluster-1 + serverStartState: "RUNNING" + serverPod: + # Instructs Kubernetes scheduler to prefer nodes for new cluster members where there are not + # already members of the same cluster. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "weblogic.clusterName" + operator: In + values: + - \$(CLUSTER_NAME) + topologyKey: "kubernetes.io/hostname" + # The number of managed servers to start for unlisted clusters + replicas: ${replicas} + + # Change the restartVersion to force the introspector job to rerun + # and apply any new model configuration, to also force a subsequent + # roll of your domain's WebLogic Server pods. + restartVersion: '${restartVersion}' + + configuration: + + # Settings for domainHomeSourceType 'FromModel' + model: + # Valid model domain types are 'WLS', 'JRF', and 'RestrictedJRF', default is 'WLS' + domainType: "WLS" + # All 'FromModel' domains require a runtimeEncryptionSecret with a 'password' field + runtimeEncryptionSecret: "${wlsDomainUID}-runtime-encryption-secret" +EOF + +echo "set configmap..." +if [[ "${configMap}" != "null" ]];then + cat <>$filePath + # Optional configmap for additional models and variable files + configMap: ${configMap} +EOF +fi + +echo "set secrets" +if [[ "${secretList}" != "null" ]];then + secretLength=$(cat ${currentConfig} | jq '. | .spec.configuration.secrets | length') + cat <>$filePath + secrets: +EOF + index=0 + while [ $index -lt ${secretLength} ]; do + secretItemValue=$(cat ${currentConfig} | jq ". | .spec.configuration.secrets[$index]") + cat <>$filePath + - ${secretItemValue} +EOF + index=$((index+1)) + done +fi \ No newline at end of file diff --git a/src/main/arm/scripts/utility.sh b/src/main/arm/scripts/utility.sh index a99c4ecd7..b32b10025 100644 --- a/src/main/arm/scripts/utility.sh +++ b/src/main/arm/scripts/utility.sh @@ -22,4 +22,94 @@ function install_kubectl() { echo "Failed to install kubectl." exit 1 fi +} + +# Call this function to make sure pods of a domain are running. +# * Make sure the admin server pod is running +# * Make sure all the managed server pods are running +# Assuming there is only one cluster in the domain +# Parameters: +# * appReplicas: replicas of the managed server +# * wlsDomainNS: name space +# * checkPodStatusMaxAttemps: max attempts to query the pods status if they are not all running. +# * checkPodStatusInterval: interval of query the pods status +function utility_wait_for_pod_completed() { + appReplicas=$1 + wlsDomainNS=$2 + checkPodStatusMaxAttemps=$3 + checkPodStatusInterval=$4 + + echo "Waiting for $((appReplicas+1)) pods are running." + + readyPodNum=0 + attempt=0 + while [[ ${readyPodNum} -le ${appReplicas} && $attempt -le ${checkPodStatusMaxAttemps} ]];do + ret=$(kubectl get pods -n ${wlsDomainNS} -o json \ + | jq '.items[] | .status.phase' \ + | grep "Running") + if [ -z "${ret}" ];then + readyPodNum=0 + else + readyPodNum=$(kubectl get pods -n ${wlsDomainNS} -o json \ + | jq '.items[] | .status.phase' \ + | grep -c "Running") + fi + echo "Number of new running pod: ${readyPodNum}" + attempt=$((attempt+1)) + sleep ${checkPodStatusInterval} + done + + if [ ${attempt} -gt ${checkPodStatusMaxAttemps} ];then + echo "It takes too long to wait for all the pods are running, please refer to http://oracle.github.io/weblogic-kubernetes-operator/samples/simple/azure-kubernetes-service/#troubleshooting" + exit 1 + fi +} + + +# Call this function to make sure pods of a domain are updated with expected image. +# * Make sure the admin server pod is updated with expected image +# * Make sure all the managed server pods are updated with expected image +# Assuming there is only one cluster in the domain +# Parameters: +# * acrImagePath: image path +# * appReplicas: replicas of the managed server +# * wlsDomainNS: name space +# * checkPodStatusMaxAttemps: max attempts to query the pods status if they are not all running. +# * checkPodStatusInterval: interval of query the pods status +function utility_wait_for_image_update_completed() { + # Make sure all of the pods are updated with new image. + # Assumption: we have only one cluster currently. + acrImagePath=$1 + appReplicas=$2 + wlsDomainNS=$3 + checkPodStatusMaxAttemps=$4 + checkPodStatusInterval=$5 + + echo "Waiting for $((appReplicas+1)) new pods created with image ${acrImagePath}" + + updatedPodNum=0 + attempt=0 + while [ ${updatedPodNum} -le ${appReplicas} ] && [ $attempt -le ${checkPodStatusMaxAttemps} ];do + echo "attempts ${attempt}" + ret=$(kubectl get pods -n ${wlsDomainNS} -o json \ + | jq '.items[] | .spec | .containers[] | select(.name == "weblogic-server") | .image' \ + | grep "${acrImagePath}") + + if [ -z "${ret}" ];then + updatedPodNum=0 + else + updatedPodNum=$(kubectl get pods -n ${wlsDomainNS} -o json \ + | jq '.items[] | .spec | .containers[] | select(.name == "weblogic-server") | .image' \ + | grep -c "${acrImagePath}") + fi + echo "Number of new pod: ${updatedPodNum}" + + attempt=$((attempt+1)) + sleep ${checkPodStatusInterval} + done + + if [ ${attempt} -gt ${checkPodStatusMaxAttemps} ];then + echo "Failed to update image ${acrImagePath} to all weblogic server pods. " + exit 1 + fi } \ No newline at end of file diff --git a/src/main/bicep/mainTemplate.bicep b/src/main/bicep/mainTemplate.bicep index 8215dbfb9..5a0e9363a 100644 --- a/src/main/bicep/mainTemplate.bicep +++ b/src/main/bicep/mainTemplate.bicep @@ -12,6 +12,7 @@ * $ az deployment group create -f mainTemplate.json -g * * Build marketplace offer for test: +* Replace the partner center pid in .\modules\_pids\_pid-dev.bicep, then run the following command to generate the ARM package, and upload it to partner center. * $ mvn -Pbicep -Ddev -Passembly clean install */ @@ -200,24 +201,36 @@ param wlsUserName string = 'weblogic' var const_appGatewaySSLCertOptionHaveCert = 'haveCert' var const_appGatewaySSLCertOptionHaveKeyVault = 'haveKeyVault' var const_azureSubjectName = '${format('{0}.{1}.{2}', name_domainLabelforApplicationGateway, location, 'cloudapp.azure.com')}' +var const_hasTags = contains(resourceGroup(), 'tags') +// If there is not tag 'wlsKeyVault' and key vault is created for the following usage: +// * upload custom TLS/SSL certificates for WLS trust and identity. +// * upload custom certificate for gateway frontend TLS/SSL. +// * generate selfsigned certificate for gateway frontend TLS/SSL. +var const_bCreateNewKeyVault = (!const_hasTags || !contains(resourceGroup().tags, name_tagNameForKeyVault) || empty(resourceGroup().tags.wlsKeyVault)) && ((enableCustomSSL && sslConfigurationAccessOption != const_wlsSSLCertOptionKeyVault) || (enableAppGWIngress && (appGatewayCertificateOption != const_appGatewaySSLCertOptionHaveKeyVault))) +var const_bCreateStorageAccount = (createAKSCluster || !const_hasStorageAccount) && const_enablePV var const_defaultKeystoreType = 'PKCS12' var const_enableNetworking = (length(lbSvcValues) > 0) || enableAppGWIngress var const_enablePV = enableCustomSSL || enableAzureFileShare +var const_hasStorageAccount = !createAKSCluster && reference('query-existing-storage-account').outputs.storageAccount.value != 'null' var const_identityKeyStoreType = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomIdentityKeyStoreType : sslUploadedCustomIdentityKeyStoreType +var const_keyvaultNameFromTag = const_hasTags && contains(resourceGroup().tags, name_tagNameForKeyVault) ? resourceGroup().tags.wlsKeyVault : '' var const_trustKeyStoreType = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomTrustKeyStoreType : sslUploadedCustomTrustKeyStoreType var const_wlsSSLCertOptionKeyVault = 'keyVaultStoredConfig' var name_defaultPidDeployment = 'pid' var name_dnsNameforApplicationGateway = '${concat(dnsNameforApplicationGateway, take(utcValue, 6))}' var name_domainLabelforApplicationGateway = '${take(concat(name_dnsNameforApplicationGateway, '-', toLower(resourceGroup().name), '-', toLower(wlsDomainName)), 63)}' -var name_identityKeyStoreDataSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomIdentityKeyStoreDataSecretName : 'myIdentityKeyStoreData${uniqueString(utcValue)}' -var name_identityKeyStorePswSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomIdentityKeyStorePassPhraseSecretName : 'myIdentityKeyStorePsw${uniqueString(utcValue)}' -var name_keyVaultName = '${take(concat('wls-kv', uniqueString(utcValue)), 24)}' -var name_privateKeyAliasSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultPrivateKeyAliasSecretName : 'privateKeyAlias${uniqueString(utcValue)}' -var name_privateKeyPswSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultPrivateKeyPassPhraseSecretName : 'privateKeyPsw${uniqueString(utcValue)}' +var name_identityKeyStoreDataSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomIdentityKeyStoreDataSecretName : 'myIdentityKeyStoreData' +var name_identityKeyStorePswSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomIdentityKeyStorePassPhraseSecretName : 'myIdentityKeyStorePsw' +var name_keyVaultName = empty(const_keyvaultNameFromTag) ? '${take(concat('wls-kv', uniqueString(utcValue)), 24)}' : resourceGroup().tags.wlsKeyVault +var name_privateKeyAliasSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultPrivateKeyAliasSecretName : 'privateKeyAlias' +var name_privateKeyPswSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultPrivateKeyPassPhraseSecretName : 'privateKeyPsw' var name_rgKeyvaultForWLSSSL = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultResourceGroup : resourceGroup().name -var name_trustKeyStoreDataSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomTrustKeyStoreDataSecretName : 'myTrustKeyStoreData${uniqueString(utcValue)}' -var name_trustKeyStorePswSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomTrustKeyStorePassPhraseSecretName : 'myTrustKeyStorePsw${uniqueString(utcValue)}' -var ref_wlsDomainDeployment = reference(resourceId('Microsoft.Resources/deployments', (enableCustomSSL)? 'setup-wls-cluster-with-custom-ssl-enabled' : 'setup-wls-cluster')) +var name_storageAccountName = const_hasStorageAccount ? reference('query-existing-storage-account').outputs.storageAccount.value : 'wls${uniqueString(utcValue)}' +var name_tagNameForKeyVault = 'wlsKeyVault' +var name_tagNameForStorageAccount = 'wlsStorageAccount' +var name_trustKeyStoreDataSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomTrustKeyStoreDataSecretName : 'myTrustKeyStoreData' +var name_trustKeyStorePswSecret = (sslConfigurationAccessOption == const_wlsSSLCertOptionKeyVault) ? sslKeyVaultCustomTrustKeyStorePassPhraseSecretName : 'myTrustKeyStorePsw' +var ref_wlsDomainDeployment = reference(resourceId('Microsoft.Resources/deployments', (enableCustomSSL) ? 'setup-wls-cluster-with-custom-ssl-enabled' : 'setup-wls-cluster')) /* * Beginning of the offer deployment */ @@ -225,6 +238,12 @@ module pids './modules/_pids/_pid.bicep' = { name: 'initialization' } +// Have to hard code the pid here +// For test, replace the pid with testing one, and build the package. +module partnerCenterPid './modules/_pids/_empty.bicep' = { + name: 'pid-a1775ed4-512c-4cfa-9e68-f0b09b36de90-partnercenter' +} + module wlsSSLCertSecretsDeployment 'modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep' = if (enableCustomSSL && sslConfigurationAccessOption != const_wlsSSLCertOptionKeyVault) { name: 'upload-wls-ssl-cert-to-keyvault' params: { @@ -254,6 +273,18 @@ resource sslKeyvault 'Microsoft.KeyVault/vaults@2019-09-01' existing = if (enabl scope: resourceGroup(name_rgKeyvaultForWLSSSL) } +// If updating an existing aks cluster, query the storage account that is being used. +// Return "null" is no storage account is applied. +module queryStorageAccount 'modules/_deployment-scripts/_ds-query-storage-account.bicep' = if (!createAKSCluster) { + name: 'query-existing-storage-account' + params: { + aksClusterName: aksClusterName + aksClusterRGName: aksClusterRGName + identity: identity + wlsDomainUID: wlsDomainUID + } +} + module wlsDomainDeployment 'modules/setupWebLogicCluster.bicep' = if (!enableCustomSSL) { name: 'setup-wls-cluster' params: { @@ -272,13 +303,12 @@ module wlsDomainDeployment 'modules/setupWebLogicCluster.bicep' = if (!enableCus aksClusterRGName: aksClusterRGName aksClusterName: aksClusterName aksVersion: aksVersion - appgwAlias: const_azureSubjectName appPackageUrls: appPackageUrls appReplicas: appReplicas createACR: createACR createAKSCluster: createAKSCluster + createStorageAccount: const_bCreateStorageAccount enableAzureMonitoring: enableAzureMonitoring - enableAzureFileShare: const_enablePV enableCustomSSL: enableCustomSSL enablePV: const_enablePV identity: identity @@ -286,6 +316,7 @@ module wlsDomainDeployment 'modules/setupWebLogicCluster.bicep' = if (!enableCus managedServerPrefix: managedServerPrefix ocrSSOPSW: ocrSSOPSW ocrSSOUser: ocrSSOUser + storageAccountName: name_storageAccountName wdtRuntimePassword: wdtRuntimePassword wlsClusterSize: wlsClusterSize wlsCPU: wlsCPU @@ -306,6 +337,7 @@ module wlsDomainDeployment 'modules/setupWebLogicCluster.bicep' = if (!enableCus } dependsOn: [ pids + queryStorageAccount ] } @@ -327,13 +359,12 @@ module wlsDomainWithCustomSSLDeployment 'modules/setupWebLogicCluster.bicep' = i aksClusterRGName: aksClusterRGName aksClusterName: aksClusterName aksVersion: aksVersion - appgwAlias: const_azureSubjectName appPackageUrls: appPackageUrls appReplicas: appReplicas createACR: createACR createAKSCluster: createAKSCluster + createStorageAccount: const_bCreateStorageAccount enableAzureMonitoring: enableAzureMonitoring - enableAzureFileShare: const_enablePV enableCustomSSL: enableCustomSSL enablePV: const_enablePV identity: identity @@ -341,6 +372,7 @@ module wlsDomainWithCustomSSLDeployment 'modules/setupWebLogicCluster.bicep' = i managedServerPrefix: managedServerPrefix ocrSSOPSW: ocrSSOPSW ocrSSOUser: ocrSSOUser + storageAccountName: name_storageAccountName wdtRuntimePassword: wdtRuntimePassword wlsClusterSize: wlsClusterSize wlsCPU: wlsCPU @@ -361,6 +393,7 @@ module wlsDomainWithCustomSSLDeployment 'modules/setupWebLogicCluster.bicep' = i } dependsOn: [ wlsSSLCertSecretsDeployment + queryStorageAccount ] } @@ -381,6 +414,22 @@ module appgwSecretDeployment 'modules/_azure-resoruces/_keyvaultAdapter.bicep' = ] } +/* + * Update tags to save key vault name and storage account name that are used for current configuration +*/ +resource applyTags 'Microsoft.Resources/tags@2021-04-01' = { + name: 'default' + properties: { + tags: { + '${name_tagNameForKeyVault}': const_bCreateNewKeyVault ? name_keyVaultName : const_keyvaultNameFromTag + '${name_tagNameForStorageAccount}': (const_bCreateStorageAccount || const_hasStorageAccount) ? name_storageAccountName : '' + } + } + dependsOn: [ + appgwSecretDeployment + ] +} + module networkingDeployment 'modules/networking.bicep' = if (const_enableNetworking) { name: 'networking-deployment' params: { @@ -417,7 +466,7 @@ module networkingDeployment 'modules/networking.bicep' = if (const_enableNetwork wlsDomainName: wlsDomainName wlsDomainUID: wlsDomainUID } - dependsOn:[ + dependsOn: [ appgwSecretDeployment ] } diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep index 7b8116154..ba9d17416 100644 --- a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep @@ -7,6 +7,7 @@ param enabledForTemplateDeployment bool = true param keyVaultName string @description('Price tier for Key Vault.') param sku string +param utcValue string = utcNow() param wlsIdentityKeyStoreData string param wlsIdentityKeyStoreDataSecretName string @secure() @@ -35,6 +36,9 @@ resource keyvault 'Microsoft.KeyVault/vaults@2019-09-01' = { accessPolicies: [] tenantId: subscription().tenantId } + tags:{ + 'managed-by-azure-weblogic': utcValue + } } resource identityKeyStoreDataSecret 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep index 99d5e353a..192a8feaa 100644 --- a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep @@ -22,6 +22,8 @@ param keyVaultName string @description('Price tier for Key Vault.') param sku string +param utcValue string = utcNow() + resource keyvault 'Microsoft.KeyVault/vaults@2019-09-01' = { name: keyVaultName location: resourceGroup().location @@ -34,6 +36,9 @@ resource keyvault 'Microsoft.KeyVault/vaults@2019-09-01' = { accessPolicies: [] tenantId: subscription().tenantId } + tags:{ + 'managed-by-azure-weblogic': utcValue + } } resource secretForCertificate 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = { diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep index 49a6f94d0..32c48ed74 100644 --- a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep @@ -51,6 +51,9 @@ resource keyvault 'Microsoft.KeyVault/vaults@2019-09-01' = { enabledForTemplateDeployment: true enableSoftDelete: true } + tags:{ + 'managed-by-azure-weblogic': utcValue + } } resource createAddCertificate 'Microsoft.Resources/deploymentScripts@2020-10-01' = { diff --git a/src/main/bicep/modules/_azure-resoruces/_storage.bicep b/src/main/bicep/modules/_azure-resoruces/_storage.bicep index 8713b77f3..c405cfe1e 100644 --- a/src/main/bicep/modules/_azure-resoruces/_storage.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_storage.bicep @@ -2,15 +2,15 @@ // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. param location string = 'eastus' +param storageAccountName string param utcValue string = utcNow() var const_shareQuota = 5120 var const_sku = 'Standard_LRS' var name_fileShare = 'weblogic' -var name_storageAccount = 'stg${uniqueString(utcValue)}' resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = { - name: name_storageAccount + name: storageAccountName location: location kind: 'StorageV2' sku: { @@ -36,6 +36,9 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = { } accessTier: 'Hot' } + tags:{ + 'managed-by-azure-weblogic': utcValue + } } resource fileService 'Microsoft.Storage/storageAccounts/fileServices/shares@2021-02-01' = { @@ -49,5 +52,3 @@ resource fileService 'Microsoft.Storage/storageAccounts/fileServices/shares@2021 storageAccount ] } - -output storageAccountName string = name_storageAccount diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep index f1eddf21a..67f0b9ae4 100644 --- a/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep +++ b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep @@ -8,7 +8,6 @@ param _artifactsLocationSasToken string = '' param aksClusterRGName string = '' param aksClusterName string = '' param acrName string = '' -param appgwAlias string = 'contoso' param appPackageUrls array = [] param appReplicas int = 2 param enableCustomSSL bool = false @@ -52,7 +51,7 @@ param wlsTrustKeyStorePassPhrase string = newGuid() param wlsTrustKeyStoreType string = 'PKCS12' param wlsUserName string = 'weblogic' -var const_arguments = '${ocrSSOUser} ${ocrSSOPSW} ${aksClusterRGName} ${aksClusterName} ${wlsImageTag} ${acrName} ${wlsDomainName} ${wlsDomainUID} ${wlsUserName} ${wlsPassword} ${wdtRuntimePassword} ${wlsCPU} ${wlsMemory} ${managedServerPrefix} ${appReplicas} ${string(appPackageUrls)} ${resourceGroup().name} ${const_scriptLocation} ${storageAccountName} ${wlsClusterSize} ${enableCustomSSL} ${wlsIdentityKeyStoreData} ${wlsIdentityKeyStorePassphrase} ${wlsIdentityKeyStoreType} ${wlsPrivateKeyAlias} ${wlsPrivateKeyPassPhrase} ${wlsTrustKeyStoreData} ${wlsTrustKeyStorePassPhrase} ${wlsTrustKeyStoreType} ${appgwAlias} ${enablePV} ' +var const_arguments = '${ocrSSOUser} ${ocrSSOPSW} ${aksClusterRGName} ${aksClusterName} ${wlsImageTag} ${acrName} ${wlsDomainName} ${wlsDomainUID} ${wlsUserName} ${wlsPassword} ${wdtRuntimePassword} ${wlsCPU} ${wlsMemory} ${managedServerPrefix} ${appReplicas} ${string(appPackageUrls)} ${resourceGroup().name} ${const_scriptLocation} ${storageAccountName} ${wlsClusterSize} ${enableCustomSSL} ${wlsIdentityKeyStoreData} ${wlsIdentityKeyStorePassphrase} ${wlsIdentityKeyStoreType} ${wlsPrivateKeyAlias} ${wlsPrivateKeyPassPhrase} ${wlsTrustKeyStoreData} ${wlsTrustKeyStorePassPhrase} ${wlsTrustKeyStoreType} ${enablePV} ' var const_buildDockerImageScript='createVMAndBuildImage.sh' var const_commonScript = 'common.sh' var const_pvTempalte = 'pv.yaml.template' @@ -60,6 +59,7 @@ var const_pvcTempalte = 'pvc.yaml.template' var const_scriptLocation = uri(_artifactsLocation, 'scripts/') var const_genDomainConfigScript= 'genDomainConfig.sh' var const_setUpDomainScript = 'setupWLSDomain.sh' +var const_updateDomainConfigScript= 'updateDomainConfig.sh' var const_utilityScript= 'utility.sh' resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { @@ -78,6 +78,7 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { uri(const_scriptLocation, '${const_pvcTempalte}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_commonScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_buildDockerImageScript}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_updateDomainConfigScript}${_artifactsLocationSasToken}') ] cleanupPreference: 'OnSuccess' retentionInterval: 'P1D' diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-query-storage-account.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-query-storage-account.bicep new file mode 100644 index 000000000..14244907a --- /dev/null +++ b/src/main/bicep/modules/_deployment-scripts/_ds-query-storage-account.bicep @@ -0,0 +1,30 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +param aksClusterName string = '' +param aksClusterRGName string = '' + +param identity object +param utcValue string = utcNow() +param wlsDomainUID string = 'sample-domain1' + +var const_arguments = '${aksClusterRGName} ${aksClusterName} ${wlsDomainUID}' +var const_azcliVersion='2.15.0' +var const_deploymentName='ds-query-storage-account' + +resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: const_deploymentName + location: resourceGroup().location + kind: 'AzureCLI' + identity: identity + properties: { + azCliVersion: const_azcliVersion + arguments: const_arguments + scriptContent: loadTextContent('../../../arm/scripts/queryStorageAccount.sh') + cleanupPreference: 'OnSuccess' + retentionInterval: 'P1D' + forceUpdateTag: utcValue + } +} + +output storageAccount string = string(reference(const_deploymentName).outputs.storageAccount) diff --git a/src/main/bicep/modules/setupWebLogicCluster.bicep b/src/main/bicep/modules/setupWebLogicCluster.bicep index ebab9dad9..67a15d7a0 100644 --- a/src/main/bicep/modules/setupWebLogicCluster.bicep +++ b/src/main/bicep/modules/setupWebLogicCluster.bicep @@ -44,7 +44,6 @@ param aksClusterRGName string = '' param aksClusterName string = '' @description('The AKS version.') param aksVersion string = 'default' -param appgwAlias string = 'contoso' @description('Urls of Java EE application packages.') param appPackageUrls array = [] @description('The number of managed server to start.') @@ -53,10 +52,10 @@ param appReplicas int = 2 param createACR bool = false @description('true to create a new AKS cluster.') param createAKSCluster bool = true +param createStorageAccount bool = false @description('In addition to the CPU and memory metrics included in AKS by default, you can enable Container Insights for more comprehensive data on the overall performance and health of your cluster. Billing is based on data ingestion and retention settings.') param enableAzureMonitoring bool = false @description('true to create persistent volume using file share.') -param enableAzureFileShare bool = false param enableCustomSSL bool = false param enablePV bool = false @description('An user assigned managed identity. Make sure the identity has permission to create/update/delete/list Azure resources.') @@ -69,6 +68,7 @@ param managedServerPrefix string = 'managed-server' param ocrSSOPSW string @description('User name of Oracle SSO account.') param ocrSSOUser string +param storageAccountName string @secure() @description('Password for model WebLogic Deploy Tooling runtime encrytion.') param wdtRuntimePassword string @@ -156,10 +156,11 @@ module acrDeployment './_azure-resoruces/_acr.bicep' = if (createACR) { } // enableAppGWIngress: if true, will create storage for certificates. -module storageDeployment './_azure-resoruces/_storage.bicep' = if (enablePV) { +module storageDeployment './_azure-resoruces/_storage.bicep' = if (createStorageAccount) { name: 'storage-deployment' params: { location: location + storageAccountName: storageAccountName } dependsOn: [ pidStart @@ -177,7 +178,6 @@ module wlsDomainDeployment './_deployment-scripts/_ds-create-wls-cluster.bicep' aksClusterRGName: createAKSCluster ? resourceGroup().name : aksClusterRGName aksClusterName: createAKSCluster ? aksClusterDeployment.outputs.aksClusterName : aksClusterName acrName: createACR ? acrDeployment.outputs.acrName : acrName - appgwAlias: appgwAlias appPackageUrls: appPackageUrls appReplicas: appReplicas enableCustomSSL: enableCustomSSL @@ -185,7 +185,7 @@ module wlsDomainDeployment './_deployment-scripts/_ds-create-wls-cluster.bicep' identity: identity location: location managedServerPrefix: managedServerPrefix - storageAccountName: enableAzureFileShare ? storageDeployment.outputs.storageAccountName : 'null' + storageAccountName: storageAccountName ocrSSOUser: ocrSSOUser ocrSSOPSW: ocrSSOPSW wdtRuntimePassword: wdtRuntimePassword From 6493b5bad14a1d6930c8b211bffd144a625dd741 Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Wed, 11 Aug 2021 22:01:24 +0000 Subject: [PATCH 26/37] Merged PR 343730: Configure Data source Description: - Configure data source using offer - Configure data source using ARM - post deployment Support data source: - PostgreSQL - Oracle - Azure SQL Performance: - create/update/delete database: 6min - create/update with invalid database: 10 min Test: - PostgreSQL - Azure SQL Related work items: #1364822, #1364824, #1366287, #1367535, #1367537 --- pom.xml | 2 +- src/main/arm/createUiDefinition.json | 136 ++++++++ src/main/arm/scripts/common.sh | 4 +- src/main/arm/scripts/dbUtility.sh | 166 +++++++++ src/main/arm/scripts/genDatasourceModel.sh | 39 +++ src/main/arm/scripts/setupDBConnections.sh | 314 ++++++++++++++++++ src/main/arm/scripts/updateDomainConfig.sh | 26 +- src/main/arm/scripts/utility.sh | 60 ++++ src/main/bicep/mainTemplate.bicep | 57 +++- .../_ds-datasource-connection.bicep | 52 +++ src/main/bicep/modules/_pids/_pid-dev.bicep | 2 + src/main/bicep/modules/_pids/_pid.bicep | 2 + .../bicep/modules/_setupDBConnection.bicep | 79 +++++ .../bicep/modules/setupDBConnection.bicep | 96 ++++++ 14 files changed, 1014 insertions(+), 21 deletions(-) create mode 100644 src/main/arm/scripts/dbUtility.sh create mode 100644 src/main/arm/scripts/genDatasourceModel.sh create mode 100644 src/main/arm/scripts/setupDBConnections.sh create mode 100644 src/main/bicep/modules/_deployment-scripts/_ds-datasource-connection.bicep create mode 100644 src/main/bicep/modules/_setupDBConnection.bicep create mode 100644 src/main/bicep/modules/setupDBConnection.bicep diff --git a/pom.xml b/pom.xml index a1613dc34..9cd784925 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ com.oracle.weblogic.azure wls-on-aks-azure-marketplace - 1.0.6 + 1.0.7 com.microsoft.azure.iaas diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index dd5644652..ebc604f1a 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -1375,6 +1375,136 @@ "visible": true } ] + }, + { + "name": "section_database", + "type": "Microsoft.Common.Section", + "label": "Database", + "subLabel": { + "preValidation": "Configure integrations to database", + "postValidation": "Done" + }, + "bladeTitle": "Database", + "elements": [ + { + "name": "aboutDatabase", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "Selecting 'Yes' here and providing the configuration will cause the template to configure the WebLogic Server to connect to the desired pre-existing database. The database must be network accessible to the VNET and subnets created by the template." + } + }, + { + "name": "enableDB", + "type": "Microsoft.Common.OptionsGroup", + "label": "Connect to database?", + "defaultValue": "No", + "toolTip": "Select 'Yes' and provide required info to configure the connection to a database.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": "true" + }, + { + "label": "No", + "value": "false" + } + ], + "required": true + } + }, + { + "name": "databaseConnectionInfo", + "type": "Microsoft.Common.Section", + "label": "Connection settings", + "elements": [ + { + "name": "databaseType", + "type": "Microsoft.Common.DropDown", + "label": "Choose database type", + "toolTip": "Choose database type", + "defaultValue": "Oracle database", + "constraints": { + "allowedValues": [ + { + "label": "Azure database for PostgreSQL", + "value": "postgresql" + }, + { + "label": "Oracle database", + "value": "oracle" + }, + { + "label": "Azure SQL", + "value": "sqlserver" + } + ], + "required": true + }, + "visible": true + }, + { + "name": "jdbcDataSourceName", + "type": "Microsoft.Common.TextBox", + "label": "JNDI Name", + "toolTip": "The JNDI name for the database JDBC connection", + "defaultValue": "", + "constraints": { + "required": "[bool(steps('section_database').enableDB)]", + "regex": "^[a-z0-9A-Z/]{1,30}$", + "validationMessage": "The value must be 1-30 characters long and must only contain letters, numbers, and slashes (/)." + }, + "visible": true + }, + { + "name": "dsConnectionURL", + "type": "Microsoft.Common.TextBox", + "label": "DataSource Connection String", + "toolTip": "The JDBC connection string for the database", + "defaultValue": "", + "constraints": { + "required": "[bool(steps('section_database').enableDB)]", + "regex": "[concat('^jdbc:', coalesce(steps('section_database').databaseConnectionInfo.databaseType, ''), '.*$')]", + "validationMessage": "A valid JDBC URL for the chosen database type must be provided" + }, + "visible": true + }, + { + "name": "dbUser", + "type": "Microsoft.Common.TextBox", + "label": "Database username", + "toolTip": "Use only letters and numbers", + "defaultValue": "", + "constraints": { + "required": "[bool(steps('section_database').enableDB)]", + "regex": "^(?!\\-)([a-z0-9A-Z@\\-]{1,128})([^\\-])$", + "validationMessage": "The value must be 1-128 characters long and must only contain letters, numbers, hyphen(-) and the at sign, no hyphen allowed at the beginning and the end of database username." + }, + "visible": true + }, + { + "name": "dbPassword", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Database Password", + "confirmPassword": "Confirm password" + }, + "toolTip": "Database Password", + "constraints": { + "required": "[bool(steps('section_database').enableDB)]", + "regex": "^((?=.*[0-9])(?=.*[a-zA-Z!@#$%^&*])).{5,128}$", + "validationMessage": "The password must be between five and 128 characters long and have at least one number." + }, + "options": { + "hideConfirmation": false + }, + "visible": true + } + ], + "visible": "[bool(steps('section_database').enableDB)]" + } + ] } ], "outputs": { @@ -1391,17 +1521,23 @@ "createACR": "[bool(steps('section_aks').acrInfo.createACR)]", "createAKSCluster": "[bool(steps('section_aks').clusterInfo.createAKSCluster)]", "createDNSZone": "[not(bool(steps('section_appGateway').dnsConfiguration.bringDNSZone))]", + "dbPassword": "[steps('section_database').databaseConnectionInfo.dbPassword]", + "dbUser": "[steps('section_database').databaseConnectionInfo.dbUser]", + "databaseType": "[steps('section_database').databaseConnectionInfo.databaseType]", "dnszoneAdminConsoleLabel": "[steps('section_appGateway').dnsConfiguration.dnszoneAdminConsoleLabel]", "dnszoneAppGatewayLabel": "[steps('section_appGateway').dnsConfiguration.dnszoneGatewayLabel]", "dnszoneName": "[steps('section_appGateway').dnsConfiguration.dnszoneName]", "dnszoneRGName": "[steps('section_appGateway').dnsConfiguration.dnsZoneResourceGroup]", + "dsConnectionURL": "[steps('section_database').databaseConnectionInfo.dsConnectionURL]", "enableAppGWIngress": "[steps('section_appGateway').appgwIngress.enableAppGateway]", "enableAzureMonitoring": "[bool(steps('section_aks').clusterInfo.enableAzureMonitoring)]", "enableAzureFileShare": "[bool(steps('section_aks').clusterInfo.enableAzureFileShare)]", "enableCookieBasedAffinity": "[bool(steps('section_appGateway').appgwIngress.enableCookieBasedAffinity)]", "enableCustomSSL": "[bool(steps('section_sslConfiguration').enableCustomSSL)]", + "enableDB": "[bool(steps('section_database').enableDB)]", "enableDNSConfiguration": "[bool(steps('section_appGateway').dnsConfiguration.enableDNSConfiguration)]", "identity": "[basics('basicsRequired').identity]", + "jdbcDataSourceName": "[steps('section_database').databaseConnectionInfo.jdbcDataSourceName]", "lbSvcValues": "[steps('section_appGateway').lbSVCInfo.lbSVC]", "location": "[location()]", "keyVaultName": "[steps('section_appGateway').appgwIngress.keyVaultName]", diff --git a/src/main/arm/scripts/common.sh b/src/main/arm/scripts/common.sh index 901c93361..4aa777db4 100644 --- a/src/main/arm/scripts/common.sh +++ b/src/main/arm/scripts/common.sh @@ -1,5 +1,5 @@ -export checkPodStatusInterval=1m # interval to check pod status. -export checkPodStatusMaxAttemps=10 # interval to check pod status. +export checkPodStatusInterval=20 # interval to check pod status. +export checkPodStatusMaxAttemps=30 # interval to check pod status. export constFalse="false" export constTrue="true" diff --git a/src/main/arm/scripts/dbUtility.sh b/src/main/arm/scripts/dbUtility.sh new file mode 100644 index 000000000..e21c9a2d9 --- /dev/null +++ b/src/main/arm/scripts/dbUtility.sh @@ -0,0 +1,166 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +echo "Script ${0} starts" + +function generate_ds_model() { + databaseDriver=${driverOracle} + databaseTestTableName=${testTableOracle} + if [[ "${databaseType}" == "${dbTypePostgre}" ]]; then + databaseDriver=${driverPostgre} + databaseTestTableName=${testTablePostgre} + elif [[ "${databaseType}" == "${dbTypePostgre}" ]]; then + databaseDriver=${driverSQLServer} + databaseTestTableName=${testTableSQLServer} + fi + + echo "generate data source model file" + chmod ugo+x $scriptDir/genDatasourceModel.sh + dsModelFilePath=$scriptDir/${dbSecretName}.yaml + bash $scriptDir/genDatasourceModel.sh \ + ${dsModelFilePath} \ + "${jdbcDataSourceName}" \ + "${clusterName}" \ + "${databaseDriver}" \ + "${databaseTestTableName}" \ + "${dbSecretName}" +} + +function export_models_and_delete_configmap() { + # create folder to store model files + modelFilePath=$scriptDir/models + if [ -d "${modelFilePath}" ]; then + rm ${modelFilePath} -f -r + fi + + mkdir ${modelFilePath} + + echo "check if configmap ${wlsConfigmapName} exists" + ret=$(kubectl -n ${domainNamespace} get configmap | grep "${wlsConfigmapName}") + if [ -n "${ret}" ]; then + echo "configmap ${wlsConfigmapName} exists, update it with the datasource model." + export wlsConfigmap=${scriptDir}/wdtconfigmap.json + rm -f ${scriptDir}/wdtconfigmap.json + kubectl -n ${domainNamespace} get configmap ${wlsConfigmapName} -o json >${wlsConfigmap} + + echo "query model keys" + keyList=$(cat ${wlsConfigmap} | jq '.data | keys[]' | tr -d "\"") + for item in $keyList; do + echo "key: $item" + if [[ "${item}" == "${dbSecretName}.yaml" ]]; then + continue + fi + + data=$(cat ${wlsConfigmap} | jq ".data[\"${item}\"]") + data=$(echo "${data:1:${#data}-2}") + echo -e "${data}" >${modelFilePath}/${item} + done + + # remove current configmap and create a new one + kubectl -n ${domainNamespace} delete configmap ${wlsConfigmapName} + fi +} + +function cleanup_secret_and_model() { + echo "check if the datasource secret exists" + jndiLabel=${jdbcDataSourceName//\//\_} + secretLen=$(kubectl get secret -n ${domainNamespace} -l datasource.JNDI="${jndiLabel}" -o json | + jq '.items | length') + if [ ${secretLen} -ge 1 ]; then + echo "secret for ${jdbcDataSourceName} exists" + # delete the secrets + index=0 + while [ $index -lt ${secretLen} ]; do + # get secret name + secretName=$(kubectl get secret -n ${domainNamespace} -l datasource.JNDI="${jndiLabel}" -o json | + jq ".items[$index].metadata.name" | + tr -d "\"") + # remove the secret + kubectl delete secret ${secretName} -n ${domainNamespace} + # remove model if there is. + rm -f ${modelFilePath}/${secretName}.yaml + + index=$((index + 1)) + done + fi +} + +function create_datasource_secret() { + cleanup_secret_and_model + + echo "create/update secret ${dbSecretName} for ${jdbcDataSourceName}" + kubectl -n ${domainNamespace} create secret generic \ + ${dbSecretName} \ + --from-literal=password="${dbPassword}" \ + --from-literal=url="${dsConnectionURL}" \ + --from-literal=user="${dbUser}" + + kubectl -n sample-domain1-ns label secret \ + ${dbSecretName} \ + weblogic.domainUID=${wlsDomainUID} \ + datasource.JNDI="${jndiLabel}" +} + +function update_configmap() { + echo "output all the models from configmap" + export_models_and_delete_configmap + # remove existing model if there is + rm -f ${modelFilePath}/${dbSecretName}.yaml + # copy the new model to model folder + cp ${dsModelFilePath} ${modelFilePath}/${dbSecretName}.yaml + + echo "update configmap" + kubectl -n ${domainNamespace} create configmap ${wlsConfigmapName} \ + --from-file=${modelFilePath} + kubectl -n ${domainNamespace} label configmap ${wlsConfigmapName} \ + weblogic.domainUID=${wlsDomainUID} +} + +function delete_model_and_secret() { + # delete db models and secrets for the specified jndi name. + echo "output all the models from configmap" + export_models_and_delete_configmap + + cleanup_secret_and_model + + echo "update configmap" + kubectl -n ${domainNamespace} create configmap ${wlsConfigmapName} \ + --from-file=${modelFilePath} + kubectl -n ${domainNamespace} label configmap ${wlsConfigmapName} \ + weblogic.domainUID=${wlsDomainUID} +} + +# Main script +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +export databaseType=$1 +export dbPassword=$2 +export dbUser=$3 +export dsConnectionURL=$4 +export jdbcDataSourceName=$5 +export wlsDomainUID=$6 +export dbSecretName=$7 +export operationType=$8 + +export domainNamespace=${wlsDomainUID}-ns +export clusterName="cluster-1" +export dbTypeOracle="oracle" +export dbTypePostgre="postgresql" +export dbTypeSQLServer="sqlserver" +export driverOracle="oracle.jdbc.OracleDriver" +export driverPostgre="org.postgresql.Driver" +export driverSQLServer="com.microsoft.sqlserver.jdbc.SQLServerDriver" +export optTypeDelete='delete' +export testTableOracle="SQL ISVALID" +export testTablePostgre="SQL SELECT 1" +export testTableSQLServer="SQL SELECT 1" +export wlsConfigmapName="${wlsDomainUID}-wdt-config-map" + +if [[ "${operationType}" == "${optTypeDelete}" ]]; then + delete_model_and_secret +else + generate_ds_model + update_configmap + create_datasource_secret +fi diff --git a/src/main/arm/scripts/genDatasourceModel.sh b/src/main/arm/scripts/genDatasourceModel.sh new file mode 100644 index 000000000..3cfa4904c --- /dev/null +++ b/src/main/arm/scripts/genDatasourceModel.sh @@ -0,0 +1,39 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +# Initialize +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +export filePath=$1 +export jndiName=$2 +export target=$3 +export driver=$4 +export testTableName=$5 +export secretName=$6 + +cat <${filePath} +# Copyright (c) 2020, 2021, Oracle and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +resources: + JDBCSystemResource: + ${jndiName}: + Target: '${target}' + JdbcResource: + JDBCDataSourceParams: + JNDIName: [ + ${jndiName} + ] + GlobalTransactionsProtocol: TwoPhaseCommit + JDBCDriverParams: + DriverName: ${driver} + URL: '@@SECRET:${secretName}:url@@' + PasswordEncrypted: '@@SECRET:${secretName}:password@@' + Properties: + user: + Value: '@@SECRET:${secretName}:user@@' + JDBCConnectionPoolParams: + TestTableName: ${testTableName} + TestConnectionsOnReserve: true +EOF diff --git a/src/main/arm/scripts/setupDBConnections.sh b/src/main/arm/scripts/setupDBConnections.sh new file mode 100644 index 000000000..bafab0552 --- /dev/null +++ b/src/main/arm/scripts/setupDBConnections.sh @@ -0,0 +1,314 @@ +# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +echo "Script ${0} starts" + +#Function to display usage message +function usage() { + cat< \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + +EOF + if [ $1 -eq 1 ]; then + exit 1 + fi +} + +#Function to validate input +function validate_input() { + if [[ -z "$aksClusterRGName" || -z "${aksClusterName}" ]]; then + echo_stderr "aksClusterRGName and aksClusterName are required. " + usage 1 + fi + + if [ -z "$databaseType" ]; then + echo_stderr "databaseType is required. " + usage 1 + fi + + if [[ -z "$dbPassword" || -z "${dbUser}" ]]; then + echo_stderr "dbPassword and dbUser are required. " + usage 1 + fi + + if [ -z "$dsConnectionURL" ]; then + echo_stderr "dsConnectionURL is required. " + usage 1 + fi + + if [ -z "$jdbcDataSourceName" ]; then + echo_stderr "jdbcDataSourceName is required. " + usage 1 + fi + + if [ -z "$wlsDomainUID" ]; then + echo_stderr "wlsDomainUID is required. " + usage 1 + fi + + if [[ -z "$wlsUser" || -z "${wlsPassword}" ]]; then + echo_stderr "wlsUser and wlsPassword are required. " + usage 1 + fi +} + +# Connect to AKS cluster +function connect_aks_cluster() { + az aks get-credentials \ + --resource-group ${aksClusterRGName} \ + --name ${aksClusterName} \ + --overwrite-existing +} + +function create_datasource_model_configmap_and_secret() { + echo "get data source secret name" + jndiLabel=${jdbcDataSourceName//\//\_} + secretLen=$(kubectl get secret -n ${wlsDomainNS} -l datasource.JNDI="${jndiLabel}" -o json \ + | jq '.items | length') + if [ ${secretLen} -ge 1 ];then + dbSecretName=$(kubectl get secret -n ${wlsDomainNS} -l datasource.JNDI="${jndiLabel}" -o json \ + | jq ".items[0].metadata.name" \ + | tr -d "\"") + else + dbSecretName="ds-secret-${databaseType}-${datetime}" + fi + + echo "Data source secret name: ${dbSecretName}" + chmod ugo+x $scriptDir/dbUtility.sh + bash $scriptDir/dbUtility.sh \ + ${databaseType} \ + "${dbPassword}" \ + "${dbUser}" \ + "${dsConnectionURL}" \ + "${jdbcDataSourceName}" \ + "${wlsDomainUID}" \ + "${dbSecretName}" \ + "${optTypeUpdate}" +} + +function apply_datasource_to_domain() { + echo "apply datasoure" + # get domain configurations + domainConfigurationJsonFile=$scriptDir/domain.json + kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json >${domainConfigurationJsonFile} + + restartVersion=$(cat ${domainConfigurationJsonFile} | jq '. | .spec.restartVersion' | tr -d "\"") + secretList=$(cat ${domainConfigurationJsonFile} | jq -r '. | .spec.configuration.secrets') + restartVersion=$((restartVersion+1)) + + echo "current secrets: ${secretList}" + if [[ "${secretList}" != "null" ]];then + secretList=$(cat ${domainConfigurationJsonFile} | jq -r '. | .spec.configuration.secrets[]') + secretStrings="[" + index=0; + for item in $secretList; do + if [[ "${item}" == "${dbSecretName}" ]]; then + continue + fi + + if [ $index -eq 0 ];then + secretStrings="${secretStrings}\"${item}\"," + else + secretStrings="${secretStrings}\"${item}\"," + fi + + index=$((index+1)) + done + + secretStrings="${secretStrings}\"${dbSecretName}\"]" + else + secretStrings="[\"${dbSecretName}\"]" + fi + + echo "secrets: ${secretStrings}" + + # apply the configmap + # apply the secret + # restart the domain + timestampBeforePatchingDomain=$(date +%s) + kubectl -n ${wlsDomainNS} patch domain ${wlsDomainUID} \ + --type=json \ + -p '[{"op": "replace", "path": "/spec/restartVersion", "value": "'${restartVersion}'" }, {"op": "replace", "path": "/spec/configuration/model/configMap", "value":'${wlsConfigmapName}'}, {"op": "replace", "path": "/spec/configuration/secrets", "value": '${secretStrings}'}]' +} + +function remove_datasource_from_domain() { + echo "rollback datasoure" + # get domain configurations + domainConfigurationJsonFile=$scriptDir/domain.json + kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json >${domainConfigurationJsonFile} + + restartVersion=$(cat ${domainConfigurationJsonFile} | jq '. | .spec.restartVersion' | tr -d "\"") + secretList=$(cat ${domainConfigurationJsonFile} | jq -r '. | .spec.configuration.secrets') + restartVersion=$((restartVersion+1)) + + echo "current secrets: ${secretList}" + if [[ "${secretList}" != "null" ]];then + secretList=$(cat ${domainConfigurationJsonFile} | jq -r '. | .spec.configuration.secrets[]') + secretStrings="[" + index=0; + for item in $secretList; do + ret=$(kubectl -n ${wlsDomainNS} get secret | grep "${item}") + # the secret should have been deleted. + if [ -z "${ret}" ]; then + continue + fi + + if [ $index -eq 0 ];then + secretStrings="${secretStrings}\"${item}\"," + else + secretStrings="${secretStrings}\"${item}\"," + fi + + index=$((index+1)) + done + + secretStrings="${secretStrings}]" + else + secretStrings="[]" + fi + + echo "secrets: ${secretStrings}" + + # apply the configmap + # apply the secret + # restart the domain + timestampBeforePatchingDomain=$(date +%s) + kubectl -n ${wlsDomainNS} patch domain ${wlsDomainUID} \ + --type=json \ + -p '[{"op": "replace", "path": "/spec/restartVersion", "value": "'${restartVersion}'" }, {"op": "replace", "path": "/spec/configuration/model/configMap", "value":'${wlsConfigmapName}'}, {"op": "replace", "path": "/spec/configuration/secrets", "value": '${secretStrings}'}]' +} + +function wait_for_operation_completed() { + # Make sure all of the pods are running. + replicas=$(kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json \ + | jq '. | .spec.clusters[] | .replicas') + + utility_wait_for_pod_restarted \ + ${timestampBeforePatchingDomain} \ + ${replicas} \ + ${wlsDomainUID} \ + ${checkPodStatusMaxAttemps} \ + ${checkPodStatusInterval} + + utility_wait_for_pod_completed \ + ${replicas} \ + ${wlsDomainNS} \ + ${checkPodStatusMaxAttemps} \ + ${checkPodStatusInterval} +} + +function delete_datasource() { + echo "remove secret and model of data source ${jdbcDataSourceName}" + # remove secret + # remove model + chmod ugo+x $scriptDir/dbUtility.sh + bash $scriptDir/dbUtility.sh \ + ${databaseType} \ + "${dbPassword}" \ + "${dbUser}" \ + "${dsConnectionURL}" \ + "${jdbcDataSourceName}" \ + "${wlsDomainUID}" \ + "${dbSecretName}" \ + "${optTypeDelete}" + + # update weblogic domain + remove_datasource_from_domain + + wait_for_operation_completed +} + +function validate_datasource() { + dsScriptFileName=get-datasource-status.py + testDatasourceScript=${scriptDir}/${dsScriptFileName} + podNum=$(kubectl -n ${wlsDomainNS} get pod -l weblogic.clusterName=${wlsClusterName} -o json | jq '.items| length') + if [ ${podNum} -le 0 ]; then + echo "Ensure your cluster has at least one pod." + exit 1 + fi + + podName=$(kubectl -n ${wlsDomainNS} get pod -l weblogic.clusterName=${wlsClusterName} -o json \ + | jq '.items[0] | .metadata.name' \ + | tr -d "\"") + + clusterTargetPort=$(kubectl describe service ${wlsClusterSvcName} -n ${wlsDomainNS} | grep 'default' | tr -d -c 0-9) + t3ConnectionString="t3://${wlsClusterSvcName}.${wlsDomainNS}.svc.cluster.local:${clusterTargetPort}" + cat <${testDatasourceScript} +connect('${wlsUser}', '${wlsPassword}', '${t3ConnectionString}') +serverRuntime() +print 'start to query data source jndi bean' +dsMBeans = cmo.getJDBCServiceRuntime().getJDBCDataSourceRuntimeMBeans() +ds_name = '${jdbcDataSourceName}' +for ds in dsMBeans: + if (ds_name == ds.getName()): + print 'DS name is: '+ds.getName() + print 'State is ' +ds.getState() +EOF + + echo "copy test script ${testDatasourceScript} to pod path /tmp/${dsScriptFileName}" + targetDSFilePath=/tmp/${dsScriptFileName} + kubectl cp ${testDatasourceScript} -n ${wlsDomainNS} ${podName}:${targetDSFilePath} + kubectl exec -it ${podName} -n ${wlsDomainNS} -- bash -c "wlst.sh ${targetDSFilePath}" | grep "State is Running" + + if [ $? == 1 ];then + echo "Failed to configure datasource ${jdbcDataSourceName}. Please make sure the input values are correct." + delete_datasource + exit 1 + fi +} + + +# Main script +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +source ${scriptDir}/common.sh +source ${scriptDir}/utility.sh + +export aksClusterRGName=$1 +export aksClusterName=$2 +export databaseType=$3 +export dbPassword=$4 +export dbUser=$5 +export dsConnectionURL=$6 +export jdbcDataSourceName=$7 +export wlsDomainUID=$8 +export wlsUser=$9 +export wlsPassword=${10} +export dbOptType=${11} + +export datetime=$(date +%s) +export optTypeDelete='delete' +export optTypeUpdate='createOrUpdate' +export wlsClusterName="cluster-1" +export wlsClusterSvcName="${wlsDomainUID}-cluster-${wlsClusterName}" +export wlsConfigmapName="${wlsDomainUID}-wdt-config-map" +export wlsDomainNS="${wlsDomainUID}-ns" + +validate_input + +connect_aks_cluster + +install_kubectl + +if [[ "${dbOptType}" == "${optTypeDelete}" ]];then + echo "delete date source: ${jdbcDataSourceName}" + delete_datasource +else + echo "create/update data source: ${jdbcDataSourceName}" + create_datasource_model_configmap_and_secret + apply_datasource_to_domain + wait_for_operation_completed + validate_datasource +fi diff --git a/src/main/arm/scripts/updateDomainConfig.sh b/src/main/arm/scripts/updateDomainConfig.sh index e65c163d1..89bee7ac7 100644 --- a/src/main/arm/scripts/updateDomainConfig.sh +++ b/src/main/arm/scripts/updateDomainConfig.sh @@ -19,17 +19,17 @@ export javaOptions=${11} export wlsDomainNS="${wlsDomainUID}-ns" # output the existing domain configuration -export currentConfig=${scriptDir}/currentDomain.json -kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json >${currentConfig} +export previousConfig=${scriptDir}/previousDomain.json +kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json >${previousConfig} # query logHomeEnabled -logHomeEnabled=$(cat ${currentConfig} | jq '. | .spec.logHomeEnabled') -logHome=$(cat ${currentConfig} | jq '. | .spec.logHome') -envList=$(cat ${currentConfig} | jq '. | .spec.serverPod.env') -envLength=$(cat ${currentConfig} | jq '. | .spec.serverPod.env | length') -restartVersion=$(cat ${currentConfig} | jq '. | .spec.restartVersion' | tr -d "\"") -configMap=$(cat ${currentConfig} | jq '. | .spec.configuration.model.configMap') -secretList=$(cat ${currentConfig} | jq '. | .spec.configuration.secrets') +logHomeEnabled=$(cat ${previousConfig} | jq '. | .spec.logHomeEnabled') +logHome=$(cat ${previousConfig} | jq '. | .spec.logHome') +envList=$(cat ${previousConfig} | jq '. | .spec.serverPod.env') +envLength=$(cat ${previousConfig} | jq '. | .spec.serverPod.env | length') +restartVersion=$(cat ${previousConfig} | jq '. | .spec.restartVersion' | tr -d "\"") +configMap=$(cat ${previousConfig} | jq '. | .spec.configuration.model.configMap') +secretList=$(cat ${previousConfig} | jq '. | .spec.configuration.secrets') restartVersion=$((restartVersion+1)) cat <$filePath @@ -152,8 +152,8 @@ fi index=0 while [ $index -lt ${envLength} ]; do - envItemName=$(cat ${currentConfig} | jq ". | .spec.serverPod.env[$index] | .name" | tr -d "\"") - envItemValue=$(cat ${currentConfig} | jq ". | .spec.serverPod.env[$index] | .value") + envItemName=$(cat ${previousConfig} | jq ". | .spec.serverPod.env[$index] | .name" | tr -d "\"") + envItemValue=$(cat ${previousConfig} | jq ". | .spec.serverPod.env[$index] | .value") index=$((index+1)) if [[ "${envItemName}" == "JAVA_OPTIONS" ]];then @@ -254,13 +254,13 @@ fi echo "set secrets" if [[ "${secretList}" != "null" ]];then - secretLength=$(cat ${currentConfig} | jq '. | .spec.configuration.secrets | length') + secretLength=$(cat ${previousConfig} | jq '. | .spec.configuration.secrets | length') cat <>$filePath secrets: EOF index=0 while [ $index -lt ${secretLength} ]; do - secretItemValue=$(cat ${currentConfig} | jq ". | .spec.configuration.secrets[$index]") + secretItemValue=$(cat ${previousConfig} | jq ". | .spec.configuration.secrets[$index]") cat <>$filePath - ${secretItemValue} EOF diff --git a/src/main/arm/scripts/utility.sh b/src/main/arm/scripts/utility.sh index b32b10025..126d89d45 100644 --- a/src/main/arm/scripts/utility.sh +++ b/src/main/arm/scripts/utility.sh @@ -1,5 +1,6 @@ # Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# This script runs on Alpine Linux function install_jdk() { # Install Microsoft OpenJDK @@ -24,6 +25,17 @@ function install_kubectl() { fi } +#Function to output message to stdout +function echo_stderr() { + echo "$@" >&2 + echo "$@" >>stdout +} + +function echo_stdout() { + echo "$@" + echo "$@" >>stdout +} + # Call this function to make sure pods of a domain are running. # * Make sure the admin server pod is running # * Make sure all the managed server pods are running @@ -112,4 +124,52 @@ function utility_wait_for_image_update_completed() { echo "Failed to update image ${acrImagePath} to all weblogic server pods. " exit 1 fi +} + +# Call this function to make sure pods of a domain are restarted. +# Assuming there is only one cluster in the domain +# Parameters: +# * baseTime: time stamp that should be earlier then pod restarts +# * appReplicas: replicas of the managed server +# * wlsDomainNS: name space +# * checkPodStatusMaxAttemps: max attempts to query the pods status if they are not all running. +# * checkPodStatusInterval: interval of query the pods status +function utility_wait_for_pod_restarted() { + baseTime=$1 + appReplicas=$2 + wlsDomainUID=$3 + checkPodStatusMaxAttemps=$4 + checkPodStatusInterval=$5 + + wlsDomainNS=${wlsDomainUID}-ns + + updatedPodNum=0 + attempt=0 + while [ ${updatedPodNum} -le ${appReplicas} ] && [ $attempt -le ${checkPodStatusMaxAttemps} ];do + echo "attempts ${attempt}" + ret=$(kubectl get pods -n ${wlsDomainNS} -l weblogic.domainUID=${wlsDomainUID} -o json \ + | jq '.items[] | .metadata.creationTimestamp' | tr -d "\"") + + counter=0 + for item in $ret; do + # conver the time format from YYYY-MM-DDThh:mm:ssZ to YYYY.MM.DD-hh:mm:ss + alpineItem=$(echo "${item}" | sed -e "s/-/./g;s/T/-/g;s/Z//g") + podCreateTimeStamp=$(date -u -d "${alpineItem}" +"%s") + echo "pod create time: $podCreateTimeStamp, base time: ${baseTime}" + if [ ${podCreateTimeStamp} -gt ${baseTime} ]; then + counter=$((counter+1)) + fi + done + + updatedPodNum=$counter + echo "Number of new pod: ${updatedPodNum}" + + attempt=$((attempt+1)) + sleep ${checkPodStatusInterval} + done + + if [ ${attempt} -gt ${checkPodStatusMaxAttemps} ];then + echo "Failed to restart all weblogic server pods. " + exit 1 + fi } \ No newline at end of file diff --git a/src/main/bicep/mainTemplate.bicep b/src/main/bicep/mainTemplate.bicep index 5a0e9363a..3feb97b72 100644 --- a/src/main/bicep/mainTemplate.bicep +++ b/src/main/bicep/mainTemplate.bicep @@ -1,7 +1,7 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. -// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. - /* +* Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +* * Terms: * aci is short for Azure Container Insight * aks is short for Azure Kubernetes Service @@ -12,7 +12,7 @@ * $ az deployment group create -f mainTemplate.json -g * * Build marketplace offer for test: -* Replace the partner center pid in .\modules\_pids\_pid-dev.bicep, then run the following command to generate the ARM package, and upload it to partner center. +* Replace the partner center pid in mainTemplate.bicep, then run the following command to generate the ARM package, and upload it to partner center. * $ mvn -Pbicep -Ddev -Passembly clean install */ @@ -70,6 +70,23 @@ param createACR bool = false param createAKSCluster bool = true @description('If true, the template will update records to the existing DNS Zone. If false, the template will create a new DNS Zone.') param createDNSZone bool = false +@allowed([ + 'oracle' + 'postgresql' + 'sqlserver' +]) +@description('One of the supported database types') +param databaseType string = 'oracle' +@allowed([ + 'createOrUpdate' + 'delete' +]) +@description('createOrUpdate: create a new data source connection, or update an existing data source connection. delete: delete an existing data source connection') +param dbConfigurationType string = 'createOrUpdate' +@description('Password for Database') +param dbPassword string = newGuid() +@description('User id of Database') +param dbUser string = 'contosoDbUser' @description('DNS prefix for ApplicationGateway') param dnsNameforApplicationGateway string = 'wlsgw' @description('Azure DNS Zone name.') @@ -78,6 +95,8 @@ param dnszoneAdminConsoleLabel string = 'admin' param dnszoneAppGatewayLabel string = 'www' param dnszoneName string = 'contoso.xyz' param dnszoneRGName string = 'dns-contoso-rg' +@description('JDBC Connection String') +param dsConnectionURL string = 'jdbc:postgresql://contoso.postgres.database.azure.com:5432/postgres' @description('true to set up Application Gateway ingress.') param enableAppGWIngress bool = false @description('In addition to the CPU and memory metrics included in AKS by default, you can enable Container Insights for more comprehensive data on the overall performance and health of your cluster. Billing is based on data ingestion and retention settings.') @@ -87,9 +106,12 @@ param enableAzureFileShare bool = false @description('true to enable cookie based affinity.') param enableCookieBasedAffinity bool = false param enableCustomSSL bool = false +param enableDB bool = false param enableDNSConfiguration bool = false @description('An user assigned managed identity. Make sure the identity has permission to create/update/delete/list Azure resources.') param identity object +@description('JNDI Name for JDBC Datasource') +param jdbcDataSourceName string = 'jdbc/contoso' @description('Existing Key Vault Name') param keyVaultName string = 'kv-contoso' @description('Resource group name in current subscription containing the KeyVault') @@ -238,7 +260,7 @@ module pids './modules/_pids/_pid.bicep' = { name: 'initialization' } -// Have to hard code the pid here +// Due to lack of preprocessor solution for the way we use bicep, must hard-code the pid here. // For test, replace the pid with testing one, and build the package. module partnerCenterPid './modules/_pids/_empty.bicep' = { name: 'pid-a1775ed4-512c-4cfa-9e68-f0b09b36de90-partnercenter' @@ -471,6 +493,31 @@ module networkingDeployment 'modules/networking.bicep' = if (const_enableNetwork ] } +module datasourceDeployment 'modules/_setupDBConnection.bicep' = if (enableDB) { + name: 'datasource-deployment' + params: { + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + _pidEnd: pids.outputs.dbEnd + _pidStart: pids.outputs.dbStart + aksClusterRGName: ref_wlsDomainDeployment.outputs.aksClusterRGName.value + aksClusterName: ref_wlsDomainDeployment.outputs.aksClusterName.value + databaseType: databaseType + dbConfigurationType: dbConfigurationType + dbPassword: dbPassword + dbUser: dbUser + dsConnectionURL: dsConnectionURL + identity: identity + jdbcDataSourceName: jdbcDataSourceName + wlsDomainUID: wlsDomainUID + wlsPassword: wlsPassword + wlsUserName: wlsUserName + } + dependsOn: [ + networkingDeployment + ] +} + output aksClusterName string = ref_wlsDomainDeployment.outputs.aksClusterName.value output adminConsoleInternalUrl string = ref_wlsDomainDeployment.outputs.adminServerUrl.value output adminConsoleExternalUrl string = const_enableNetworking ? networkingDeployment.outputs.adminConsoleExternalUrl : '' diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-datasource-connection.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-datasource-connection.bicep new file mode 100644 index 000000000..d1f169375 --- /dev/null +++ b/src/main/bicep/modules/_deployment-scripts/_ds-datasource-connection.bicep @@ -0,0 +1,52 @@ +// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +param _artifactsLocation string +@secure() +param _artifactsLocationSasToken string = '' + +param aksClusterName string +param aksClusterRGName string +param databaseType string = 'oracle' +param dbConfigurationType string +param dbPassword string = newGuid() +param dbUser string +param dsConnectionURL string +param identity object +param jdbcDataSourceName string +param utcValue string = utcNow() +param wlsDomainUID string = 'sample-domain1' +@secure() +param wlsPassword string +@description('User name for WebLogic Administrator.') +param wlsUserName string = 'weblogic' + +var const_arguments = '${aksClusterRGName} ${aksClusterName} ${databaseType} ${dbPassword} ${dbUser} "${dsConnectionURL}" ${jdbcDataSourceName} ${wlsDomainUID} ${wlsUserName} ${wlsPassword} ${dbConfigurationType}' +var const_azcliVersion='2.15.0' +var const_datasourceScript='setupDBConnections.sh' +var const_datasourceModelScript='genDatasourceModel.sh' +var const_dbUtilityScript='dbUtility.sh' +var const_commonScript = 'common.sh' +var const_scriptLocation = uri(_artifactsLocation, 'scripts/') +var const_utilityScript= 'utility.sh' + +resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: 'ds-wls-db-connection' + location: resourceGroup().location + kind: 'AzureCLI' + identity: identity + properties: { + azCliVersion: const_azcliVersion + arguments: const_arguments + primaryScriptUri: uri(const_scriptLocation, '${const_datasourceScript}${_artifactsLocationSasToken}') + supportingScriptUris: [ + uri(const_scriptLocation, '${const_commonScript}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_utilityScript}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_dbUtilityScript}${_artifactsLocationSasToken}') + uri(const_scriptLocation, '${const_datasourceModelScript}${_artifactsLocationSasToken}') + ] + cleanupPreference: 'OnSuccess' + retentionInterval: 'P1D' + forceUpdateTag: utcValue + } +} diff --git a/src/main/bicep/modules/_pids/_pid-dev.bicep b/src/main/bicep/modules/_pids/_pid-dev.bicep index fe9951f5e..ca5729d4f 100644 --- a/src/main/bicep/modules/_pids/_pid-dev.bicep +++ b/src/main/bicep/modules/_pids/_pid-dev.bicep @@ -12,6 +12,8 @@ module pidStart './_empty.bicep' = if (name != 'pid'){ output appgwEnd string = '38647ff6-ea8d-59e5-832d-b036a4d29c73' output appgwStart string = '8ba7beaa-96fd-576a-acd8-28f7a6efa83a' +output dbEnd string = 'ffab0a3f-90cb-585a-a7f9-ec0a62faeec1' +output dbStart string = 'e64361eb-fea0-5f15-a313-c76daadbc648' output networkingEnd string = '39d32fcd-1d02-50b6-9455-4b767a8e769e' output networkingStart string = 'ed47756f-2475-56dd-b13a-26027749b6e1' output wlsAKSEnd string = '17328b4d-841f-57b5-a9c5-861ad48f9d0d' diff --git a/src/main/bicep/modules/_pids/_pid.bicep b/src/main/bicep/modules/_pids/_pid.bicep index 99118a842..cc5053b23 100644 --- a/src/main/bicep/modules/_pids/_pid.bicep +++ b/src/main/bicep/modules/_pids/_pid.bicep @@ -12,6 +12,8 @@ module pidStart './_empty.bicep' = if (name != 'pid'){ output appgwEnd string = '47ea43a0-95cf-52c7-aee8-7ee6106fc1bf' output appgwStart string = '01288010-2672-5831-a66b-7b8b45cace1b' +output dbEnd string = 'd7a9c78e-39d9-5a47-928d-8645ed86dafd' +output dbStart string = '0cc86800-37f4-5191-9368-2953394309ec' output networkingEnd string = '2798165c-49fa-5701-b608-b80ed3986176' output networkingStart string = '0793308f-de9d-5f0d-92f9-d9fc4b413b8b' output wlsAKSEnd string = '2571f846-2f66-5c22-9fe6-38ecea7889ac' diff --git a/src/main/bicep/modules/_setupDBConnection.bicep b/src/main/bicep/modules/_setupDBConnection.bicep new file mode 100644 index 000000000..b665115ce --- /dev/null +++ b/src/main/bicep/modules/_setupDBConnection.bicep @@ -0,0 +1,79 @@ +/* +Copyright (c) 2018, 2021, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +*/ + +param _artifactsLocation string = '' +@secure() +param _artifactsLocationSasToken string = '' +param _pidEnd string = '' +param _pidStart string = '' + +@description('Name of an existing AKS cluster.') +param aksClusterName string = '' +param aksClusterRGName string = '' +@description('One of the supported database types') +param databaseType string = 'oracle' +@allowed([ + 'createOrUpdate' + 'delete' +]) +param dbConfigurationType string = 'createOrUpdate' +@description('Password for Database') +param dbPassword string = newGuid() +@description('User id of Database') +param dbUser string = 'contosoDbUser' +@description('JDBC Connection String') +param dsConnectionURL string = 'jdbc:postgresql://contoso.postgres.database.azure.com:5432/postgres' + +param identity object + +@description('JNDI Name for JDBC Datasource') +param jdbcDataSourceName string = 'jdbc/contoso' +@description('UID of WebLogic domain, used in WebLogic Operator.') +param wlsDomainUID string = 'sample-domain1' +@secure() +param wlsPassword string +@description('User name for WebLogic Administrator.') +param wlsUserName string = 'weblogic' + +module pidStart './_pids/_pid.bicep' = { + name: 'wls-aks-db-start-pid-deployment' + params: { + name: _pidStart + } +} + +module configDataSource '_deployment-scripts/_ds-datasource-connection.bicep' = { + name: 'create-update-datasource' + params:{ + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + aksClusterName: aksClusterName + aksClusterRGName: aksClusterRGName + databaseType: databaseType + dbConfigurationType: dbConfigurationType + dbPassword: dbPassword + dbUser: dbUser + dsConnectionURL: dsConnectionURL + identity: identity + jdbcDataSourceName: jdbcDataSourceName + wlsDomainUID: wlsDomainUID + wlsPassword: wlsPassword + wlsUserName: wlsUserName + } + dependsOn:[ + pidStart + ] +} + + +module pidEnd './_pids/_pid.bicep' = { + name: 'wls-aks-db-end-pid-deployment' + params: { + name: _pidEnd + } + dependsOn:[ + configDataSource + ] +} diff --git a/src/main/bicep/modules/setupDBConnection.bicep b/src/main/bicep/modules/setupDBConnection.bicep new file mode 100644 index 000000000..4b1951edf --- /dev/null +++ b/src/main/bicep/modules/setupDBConnection.bicep @@ -0,0 +1,96 @@ +/* +Copyright (c) 2018, 2021, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. + +Description + - This script is to confige DB connection in an existing WebLogic Cluster. + +Pre-requisites + - There is at least one WebLogic cluster running on Azure Kubernetes Service (AKS), the cluster must be deployed using Azure WebLoigc on AKS marketplace offer. + - Azure CLI with bicep installed. + +Parameters + - _artifactsLocation: Script location. + - aksClusterName: Name of the AKS instance that runs the WebLogic cluster. + - databaseType: One of the supported database types. + - dbConfigurationType: 'createOrUpdate' or 'delete' + - createOrUpdate: create a new data source connection, or update an existing data source connection. + - delete: delete an existing data source connection + - dbPassword: Password for Database + - dbUser: User id of Database + - dsConnectionURL: JDBC Connection String + - identity: Azure user managed identity used, make sure the identity has permission to create/update/delete Azure resources. It's recommended to assign "Contributor" role. + - jdbcDataSourceName: JNDI Name for JDBC Datasource. + - wlsDomainUID: UID of the domain that you are going to update. Make sure it's the same with the initial cluster deployment. + +Build and run + - Run command `bicep build setupDBConnection.bicep`, you will get built ARM template setupDBConnection.json. + - Prepare parameters file parameters.json + - Run command `az deployment group create -f setupDBConnection.json -p parameters.json -g ` +*/ + +param _artifactsLocation string = '' +@secure() +param _artifactsLocationSasToken string = '' + +@description('Name of an existing AKS cluster.') +param aksClusterName string = '' +@allowed([ + 'oracle' + 'postgresql' + 'sqlserver' +]) +@description('One of the supported database types') +param databaseType string = 'oracle' +@allowed([ + 'createOrUpdate' + 'delete' +]) +@description('createOrUpdate: create a new data source connection, or update an existing data source connection. delete: delete an existing data source connection') +param dbConfigurationType string = 'createOrUpdate' +@description('Password for Database') +param dbPassword string = newGuid() +@description('User id of Database') +param dbUser string = 'contosoDbUser' +@description('JDBC Connection String') +param dsConnectionURL string = 'jdbc:postgresql://contoso.postgres.database.azure.com:5432/postgres' + +param identity object + +@description('JNDI Name for JDBC Datasource') +param jdbcDataSourceName string = 'jdbc/contoso' +@description('UID of WebLogic domain, used in WebLogic Operator.') +param wlsDomainUID string = 'sample-domain1' +@secure() +param wlsPassword string +@description('User name for WebLogic Administrator.') +param wlsUserName string = 'weblogic' + +module pids './_pids/_pid.bicep' = { + name: 'initialization' +} + +module configDataSource './_setupDBConnection.bicep' = { + name: 'create-update--delete-datasource' + params:{ + _pidEnd: pids.outputs.dbEnd + _pidStart: pids.outputs.dbStart + _artifactsLocation: _artifactsLocation + _artifactsLocationSasToken: _artifactsLocationSasToken + aksClusterName: aksClusterName + aksClusterRGName: resourceGroup().name + databaseType: databaseType + dbConfigurationType: dbConfigurationType + dbPassword: dbPassword + dbUser: dbUser + dsConnectionURL: dsConnectionURL + identity: identity + jdbcDataSourceName: jdbcDataSourceName + wlsDomainUID: wlsDomainUID + wlsPassword: wlsPassword + wlsUserName: wlsUserName + } + dependsOn:[ + pids + ] +} From 64b439133bd3337b8fd833558988bae9fbf16ed9 Mon Sep 17 00:00:00 2001 From: Edward Burns Date: Wed, 11 Aug 2021 22:04:38 +0000 Subject: [PATCH 27/37] Merged PR 343913: Ignore VS Code files Ignore VS Code files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index e5fb30ed8..ac74394de 100644 --- a/.gitignore +++ b/.gitignore @@ -334,3 +334,6 @@ ASALocalRun/ coveragetool reports/ *.cobertura.xml + +.project +.settings From b17b1a58ef31f41455484fcb2ca54fbd191e4698 Mon Sep 17 00:00:00 2001 From: Edward Burns Date: Wed, 11 Aug 2021 22:30:25 +0000 Subject: [PATCH 28/37] Merged PR 343925: On branch master Update version for partner center. Add comment for how to bu... On branch master Update version for partner center. Add comment for how to build. modified: pom.xml Signed-off-by: Ed Burns --- pom.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9cd784925..05fadd55e 100644 --- a/pom.xml +++ b/pom.xml @@ -24,9 +24,11 @@ 4.0.0 + + com.oracle.weblogic.azure wls-on-aks-azure-marketplace - 1.0.7 + 1.0.13 com.microsoft.azure.iaas From b9d6e3eff69180885bcba05e4242311aa9931aa2 Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Fri, 13 Aug 2021 03:05:25 +0000 Subject: [PATCH 29/37] Merged PR 344026: Fix MS SQL driver. Fix MS SQL driver. Related work items: #1370810 --- src/main/arm/scripts/dbUtility.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/arm/scripts/dbUtility.sh b/src/main/arm/scripts/dbUtility.sh index e21c9a2d9..872c77730 100644 --- a/src/main/arm/scripts/dbUtility.sh +++ b/src/main/arm/scripts/dbUtility.sh @@ -9,7 +9,7 @@ function generate_ds_model() { if [[ "${databaseType}" == "${dbTypePostgre}" ]]; then databaseDriver=${driverPostgre} databaseTestTableName=${testTablePostgre} - elif [[ "${databaseType}" == "${dbTypePostgre}" ]]; then + elif [[ "${databaseType}" == "${dbTypeSQLServer}" ]]; then databaseDriver=${driverSQLServer} databaseTestTableName=${testTableSQLServer} fi From 3747391b9ad6e571f4d24b4be05ed2995e8e7162 Mon Sep 17 00:00:00 2001 From: Haixia Cheng Date: Wed, 18 Aug 2021 03:43:56 +0000 Subject: [PATCH 30/37] Merged PR 344913: Remote console and security enhancement - Support remote access - Read sensitive parameters from stdin ### Test: - Remote console - non-SSL is working - Remote console - SSL is working, see steps in #1370834 Logs: ``` 2021.08.13 14:34:26 INFO com.oracle.weblogic.console.backend.services.connection.ConnectionResource !thread!: POST data: {"domainUrl":"https://admin0813.wlseng-azuretest.org/console"} 2021.08.13 14:34:26 WARNING org.glassfish.jersey.message.internal.MessagingBinders !thread!: A class javax.activation.DataSource for a default provider MessageBodyWriter was not found. The provider is not available. 2021.08.13 14:34:28 INFO weblogic.console.backend.connection.ConnectionManager !thread!: Connection response from WebLogic Domain: InboundJaxrsResponse{context=ClientResponse{method=GET, uri=https://admin0813.wlseng-azuretest.org/console/management/weblogic/12.2.1.0.0/domainConfig?links=none&fields=name%2CdomainVersion&enableConsoleRestExtension=true, status=200, reason=OK}} 2021.08.13 14:34:28 INFO weblogic.console.backend.connection.ConnectionManager !thread!: >>>> Connected to the WebLogic Domain 'domain1' with version '12.2.1.4.0' <<<< ``` Related work items: #1365662, #1370834, #1370835, #1371638, #1373891 --- pom.xml | 2 +- src/main/arm/createUiDefinition.json | 21 ++ src/main/arm/scripts/buildWLSDockerImage.sh | 23 ++- src/main/arm/scripts/createVMAndBuildImage.sh | 27 +-- src/main/arm/scripts/dbUtility.sh | 22 +- src/main/arm/scripts/genDatasourceModel.sh | 2 +- src/main/arm/scripts/genDomainConfig.sh | 2 +- src/main/arm/scripts/genImageModel.sh | 2 +- .../arm/scripts/invokeSetupDBConnections.sh | 63 ++++++ src/main/arm/scripts/invokeSetupNetworking.sh | 105 ++++++++++ src/main/arm/scripts/invokeSetupWLSDomain.sh | 120 +++++++++++ .../arm/scripts/invokeUpdateApplications.sh | 69 +++++++ src/main/arm/scripts/model.properties | 2 +- src/main/arm/scripts/pv.yaml.template | 2 +- src/main/arm/scripts/pvc.yaml.template | 2 +- src/main/arm/scripts/setupDBConnections.sh | 64 +++--- src/main/arm/scripts/setupNetworking.sh | 194 +++++++++++++++--- src/main/arm/scripts/setupWLSDomain.sh | 162 +++++++-------- src/main/arm/scripts/updateApplications.sh | 123 +++++++++-- src/main/arm/scripts/updateDomainConfig.sh | 2 +- src/main/arm/scripts/utility.sh | 17 +- src/main/bicep/mainTemplate.bicep | 8 +- .../bicep/modules/_azure-resoruces/_acr.bicep | 2 +- .../bicep/modules/_azure-resoruces/_aks.bicep | 2 +- .../_azure-resoruces/_appgateway.bicep | 2 +- .../_keyvault/_keyvaultForWLSSSLCert.bicep | 2 +- .../_keyvault/_keyvaultWithExistingCert.bicep | 2 +- .../_keyvault/_keyvaultWithNewCert.bicep | 2 +- .../_azure-resoruces/_keyvaultAdapter.bicep | 2 +- .../modules/_azure-resoruces/_storage.bicep | 2 +- .../_ds-create-networking.bicep | 9 +- .../_ds-create-wls-cluster.bicep | 6 +- .../_ds-datasource-connection.bicep | 8 +- .../_ds-query-storage-account.bicep | 2 +- .../_ds_update-applications.bicep | 6 +- src/main/bicep/modules/_pids/_empty.bicep | 2 +- src/main/bicep/modules/_pids/_pid-dev.bicep | 2 +- src/main/bicep/modules/_pids/_pid.bicep | 2 +- .../bicep/modules/_setupDBConnection.bicep | 2 +- src/main/bicep/modules/networking.bicep | 11 +- .../bicep/modules/setupDBConnection.bicep | 2 +- .../bicep/modules/setupWebLogicCluster.bicep | 2 +- .../modules/updateWebLogicApplications.bicep | 2 +- 43 files changed, 871 insertions(+), 235 deletions(-) create mode 100644 src/main/arm/scripts/invokeSetupDBConnections.sh create mode 100644 src/main/arm/scripts/invokeSetupNetworking.sh create mode 100644 src/main/arm/scripts/invokeSetupWLSDomain.sh create mode 100644 src/main/arm/scripts/invokeUpdateApplications.sh diff --git a/pom.xml b/pom.xml index 05fadd55e..1e24ae262 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ com.oracle.weblogic.azure wls-on-aks-azure-marketplace - 1.0.13 + 1.0.15 com.microsoft.azure.iaas diff --git a/src/main/arm/createUiDefinition.json b/src/main/arm/createUiDefinition.json index ebc604f1a..2c410f072 100644 --- a/src/main/arm/createUiDefinition.json +++ b/src/main/arm/createUiDefinition.json @@ -1254,6 +1254,26 @@ ] }, "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]" + }, + { + "name": "appgwForAdminRemote", + "type": "Microsoft.Common.OptionsGroup", + "label": "Create ingress for WebLogic Remote Console. Make sure no application with path /remoteconsole*, it will cause conflict with WebLogic Remote Console path.", + "defaultValue": "Yes", + "toolTip": "Select 'Yes' to Create ingress for WebLogic Remote Console. Make sure no application with path /remoteconsole*, it will cause conflict with WebLogic Remote Console path.", + "constraints": { + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ] + }, + "visible": "[steps('section_appGateway').appgwIngress.enableAppGateway]" } ], "visible": true @@ -1516,6 +1536,7 @@ "appGatewaySSLCertData": "[steps('section_appGateway').appgwIngress.keyVaultSSLCertData]", "appGatewaySSLCertPassword": "[steps('section_appGateway').appgwIngress.appGatewaySSLCertPassword]", "appgwForAdminServer": "[steps('section_appGateway').appgwIngress.appgwForAdminServer]", + "appgwForRemoteConsole": "[steps('section_appGateway').appgwIngress.appgwForAdminRemote]", "appPackageUrls": "[steps('section_aks').jeeAppInfo.appPackageUrl]", "appReplicas": "[int(steps('section_aks').jeeAppInfo.appReplicas)]", "createACR": "[bool(steps('section_aks').acrInfo.createACR)]", diff --git a/src/main/arm/scripts/buildWLSDockerImage.sh b/src/main/arm/scripts/buildWLSDockerImage.sh index 41709e7ff..07480bafa 100644 --- a/src/main/arm/scripts/buildWLSDockerImage.sh +++ b/src/main/arm/scripts/buildWLSDockerImage.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. echo "Script ${0} starts" @@ -8,9 +8,14 @@ function echo_stderr() { echo "$@" >&2 } +# read and from stdin +function read_sensitive_parameters_from_stdin() { + read azureACRPassword ocrSSOPSW +} + #Function to display usage message function usage() { - echo_stdout "./buildWLSDockerImage.sh " + echo " ./buildWLSDockerImage.sh ./buildWLSDockerImage.sh " if [ $1 -eq 1 ]; then exit 1 fi @@ -226,13 +231,11 @@ source ${scriptDir}/common.sh export wlsImagePath=$1 export azureACRServer=$2 export azureACRUserName=$3 -export azureACRPassword=$4 -export imageTag=$5 -export appPackageUrls=$6 -export ocrSSOUser=$7 -export ocrSSOPSW=$8 -export wlsClusterSize=$9 -export enableSSL=${10} +export imageTag=$4 +export appPackageUrls=$5 +export ocrSSOUser=$6 +export wlsClusterSize=$7 +export enableSSL=$8 export acrImagePath="$azureACRServer/aks-wls-images:${imageTag}" export ocrLoginServer="container-registry.oracle.com" @@ -241,6 +244,8 @@ export witDownloadURL="https://github.com/oracle/weblogic-image-tool/releases/do export wlsPostgresqlDriverUrl="https://jdbc.postgresql.org/download/postgresql-42.2.8.jar" export wlsMSSQLDriverUrl="https://repo.maven.apache.org/maven2/com/microsoft/sqlserver/mssql-jdbc/7.4.1.jre8/mssql-jdbc-7.4.1.jre8.jar" +read_sensitive_parameters_from_stdin + validate_inputs initialize diff --git a/src/main/arm/scripts/createVMAndBuildImage.sh b/src/main/arm/scripts/createVMAndBuildImage.sh index 5352ae9ec..bb2dcfb24 100644 --- a/src/main/arm/scripts/createVMAndBuildImage.sh +++ b/src/main/arm/scripts/createVMAndBuildImage.sh @@ -1,6 +1,11 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# read and from stdin +function read_sensitive_parameters_from_stdin() { + read azureACRPassword ocrSSOPSW +} + function cleanup_vm() { #Remove VM resources az extension add --name resource-graph @@ -89,7 +94,7 @@ function build_docker_image() { --publisher Microsoft.Azure.Extensions \ --version 2.0 \ --settings "{ \"fileUris\": [\"${scriptURL}model.properties\",\"${scriptURL}genImageModel.sh\",\"${scriptURL}buildWLSDockerImage.sh\",\"${scriptURL}common.sh\"]}" \ - --protected-settings "{\"commandToExecute\":\"bash buildWLSDockerImage.sh ${wlsImagePath} ${azureACRServer} ${azureACRUserName} ${azureACRPassword} ${newImageTag} \\\"${appPackageUrls}\\\" ${ocrSSOUser} ${ocrSSOPSW} ${wlsClusterSize} ${enableCustomSSL} \"}" + --protected-settings "{\"commandToExecute\":\"echo ${azureACRPassword} ${ocrSSOPSW} | bash buildWLSDockerImage.sh ${wlsImagePath} ${azureACRServer} ${azureACRUserName} ${newImageTag} \\\"${appPackageUrls}\\\" ${ocrSSOUser} ${wlsClusterSize} ${enableCustomSSL} \"}" cleanup_vm } @@ -101,16 +106,14 @@ export currentResourceGroup=$1 export wlsImageTag=$2 export azureACRServer=$3 export azureACRUserName=$4 -export azureACRPassword=$5 -export newImageTag=$6 -export appPackageUrls=$7 -export ocrSSOUser=$8 -export ocrSSOPSW=$9 -export wlsClusterSize=${10} -export enableCustomSSL=${11} -export scriptURL=${12} - -echo ${scriptURL} +export newImageTag=$5 +export appPackageUrls=$6 +export ocrSSOUser=$7 +export wlsClusterSize=$8 +export enableCustomSSL=$9 +export scriptURL=${10} + +read_sensitive_parameters_from_stdin build_docker_image diff --git a/src/main/arm/scripts/dbUtility.sh b/src/main/arm/scripts/dbUtility.sh index 872c77730..9bc9c86ff 100644 --- a/src/main/arm/scripts/dbUtility.sh +++ b/src/main/arm/scripts/dbUtility.sh @@ -1,8 +1,13 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. echo "Script ${0} starts" +# read from stdin +function read_sensitive_parameters_from_stdin() { + read dbPassword +} + function generate_ds_model() { databaseDriver=${driverOracle} databaseTestTableName=${testTableOracle} @@ -135,13 +140,12 @@ export script="${BASH_SOURCE[0]}" export scriptDir="$(cd "$(dirname "${script}")" && pwd)" export databaseType=$1 -export dbPassword=$2 -export dbUser=$3 -export dsConnectionURL=$4 -export jdbcDataSourceName=$5 -export wlsDomainUID=$6 -export dbSecretName=$7 -export operationType=$8 +export dbUser=$2 +export dsConnectionURL=$3 +export jdbcDataSourceName=$4 +export wlsDomainUID=$5 +export dbSecretName=$6 +export operationType=$7 export domainNamespace=${wlsDomainUID}-ns export clusterName="cluster-1" @@ -157,6 +161,8 @@ export testTablePostgre="SQL SELECT 1" export testTableSQLServer="SQL SELECT 1" export wlsConfigmapName="${wlsDomainUID}-wdt-config-map" +read_sensitive_parameters_from_stdin + if [[ "${operationType}" == "${optTypeDelete}" ]]; then delete_model_and_secret else diff --git a/src/main/arm/scripts/genDatasourceModel.sh b/src/main/arm/scripts/genDatasourceModel.sh index 3cfa4904c..19c9543e7 100644 --- a/src/main/arm/scripts/genDatasourceModel.sh +++ b/src/main/arm/scripts/genDatasourceModel.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Initialize diff --git a/src/main/arm/scripts/genDomainConfig.sh b/src/main/arm/scripts/genDomainConfig.sh index ac7154e64..2f9e716b8 100644 --- a/src/main/arm/scripts/genDomainConfig.sh +++ b/src/main/arm/scripts/genDomainConfig.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. export script="${BASH_SOURCE[0]}" diff --git a/src/main/arm/scripts/genImageModel.sh b/src/main/arm/scripts/genImageModel.sh index 96d33cd97..4d07a234e 100644 --- a/src/main/arm/scripts/genImageModel.sh +++ b/src/main/arm/scripts/genImageModel.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Initialize diff --git a/src/main/arm/scripts/invokeSetupDBConnections.sh b/src/main/arm/scripts/invokeSetupDBConnections.sh new file mode 100644 index 000000000..903e97ced --- /dev/null +++ b/src/main/arm/scripts/invokeSetupDBConnections.sh @@ -0,0 +1,63 @@ +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# This script runs on Azure Container Instance with Alpine Linux that Azure Deployment script creates. + +#Function to display usage message +function usage() { + usage=$(cat <<-END +Usage: +./invokeSetupDBConnections.sh + + + + + + + + + + + +END +) + echo_stdout "${usage}" + if [ $1 -eq 1 ]; then + echo_stderr "${usage}" + exit 1 + fi +} + +# Main script +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +source ${scriptDir}/utility.sh + +export aksClusterRGName=$1 +export aksClusterName=$2 +export databaseType=$3 +dbPassword=$4 +export dbUser=$5 +export dsConnectionURL=$6 +export jdbcDataSourceName=$7 +export wlsDomainUID=$8 +export wlsUser=$9 +wlsPassword=${10} +export dbOptType=${11} + +echo ${dbPassword} \ + ${wlsPassword} | \ + bash ./setupDBConnections.sh \ + ${aksClusterRGName} \ + ${aksClusterName} \ + ${databaseType} \ + ${dbUser} \ + ${dsConnectionURL} \ + ${jdbcDataSourceName} \ + ${wlsDomainUID} \ + ${wlsUser} \ + ${dbOptType} + +if [ $? -ne 0 ]; then + usage 1 +fi diff --git a/src/main/arm/scripts/invokeSetupNetworking.sh b/src/main/arm/scripts/invokeSetupNetworking.sh new file mode 100644 index 000000000..0e87e661b --- /dev/null +++ b/src/main/arm/scripts/invokeSetupNetworking.sh @@ -0,0 +1,105 @@ +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# This script runs on Azure Container Instance with Alpine Linux that Azure Deployment script creates. + +#Function to display usage message +function usage() { + usage=$(cat <<-END +Usage: +./invokeSetupNetworking.sh + + + + + + + + + + + + + + + + + + + + + + + + + +END +) + echo_stdout "${usage}" + if [ $1 -eq 1 ]; then + echo_stderr "${usage}" + exit 1 + fi +} + +# Main script +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +source ${scriptDir}/utility.sh + +export aksClusterRGName=$1 +export aksClusterName=$2 +export wlsDomainName=$3 +export wlsDomainUID=$4 +export lbSvcValues=$5 +export enableAppGWIngress=$6 +export subID=$7 +export curRGName=${8} +export appgwName=${9} +export vnetName=${10} +spBase64String=${11} +export appgwForAdminServer=${12} +export enableCustomDNSAlias=${13} +export dnsRGName=${14} +export dnsZoneName=${15} +export dnsAdminLabel=${16} +export dnsClusterLabel=${17} +export appgwAlias=${18} +export enableInternalLB=${19} +export appgwFrontendSSLCertData=${20} +appgwFrontendSSLCertPsw=${21} +export appgwCertificateOption=${22} +export enableCustomSSL=${23} +export enableCookieBasedAffinity=${24} +export enableRemoteConsole=${25} + +echo ${spBase64String} \ + ${appgwFrontendSSLCertPsw} | \ + bash ./setupNetworking.sh \ + ${aksClusterRGName} \ + ${aksClusterName} \ + ${wlsDomainName} \ + ${wlsDomainUID} \ + ${lbSvcValues} \ + ${enableAppGWIngress} \ + ${subID} \ + ${curRGName} \ + ${appgwName} \ + ${vnetName} \ + ${appgwForAdminServer} \ + ${enableCustomDNSAlias} \ + ${dnsRGName} \ + ${dnsZoneName} \ + ${dnsAdminLabel} \ + ${dnsClusterLabel} \ + ${appgwAlias} \ + ${enableInternalLB} \ + ${appgwFrontendSSLCertData} \ + ${appgwCertificateOption} \ + ${enableCustomSSL} \ + ${enableCookieBasedAffinity} \ + ${enableRemoteConsole} + +if [ $? -ne 0 ]; then + usage 1 +fi diff --git a/src/main/arm/scripts/invokeSetupWLSDomain.sh b/src/main/arm/scripts/invokeSetupWLSDomain.sh new file mode 100644 index 000000000..1a5bf0cc7 --- /dev/null +++ b/src/main/arm/scripts/invokeSetupWLSDomain.sh @@ -0,0 +1,120 @@ +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# This script runs on Azure Container Instance with Alpine Linux that Azure Deployment script creates. + +#Function to display usage message +function usage() { + usage=$(cat <<-END +Usage: +./invokeSetupWLSDomain.sh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +END +) + echo_stdout "${usage}" + if [ $1 -eq 1 ]; then + echo_stderr "${usage}" + exit 1 + fi +} + +# Main script +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +source ${scriptDir}/utility.sh + +export ocrSSOUser=$1 +ocrSSOPSW=$2 +export aksClusterRGName=$3 +export aksClusterName=$4 +export wlsImageTag=$5 +export acrName=$6 +export wlsDomainName=$7 +export wlsDomainUID=$8 +export wlsUserName=$9 +wlsPassword=${10} +wdtRuntimePassword=${11} +export wlsCPU=${12} +export wlsMemory=${13} +export managedServerPrefix=${14} +export appReplicas=${15} +export appPackageUrls=${16} +export currentResourceGroup=${17} +export scriptURL=${18} +export storageAccountName=${19} +export wlsClusterSize=${20} +export enableCustomSSL=${21} +export wlsIdentityData=${22} +wlsIdentityPsw=${23} +export wlsIdentityType=${24} +export wlsIdentityAlias=${25} +wlsIdentityKeyPsw=${26} +export wlsTrustData=${27} +wlsTrustPsw=${28} +export wlsTrustType=${29} +export enablePV=${30} + +echo ${ocrSSOPSW} \ + ${wlsPassword} \ + ${wdtRuntimePassword} \ + ${wlsIdentityPsw} \ + ${wlsIdentityKeyPsw} \ + ${wlsTrustPsw} | \ + bash ./setupWLSDomain.sh \ + ${ocrSSOUser} \ + ${aksClusterRGName} \ + ${aksClusterName} \ + ${wlsImageTag} \ + ${acrName} \ + ${wlsDomainName} \ + ${wlsDomainUID} \ + ${wlsUserName} \ + ${wlsCPU} \ + ${wlsMemory} \ + ${managedServerPrefix} \ + ${appReplicas} \ + ${appPackageUrls} \ + ${currentResourceGroup} \ + ${scriptURL} \ + ${storageAccountName} \ + ${wlsClusterSize} \ + ${enableCustomSSL} \ + ${wlsIdentityData} \ + ${wlsIdentityType} \ + ${wlsIdentityAlias} \ + ${wlsTrustData} \ + ${wlsTrustType} \ + ${enablePV} + +if [ $? -ne 0 ]; then + usage 1 +fi diff --git a/src/main/arm/scripts/invokeUpdateApplications.sh b/src/main/arm/scripts/invokeUpdateApplications.sh new file mode 100644 index 000000000..2d5234750 --- /dev/null +++ b/src/main/arm/scripts/invokeUpdateApplications.sh @@ -0,0 +1,69 @@ +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. +# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# This script runs on Azure Container Instance with Alpine Linux that Azure Deployment script creates. + +#Function to display usage message +function usage() { + usage=$(cat <<-END +Usage: +./invokeUpdateApplications.sh + + + + + + + + + + + + + +END +) + echo_stdout "${usage}" + if [ $1 -eq 1 ]; then + echo_stderr "${usage}" + exit 1 + fi +} + +# Main script +export script="${BASH_SOURCE[0]}" +export scriptDir="$(cd "$(dirname "${script}")" && pwd)" + +source ${scriptDir}/utility.sh + +export ocrSSOUser=$1 +ocrSSOPSW=$2 +export aksClusterRGName=$3 +export aksClusterName=$4 +export wlsImageTag=$5 +export acrName=$6 +export wlsDomainName=$7 +export wlsDomainUID=$8 +export currentResourceGroup=$9 +export appPackageUrls=${10} +export scriptURL=${11} +export appStorageAccountName=${12} +export appContainerName=${13} + +echo ${ocrSSOPSW} | \ + bash ./updateApplications.sh \ + ${ocrSSOUser} \ + ${aksClusterRGName} \ + ${aksClusterName} \ + ${wlsImageTag} \ + ${acrName} \ + ${wlsDomainName} \ + ${wlsDomainUID} \ + ${currentResourceGroup} \ + ${appPackageUrls} \ + ${scriptURL} \ + ${appStorageAccountName} \ + ${appContainerName} + +if [ $? -ne 0 ]; then + usage 1 +fi diff --git a/src/main/arm/scripts/model.properties b/src/main/arm/scripts/model.properties index b94145204..3db0f8e3c 100644 --- a/src/main/arm/scripts/model.properties +++ b/src/main/arm/scripts/model.properties @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Based on ./kubernetes/samples/scripts/create-weblogic-domain/model-in-image/model-images/model-in-image__WLS-v1/model.10.properties diff --git a/src/main/arm/scripts/pv.yaml.template b/src/main/arm/scripts/pv.yaml.template index fde59f802..349ced5c6 100644 --- a/src/main/arm/scripts/pv.yaml.template +++ b/src/main/arm/scripts/pv.yaml.template @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Based on ./kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-file-pv-template.yaml diff --git a/src/main/arm/scripts/pvc.yaml.template b/src/main/arm/scripts/pvc.yaml.template index baab56058..da17ffd4a 100644 --- a/src/main/arm/scripts/pvc.yaml.template +++ b/src/main/arm/scripts/pvc.yaml.template @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. # Based on ./kubernetes/samples/scripts/create-weblogic-domain-on-azure-kubernetes-service/azure-file-pvc-template.yaml diff --git a/src/main/arm/scripts/setupDBConnections.sh b/src/main/arm/scripts/setupDBConnections.sh index bafab0552..3149a522f 100644 --- a/src/main/arm/scripts/setupDBConnections.sh +++ b/src/main/arm/scripts/setupDBConnections.sh @@ -1,26 +1,33 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. echo "Script ${0} starts" +# read from stdin +function read_sensitive_parameters_from_stdin() { + read dbPassword wlsPassword +} + #Function to display usage message function usage() { - cat< \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - -EOF +echo | + ./setupDBConnections.sh + + + + + + + + + +END +) + echo_stdout "${usage}" if [ $1 -eq 1 ]; then + echo_stderr "${usage}" exit 1 fi } @@ -86,9 +93,9 @@ function create_datasource_model_configmap_and_secret() { echo "Data source secret name: ${dbSecretName}" chmod ugo+x $scriptDir/dbUtility.sh - bash $scriptDir/dbUtility.sh \ + echo "${dbPassword}" | \ + bash $scriptDir/dbUtility.sh \ ${databaseType} \ - "${dbPassword}" \ "${dbUser}" \ "${dsConnectionURL}" \ "${jdbcDataSourceName}" \ @@ -213,9 +220,9 @@ function delete_datasource() { # remove secret # remove model chmod ugo+x $scriptDir/dbUtility.sh - bash $scriptDir/dbUtility.sh \ + echo "${dbPassword}" | \ + bash $scriptDir/dbUtility.sh \ ${databaseType} \ - "${dbPassword}" \ "${dbUser}" \ "${dsConnectionURL}" \ "${jdbcDataSourceName}" \ @@ -242,7 +249,8 @@ function validate_datasource() { | jq '.items[0] | .metadata.name' \ | tr -d "\"") - clusterTargetPort=$(kubectl describe service ${wlsClusterSvcName} -n ${wlsDomainNS} | grep 'default' | tr -d -c 0-9) + # get non-ssl port + clusterTargetPort=$(kubectl get svc ${wlsClusterSvcName} -n ${wlsDomainNS} -o json | jq '.spec.ports[] | select(.name=="default") | .port') t3ConnectionString="t3://${wlsClusterSvcName}.${wlsDomainNS}.svc.cluster.local:${clusterTargetPort}" cat <${testDatasourceScript} connect('${wlsUser}', '${wlsPassword}', '${t3ConnectionString}') @@ -279,14 +287,12 @@ source ${scriptDir}/utility.sh export aksClusterRGName=$1 export aksClusterName=$2 export databaseType=$3 -export dbPassword=$4 -export dbUser=$5 -export dsConnectionURL=$6 -export jdbcDataSourceName=$7 -export wlsDomainUID=$8 -export wlsUser=$9 -export wlsPassword=${10} -export dbOptType=${11} +export dbUser=$4 +export dsConnectionURL=$5 +export jdbcDataSourceName=$6 +export wlsDomainUID=$7 +export wlsUser=$8 +export dbOptType=$9 export datetime=$(date +%s) export optTypeDelete='delete' @@ -296,6 +302,8 @@ export wlsClusterSvcName="${wlsDomainUID}-cluster-${wlsClusterName}" export wlsConfigmapName="${wlsDomainUID}-wdt-config-map" export wlsDomainNS="${wlsDomainUID}-ns" +read_sensitive_parameters_from_stdin + validate_input connect_aks_cluster diff --git a/src/main/arm/scripts/setupNetworking.sh b/src/main/arm/scripts/setupNetworking.sh index 0a8b4e8f9..0a70a1aac 100644 --- a/src/main/arm/scripts/setupNetworking.sh +++ b/src/main/arm/scripts/setupNetworking.sh @@ -1,17 +1,11 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. echo "Script ${0} starts" -#Function to output message to stdout -function echo_stderr() { - echo "$@" >&2 - echo "$@" >>stdout -} - -function echo_stdout() { - echo "$@" >&2 - echo "$@" >>stdout +# read from stdin +function read_sensitive_parameters_from_stdin() { + read spBase64String appgwFrontendSSLCertPsw } function install_helm() { @@ -62,10 +56,40 @@ function output_result() { #Function to display usage message function usage() { - echo_stdout "./setupNetworking.sh " - if [ $1 -eq 1 ]; then - exit 1 - fi + usage=$(cat <<-END +Usage: +echo | + ./setupNetworking.sh + + + + + + + + + + + + + + + + + + + + + + + +END +) + echo_stdout ${usage} + if [ $1 -eq 1 ]; then + echo_stderr ${usage} + exit 1 + fi } #Validate teminal status with $?, exit with exception if errors happen. @@ -194,6 +218,11 @@ function validate_input() { echo_stderr "enableCookieBasedAffinity is required. " usage 1 fi + + if [ -z "$enableRemoteConsole" ]; then + echo_stderr "enableRemoteConsole is required. " + usage 1 + fi } function generate_admin_lb_definicion() { @@ -396,7 +425,7 @@ metadata: kubernetes.io/ingress.class: azure/application-gateway EOF -if [[ "${enableCookieBasedAffinity,,}" == "true" ]];then + if [[ "${enableCookieBasedAffinity,,}" == "true" ]];then cat <>${adminAppgwIngressYamlPath} appgw.ingress.kubernetes.io/cookie-based-affinity: "true" EOF @@ -417,6 +446,40 @@ spec: EOF } +function generate_appgw_admin_remote_config_file_nossl() +{ + cat <${adminRemoteAppgwIngressYamlPath} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${adminRemoteIngressName} + namespace: ${wlsDomainNS} + annotations: + kubernetes.io/ingress.class: azure/application-gateway + appgw.ingress.kubernetes.io/backend-path-prefix: "/" +EOF + + if [[ "${enableCookieBasedAffinity,,}" == "true" ]];then + cat <>${adminRemoteAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/cookie-based-affinity: "true" +EOF +fi + +cat <>${adminRemoteAppgwIngressYamlPath} +spec: + rules: + - http: + paths: + - path: /remoteconsole* + pathType: Prefix + backend: + service: + name: ${svcAdminServer} + port: + number: ${adminTargetPort} +EOF +} + function generate_appgw_admin_config_file_ssl() { export adminIngressName=${wlsDomainUID}-admin-appgw-ingress-svc @@ -468,6 +531,57 @@ spec: EOF } +function generate_appgw_admin_remote_config_file_ssl() +{ + cat <${adminRemoteAppgwIngressYamlPath} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${adminRemoteIngressName} + namespace: ${wlsDomainNS} + annotations: + kubernetes.io/ingress.class: azure/application-gateway + appgw.ingress.kubernetes.io/backend-path-prefix: "/" + appgw.ingress.kubernetes.io/ssl-redirect: "true" + appgw.ingress.kubernetes.io/backend-protocol: "https" + +EOF + + if [[ "${enableCustomDNSAlias,,}" == "true" ]];then + cat <>${adminRemoteAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/backend-hostname: "${dnsAdminLabel}.${dnsZoneName}" +EOF + else + cat <>${adminRemoteAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/backend-hostname: "${appgwAlias}" +EOF + fi + + if [[ "${enableCookieBasedAffinity,,}" == "true" ]];then + cat <>${adminRemoteAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/cookie-based-affinity: "true" +EOF +fi + + cat <>${adminRemoteAppgwIngressYamlPath} + appgw.ingress.kubernetes.io/appgw-trusted-root-certificate: "${appgwBackendSecretName}" + +spec: + tls: + - secretName: ${appgwFrontendSecretName} + rules: + - http: + paths: + - path: /remoteconsole* + pathType: Prefix + backend: + service: + name: ${svcAdminServer} + port: + number: ${adminTargetPort} +EOF +} + function generate_appgw_cluster_config_file() { if [[ "${enableCustomSSL,,}" == "true" ]];then generate_appgw_cluster_config_file_ssl @@ -485,6 +599,14 @@ function generate_appgw_admin_config_file() { fi } +function generate_appgw_admin_remote_config_file() { + if [[ "${enableCustomSSL,,}" == "true" ]];then + generate_appgw_admin_remote_config_file_ssl + else + generate_appgw_admin_remote_config_file_nossl + fi +} + function output_create_gateway_ssl_k8s_secret(){ echo "export gateway frontend certificates" echo "$appgwFrontendSSLCertData" | base64 -d >${scriptDir}/$appgwFrontCertFileName @@ -807,13 +929,23 @@ function create_appgw_ingress() { waitfor_svc_completed ${clusterIngressHttpsName} fi - if [[ ${appgwForAdminServer,,} == "true" ]]; then + if [[ "${appgwForAdminServer,,}" == "true" ]]; then generate_appgw_admin_config_file kubectl apply -f ${adminAppgwIngressYamlPath} validate_status "Create appgw ingress svc." waitfor_svc_completed ${adminIngressName} fi + if [[ "${enableRemoteConsole,,}" == "true" ]]; then + export adminRemoteIngressName=${wlsDomainUID}-admin-remote-appgw-ingress-svc + export adminRemoteAppgwIngressYamlPath=${scriptDir}/appgw-admin-remote-ingress-svc.yaml + generate_appgw_admin_remote_config_file + + kubectl apply -f ${adminRemoteAppgwIngressYamlPath} + validate_status "Create appgw ingress svc." + waitfor_svc_completed ${adminRemoteIngressName} + fi + create_dns_CNAME_record } @@ -822,6 +954,7 @@ export script="${BASH_SOURCE[0]}" export scriptDir="$(cd "$(dirname "${script}")" && pwd)" source ${scriptDir}/common.sh +source ${scriptDir}/utility.sh export aksClusterRGName=$1 export aksClusterName=$2 @@ -833,20 +966,19 @@ export subID=$7 export curRGName=${8} export appgwName=${9} export vnetName=${10} -export spBase64String=${11} -export appgwForAdminServer=${12} -export enableCustomDNSAlias=${13} -export dnsRGName=${14} -export dnsZoneName=${15} -export dnsAdminLabel=${16} -export dnsClusterLabel=${17} -export appgwAlias=${18} -export enableInternalLB=${19} -export appgwFrontendSSLCertData=${20} -export appgwFrontendSSLCertPsw=${21} -export appgwCertificateOption=${22} -export enableCustomSSL=${23} -export enableCookieBasedAffinity=${24} +export appgwForAdminServer=${11} +export enableCustomDNSAlias=${12} +export dnsRGName=${13} +export dnsZoneName=${14} +export dnsAdminLabel=${15} +export dnsClusterLabel=${16} +export appgwAlias=${17} +export enableInternalLB=${18} +export appgwFrontendSSLCertData=${19} +export appgwCertificateOption=${20} +export enableCustomSSL=${21} +export enableCookieBasedAffinity=${22} +export enableRemoteConsole=${23} export adminServerName="admin-server" export adminConsoleEndpoint="null" @@ -872,7 +1004,7 @@ export svcCluster="${wlsDomainUID}-cluster-${clusterName}" export wlsDomainNS="${wlsDomainUID}-ns" export appgwBackendCertPath="${sharedPath}/security/root.cert" -echo $lbSvcValues +read_sensitive_parameters_from_stdin validate_input diff --git a/src/main/arm/scripts/setupWLSDomain.sh b/src/main/arm/scripts/setupWLSDomain.sh index be94147e1..2c98cba91 100644 --- a/src/main/arm/scripts/setupWLSDomain.sh +++ b/src/main/arm/scripts/setupWLSDomain.sh @@ -1,56 +1,49 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +# This script runs on Azure Container Instance with Alpine Linux that Azure Deployment script creates. echo "Script ${0} starts" -#Function to output message to stdout -function echo_stderr() { - echo "$@" >&2 - echo "$@" >>stdout -} - -function echo_stdout() { - echo "$@" - echo "$@" >>stdout +# read from stdin +function read_sensitive_parameters_from_stdin() { + read ocrSSOPSW wlsPassword wdtRuntimePassword wlsIdentityPsw wlsIdentityKeyPsw wlsTrustPsw } #Function to display usage message function usage() { - cat< \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ + usage=$(cat <<-END +Usage: +echo | +./setupWLSDomain.sh + + + + + + + + + + + + + + + + + + + + + + + -EOF +END +) + echo_stdout ${usage} if [ $1 -eq 1 ]; then + echo_stderr ${usage} exit 1 fi } @@ -329,16 +322,15 @@ function query_acr_credentials() { function build_docker_image() { echo "build a new image including the new applications" chmod ugo+x $scriptDir/createVMAndBuildImage.sh - bash $scriptDir/createVMAndBuildImage.sh \ + echo $azureACRPassword $ocrSSOPSW | \ + bash $scriptDir/createVMAndBuildImage.sh \ $currentResourceGroup \ $wlsImageTag \ $azureACRServer \ $azureACRUserName \ - $azureACRPassword \ $newImageTag \ "$appPackageUrls" \ $ocrSSOUser \ - $ocrSSOPSW \ $wlsClusterSize \ $enableCustomSSL \ "$scriptURL" @@ -564,17 +556,17 @@ function create_domain_namespace() { else updateNamepace=${constTrue} echo "Remove existing secrets and replace with new values" - kubectl -n ${wlsDomainNS} delete secret ${kubectlWLSCredentials} + kubectl -n ${wlsDomainNS} delete secret ${kubectlWLSCredentialName} kubectl -n ${wlsDomainNS} delete secret ${kubectlWDTEncryptionSecret} kubectl -n ${wlsDomainNS} delete secret ${kubectlSecretForACR} fi kubectl -n ${wlsDomainNS} create secret generic \ - ${kubectlWLSCredentials} \ + ${kubectlWLSCredentialName} \ --from-literal=username=${wlsUserName} \ --from-literal=password=${wlsPassword} - kubectl -n ${wlsDomainNS} label secret ${kubectlWLSCredentials} weblogic.domainUID=${wlsDomainUID} + kubectl -n ${wlsDomainNS} label secret ${kubectlWLSCredentialName} weblogic.domainUID=${wlsDomainUID} kubectl -n ${wlsDomainNS} create secret generic ${kubectlWDTEncryptionSecret} \ --from-literal=password=${wdtRuntimePassword} @@ -605,14 +597,14 @@ function parsing_ssl_certs_and_create_ssl_secret() { validate_ssl_keystores unmount_fileshare - echo "check if ${kubectlWLSSSLCredentials} exists." - ret=$(kubectl get secret -n ${wlsDomainNS} | grep "${kubectlWLSSSLCredentials}") + echo "check if ${kubectlWLSSSLCredentialsName} exists." + ret=$(kubectl get secret -n ${wlsDomainNS} | grep "${kubectlWLSSSLCredentialsName}") if [ -n "${ret}" ]; then - echo "delete secret ${kubectlWLSSSLCredentials}" - kubectl -n ${wlsDomainNS} delete secret ${kubectlWLSSSLCredentials} + echo "delete secret ${kubectlWLSSSLCredentialsName}" + kubectl -n ${wlsDomainNS} delete secret ${kubectlWLSSSLCredentialsName} fi - echo "create secret ${kubectlWLSSSLCredentials}" - kubectl -n ${wlsDomainNS} create secret generic ${kubectlWLSSSLCredentials} \ + echo "create secret ${kubectlWLSSSLCredentialsName}" + kubectl -n ${wlsDomainNS} create secret generic ${kubectlWLSSSLCredentialsName} \ --from-literal=sslidentitykeyalias=${wlsIdentityAlias} \ --from-literal=sslidentitykeypassword=${wlsIdentityKeyPsw} \ --from-literal=sslidentitystorepath=${sharedPath}/$wlsIdentityKeyStoreFileName \ @@ -622,7 +614,7 @@ function parsing_ssl_certs_and_create_ssl_secret() { --from-literal=ssltruststoretype=${wlsTrustType} \ --from-literal=ssltruststorepassword=${wlsTrustPsw} - kubectl -n ${wlsDomainNS} label secret ${kubectlWLSSSLCredentials} weblogic.domainUID=${wlsDomainUID} + kubectl -n ${wlsDomainNS} label secret ${kubectlWLSSSLCredentialsName} weblogic.domainUID=${wlsDomainUID} javaOptions="-Dweblogic.security.SSL.ignoreHostnameVerification=true -Dweblogic.security.SSL.trustedCAKeyStore=${sharedPath}/${wlsTrustKeyStoreJKSFileName}" fi } @@ -705,43 +697,37 @@ source ${scriptDir}/common.sh source ${scriptDir}/utility.sh export ocrSSOUser=$1 -export ocrSSOPSW=$2 -export aksClusterRGName=$3 -export aksClusterName=$4 -export wlsImageTag=$5 -export acrName=$6 -export wlsDomainName=$7 -export wlsDomainUID=$8 -export wlsUserName=$9 -export wlsPassword=${10} -export wdtRuntimePassword=${11} -export wlsCPU=${12} -export wlsMemory=${13} -export managedServerPrefix=${14} -export appReplicas=${15} -export appPackageUrls=${16} -export currentResourceGroup=${17} -export scriptURL=${18} -export storageAccountName=${19} -export wlsClusterSize=${20} -export enableCustomSSL=${21} -export wlsIdentityData=${22} -export wlsIdentityPsw=${23} -export wlsIdentityType=${24} -export wlsIdentityAlias=${25} -export wlsIdentityKeyPsw=${26} -export wlsTrustData=${27} -export wlsTrustPsw=${28} -export wlsTrustType=${29} -export enablePV=${30} +export aksClusterRGName=$2 +export aksClusterName=$3 +export wlsImageTag=$4 +export acrName=$5 +export wlsDomainName=$6 +export wlsDomainUID=$7 +export wlsUserName=$8 +export wlsCPU=$9 +export wlsMemory=${10} +export managedServerPrefix=${11} +export appReplicas=${12} +export appPackageUrls=${13} +export currentResourceGroup=${14} +export scriptURL=${15} +export storageAccountName=${16} +export wlsClusterSize=${17} +export enableCustomSSL=${18} +export wlsIdentityData=${19} +export wlsIdentityType=${20} +export wlsIdentityAlias=${21} +export wlsTrustData=${22} +export wlsTrustType=${23} +export enablePV=${24} export adminServerName="admin-server" export azFileShareName="weblogic" export exitCode=0 export kubectlSecretForACR="regsecret" -export kubectlWLSCredentials="${wlsDomainUID}-weblogic-credentials" -export kubectlWLSSSLCredentials="${wlsDomainUID}-weblogic-ssl-credentials" export kubectlWDTEncryptionSecret="${wlsDomainUID}-runtime-encryption-secret" +export kubectlWLSCredentialName="${wlsDomainUID}-weblogic-credentials" +export kubectlWLSSSLCredentialsName="${wlsDomainUID}-weblogic-ssl-credentials" export newImageTag=$(date +%s) export operatorName="weblogic-operator" export storageFileShareName="weblogic" @@ -758,6 +744,8 @@ export wlsTrustKeyStoreFileName="security/trust.keystore" export wlsTrustKeyStoreJKSFileName="security/trust.jks" export wlsIdentityRootCertFileName="security/root.cert" +read_sensitive_parameters_from_stdin + validate_input install_utilities diff --git a/src/main/arm/scripts/updateApplications.sh b/src/main/arm/scripts/updateApplications.sh index e08460261..c1a5fa945 100644 --- a/src/main/arm/scripts/updateApplications.sh +++ b/src/main/arm/scripts/updateApplications.sh @@ -1,8 +1,97 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. echo "Script ${0} starts" +# read from stdin +function read_sensitive_parameters_from_stdin() { + read ocrSSOPSW +} + +function usage() { + usage=$(cat <<-END +Usage: +echo | + ./updateApplications.sh + + + + + + + + + + + + +END +) + echo_stdout "${usage}" + if [ $1 -eq 1 ]; then + echo_stderr "${usage}" + exit 1 + fi +} + +#Function to validate input +function validate_input() { + if [[ -z "$ocrSSOUser" || -z "${ocrSSOPSW}" ]]; then + echo_stderr "Oracle SSO account is required. " + usage 1 + fi + + if [[ -z "$aksClusterRGName" || -z "${aksClusterName}" ]]; then + echo_stderr "AKS cluster name and resource group name are required. " + usage 1 + fi + + if [ -z "$wlsImageTag" ]; then + echo_stderr "wlsImageTag is required. " + usage 1 + fi + + if [ -z "$acrName" ]; then + echo_stderr "acrName is required. " + usage 1 + fi + + if [ -z "$wlsDomainName" ]; then + echo_stderr "wlsDomainName is required. " + usage 1 + fi + + if [ -z "$wlsDomainUID" ]; then + echo_stderr "wlsDomainUID is required. " + usage 1 + fi + + if [ -z "$currentResourceGroup" ]; then + echo_stderr "currentResourceGroup is required. " + usage 1 + fi + + if [ -z "$appPackageUrls" ]; then + echo_stderr "appPackageUrls is required. " + usage 1 + fi + + if [ -z "$scriptURL" ]; then + echo_stderr "scriptURL is required. " + usage 1 + fi + + if [ -z "$appStorageAccountName" ]; then + echo_stderr "appStorageAccountName is required. " + usage 1 + fi + + if [ -z "$appContainerName" ]; then + echo_stderr "appContainerName is required. " + usage 1 + fi +} + # Connect to AKS cluster function connect_aks_cluster() { az aks get-credentials \ @@ -97,16 +186,15 @@ function query_app_urls() { function build_docker_image() { echo "build a new image including the new applications" chmod ugo+x $scriptDir/createVMAndBuildImage.sh - bash $scriptDir/createVMAndBuildImage.sh \ + echo $azureACRPassword $ocrSSOPSW | \ + bash $scriptDir/createVMAndBuildImage.sh \ $currentResourceGroup \ $wlsImageTag \ $azureACRServer \ $azureACRUserName \ - $azureACRPassword \ $newImageTag \ "$appPackageUrls" \ $ocrSSOUser \ - $ocrSSOPSW \ $wlsClusterSize \ $enableCustomSSL \ "$scriptURL" @@ -173,18 +261,17 @@ source ${scriptDir}/common.sh source ${scriptDir}/utility.sh export ocrSSOUser=$1 -export ocrSSOPSW=$2 -export aksClusterRGName=$3 -export aksClusterName=$4 -export wlsImageTag=$5 -export acrName=$6 -export wlsDomainName=$7 -export wlsDomainUID=$8 -export currentResourceGroup=$9 -export appPackageUrls=${10} -export scriptURL=${11} -export appStorageAccountName=${12} -export appContainerName=${13} +export aksClusterRGName=$2 +export aksClusterName=$3 +export wlsImageTag=$4 +export acrName=$5 +export wlsDomainName=$6 +export wlsDomainUID=$7 +export currentResourceGroup=$8 +export appPackageUrls=$9 +export scriptURL=${10} +export appStorageAccountName=${11} +export appContainerName=${12} export newImageTag=$(date +%s) # seconds @@ -193,6 +280,10 @@ export sslIdentityEnvName="SSL_IDENTITY_PRIVATE_KEY_ALIAS" export wlsClusterName="cluster-1" export wlsDomainNS="${wlsDomainUID}-ns" +read_sensitive_parameters_from_stdin + +validate_input + install_kubectl connect_aks_cluster diff --git a/src/main/arm/scripts/updateDomainConfig.sh b/src/main/arm/scripts/updateDomainConfig.sh index 89bee7ac7..17e3ebca7 100644 --- a/src/main/arm/scripts/updateDomainConfig.sh +++ b/src/main/arm/scripts/updateDomainConfig.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. export script="${BASH_SOURCE[0]}" diff --git a/src/main/arm/scripts/utility.sh b/src/main/arm/scripts/utility.sh index 126d89d45..710ba5121 100644 --- a/src/main/arm/scripts/utility.sh +++ b/src/main/arm/scripts/utility.sh @@ -1,6 +1,6 @@ -# Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +# Copyright (c) 2021, Oracle Corporation and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. -# This script runs on Alpine Linux +# This script runs on Azure Container Instance with Alpine Linux that Azure Deployment script creates. function install_jdk() { # Install Microsoft OpenJDK @@ -25,15 +25,18 @@ function install_kubectl() { fi } -#Function to output message to stdout function echo_stderr() { - echo "$@" >&2 - echo "$@" >>stdout + >&2 echo "$@" + # The function is used for scripts running within Azure Deployment Script + # The value of AZ_SCRIPTS_OUTPUT_PATH is /mnt/azscripts/azscriptoutput + echo -e "$@" >>${AZ_SCRIPTS_PATH_OUTPUT_DIRECTORY}/errors.log } function echo_stdout() { - echo "$@" - echo "$@" >>stdout + echo "$@" + # The function is used for scripts running within Azure Deployment Script + # The value of AZ_SCRIPTS_OUTPUT_PATH is /mnt/azscripts/azscriptoutput + echo -e "$@" >>${AZ_SCRIPTS_PATH_OUTPUT_DIRECTORY}/debug.log } # Call this function to make sure pods of a domain are running. diff --git a/src/main/bicep/mainTemplate.bicep b/src/main/bicep/mainTemplate.bicep index 3feb97b72..d53baea6c 100644 --- a/src/main/bicep/mainTemplate.bicep +++ b/src/main/bicep/mainTemplate.bicep @@ -1,5 +1,5 @@ /* -* Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +* Copyright (c) 2021, Oracle Corporation and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. * * Terms: @@ -60,6 +60,8 @@ param appGatewaySSLCertData string = 'appgw-ssl-data' param appGatewaySSLCertPassword string = newGuid() @description('Create Application Gateway ingress for admin console.') param appgwForAdminServer bool = true +@description('Create Application Gateway ingress for remote console.') +param appgwForRemoteConsole bool = true @description('Urls of Java EE application packages.') param appPackageUrls array = [] @description('The number of managed server to start.') @@ -466,6 +468,7 @@ module networkingDeployment 'modules/networking.bicep' = if (const_enableNetwork appGatewayCertificateOption: appGatewayCertificateOption appGatewayPublicIPAddressName: appGatewayPublicIPAddressName appgwForAdminServer: appgwForAdminServer + appgwForRemoteConsole: appgwForRemoteConsole createDNSZone: createDNSZone dnsNameforApplicationGateway: name_domainLabelforApplicationGateway dnszoneAdminConsoleLabel: dnszoneAdminConsoleLabel @@ -522,6 +525,9 @@ output aksClusterName string = ref_wlsDomainDeployment.outputs.aksClusterName.va output adminConsoleInternalUrl string = ref_wlsDomainDeployment.outputs.adminServerUrl.value output adminConsoleExternalUrl string = const_enableNetworking ? networkingDeployment.outputs.adminConsoleExternalUrl : '' output adminConsoleExternalSecuredUrl string = const_enableNetworking ? networkingDeployment.outputs.adminConsoleExternalSecuredUrl : '' +// If TLS/SSL enabled, only secured url is working, will not output HTTP url. +output adminRemoteConsoleUrl string = const_enableNetworking && !enableCustomSSL ? networkingDeployment.outputs.adminRemoteConsoleUrl: '' +output adminRemoteConsoleSecuredUrl string = const_enableNetworking ? networkingDeployment.outputs.adminRemoteConsoleSecuredUrl: '' output clusterInternalUrl string = ref_wlsDomainDeployment.outputs.clusterSVCUrl.value output clusterExternalUrl string = const_enableNetworking ? networkingDeployment.outputs.clusterExternalUrl : '' output clusterExternalSecuredUrl string = const_enableNetworking ? networkingDeployment.outputs.clusterExternalSecuredUrl : '' diff --git a/src/main/bicep/modules/_azure-resoruces/_acr.bicep b/src/main/bicep/modules/_azure-resoruces/_acr.bicep index f4c179f89..ed3ce91aa 100644 --- a/src/main/bicep/modules/_azure-resoruces/_acr.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_acr.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. param acrNamePrefix string = 'wlsaksacr' diff --git a/src/main/bicep/modules/_azure-resoruces/_aks.bicep b/src/main/bicep/modules/_azure-resoruces/_aks.bicep index c213599f2..b2bbc5cf6 100644 --- a/src/main/bicep/modules/_azure-resoruces/_aks.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_aks.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. @description('true to use resource or workspace permissions. false to require workspace permissions.') diff --git a/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep b/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep index 1c19943da..f0d9f0d94 100644 --- a/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_appgateway.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. @description('DNS for ApplicationGateway') diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep index ba9d17416..f32233ae9 100644 --- a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultForWLSSSLCert.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. @description('Property to specify whether Azure Resource Manager is permitted to retrieve secrets from the key vault.') diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep index 192a8feaa..5b02d09f8 100644 --- a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithExistingCert.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. @description('Secret name of certificate data.') diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep index 32c48ed74..eb508ee6a 100644 --- a/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_keyvault/_keyvaultWithNewCert.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. @description('Managed identity to be used for the deployment script. Currently, only user-assigned MSI is supported.') diff --git a/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep b/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep index df69d23a1..e44cd37c2 100644 --- a/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_keyvaultAdapter.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // Deploy Application Gateway certificate secrets. diff --git a/src/main/bicep/modules/_azure-resoruces/_storage.bicep b/src/main/bicep/modules/_azure-resoruces/_storage.bicep index c405cfe1e..a79884a9b 100644 --- a/src/main/bicep/modules/_azure-resoruces/_storage.bicep +++ b/src/main/bicep/modules/_azure-resoruces/_storage.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. param location string = 'eastus' diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep index ef900e55d..d4a08390a 100644 --- a/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep +++ b/src/main/bicep/modules/_deployment-scripts/_ds-create-networking.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. param _artifactsLocation string @@ -15,6 +15,7 @@ param appgwName string = 'appgw-contoso' @description('Three scenarios we support for deploying app gateway') param appgwCertificateOption string = 'haveCert' param appgwForAdminServer bool = true +param appgwForRemoteConsole bool = true @secure() param appgwFrontendSSLCertData string = newGuid() @secure() @@ -42,10 +43,11 @@ param wlsDomainUID string = 'sample-domain1' var const_appgwHelmConfigTemplate='appgw-helm-config.yaml.template' var const_appgwSARoleBindingFile='appgw-ingress-clusterAdmin-roleBinding.yaml' -var const_arguments = '${aksClusterRGName} ${aksClusterName} ${wlsDomainName} ${wlsDomainUID} "${string(lbSvcValues)}" ${enableAppGWIngress} ${subscription().id} ${resourceGroup().name} ${appgwName} ${vnetName} ${string(servicePrincipal)} ${appgwForAdminServer} ${enableDNSConfiguration} ${dnszoneRGName} ${dnszoneName} ${dnszoneAdminConsoleLabel} ${dnszoneAppGatewayLabel} ${appgwAlias} ${useInternalLB} ${appgwFrontendSSLCertData} ${appgwFrontendSSLCertPsw} ${appgwCertificateOption} ${enableCustomSSL} ${enableCookieBasedAffinity}' +var const_arguments = '${aksClusterRGName} ${aksClusterName} ${wlsDomainName} ${wlsDomainUID} "${string(lbSvcValues)}" ${enableAppGWIngress} ${subscription().id} ${resourceGroup().name} ${appgwName} ${vnetName} ${string(servicePrincipal)} ${appgwForAdminServer} ${enableDNSConfiguration} ${dnszoneRGName} ${dnszoneName} ${dnszoneAdminConsoleLabel} ${dnszoneAppGatewayLabel} ${appgwAlias} ${useInternalLB} ${appgwFrontendSSLCertData} ${appgwFrontendSSLCertPsw} ${appgwCertificateOption} ${enableCustomSSL} ${enableCookieBasedAffinity} ${appgwForRemoteConsole}' var const_commonScript = 'common.sh' var const_scriptLocation = uri(_artifactsLocation, 'scripts/') -var const_primaryScript = 'setupNetworking.sh' +var const_setupNetworkingScript= 'setupNetworking.sh' +var const_primaryScript = 'invokeSetupNetworking.sh' resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { name: 'ds-networking-deployment' @@ -57,6 +59,7 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { arguments: const_arguments primaryScriptUri: uri(const_scriptLocation, '${const_primaryScript}${_artifactsLocationSasToken}') supportingScriptUris: [ + uri(const_scriptLocation, '${const_setupNetworkingScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_appgwHelmConfigTemplate}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_appgwSARoleBindingFile}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_commonScript}${_artifactsLocationSasToken}') diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep index 67f0b9ae4..a2616977c 100644 --- a/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep +++ b/src/main/bicep/modules/_deployment-scripts/_ds-create-wls-cluster.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. param _artifactsLocation string @@ -54,6 +54,7 @@ param wlsUserName string = 'weblogic' var const_arguments = '${ocrSSOUser} ${ocrSSOPSW} ${aksClusterRGName} ${aksClusterName} ${wlsImageTag} ${acrName} ${wlsDomainName} ${wlsDomainUID} ${wlsUserName} ${wlsPassword} ${wdtRuntimePassword} ${wlsCPU} ${wlsMemory} ${managedServerPrefix} ${appReplicas} ${string(appPackageUrls)} ${resourceGroup().name} ${const_scriptLocation} ${storageAccountName} ${wlsClusterSize} ${enableCustomSSL} ${wlsIdentityKeyStoreData} ${wlsIdentityKeyStorePassphrase} ${wlsIdentityKeyStoreType} ${wlsPrivateKeyAlias} ${wlsPrivateKeyPassPhrase} ${wlsTrustKeyStoreData} ${wlsTrustKeyStorePassPhrase} ${wlsTrustKeyStoreType} ${enablePV} ' var const_buildDockerImageScript='createVMAndBuildImage.sh' var const_commonScript = 'common.sh' +var const_invokeSetUpDomainScript = 'invokeSetupWLSDomain.sh' var const_pvTempalte = 'pv.yaml.template' var const_pvcTempalte = 'pvc.yaml.template' var const_scriptLocation = uri(_artifactsLocation, 'scripts/') @@ -70,8 +71,9 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { properties: { azCliVersion: '2.15.0' arguments: const_arguments - primaryScriptUri: uri(const_scriptLocation, '${const_setUpDomainScript}${_artifactsLocationSasToken}') + primaryScriptUri: uri(const_scriptLocation, '${const_invokeSetUpDomainScript}${_artifactsLocationSasToken}') supportingScriptUris: [ + uri(const_scriptLocation, '${const_setUpDomainScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_genDomainConfigScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_utilityScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_pvTempalte}${_artifactsLocationSasToken}') diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-datasource-connection.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-datasource-connection.bicep index d1f169375..b647a4bd6 100644 --- a/src/main/bicep/modules/_deployment-scripts/_ds-datasource-connection.bicep +++ b/src/main/bicep/modules/_deployment-scripts/_ds-datasource-connection.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. param _artifactsLocation string @@ -23,10 +23,11 @@ param wlsUserName string = 'weblogic' var const_arguments = '${aksClusterRGName} ${aksClusterName} ${databaseType} ${dbPassword} ${dbUser} "${dsConnectionURL}" ${jdbcDataSourceName} ${wlsDomainUID} ${wlsUserName} ${wlsPassword} ${dbConfigurationType}' var const_azcliVersion='2.15.0' +var const_commonScript = 'common.sh' var const_datasourceScript='setupDBConnections.sh' var const_datasourceModelScript='genDatasourceModel.sh' var const_dbUtilityScript='dbUtility.sh' -var const_commonScript = 'common.sh' +var const_invokeSetupDBConnectionsScript='invokeSetupDBConnections.sh' var const_scriptLocation = uri(_artifactsLocation, 'scripts/') var const_utilityScript= 'utility.sh' @@ -38,8 +39,9 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { properties: { azCliVersion: const_azcliVersion arguments: const_arguments - primaryScriptUri: uri(const_scriptLocation, '${const_datasourceScript}${_artifactsLocationSasToken}') + primaryScriptUri: uri(const_scriptLocation, '${const_invokeSetupDBConnectionsScript}${_artifactsLocationSasToken}') supportingScriptUris: [ + uri(const_scriptLocation, '${const_datasourceScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_commonScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_utilityScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_dbUtilityScript}${_artifactsLocationSasToken}') diff --git a/src/main/bicep/modules/_deployment-scripts/_ds-query-storage-account.bicep b/src/main/bicep/modules/_deployment-scripts/_ds-query-storage-account.bicep index 14244907a..92d096b28 100644 --- a/src/main/bicep/modules/_deployment-scripts/_ds-query-storage-account.bicep +++ b/src/main/bicep/modules/_deployment-scripts/_ds-query-storage-account.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. param aksClusterName string = '' diff --git a/src/main/bicep/modules/_deployment-scripts/_ds_update-applications.bicep b/src/main/bicep/modules/_deployment-scripts/_ds_update-applications.bicep index 3a756e414..6e83cc448 100644 --- a/src/main/bicep/modules/_deployment-scripts/_ds_update-applications.bicep +++ b/src/main/bicep/modules/_deployment-scripts/_ds_update-applications.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. param _artifactsLocation string @@ -28,6 +28,7 @@ var const_arguments = '${ocrSSOUser} ${ocrSSOPSW} ${aksClusterRGName} ${aksClust var const_azcliVersion='2.15.0' var const_buildDockerImageScript='createVMAndBuildImage.sh' var const_commonScript = 'common.sh' +var const_invokeScript = 'invokeUpdateApplications.sh' var const_scriptLocation = uri(_artifactsLocation, 'scripts/') var const_updateAppScript= 'updateApplications.sh' var const_utilityScript= 'utility.sh' @@ -40,8 +41,9 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { properties: { azCliVersion: const_azcliVersion arguments: const_arguments - primaryScriptUri: uri(const_scriptLocation, '${const_updateAppScript}${_artifactsLocationSasToken}') + primaryScriptUri: uri(const_scriptLocation, '${const_invokeScript}${_artifactsLocationSasToken}') supportingScriptUris: [ + uri(const_scriptLocation, '${const_updateAppScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_commonScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_utilityScript}${_artifactsLocationSasToken}') uri(const_scriptLocation, '${const_buildDockerImageScript}${_artifactsLocationSasToken}') diff --git a/src/main/bicep/modules/_pids/_empty.bicep b/src/main/bicep/modules/_pids/_empty.bicep index deb7ea7f4..f30cdc641 100644 --- a/src/main/bicep/modules/_pids/_empty.bicep +++ b/src/main/bicep/modules/_pids/_empty.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. /* diff --git a/src/main/bicep/modules/_pids/_pid-dev.bicep b/src/main/bicep/modules/_pids/_pid-dev.bicep index ca5729d4f..9f0a680e3 100644 --- a/src/main/bicep/modules/_pids/_pid-dev.bicep +++ b/src/main/bicep/modules/_pids/_pid-dev.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // Deployment for pids. diff --git a/src/main/bicep/modules/_pids/_pid.bicep b/src/main/bicep/modules/_pids/_pid.bicep index cc5053b23..ed81fb5b7 100644 --- a/src/main/bicep/modules/_pids/_pid.bicep +++ b/src/main/bicep/modules/_pids/_pid.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. // Deployment for pids. diff --git a/src/main/bicep/modules/_setupDBConnection.bicep b/src/main/bicep/modules/_setupDBConnection.bicep index b665115ce..cb2fcbc09 100644 --- a/src/main/bicep/modules/_setupDBConnection.bicep +++ b/src/main/bicep/modules/_setupDBConnection.bicep @@ -1,5 +1,5 @@ /* -Copyright (c) 2018, 2021, Oracle and/or its affiliates. + Copyright (c) 2021, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ diff --git a/src/main/bicep/modules/networking.bicep b/src/main/bicep/modules/networking.bicep index 47b1a428b..54aa351ed 100644 --- a/src/main/bicep/modules/networking.bicep +++ b/src/main/bicep/modules/networking.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. param _artifactsLocation string = deployment().properties.templateLink.uri @@ -23,6 +23,8 @@ param appGatewayCertificateOption string = 'haveCert' param appGatewayPublicIPAddressName string = 'gwip' @description('Create Application Gateway ingress for admin console.') param appgwForAdminServer bool = true +@description('Create Application Gateway ingress for remote console.') +param appgwForRemoteConsole bool = true @description('If true, the template will update records to the existing DNS Zone. If false, the template will create a new DNS Zone.') param createDNSZone bool = false @description('DNS prefix for ApplicationGateway') @@ -114,6 +116,7 @@ module networkingDeployment '_deployment-scripts/_ds-create-networking.bicep' = appgwAlias: enableAppGWIngress ? appgwDeployment.outputs.appGatewayAlias : 'null' appgwCertificateOption: appGatewayCertificateOption appgwForAdminServer: appgwForAdminServer + appgwForRemoteConsole: appgwForRemoteConsole appgwFrontendSSLCertData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) appgwFrontendSSLCertPsw: existingKeyvault.getSecret(keyVaultSSLCertPasswordSecretName) aksClusterRGName: aksClusterRGName @@ -151,6 +154,7 @@ module networkingDeployment2 '_deployment-scripts/_ds-create-networking.bicep' = appgwAlias: enableAppGWIngress ? appgwDeployment.outputs.appGatewayAlias : 'null' appgwCertificateOption: appGatewayCertificateOption appgwForAdminServer: appgwForAdminServer + appgwForRemoteConsole: appgwForRemoteConsole appgwFrontendSSLCertData: existingKeyvault.getSecret(keyVaultSSLCertDataSecretName) appgwFrontendSSLCertPsw: 'null' aksClusterRGName: aksClusterRGName @@ -187,6 +191,7 @@ module networkingDeployment3 '_deployment-scripts/_ds-create-networking.bicep' = appgwAlias: enableAppGWIngress ? appgwDeployment.outputs.appGatewayAlias : 'null' appgwCertificateOption: appGatewayCertificateOption appgwForAdminServer: appgwForAdminServer + appgwForRemoteConsole: appgwForRemoteConsole appgwFrontendSSLCertData: 'null' appgwFrontendSSLCertPsw: 'null' aksClusterRGName: aksClusterRGName @@ -235,6 +240,8 @@ module pidNetworkingEnd './_pids/_pid.bicep' = { } output adminConsoleExternalUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('http://{0}console', const_appgwAdminCustomDNSAlias) : format('http://{0}/console', appgwDeployment.outputs.appGatewayAlias)) : networkingDeployment3.outputs.adminConsoleLBUrl -output adminConsoleExternalSecuredUrl string = enableAppGWIngress && enableCustomSSL ? (enableDNSConfiguration ? format('https://{0}console', const_appgwAdminCustomDNSAlias) : format('https://{0}/console', appgwDeployment.outputs.appGatewayAlias)) : '' +output adminConsoleExternalSecuredUrl string = enableAppGWIngress && enableCustomSSL && enableDNSConfiguration ? format('https://{0}console', const_appgwAdminCustomDNSAlias) : '' +output adminRemoteConsoleUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('http://{0}remoteconsole', const_appgwAdminCustomDNSAlias) : format('http://{0}/remoteconsole', appgwDeployment.outputs.appGatewayAlias)) : networkingDeployment3.outputs.adminConsoleLBUrl +output adminRemoteConsoleSecuredUrl string = enableAppGWIngress && enableCustomSSL && enableDNSConfiguration ? format('https://{0}remoteconsole', const_appgwAdminCustomDNSAlias) : '' output clusterExternalUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('http://{0}', const_appgwCustomDNSAlias) : appgwDeployment.outputs.appGatewayURL) : networkingDeployment3.outputs.clusterLBUrl output clusterExternalSecuredUrl string = enableAppGWIngress ? (enableDNSConfiguration ? format('https://{0}', const_appgwCustomDNSAlias) : appgwDeployment.outputs.appGatewaySecuredURL) : '' diff --git a/src/main/bicep/modules/setupDBConnection.bicep b/src/main/bicep/modules/setupDBConnection.bicep index 4b1951edf..e5a4bf977 100644 --- a/src/main/bicep/modules/setupDBConnection.bicep +++ b/src/main/bicep/modules/setupDBConnection.bicep @@ -1,5 +1,5 @@ /* -Copyright (c) 2018, 2021, Oracle and/or its affiliates. + Copyright (c) 2021, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. Description diff --git a/src/main/bicep/modules/setupWebLogicCluster.bicep b/src/main/bicep/modules/setupWebLogicCluster.bicep index 67a15d7a0..b081b3f46 100644 --- a/src/main/bicep/modules/setupWebLogicCluster.bicep +++ b/src/main/bicep/modules/setupWebLogicCluster.bicep @@ -1,4 +1,4 @@ -// Copyright (c) 2019, 2020, Oracle Corporation and/or its affiliates. +// Copyright (c) 2021, Oracle Corporation and/or its affiliates. // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. /* diff --git a/src/main/bicep/modules/updateWebLogicApplications.bicep b/src/main/bicep/modules/updateWebLogicApplications.bicep index 58944e360..da0400401 100644 --- a/src/main/bicep/modules/updateWebLogicApplications.bicep +++ b/src/main/bicep/modules/updateWebLogicApplications.bicep @@ -1,5 +1,5 @@ /* -Copyright (c) 2018, 2021, Oracle and/or its affiliates. + Copyright (c) 2021, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. Description From 6eeb6ca7cc839d72873352d773f6a343bb294a38 Mon Sep 17 00:00:00 2001 From: "haixia.cheng@microsoft.com" Date: Thu, 19 Aug 2021 09:19:35 +0800 Subject: [PATCH 31/37] On branch main: modify with UPL licence info in pom file. Changes to be committed: modified: pom.xml --- weblogic-azure-aks/pom.xml | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/weblogic-azure-aks/pom.xml b/weblogic-azure-aks/pom.xml index 1e24ae262..bee3edb16 100644 --- a/weblogic-azure-aks/pom.xml +++ b/weblogic-azure-aks/pom.xml @@ -1,24 +1,7 @@ From 6d2890a28167f5108a1d3d85dcaf6661faf009db Mon Sep 17 00:00:00 2001 From: "haixia.cheng@microsoft.com" Date: Thu, 19 Aug 2021 09:25:06 +0800 Subject: [PATCH 32/37] On branch main: capitalize product name Helm. Changes to be committed: modified: src/main/arm/scripts/appgw-helm-config.yaml.template modified: src/main/arm/scripts/setupNetworking.sh modified: src/main/arm/scripts/setupWLSDomain.sh --- .../main/arm/scripts/appgw-helm-config.yaml.template | 2 +- .../src/main/arm/scripts/setupNetworking.sh | 10 +++++----- .../src/main/arm/scripts/setupWLSDomain.sh | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template b/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template index 93f50fdb9..d4c7f38f2 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template +++ b/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template @@ -3,7 +3,7 @@ # # Based on https://raw.githubusercontent.com/Azure/application-gateway-kubernetes-ingress/master/docs/examples/sample-helm-config.yaml -# This file contains the essential configs for the ingress controller helm chart +# This file contains the essential configs for the ingress controller Helm chart # Verbosity level of the App Gateway Ingress Controller verbosityLevel: 3 diff --git a/weblogic-azure-aks/src/main/arm/scripts/setupNetworking.sh b/weblogic-azure-aks/src/main/arm/scripts/setupNetworking.sh index 0a70a1aac..9f82ec1ad 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/setupNetworking.sh +++ b/weblogic-azure-aks/src/main/arm/scripts/setupNetworking.sh @@ -9,7 +9,7 @@ function read_sensitive_parameters_from_stdin() { } function install_helm() { - # Install helm + # Install Helm browserURL=$(curl -m ${curlMaxTime} -s https://api.github.com/repos/helm/helm/releases/latest | grep "browser_download_url.*linux-amd64.tar.gz.asc" | cut -d : -f 2,3 | @@ -20,12 +20,12 @@ function install_helm() { curl -m ${curlMaxTime} -fL https://get.helm.sh/${helmPackageName} -o /tmp/${helmPackageName} tar -zxvf /tmp/${helmPackageName} -C /tmp mv /tmp/linux-amd64/helm /usr/local/bin/helm - echo "helm version" + echo "Helm version" helm version - validate_status "Finished installing helm." + validate_status "Finished installing Helm." } -# Install latest kubectl and helm +# Install latest kubectl and Helm function install_utilities() { if [ -d "apps" ]; then rm apps -f -r @@ -862,7 +862,7 @@ function create_appgw_ingress() { # query identity client id # identityClientId=$(az identity show --ids ${identityId} -o tsv --query "clientId") - # generate helm config + # generate Helm config customAppgwHelmConfig=${scriptDir}/appgw-helm-config.yaml cp ${scriptDir}/appgw-helm-config.yaml.template ${customAppgwHelmConfig} subID=${subID#*\/subscriptions\/} diff --git a/weblogic-azure-aks/src/main/arm/scripts/setupWLSDomain.sh b/weblogic-azure-aks/src/main/arm/scripts/setupWLSDomain.sh index 2c98cba91..f987483a5 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/setupWLSDomain.sh +++ b/weblogic-azure-aks/src/main/arm/scripts/setupWLSDomain.sh @@ -187,7 +187,7 @@ function validate_status() { fi } -# Install latest kubectl and helm +# Install latest kubectl and Helm function install_utilities() { if [ -d "apps" ]; then rm apps -f -r @@ -202,7 +202,7 @@ function install_utilities() { ret=$(kubectl --help) validate_status ${ret} - # Install helm + # Install Helm browserURL=$(curl -m ${curlMaxTime} -s https://api.github.com/repos/helm/helm/releases/latest | grep "browser_download_url.*linux-amd64.tar.gz.asc" | cut -d : -f 2,3 | @@ -213,9 +213,9 @@ function install_utilities() { curl -m ${curlMaxTime} -fL https://get.helm.sh/${helmPackageName} -o /tmp/${helmPackageName} tar -zxvf /tmp/${helmPackageName} -C /tmp mv /tmp/linux-amd64/helm /usr/local/bin/helm - echo "helm version" + echo "Helm version" helm version - validate_status "Finished installing helm." + validate_status "Finished installing Helm." echo "az cli version" ret=$(az --version) From 37f4036c9bd1b88938c9697d764be5b303a6378b Mon Sep 17 00:00:00 2001 From: "haixia.cheng@microsoft.com" Date: Thu, 19 Aug 2021 09:28:29 +0800 Subject: [PATCH 33/37] On branch main: capitalize product name Kubernetes Changes to be committed: modified: src/main/arm/scripts/appgw-helm-config.yaml.template --- .../src/main/arm/scripts/appgw-helm-config.yaml.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template b/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template index d4c7f38f2..614a3cd50 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template +++ b/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template @@ -23,7 +23,7 @@ appgw: shared: false ################################################################################ -# Specify which kubernetes namespace the ingress controller will watch +# Specify which Kubernetes namespace the ingress controller will watch # Default value is "default" # Leaving this variable out or setting it to blank or empty string would # result in Ingress Controller observing all acessible namespaces. From 820c7e4d8cf88840b49eb2e1ab953139ec5855db Mon Sep 17 00:00:00 2001 From: "haixia.cheng@microsoft.com" Date: Thu, 19 Aug 2021 09:30:35 +0800 Subject: [PATCH 34/37] On branch main: lowercase name of ingress controller --- .../src/main/arm/scripts/appgw-helm-config.yaml.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template b/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template index 614a3cd50..3f9dd7537 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template +++ b/weblogic-azure-aks/src/main/arm/scripts/appgw-helm-config.yaml.template @@ -26,7 +26,7 @@ appgw: # Specify which Kubernetes namespace the ingress controller will watch # Default value is "default" # Leaving this variable out or setting it to blank or empty string would -# result in Ingress Controller observing all acessible namespaces. +# result in ingress controller observing all acessible namespaces. # kubernetes: watchNamespace: @WATCH_NAMESPACE@ From e599233edb163bdfa597ea2551aee0771f364913 Mon Sep 17 00:00:00 2001 From: "haixia.cheng@microsoft.com" Date: Thu, 19 Aug 2021 10:16:06 +0800 Subject: [PATCH 35/37] On branch main: fix wording in comments. Changes to be committed: modified: src/main/arm/scripts/buildWLSDockerImage.sh modified: src/main/arm/scripts/createVMAndBuildImage.sh --- weblogic-azure-aks/src/main/arm/scripts/buildWLSDockerImage.sh | 2 +- .../src/main/arm/scripts/createVMAndBuildImage.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/weblogic-azure-aks/src/main/arm/scripts/buildWLSDockerImage.sh b/weblogic-azure-aks/src/main/arm/scripts/buildWLSDockerImage.sh index 07480bafa..3c19e2cb5 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/buildWLSDockerImage.sh +++ b/weblogic-azure-aks/src/main/arm/scripts/buildWLSDockerImage.sh @@ -172,7 +172,7 @@ function prepare_wls_models() { CLUSTER_SIZE=${wlsClusterSize} EOF - echo "Starting generating image model file..." + echo "Starting generation of image model file..." modelFilePath="$scriptDir/model.yaml" chmod ugo+x $scriptDir/genImageModel.sh diff --git a/weblogic-azure-aks/src/main/arm/scripts/createVMAndBuildImage.sh b/weblogic-azure-aks/src/main/arm/scripts/createVMAndBuildImage.sh index bb2dcfb24..cc06553c3 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/createVMAndBuildImage.sh +++ b/weblogic-azure-aks/src/main/arm/scripts/createVMAndBuildImage.sh @@ -73,7 +73,7 @@ function build_docker_image() { # MICROSOFT_INTERNAL # Specify tag 'SkipASMAzSecPack' to skip policy 'linuxazuresecuritypackautodeployiaas_1.6' - # Specify tag 'SkipNRMS*' to skip Microsoft internal NRMS policy, which cause vm-redeployed issue + # Specify tag 'SkipNRMS*' to skip Microsoft internal NRMS policy, which causes vm-redeployed issue az vm create \ --resource-group ${currentResourceGroup} \ --name ${vmName} \ From d8d34fb121d0630032082570ece636866141e026 Mon Sep 17 00:00:00 2001 From: "haixia.cheng@microsoft.com" Date: Thu, 19 Aug 2021 10:34:04 +0800 Subject: [PATCH 36/37] On branch main: make sure pv is bound. Changes to be committed: modified: src/main/arm/scripts/common.sh modified: src/main/arm/scripts/setupWLSDomain.sh modified: src/main/arm/scripts/utility.sh --- .../src/main/arm/scripts/common.sh | 8 ++++--- .../src/main/arm/scripts/setupWLSDomain.sh | 2 ++ .../src/main/arm/scripts/utility.sh | 23 +++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/weblogic-azure-aks/src/main/arm/scripts/common.sh b/weblogic-azure-aks/src/main/arm/scripts/common.sh index 4aa777db4..dfe1faec0 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/common.sh +++ b/weblogic-azure-aks/src/main/arm/scripts/common.sh @@ -1,10 +1,12 @@ -export checkPodStatusInterval=20 # interval to check pod status. -export checkPodStatusMaxAttemps=30 # interval to check pod status. +export checkPodStatusInterval=20 # interval of checking pod status. +export checkPodStatusMaxAttemps=30 # max attempt to check pod status. +export checkPVStateInterval=5 # interval of checking pvc status. +export checkPVStateMaxAttempt=10 # max attempt to check pvc status. export constFalse="false" export constTrue="true" export curlMaxTime=120 # seconds export ocrLoginServer="container-registry.oracle.com" -export optUninstallMaxTry=5 # Max try number to wait for the operator uninstalled +export optUninstallMaxTry=5 # Max attempts to wait for the operator uninstalled export optUninstallInterval=10 diff --git a/weblogic-azure-aks/src/main/arm/scripts/setupWLSDomain.sh b/weblogic-azure-aks/src/main/arm/scripts/setupWLSDomain.sh index f987483a5..3e1bb69d4 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/setupWLSDomain.sh +++ b/weblogic-azure-aks/src/main/arm/scripts/setupWLSDomain.sh @@ -511,7 +511,9 @@ function create_pv() { sed -i -e "s:@STORAGE_ACCOUNT@:${storageAccountName}:g" ${customPVCYaml} kubectl apply -f ${customPVYaml} + utility_check_pv_state ${pvName} "Available" ${checkPVStateMaxAttempt} ${checkPVStateInterval} kubectl apply -f ${customPVCYaml} + utility_check_pv_state ${pvName} "Bound" ${checkPVStateMaxAttempt} ${checkPVStateInterval} # validate PV PVC ret=$(kubectl get pv | grep "${pvName}" | grep "${pvcName}") diff --git a/weblogic-azure-aks/src/main/arm/scripts/utility.sh b/weblogic-azure-aks/src/main/arm/scripts/utility.sh index 710ba5121..b23d2006d 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/utility.sh +++ b/weblogic-azure-aks/src/main/arm/scripts/utility.sh @@ -39,6 +39,29 @@ function echo_stdout() { echo -e "$@" >>${AZ_SCRIPTS_PATH_OUTPUT_DIRECTORY}/debug.log } +# +# Check the state of a persistent volume. +# Leverage source code from function "checkPvState" in weblogic-operator, kubernetes\samples\scripts\common\utility.sh +# $1 - name of volume +# $2 - expected state of volume +# $3 - max attempt +# $4 - interval +function utility_check_pv_state { + + echo_stdout "Checking if the persistent volume ${1:?} is ${2:?}" + local pv_state=`kubectl get pv $1 -o jsonpath='{.status.phase}'` + attempts=0 + while [ ! "$pv_state" = "$2" ] && [ ! $attempts -eq $3 ]; do + attempts=$((attempts + 1)) + sleep $4 + pv_state=`kubectl get pv $1 -o jsonpath='{.status.phase}'` + done + if [ "$pv_state" != "$2" ]; then + echo_stderr "The persistent volume state should be $2 but is $pv_state" + exit 1 + fi +} + # Call this function to make sure pods of a domain are running. # * Make sure the admin server pod is running # * Make sure all the managed server pods are running From 4971d00b818b08b20f1361ebb82c61927ee7884f Mon Sep 17 00:00:00 2001 From: "haixia.cheng@microsoft.com" Date: Thu, 19 Aug 2021 14:11:46 +0800 Subject: [PATCH 37/37] On branch main: make sure the patching secret string is correct in db connection script. Specify weblogic container name in `kubectl exec` commands. Changes to be committed: modified: src/main/arm/scripts/common.sh modified: src/main/arm/scripts/setupDBConnections.sh --- .../src/main/arm/scripts/common.sh | 2 ++ .../main/arm/scripts/setupDBConnections.sh | 26 +++++++------------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/weblogic-azure-aks/src/main/arm/scripts/common.sh b/weblogic-azure-aks/src/main/arm/scripts/common.sh index dfe1faec0..f985e809f 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/common.sh +++ b/weblogic-azure-aks/src/main/arm/scripts/common.sh @@ -10,3 +10,5 @@ export curlMaxTime=120 # seconds export ocrLoginServer="container-registry.oracle.com" export optUninstallMaxTry=5 # Max attempts to wait for the operator uninstalled export optUninstallInterval=10 + +export wlsContainerName="weblogic-server" diff --git a/weblogic-azure-aks/src/main/arm/scripts/setupDBConnections.sh b/weblogic-azure-aks/src/main/arm/scripts/setupDBConnections.sh index 3149a522f..fd5283aa8 100644 --- a/weblogic-azure-aks/src/main/arm/scripts/setupDBConnections.sh +++ b/weblogic-azure-aks/src/main/arm/scripts/setupDBConnections.sh @@ -118,19 +118,11 @@ function apply_datasource_to_domain() { if [[ "${secretList}" != "null" ]];then secretList=$(cat ${domainConfigurationJsonFile} | jq -r '. | .spec.configuration.secrets[]') secretStrings="[" - index=0; for item in $secretList; do if [[ "${item}" == "${dbSecretName}" ]]; then continue fi - - if [ $index -eq 0 ];then - secretStrings="${secretStrings}\"${item}\"," - else - secretStrings="${secretStrings}\"${item}\"," - fi - - index=$((index+1)) + secretStrings="${secretStrings}\"${item}\"," done secretStrings="${secretStrings}\"${dbSecretName}\"]" @@ -150,7 +142,7 @@ function apply_datasource_to_domain() { } function remove_datasource_from_domain() { - echo "rollback datasoure" + echo "remove datasoure secret from domain configuration" # get domain configurations domainConfigurationJsonFile=$scriptDir/domain.json kubectl -n ${wlsDomainNS} get domain ${wlsDomainUID} -o json >${domainConfigurationJsonFile} @@ -171,15 +163,15 @@ function remove_datasource_from_domain() { continue fi - if [ $index -eq 0 ];then - secretStrings="${secretStrings}\"${item}\"," - else - secretStrings="${secretStrings}\"${item}\"," - fi - + secretStrings="${secretStrings}\"${item}\"," index=$((index+1)) done + if [ $index -ge 1 ]; then + # remove the last comma + secretStrings=$(echo "${secretStrings:0:${#secretStrings}-1}") + fi + secretStrings="${secretStrings}]" else secretStrings="[]" @@ -267,7 +259,7 @@ EOF echo "copy test script ${testDatasourceScript} to pod path /tmp/${dsScriptFileName}" targetDSFilePath=/tmp/${dsScriptFileName} kubectl cp ${testDatasourceScript} -n ${wlsDomainNS} ${podName}:${targetDSFilePath} - kubectl exec -it ${podName} -n ${wlsDomainNS} -- bash -c "wlst.sh ${targetDSFilePath}" | grep "State is Running" + kubectl exec -it ${podName} -n ${wlsDomainNS} -c ${wlsContainerName} -- bash -c "wlst.sh ${targetDSFilePath}" | grep "State is Running" if [ $? == 1 ];then echo "Failed to configure datasource ${jdbcDataSourceName}. Please make sure the input values are correct."