├── .gitignore ├── LICENSE ├── README.md ├── SECURITY.md ├── doc-azure-resrouces-dedicated-subnet └── README.md ├── doc-azure-training ├── Data_and_AI_Training.md ├── IoT_Training.md ├── MicrosoftAcademy.md ├── MicrosoftLearn.md ├── OtherResources.md ├── README.md └── files │ ├── image005.png │ ├── image006.png │ ├── image007.png │ ├── image008.jpg │ ├── image009.png │ ├── image010.png │ ├── image011.jpg │ ├── image012.png │ ├── image013.jpg │ ├── image014.png │ ├── image015.jpg │ ├── image016.png │ ├── image017.png │ ├── image018.jpg │ ├── image019.png │ ├── image020.png │ ├── image021.png │ ├── image022.jpg │ ├── image023.png │ ├── image024.jpg │ ├── image025.png │ ├── image026.jpg │ ├── image027.png │ └── image028.jpg ├── psh-B2B-two-tenant-sync ├── B2BSync-MyVars.ps1 ├── B2BSync.ps1 ├── B2BSync_CustomAttributes.ps1 ├── B2BSync_DestSetup.ps1 ├── B2BSync_SourceSetup.ps1 └── README.md ├── psh-BlobDataSync-Workflow ├── BlobSync.ps1 └── README.md ├── psh-GetArmLimitsViaAPI ├── GetArmLimits.ps1 └── README.md ├── psh-ManagedDiskUtils ├── CopyDiskImage_verified.ps1 ├── ManagedDiskCopy.ps1 ├── README.md └── RemoveOrphanedDisks.ps1 ├── psh-RBAC-CopyPaste ├── ApplyRBAC.ps1 ├── CreateDestSPs.ps1 ├── ExtractRBAC.ps1 ├── Utils.ps1 ├── readme.md └── settings.json ├── psh-aad-b2b-batch-invite ├── B2BBatchPSH.ps1 └── README.md ├── psh-b2c-custom-attributes ├── B2C_CustomAttributes.ps1 └── README.md ├── psh-exo-encrypted-mailsend-script ├── Files │ └── mail-flow-rules.png ├── README.md ├── SendEXOMailTest1.ps1 └── SendEXOMailTest2.ps1 ├── psh-poison-queue ├── README.md └── StorageQueueMovePoison.ps1 ├── psh-sql-update-firewall ├── UpdateAllSqlFirewall.ps1 └── readme.md ├── sa-dsml-many-models ├── LICENSE ├── README.md ├── code │ ├── aml_prs │ │ ├── Data_Preparation.ipynb │ │ ├── model_train.py │ │ ├── prediction.py │ │ └── prs_many_models.ipynb │ ├── deployment │ │ ├── 1-sai-create-endpoint.yml │ │ ├── azureml_cli.txt │ │ ├── conda.yml │ │ ├── custom_role.json │ │ ├── main_deployment.ipynb │ │ ├── many_model.yml │ │ ├── prs_1000_dominicks │ │ ├── score.py │ │ ├── test_data.csv │ │ ├── test_data_1000_dominicks.csv │ │ ├── test_data_1002_tropicana.csv │ │ └── test_deployment.ipynb │ ├── spark │ │ └── many_models_spark.ipynb │ ├── swagger.json │ └── util │ │ └── timeseries_utilities.py └── images │ ├── aml_many_models_arch.png │ ├── pandas_udf.png │ └── spark_many_models_arch.png ├── sample-AzGuardRails-Governance ├── AzurePolicy │ ├── audit │ │ ├── Audit-Managed-Disks.json │ │ ├── Audit-PublicIP.json │ │ └── Audit-Resource-Tag.json │ └── deny │ │ ├── Allowed-Locations.json │ │ ├── Prevent-Unmanaged-Disk-Creation.json │ │ └── denyPIP.json ├── CustomRBAC │ ├── JsonExamples │ │ ├── businessAnalysts.json │ │ ├── cloudDevelopers.json │ │ ├── cloudOpsAdmins.json │ │ ├── cloudOpsEngLead.json │ │ ├── digitalSecurity.json │ │ ├── networkAdmins.json │ │ └── suppReaderTickets.json │ ├── PowerShell │ │ ├── AzResourceProviderOperations.ps1 │ │ ├── Built-InRoles.ps1 │ │ ├── CustomRoleAssignment.ps1 │ │ ├── New-AzureRmDef-w-JSON.ps1 │ │ └── RoleTemplate.ps1 │ └── PowerShellRoleExamples │ │ ├── RBACBASA.ps1 │ │ ├── RBACCloudOpsLead.ps1 │ │ ├── RBACCloudOpsNonLead.ps1 │ │ ├── RBACDeveloper.ps1 │ │ ├── RBACDigitalSecurity.ps1 │ │ ├── RBACNetworking.ps1 │ │ └── README.md └── README.md ├── sample-EasyAuth-ClassicASP ├── EasyAuthClassicASP │ ├── default.asp │ ├── dockerfile │ ├── global.asa │ ├── inc │ │ ├── 500-100.asp │ │ └── EasyAuthASP.asp │ └── web.config └── README.md ├── sample-Python-KeyVault-Function ├── README.md ├── init-manual-service-principal.py ├── init-with-msi.py └── requirements.txt ├── sample-UpdateManagement ├── Kusto │ ├── Compliance │ │ ├── NotAssessedHosts.csl │ │ └── UncompliantHosts.csl │ ├── Post Analysis │ │ ├── Host-w-PatchAmountApplied.csl │ │ └── LinWin-PostAnalysis.csl │ ├── Pre-Analysis │ │ ├── Linux-PreAnalysis.csl │ │ └── Windows-PreAnalysis.csl │ └── README.md ├── PowerShell │ ├── ADDS │ │ ├── 01-InitialTask-ADDSSecurityGroups.ps1 │ │ ├── 02-SchedTask-ADDSSecurityGroups.ps1 │ │ └── README.md │ ├── AutomatedComplianceReporting │ │ ├── 01-BearerToken.ps1 │ │ ├── 02-PostAnalysisAutomation.ps1 │ │ ├── 03-PreAnalysisAutomation-Linux.ps1 │ │ ├── 04-PreAnalysisAutomation-Windows.ps1 │ │ └── README.md │ ├── AzureAutomationRunbooks │ │ ├── 3rdPartyPatching │ │ │ ├── 7Zip.ps1 │ │ │ ├── JRE.ps1 │ │ │ ├── VNC.ps1 │ │ │ ├── WinZip.ps1 │ │ │ └── Wireshark.ps1 │ │ ├── Export-RunAsCertificateToHybridWorker.ps1 │ │ ├── README.md │ │ └── RollbackPatches │ │ │ ├── WindowsRollback.ps1 │ │ │ └── linRollBack.py │ ├── GroupSchedule │ │ ├── 01-SvrGrping.ps1 │ │ ├── 02-WinSvrSched.ps1 │ │ ├── 03-LinuxSvrSched.ps1 │ │ └── README.md │ └── UpdateAgentReadiness │ │ ├── 01-BearerToken.ps1 │ │ ├── 02-UpdateAgentReadiness.ps1 │ │ └── README.md ├── README.md └── WSUS Deployment │ ├── README.md │ ├── WSUS30SP2DeployGuide.doc │ └── WSUS30SP2DeployGuide.pdf ├── sample-VMSS-DomainJoin-Arm ├── README.md ├── WindowsVirtualMachineScaleSet.parameters.json └── WindowsVirtualMachineScaleSet_DomainJoin.json └── sample-k8sRefArch ├── README.md ├── apim-appGw ├── README.md ├── aksDeploy.json ├── aksParams.json ├── appGwApimDeploy.json ├── appGwApimParams.json ├── helm-rbac.yaml ├── ingress-internal.yaml └── kubectl-helm-commands.md ├── appGw ├── README.md ├── aksDeploy.json ├── aksParams.json ├── appGwDeploy.json ├── appGwParams.json ├── echo-api.yaml ├── helm-rbac.yaml ├── ingress-internal.yaml └── kubectl-helm-commands.md ├── k8s ├── README.md ├── aksDeploy.json ├── aksParams.json ├── helm-rbac.yaml ├── ingress-internal.yaml └── kubectl-helm-commands.md ├── keyVault ├── PoSH │ ├── createKeyVault.ps1 │ └── generateSecret.ps1 ├── README.md └── armTemplate │ ├── createKeyVault.json │ └── createKeyVaultParams.json └── setup ├── README.md ├── envSetup.md └── generateSshKeys.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## CSA Miscellaneous Utilities 2 | 3 | This repo contains a variety of small projects, utilities, and documents. Naming conventions: 4 | * "doc-": A help article or how-to 5 | * "psh-": A PowerShell script 6 | * "util-": A small utility project 7 | * "sample-": A small sample or PoC 8 | * "sa-": A solution accelerator project 9 | 10 | ## Contributing 11 | 12 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 13 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 14 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 15 | 16 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 17 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 18 | provided by the bot. You will only need to do this once across all repos using our CLA. 19 | 20 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 21 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 22 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 23 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /doc-azure-resrouces-dedicated-subnet/README.md: -------------------------------------------------------------------------------- 1 |

List of Azure Services that require a dedicated subnet

2 | The following services can be deployed on a VNet but require a Subnet that cannot have any other Azure resource consiming the subnet private IPs. The minimum size for a Subnet in Azure is /29 which provides 8 IP Addresses. However, Azure reserves some IP addresses within each subnet. The first and last IP addresses of each subnet are reserved for protocol conformance, along with the x.x.x.1-x.x.x.3 addresses of each subnet, which are used for Azure services.

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 47 | 48 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
ServiceSubnet SizeDocumentation
VPN and ExpressRoute Gateways/27https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-vpn-faq 13 | https://docs.microsoft.com/en-us/azure/expressroute/expressroute-howto-add-gateway-portal-resource-manager
App Gateway/28This size gives you 11 usable IP addresses. If your application load requires more than 10 IP addresses, consider a /27 or /26 subnet size. 19 | https://docs.microsoft.com/en-us/azure/application-gateway/configuration-overview#size-of-the-subnet
Azure Firewall/26https://docs.microsoft.com/en-us/azure/firewall/tutorial-firewall-deploy-portal 25 |
App Service Environment/24https://docs.microsoft.com/en-us/azure/app-service/environment/network-info
Redis Cache/27Each Redis instance in the subnet uses two IP addresses per shard and one additional IP address for the load balancer. A non-clustered cache is considered to have one shard. 36 | https://docs.microsoft.com/en-us/azure/azure-cache-for-redis/cache-how-to-premium-vnet 37 |
API Management/27https://docs.microsoft.com/en-us/azure/api-management/api-management-faq
SQL Server Managed Instance 46 | /27https://docs.microsoft.com/en-us/azure/sql-database/sql-database-managed-instance-connectivity-architecture#network-requirements 49 |
Integration Service Environment/27https://docs.microsoft.com/en-us/azure/logic-apps/connect-virtual-network-vnet-isolated-environment#create-subnet
57 | -------------------------------------------------------------------------------- /doc-azure-training/Data_and_AI_Training.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/Data_and_AI_Training.md -------------------------------------------------------------------------------- /doc-azure-training/IoT_Training.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/IoT_Training.md -------------------------------------------------------------------------------- /doc-azure-training/MicrosoftLearn.md: -------------------------------------------------------------------------------- 1 | 2 | ### Microsoft Learn 3 | 4 | Welcome to the Microsoft Learn Step by Step guide to getting started. We are still working on the steps. Hang tight. We should be done very soon. 5 | 6 | In the mean time look around the rest of the pages. 7 | 8 | - [Data and AI Training](./Data_and_AI_Training.md#) 9 | - [Microsoft AI School](./Data_and_AI_Training.md#aischool) 10 | - [Cloud AI Bootcamp](./Data_and_AI_Training.md#learnaibootcamp) 11 | - [IoT Training](./IoT_Training.md#iottraining) 12 | - [Microsoft IoT School](./IoT_Training.md#iotschool) 13 | - [Other Learning Resources](./OtherResources.md#) 14 | -------------------------------------------------------------------------------- /doc-azure-training/OtherResources.md: -------------------------------------------------------------------------------- 1 | ## Other Learning Resources 2 | 3 | ### Quick Start Azure Training 4 | 5 | ### Azure Essentials (Pluralsight) 6 | 7 | Link to [Azure Essentials](https://aka.ms/Hvyrqt) 8 | 9 | #### Description 10 | 11 | The simplest way to learn Azure 12 | - Watch, Learn, and Try with Microsoft Azure Essentials 13 | - Jump start your Azure learning. With Azure Essentials, you can: 14 | - Learn Azure technologies in under an hour 15 | - Access free Pluralsight courses and Quickstarts 16 | - Track your learning progress 17 | - Master the skills you need for cloud roles 18 | 19 | ### Channel 9 Shows 20 | 21 | ### Azure Friday 22 | 23 | Link to [Azure Friday](https://channel9.msdn.com/Shows/Azure-Friday) 24 | 25 | #### Description 26 | 27 | This is the longest running video blog on Azure and has a very large audience. It's great for "what's new and cool" kind of content. 28 | 29 | ### Microsoft Youtube Channels 30 | 31 | ### Cloud Simplified 32 | 33 | Link to [Cloud Simplfied](https://www.youtube.com/channel/UCwDoMq6rNGsU4aIQYhCnWpA) 34 | 35 | ##### Description From Channel 36 | 37 | So why another site devoted to cloud computing in Azure? There are many dedicated sites, YouTube channels and blogs devoted to specific capabilities in Azure such as using Premium Storage with your VMs to allow them to have higher throughput and using vnet peering to connect two virtual networks in Azure but we decided to take a different approach — focusing on _how_ to use these features to practically solve business problems! 38 | 39 | In our role at Microsoft as Cloud Solution Architects (CSA) serving customers all across the US, we have a unique perspective on what customers need to accomplish in the cloud and we work to help them complete their journey successfully meeting their performance, budgetary and reliability requirements. We decided to take that tribal knowledge and provide meaningful content to the community on how we addressed specific requirements to bring workloads to the cloud after all, Knowledge is Power! 40 | 41 | 42 | ### Guy in a Cube 43 | 44 | Link to [Guy in a Cube](https://www.youtube.com/channel/UCFp1vaKzpfvoGai0vE5VJ0w) 45 | 46 | ##### Description From Channel 47 | 48 | Guy in a Cube is all about helping you master business analytics on the Microsoft Business analytics stack to allow you to drive business growth. We are just two guys that do the work. 49 | 50 | We look at how to leverage Microsoft Business Analytics to allow you to gain knowledge that is needed to shape the data your business cares about. This includes Power BI, Reporting Services, Analysis Services and Excel. If you work with our business analytics products or services, be sure to subscribe and join in the discussion with our weekly content. 51 | 52 | MONDAY: Information round up and occasional Q&A with folks in the organization. 53 | 54 | TUESDAY & WEDNESDAY: Tech videos relating to Power BI and other products and services. 55 | 56 | *** Adam Saxton and Patrick LeBlanc are Microsoft Employees *** 57 | -------------------------------------------------------------------------------- /doc-azure-training/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Azure Online Training 2 | 3 | __Contents__ 4 | 5 | - [Introduction](#intro) 6 | - [Microsoft Learn](./MicrosoftLearn.md#) 7 | - [Data and AI Training](./Data_and_AI_Training.md#) 8 | - [Microsoft AI School](./Data_and_AI_Training.md#aischool) 9 | - [Cloud AI Bootcamp](./Data_and_AI_Training.md#learnaibootcamp) 10 | - [IoT Training](./IoT_Training.md#iottraining) 11 | - [Microsoft IoT School](./IoT_Training.md#iotschool) 12 | - [Other Learning Resources](./OtherResources.md#) 13 | 14 | 15 | __Introduction__ 16 | 17 | Welcome to the updated "Getting Started with Azure Online Training" repo. This repo is designed to help you get started training on Azure fast. This is not a comprensive list. The courses listed here are what we see customer looking for as part of their digitial transformation. 18 | 19 | The prior version of this repo targeted Microsoft Academy. On January 31, 2019, Microsoft Academy will be shut down. 20 | 21 | The good news we have a much better replacement. [Microsoft Learn](https://docs.microsoft.com/en-us/learn/) 22 | 23 | [Microsoft Learn](https://docs.microsoft.com/en-us/learn/) fills in a couple of the gaps this repo and Microsoft Academy possessed. 24 | 1. __Learning Paths__. Learning Path are a pre-defined group of modules. If you are new to Azure or want to learn the basics we have an Azure 101. If you are looking to build a MEAN stack or bot we have paths for both. 25 | 2. __Role Based__ Micrsoft Learn also has role based learning paths. This has been requested by many customers for this site but was not something I was able to accomplish. Now Microsoft Learn has 5 roles for learning Azure. Each area is targeted to learning the services and configureations you may interact with in your role. 26 | 1. Administrator 27 | 2. Business Analyst 28 | 3. Business User 29 | 4. Developer 30 | 5. Solution Architect 31 | 32 | 3. __Hands-on__ Microsoft Learn has hands-on modules designed for trying something new in Azure. 33 | 34 | We still have the growing list of other learning and training resources. Take a look at our Data, IoT and Other pages for more information. 35 | 36 | Another new annoucement is Ask an Azure Advisor on Slack. This is a place where fellow Azure professional can interact. This is much like a 24/7 Virtual User Group. Visit https://askazure.io/ to get started. 37 | 38 | We encourage you to watch or star this repo as more content/ training offerings will be added overtime. Feel free to even fork this repo for your own organization's github. 39 | 40 | Let us know in the issues area if there are other trainings or content you would like to see. 41 | 42 | Finally, we do have a short URL for sharing this site with others: [aka.ms/AzureTrainingSites](https://aka.ms/AzureTrainingSites) 43 | 44 | 45 | 46 | __Disclaimer__ 47 | 48 | This document is to assist with getting started on Azure Online Training. This is not a comprehensive list of all Azure Trainings available. It is a subset for getting started quickly. This is also not an endorsement of any one vendor or partner. All course costs and Azure resourse costs are the responsibility of the person taking the course. 49 | -------------------------------------------------------------------------------- /doc-azure-training/files/image005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image005.png -------------------------------------------------------------------------------- /doc-azure-training/files/image006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image006.png -------------------------------------------------------------------------------- /doc-azure-training/files/image007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image007.png -------------------------------------------------------------------------------- /doc-azure-training/files/image008.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image008.jpg -------------------------------------------------------------------------------- /doc-azure-training/files/image009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image009.png -------------------------------------------------------------------------------- /doc-azure-training/files/image010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image010.png -------------------------------------------------------------------------------- /doc-azure-training/files/image011.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image011.jpg -------------------------------------------------------------------------------- /doc-azure-training/files/image012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image012.png -------------------------------------------------------------------------------- /doc-azure-training/files/image013.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image013.jpg -------------------------------------------------------------------------------- /doc-azure-training/files/image014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image014.png -------------------------------------------------------------------------------- /doc-azure-training/files/image015.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image015.jpg -------------------------------------------------------------------------------- /doc-azure-training/files/image016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image016.png -------------------------------------------------------------------------------- /doc-azure-training/files/image017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image017.png -------------------------------------------------------------------------------- /doc-azure-training/files/image018.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image018.jpg -------------------------------------------------------------------------------- /doc-azure-training/files/image019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image019.png -------------------------------------------------------------------------------- /doc-azure-training/files/image020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image020.png -------------------------------------------------------------------------------- /doc-azure-training/files/image021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image021.png -------------------------------------------------------------------------------- /doc-azure-training/files/image022.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image022.jpg -------------------------------------------------------------------------------- /doc-azure-training/files/image023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image023.png -------------------------------------------------------------------------------- /doc-azure-training/files/image024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image024.jpg -------------------------------------------------------------------------------- /doc-azure-training/files/image025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image025.png -------------------------------------------------------------------------------- /doc-azure-training/files/image026.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image026.jpg -------------------------------------------------------------------------------- /doc-azure-training/files/image027.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image027.png -------------------------------------------------------------------------------- /doc-azure-training/files/image028.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/doc-azure-training/files/image028.jpg -------------------------------------------------------------------------------- /psh-B2B-two-tenant-sync/B2BSync-MyVars.ps1: -------------------------------------------------------------------------------- 1 | #CREDS 2 | # objectIds 3 | $SourceTenantId = "[Replace with tenantid of source tenant 2]" 4 | $SourceGroupId = "[Replace with groupid of members to be synced from source tenant 2]" 5 | $DestTenantId = "[Replace with tenantid of destination tenant 1]" 6 | $DestGroupId = "[Replace with groupid of members that have been synced from source tenant 2 to this dest tenant]" 7 | 8 | # Create in Destination tenant - requires Azure AD configuration (refer to docs) 9 | $appID = "[appid that will be facilitating all of this - see readme]" 10 | $appSecret = "[secret for that appid]" 11 | $appReplyUrl = "[reply URL for that appid]" 12 | 13 | #if guest user is removed from the source group, also remove guest user from dest tenant? 14 | $RemoveGuest = $false 15 | 16 | $CustomOIDAttributeName = "CustomOID" -------------------------------------------------------------------------------- /psh-B2B-two-tenant-sync/B2BSync_DestSetup.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Configure DESTINATION site - one-time, add appropriate roles to the DEST site service principal 3 | MUST BE RUN AND LOGGED IN BY DESTINATION TENANT GLOBAL ADMIN 4 | See README.txt for details 5 | #IMPORTANT: edit "B2BSync-MyVars.ps1 and fill in your variables - see README 6 | #> 7 | 8 | # Dot-sourcing variables - update "B2BSync-Myvars.ps1 and use that file name 9 | . "$PSScriptRoot\B2BSync-MyVars.ps1" 10 | . "$PSScriptRoot\B2BSync_CustomAttributes.ps1" 11 | 12 | # Global Admin login to destination tenant 13 | Connect-AzureAD -TenantId $DestTenantId 14 | 15 | $RolesToEnable = @("Guest Inviter") 16 | $sp = Get-AzureADServicePrincipal -Filter "AppId eq `'$appID`'" 17 | 18 | foreach($rolename in $RolesToEnable){ 19 | Write-Host "Enabling role $Rolename for Service Principal $($sp.DisplayName)" 20 | $roleId = (Get-AzureADDirectoryRole | where { $_.DisplayName -eq $roleName }).ObjectId 21 | Add-AzureADDirectoryRoleMember -ObjectId $roleId -RefObjectId $sp.ObjectId 22 | } 23 | 24 | $aadapp = Get-AzureADApplication -Filter "DisplayName eq '$($sp.AppDisplayName)'" -ErrorAction Stop 25 | 26 | #add custom attribute to app and tenant 27 | New-AppExtAttrFromSettings -AppObjId $aadapp.ObjectId -------------------------------------------------------------------------------- /psh-B2B-two-tenant-sync/B2BSync_SourceSetup.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Configure SOURCE site - one-time, requires tenant admin to consent to the DEST site service principal 3 | See README.txt for details 4 | #IMPORTANT: edit "B2BSync-MyVars.ps1 and fill in your variables - see README 5 | #> 6 | 7 | # Dot-sourcing variables - update "B2BSync-Myvars.ps1 and use that file name 8 | . "$PSScriptRoot\B2BSync-MyVars.ps1" 9 | 10 | #consent URL (admin loads in browser) 11 | $consentUrl = "https://login.microsoftonline.com/$($SourceTenantId)/oauth2/authorize?response_type=id_token&client_id=$($appID)&redirect_uri=$appReplyUrl&response_mode=form_post&nonce=a4014117-28aa-47ec-abfb-f377be1d3cf6&resource=https://graph.windows.net&prompt=admin_consent" 12 | start $consentUrl 13 | 14 | Write-Host "If you successfully consented, you can now run B2BSync.ps1" 15 | 16 | -------------------------------------------------------------------------------- /psh-GetArmLimitsViaAPI/GetArmLimits.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | 4 | There are limits to the number of read/write operations that can be performed against the Azure Resource manager proviers in Azure. 5 | When this limit is reached there will be an HTTP 429 error returned. The documentation below outlines the specific REST call but 6 | does not provide a complete example 7 | 8 | https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-request-limits 9 | 10 | .DESCRIPTION 11 | 12 | This script creates the proper bearer token to invoke the REST API on the number of remaining Read Operations allowed against a specific 13 | subscription. The function Get-AzureCachedAccessToken provides the logic to pull the access token required to pass into the REST API 14 | 15 | #> 16 | 17 | 18 | function Get-AzureRmCachedAccessToken() 19 | { 20 | $ErrorActionPreference = 'Stop' 21 | 22 | if(-not (Get-Module AzureRm.Profile)) { 23 | Import-Module AzureRm.Profile 24 | } 25 | $azureRmProfileModuleVersion = (Get-Module AzureRm.Profile).Version 26 | # refactoring performed in AzureRm.Profile v3.0 or later 27 | if($azureRmProfileModuleVersion.Major -ge 3) { 28 | $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile 29 | if(-not $azureRmProfile.Accounts.Count) { 30 | Write-Error "Ensure you have logged in before calling this function." 31 | } 32 | } else { 33 | # AzureRm.Profile < v3.0 34 | $azureRmProfile = [Microsoft.WindowsAzure.Commands.Common.AzureRmProfileProvider]::Instance.Profile 35 | if(-not $azureRmProfile.Context.Account.Count) { 36 | Write-Error "Ensure you have logged in before calling this function." 37 | } 38 | } 39 | 40 | $currentAzureContext = Get-AzureRmContext 41 | $profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile) 42 | Write-Debug ("Getting access token for tenant" + $currentAzureContext.Subscription.TenantId) 43 | $token = $profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId) 44 | $token.AccessToken 45 | 46 | } 47 | 48 | 49 | Write-Host "Log in to your Azure subscription..." -ForegroundColor Green 50 | #Login-AzureRmAccount 51 | #Get-AzureRmSubscription -SubscriptionName $SubscriptionName | Select-AzureRmSubscription 52 | 53 | $token = Get-AzureRmCachedAccessToken 54 | $currentAzureContext = Get-AzureRmContext 55 | Write-Host ("Getting access ARM Throttle Limits for Subscription: " + $currentAzureContext.Subscription) 56 | 57 | 58 | $requestHeader = @{ 59 | "Authorization" = "Bearer " + $token 60 | "Content-Type" = "application/json" 61 | } 62 | 63 | $Uri = "https://management.azure.com/subscriptions/" + $currentAzureContext.Subscription + "/resourcegroups?api-version=2016-09-01" 64 | $r = Invoke-WebRequest -Uri $Uri -Method GET -Headers $requestHeader 65 | write-host("Remaining Read Operations: " + $r.Headers["x-ms-ratelimit-remaining-subscription-reads"]) -------------------------------------------------------------------------------- /psh-GetArmLimitsViaAPI/README.md: -------------------------------------------------------------------------------- 1 | ## Check ARM Limits for a Given Subscription 2 | 3 | There are limits to the number of read/write operations that can be performed against the Azure Resource manager providers in Azure. When this limit is reached there will be an HTTP 429 error returned. The documentation below outlines the specific REST call but does not provide a complete example. 4 | 5 | * [Details on Per-Subscription ARM Limits](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-request-limits). 6 | -------------------------------------------------------------------------------- /psh-ManagedDiskUtils/CopyDiskImage_verified.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | 4 | Managed disks in Azure have no direct facilitites to access the underlying URL/path the disk resides in since 5 | they are placed into storage accounts under the hood by Azure. Often times there's a desire to take a generalized VM 6 | image from one region and move to another region -- this script provides a mechanism to do this. 7 | 8 | .DESCRIPTION 9 | 10 | This script will require you to populate the source resource group and image name followed by details of a storage account 11 | existing or newly created storage account in the destination region to be used as an intermediate storage location. Once the disk 12 | is copied, we then create an image (Windows OS based in the script) pointed at the newly copied VHD 13 | 14 | This script takes advantage of the Grant-AzureRmDiskAccess provding a temporary SAS token to a managed disk. 15 | 16 | It does also require the specified container exist 17 | 18 | #> 19 | 20 | 21 | $sourceResourceGroupName='SourceResourceGroupName' 22 | $sourceImageName='NameOfSourceImageToCopy' 23 | 24 | $destconainer = 'containerindestinationstorageaccount' 25 | $destVHDName = 'destinationdisk.vhd' 26 | $destLocation = 'eastus' 27 | $destImageName = 'NewEmageEastUS' 28 | $destResourceGroup = 'DestResourceGroup' 29 | $destStorageAccount = 'destStorageAccountName' 30 | $destStorageAccountKey ='key' 31 | 32 | #Validation to ensure container name is lower case 33 | $destconainer = $destconainer.ToLower() 34 | 35 | $sas = Grant-AzureRmDiskAccess -ResourceGroupName $sourceResourceGroupName -DiskName $sourceImageName -DurationInSecond 3600 -Access Read 36 | $destContext = New-AzureStorageContext –StorageAccountName $destStorageAccount -StorageAccountKey $destStorageAccountKey 37 | Start-AzureStorageBlobCopy -AbsoluteUri $sas.AccessSAS -DestContainer $destconainer -DestContext $destContext -DestBlob $destVHDName -ConcurrentTaskCount 150 38 | Get-AzureStorageBlobCopyState -Container $destconainer -Blob $destVHDName -Context $destContext -WaitForComplete 39 | 40 | #build URI to new VHD 41 | $VHDPath=$destContext.StorageAccount.BlobStorageUri.PrimaryUri.AbsoluteUri 42 | $VHDPath=$VHDPath + $destconainer + "/" + $destVHDName 43 | 44 | #create image in destination based on copied VHD 45 | $imageConfig = New-AzureRmImageConfig -Location $destLocation 46 | $imageConfig = Set-AzureRmImageOsDisk -Image $imageConfig -OsType Windows -OsState Generalized -BlobUri $VHDPath 47 | $image = New-AzureRmImage -ImageName $destImageName -ResourceGroupName $destResourceGroup -Image $imageConfig 48 | 49 | #clean up the copied VHD 50 | Remove-AzureStorageBlob -Blob $destVHDName -Container $destconainer -Context $destContext 51 | 52 | -------------------------------------------------------------------------------- /psh-ManagedDiskUtils/README.md: -------------------------------------------------------------------------------- 1 | # Azure Managed Disk and Image PowerShell Utilities 2 | 3 | This is a collection of various managed disk PowerShell scripts to perform actions in an automated manner. 4 | 5 | * [Copy Managed Image to Another Region](./CopyDiskImage_verified.ps1). 6 | Managed images in Azure have no direct facilities to access the underlying URL/path the disk resides in since they are placed into storage accounts under the hood by Azure. Often times there's a desire to take a generalized VM (image) from one region and move to another region. This script provides a mechanism to do this - UPDATE! Azure now has a native means to do this via a feature called [Shared Image Gallery](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/shared-image-galleries). The benefit of this feature is that Microsoft will copy the images to your target regions automatically allowing you to build a 'private' marketplace of sorts 7 | 8 | * [Copy Managed Disk to Another Region](./ManagedDiskCopy.ps1). 9 | Managed disks in Azure have no direct facilities to access the underlying URL/path the disk resides in since they are placed into storage accounts under the hood by Azure. Often times there's a desire to take a managed disk attached to a VM from one region and move to another region where you can create a NEW VM and attach the copied disk effectively cloning the VM int the destination region. This script provides a mechanism to do this outside of native features in Azure that allow you to move resources from one region to another (requiring the entire virtual network and related VMs be moved as well) or using the Azure Site Recovery feature to replicate to another region and fail-over. This is a simple means to copy raw disks to a destination region of your choice. 10 | 11 | * [Remove Orphaned VHDs](./RemoveOrphanedDisks.ps1). 12 | Managed disks linger when the source VM is removed in the event you wish to attach them to another VM. This PowerShell script will walk through storage accounts in the subscription specified or the susbcription(s) you have access to locating any disks that are not attached to a VM 13 | -------------------------------------------------------------------------------- /psh-RBAC-CopyPaste/CreateDestSPs.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | IMPORTANT: this script doesn't attempt to replicate any API permissions, reply urls, or any other attributes of the 3 | application. It only recreates it in the destination tenant using the same name. If other settings are required, 4 | you'll need to update what's happening in "CreateSP" or manually update the app after it's created by reviewing 5 | the settings of the source object. 6 | #> 7 | 8 | # Load settings 9 | $Settings = Get-Content -Path "$PSScriptRoot\settings.json" | ConvertFrom-Json 10 | . "$PSScriptRoot\Utils.ps1" 11 | 12 | # Login 13 | Clear-AzureRmContext -Force 14 | $ctx = Connect-AzureRmAccount ` 15 | -Force ` 16 | -Tenant $Settings.DestTenantID ` 17 | -ErrorAction Stop 18 | LoginToAAD 19 | 20 | # Load source SPs 21 | $SourceSPs = Get-Content -Path "$($PSScriptRoot)\SourceSPs.json" | ConvertFrom-Json 22 | 23 | $newSPList = @() 24 | foreach($sp in $SourceSPs) { 25 | $servicePrincipal = CreateSP ` 26 | -DisplayName $sp.AppDisplayName ` 27 | -Tenant $settings.DestTenant 28 | 29 | $newSPList += @{ 30 | "AppId" = $servicePrincipal.App.AppId 31 | "DisplayName" = $servicePrincipal.App.DisplayName 32 | "SPObjectId" = $servicePrincipal.SP.ObjectId 33 | "AppObjectId" = $servicePrincipal.App.ObjectId 34 | "ServicePrincipalNames" = $servicePrincipal.SP.ServicePrincipalNames 35 | "ClearPassword" = $servicePrincipal.ClearPW 36 | "SourceAppId" = $sp.AppId 37 | "SourceObjectId" = $sp.ObjectId 38 | } 39 | $count++ 40 | } 41 | 42 | $newSPList | format-list 43 | $newSPList | ConvertTo-Json | Out-File "$($PSScriptRoot)\NewServicePrincipals.json" 44 | -------------------------------------------------------------------------------- /psh-RBAC-CopyPaste/ExtractRBAC.ps1: -------------------------------------------------------------------------------- 1 | # Load settings 2 | $Settings = Get-Content -Path "$PSScriptRoot\settings.json" | ConvertFrom-Json 3 | . "$PSScriptRoot\Utils.ps1" 4 | 5 | # Login 6 | Clear-AzureRmContext -Force 7 | $ctx = Connect-AzureRmAccount -Force ` 8 | -Tenant $Settings.SourceTenantId ` 9 | -Subscription $Settings.SubscriptionId ` 10 | -ErrorAction Stop 11 | LoginToAAD 12 | 13 | # Find currently assigned roles in the entire subscription 14 | $RoleAssignments = Get-AzureRmRoleAssignment 15 | 16 | # Get all user principals from the tenant 17 | $SourceUsers = Get-AzureADUser -All $true | Select ObjectId, ImmutableId, MailNickName, ObjectType, DisplayName, Mail, UserPrincipalName, UserType 18 | # Get all service principals from the tenant 19 | $SourceSPs = Get-AzureADServicePrincipal -All $true | Select ObjectId, ObjectType, DisplayName, AppDisplayName, AppId 20 | 21 | # Clean up the lists 22 | $AssignedSPs = @() 23 | $AssignedUsers = @() 24 | 25 | # Filter to find only the SPs that have RBAC assignments 26 | foreach($sp in $SourceSPs) { 27 | foreach($assigned in $RoleAssignments) { 28 | if ($sp.ObjectId -eq $assigned.ObjectId) { 29 | $hasRecord = $AssignedSps | Where { $_.ObjectId -eq $sp.ObjectId } 30 | if (!$hasRecord) { 31 | $AssignedSPs += $sp 32 | } 33 | } 34 | } 35 | } 36 | 37 | # IMPORTANT - verify that all DisplayNames are UNIQUE - it's all we have to key on 38 | $DupeTest = HasDuplicates -ArrayToTest $AssignedSPs 39 | if ($DupeTest.hasDupes) { 40 | Write-Warning $DupeTest.dupItems | format-list 41 | throw "Ensure that all service principles/applications to be migrated have unique display names" 42 | } 43 | 44 | # Filter to find only users that have RBAC assignments 45 | foreach($user in $SourceUsers) { 46 | foreach($assigned in $RoleAssignments) { 47 | if ($user.ObjectId -eq $assigned.ObjectId) { 48 | $hasRecord = $AssignedUsers | Where { $_.ObjectId -eq $user.ObjectId } 49 | if (!$hasRecord) { 50 | $AssignedUsers += $user 51 | } 52 | } 53 | } 54 | } 55 | 56 | # Write the lists to the file system for later import 57 | $AssignedSPs | ConvertTo-Json | Out-File "$($PSScriptRoot)\SourceSPs.json" 58 | $AssignedUsers | ConvertTo-Json | Out-File "$($PSScriptRoot)\SourceUsers.json" 59 | $RoleAssignments | ConvertTo-Json | Out-File "$($PSScriptRoot)\SourceRoleAssignments.json" 60 | -------------------------------------------------------------------------------- /psh-RBAC-CopyPaste/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "SubscriptionName": "[Subscription name]", 3 | "SubscriptionId": "[Subscription ID]", 4 | "SourceTenant": "[Source tenant name, like contoso.onmicrosoft.com]", 5 | "SourceTenantID": "[Source tenant ID]", 6 | "DestTenant": "[Destination tenant name, like fabrikam.onmicrosoft.com]", 7 | "DestTenantID": "[Destination tenant ID]" 8 | } 9 | 10 | -------------------------------------------------------------------------------- /psh-aad-b2b-batch-invite/B2BBatchPSH.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | A guest, with "Guest Inviter" privileges, may invite other users from her home tenant to the guest tenant 3 | https://docs.microsoft.com/en-us/azure/active-directory/b2b/delegate-invitations 4 | https://docs.microsoft.com/en-us/azure/active-directory/b2b/add-user-without-invite 5 | https://docs.microsoft.com/en-us/azure/active-directory/active-directory-b2b-redemption-experience 6 | 7 | The scenario here is a newly acquired company, Fabrikam, has it's own AAD tenant. Contoso, the new 8 | parent company, wants to add a number of Contoso users as guests in the Fabrikam tenant. A Contoso admin 9 | is added as a guest in Fabrikam, then that admin is granted the "Guest Inviter" role in Fabrikam. The 10 | admin then runs this script, authenticates to Contoso, then sets the focus to Fabrikam (guest tenant id), and 11 | invites an array of Contoso users to Fabrikam. Since the admin is inviting users from his own tenant into 12 | the tenant where he's a guest, those users are automatically added. As the new guests don't need to 13 | consent to the invitation, no invitation email will be sent. 14 | #> 15 | 16 | $guestTenantId = "[acquired company tenant, eg fabrikam.com]" 17 | 18 | #sign in using your home (HQ, acquiring company, like Contoso) credentials, but focused on the tenant where you're a guest 19 | Connect-AzureAD -TenantId $guestTenantId 20 | 21 | #list of users from your home tenant that you want to be automatically invited to this tenant 22 | $userList = @("bob@contoso.com","mary@contoso.com","jane@contoso.onmicrosoft.com") 23 | 24 | #optional - load list of users from a CSV file 25 | #$userList = (get-content "c:\temp\userlist.csv").Split(',') 26 | 27 | #spin through these users and send each an invitation 28 | ForEach($user in $userList) { 29 | AzureAD\New-AzureADMSInvitation -InvitedUserEmailAddress $user -SendInvitationMessage $false -InviteRedirectUrl "https://myapps.microsoft.com/$guestTenantId" 30 | } 31 | -------------------------------------------------------------------------------- /psh-aad-b2b-batch-invite/README.md: -------------------------------------------------------------------------------- 1 | A guest, with "Guest Inviter" privileges, may invite other users from her home tenant to the guest tenant. 2 | https://docs.microsoft.com/en-us/azure/active-directory/b2b/delegate-invitations 3 | https://docs.microsoft.com/en-us/azure/active-directory/b2b/add-user-without-invite 4 | https://docs.microsoft.com/en-us/azure/active-directory/active-directory-b2b-redemption-experience 5 | 6 | The scenario here is a newly acquired company, Fabrikam, has it's own AAD tenant. Contoso, the new 7 | parent company, wants to add a number of Contoso users as guests in the Fabrikam tenant. A Contoso admin 8 | is added as a guest in Fabrikam, then that admin is granted the "Guest Inviter" role in Fabrikam. The 9 | admin then runs this script, authenticates to Contoso, then sets the focus to Fabrikam (guest tenant id), and 10 | invites an array of Contoso users to Fabrikam. Since the admin is inviting users from his own tenant into 11 | the tenant where he's a guest, those users are automatically added. As the new guests don't need to 12 | consent to the invitation, no invitation email will be sent. 13 | -------------------------------------------------------------------------------- /psh-b2c-custom-attributes/README.md: -------------------------------------------------------------------------------- 1 | ## Work With B2C Custom Attributes via REST 2 | 3 | When you create a custom attribute in Azure AD B2C, like "ShoeSize", it gets saved with a funky long name. This script includes 4 | a REST call that will retrieve the collection of custom attributes and store them in memory, then refer to that list to 5 | create the correct REST url for updating that attribute. 6 | 7 | You'll need to have your access token populated in $token, and put your tenant name in $Tenant. There are sample calls commented at the bottom of the file. 8 | 9 | NOTE: This is using the Azure AD Graph API. 10 |
11 | Here’s the REST reference: 12 | https://msdn.microsoft.com/en-us/library/azure/ad/graph/api/functions-and-actions#getAvailableExtensionProperties 13 | 14 | -------------------------------------------------------------------------------- /psh-exo-encrypted-mailsend-script/Files/mail-flow-rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/psh-exo-encrypted-mailsend-script/Files/mail-flow-rules.png -------------------------------------------------------------------------------- /psh-exo-encrypted-mailsend-script/README.md: -------------------------------------------------------------------------------- 1 | ## Sending Encrypted Mail From a Script 2 | 3 | Eventually, you're going to want to send an email from a script. This is really straightforward using Exchange Online and the Microsoft Graph API. But what if you want those emails to be encrypted? Sometimes, log information may be put in an email, and there may be something sensitive in that log. 4 | 5 | Azure Information Protection enables encryption, and it's also easy. But how to activate it from a script? There’s no apparent way to specify encryption in the Graph call, but you CAN setup default rules as an Exchange admin, that apply to a given user account. So the API call is over TLS, and once it gets to EXO through Graph, EXO will encrypt it before storing it or sending it. 6 | 7 | There are two approaches to accomplishing this: 8 | 9 | * In both cases: 10 | 11 | 1. Create a new user account and assign it a mailbox in EXO (limit its permissions all you want, as long as it can send email) 12 | 13 | 2. Create an app registration. Get the AppID and create an app secret. 14 | 15 | * Case A – Sending on behalf of this user 16 | 17 | 1. Assign it Application permissions to the Microsoft Graph API, allowing it to send email impersonating anyone in the org (this requires GA approval) 18 | 19 | 2. The script authenticates as the application, using the client_credential grant type. It calls https://graph.microsoft.com/v1.0/users/{0}/sendMail, filling in the UPN of the sending account mailbox. 20 | 21 | * Case B – Sending AS the user 22 | 23 | 1. Assign it delegated permissions to the Microsoft Graph API, allowing it to send on behalf of the logged-in user (doesn’t require admin approval) 24 | 25 | 2. The script authenticates as the user, using the password grant type. It calls https://graph.microsoft.com/v1.0/me/sendMail 26 | 27 | Setting up the mail flow rules requires someone to access the Exchange Admin console. Here's an example of a resulting encryption rule: 28 | ![alt text](Files/mail-flow-rules.png) 29 | 30 |
31 | Here’s the REST reference: 32 | https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/user_sendmail 33 | 34 | Here’s the EXO mail flow encryption doc: 35 | https://docs.microsoft.com/en-us/office365/securitycompliance/define-mail-flow-rules-to-encrypt-email 36 | 37 | -------------------------------------------------------------------------------- /psh-exo-encrypted-mailsend-script/SendEXOMailTest2.ps1: -------------------------------------------------------------------------------- 1 | function getAuthHeader() { 2 | Param( 3 | [Parameter(Mandatory=$true, Position=0)] 4 | [string]$ClientID, 5 | [Parameter(Mandatory=$true, Position=1)] 6 | [string]$ClientKey, 7 | [Parameter(Mandatory=$true, Position=2)] 8 | [string]$TenantID 9 | ) 10 | 11 | $AADURI = "https://login.microsoftonline.com/$TenantID/oauth2/token" 12 | $GrantBody = "grant_type=client_credentials&client_id=$ClientID&client_secret=$ClientKey&resource=https://graph.microsoft.com" 13 | 14 | $AADTokenResponse = Invoke-RestMethod -Uri $AADURI -ContentType "application/x-www-form-urlencoded" -Body $GrantBody -Method Post 15 | return $AADTokenResponse.access_token 16 | } 17 | 18 | function SendMessage(){ 19 | Param( 20 | [Parameter(Mandatory=$true, Position=0)] 21 | [string]$Subject, 22 | [Parameter(Mandatory=$true, Position=1)] 23 | [string]$Body, 24 | [Parameter(Mandatory=$true, Position=2)] 25 | [string]$Recipient, 26 | [Parameter(Mandatory=$true, Position=3)] 27 | [string]$ClientID, 28 | [Parameter(Mandatory=$true, Position=4)] 29 | [string]$ClientKey, 30 | [Parameter(Mandatory=$true, Position=5)] 31 | [string]$TenantID, 32 | [Parameter(Mandatory=$true, Position=6)] 33 | [string]$SenderEmail 34 | ) 35 | 36 | $AADToken=getAuthHeader -ClientID $ClientID -ClientKey $ClientKey -TenantID $TenantID 37 | $Headers = @{Authorization = "Bearer $AADToken"} 38 | 39 | $message = @{ 40 | "message" = @{ 41 | "subject" = $Subject; 42 | "body" = @{ 43 | "contentType" = "text"; 44 | "content" = $Body; 45 | }; 46 | "toRecipients" = @( 47 | @{ 48 | "emailAddress" = @{ 49 | "address" = $Recipient; 50 | }; 51 | }; 52 | ); 53 | }; 54 | "savedToSentItems" = "false" 55 | } 56 | $message.message.toRecipients 57 | $body = ConvertTo-Json $message -Depth 5 58 | $SendMail="https://graph.microsoft.com/v1.0/users/{0}/sendMail" -f [uri]::EscapeDataString($senderAccountName) 59 | $res = Invoke-WebRequest -Uri $SendMail -Method Post -Headers $Headers -Body $body -ContentType "application/json" 60 | } 61 | 62 | #variables 63 | $SenderAccountName = "[Sending Account Email]" 64 | $ClientID = "[Azure AD App Registration]" 65 | $ClientKey = "[App Registration Secret]" 66 | $TenantID = "[Azure AD Tenant ID]" 67 | $Recipient = "[Email Recipient]" 68 | 69 | #execute 70 | SendMessage ` 71 | -Subject "Testing Encryption" ` 72 | -Body "Sending this from Powershell via EXO, using a service principal with app permissions to send behalf of, and specifying an email account I created in my demo O365 subscription." ` 73 | -Recipient $Recipient ` 74 | -ClientID $ClientID ` 75 | -ClientKey $ClientKey ` 76 | -TenantID $TenantID ` 77 | -SenderEmail $SenderAccountName 78 | 79 | -------------------------------------------------------------------------------- /psh-poison-queue/README.md: -------------------------------------------------------------------------------- 1 | Azure Storage Queue will move a failed queue entry to the poison queue after a set number of failures. 2 | Once the failure is corrected, it may be useful to move the items from the poison queue back into the main queue. 3 | This PowerShell script uses the Azure Storage SDK to make this easy - useful while developing a solution. -------------------------------------------------------------------------------- /psh-poison-queue/StorageQueueMovePoison.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Purpose: restore Azure Storage Queue messages from a poison queue back into a primary queue 3 | Date: 1/3/2019 4 | Author: Brett Hacker 5 | #> 6 | 7 | #Settings 8 | $connectionStr = "[Storage account connection string]" 9 | $destQueueName = "[queue name]" 10 | $srcQueueName = "$destQueueName-poison" 11 | 12 | #Path to the "Microsoft.WindowsAzure.Storage" dll (from NuGet) 13 | $storageLibPath = "[local-path-to]\Microsoft.WindowsAzure.Storage.dll" 14 | 15 | #Dot-sourced variable override (optional, comment out if not using) 16 | if (Test-Path "$($env:PSH_Settings_Files)StorageQueueMovePoison.ps1") { 17 | . "$($env:PSH_Settings_Files)StorageQueueMovePoison.ps1" 18 | } 19 | 20 | #do not alter below here 21 | [System.Reflection.Assembly]::LoadFrom($storageLibPath) | Out-Null 22 | $storageAccount = [Microsoft.WindowsAzure.Storage.CloudStorageAccount]::Parse($connectionStr); 23 | $client = $storageAccount.CreateCloudQueueClient(); 24 | $sourceQ = $client.GetQueueReference($srcQueueName) 25 | $destQ = $client.GetQueueReference($destQueueName) 26 | $count = 0 27 | $toProcess = [int]$sourceQ.ApproximateMessageCount 28 | $donepercent = 0 29 | 30 | while ($true) { 31 | $srcMsg = $sourceQ.GetMessage() 32 | if ($srcMsg -eq $null) { 33 | break; 34 | } 35 | $destMsg = [Microsoft.WindowsAzure.Storage.Queue.CloudQueueMessage]::new($srcMsg.AsString) 36 | $destQ.AddMessage($destMsg) 37 | $sourceQ.DeleteMessage($srcMsg) 38 | $count++ 39 | $donepercent = [int](($count / $toProcess) * 100) 40 | Write-Progress -Activity "Moving items..." -PercentComplete $donepercent -Status "$($donepercent)% complete ($count of $toProcess)" -ErrorAction Ignore 41 | } 42 | "" 43 | "$count messages restored" -------------------------------------------------------------------------------- /psh-sql-update-firewall/UpdateAllSqlFirewall.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Reset all accessible Azure SQL server firewall to allow access from local IP address. 3 | An AAD SP is used here and needs at least 'SQL SECURITY MANAGER' role for the managed SQL server instance 4 | 7/19/2018, Brett Hacker 5 | #> 6 | 7 | #Settings 8 | $appid = "[SP AppID]" 9 | $appSecret = '[SP secret]' 10 | $tenantId = "[SP tenant]" 11 | #default rule name 12 | $defaultRuleName="[Name of your default rule, like 'HomeLab']" 13 | 14 | #dot-source local file to override variables above 15 | if (test-path 'C:\users\brhacke\OneDrive - Microsoft\Documents\WindowsPowerShell\UpdateAllSqlFirewall-mySettings.ps1') { 16 | . 'C:\users\brhacke\OneDrive - Microsoft\Documents\WindowsPowerShell\UpdateAllSqlFirewall-mySettings.ps1' 17 | } 18 | 19 | #----------------------- 20 | #authenticate SP 21 | $SecPw = ConvertTo-SecureString $appSecret -AsPlainText -Force -ErrorAction Stop 22 | $cred = New-Object PSCredential ($appid, $SecPw) 23 | Connect-AzureRmAccount -ServicePrincipal -Credential $cred -TenantId $tenantId 24 | 25 | #get list of subscriptions for this SP 26 | $subs = (Get-AzureRmSubscription).Name 27 | 28 | #Get current Local IP (free site I'm hosting) 29 | $web = New-Object Net.WebClient 30 | $MyIp = $web.DownloadString("http://pingip.azurewebsites.net/") 31 | $web.Dispose() 32 | 33 | #loop the subs 34 | foreach($sub in $subs) { 35 | Write-Output "Updating SQL Servers in $sub..." 36 | 37 | Set-AzureRMContext -Subscription "$sub" 38 | $servers = Get-AzureRmSqlServer 39 | foreach($server in $servers) { 40 | Write-Output "Updating $server..." 41 | 42 | $rules = Get-AzureRmSqlServerFirewallRule ` 43 | -ResourceGroupName $server.ResourceGroupName ` 44 | -ServerName $server.ServerName 45 | 46 | if (($rules | where { $_.FirewallRuleName -eq $defaultRuleName }).length -eq 0) { 47 | Write-Output "Adding $defaultRuleName rule..." 48 | New-AzureRmSqlServerFirewallRule ` 49 | -EndIpAddress $MyIp ` 50 | -FirewallRuleName $defaultRuleName ` 51 | -ResourceGroupName $server.ResourceGroupName ` 52 | -ServerName $server.ServerName ` 53 | -StartIpAddress $MyIp 54 | } else { 55 | Write-Output "Updating $defaultRuleName rule..." 56 | Set-AzureRmSqlServerFirewallRule ` 57 | -EndIpAddress $MyIp ` 58 | -FirewallRuleName $defaultRuleName ` 59 | -ResourceGroupName $server.ResourceGroupName ` 60 | -ServerName $server.ServerName ` 61 | -StartIpAddress $MyIp 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /psh-sql-update-firewall/readme.md: -------------------------------------------------------------------------------- 1 | My ISP has kept my home IP pretty stable until it reset it 3 times in the last month. I'd already written a script to check and update my S2S VPN gateway IP, but I kept getting knocked out of connections to Azure SQL. So I wrote this script and added a call to it from my other scheduled job. It gets the public IP from wherever you're running it, then uses a service principal that has rights to SQL servers - in one or more subscriptions in the same tenant - spins through each subscription, finds all SQL Servers, gets all firewall rules, checks to see if a matching rule exists and updates or creates it with the new IP address. Peachy! 2 | -------------------------------------------------------------------------------- /sa-dsml-many-models/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 James Nguyen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sa-dsml-many-models/code/aml_prs/Data_Preparation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [] 9 | } 10 | ], 11 | "metadata": { 12 | "interpreter": { 13 | "hash": "f7f364c9551711cd4699acda32e0312c3edab483ae246bf330de758088cecccb" 14 | }, 15 | "kernelspec": { 16 | "display_name": "Python 3.8.5 64-bit ('dlresearch': conda)", 17 | "name": "python3" 18 | }, 19 | "language_info": { 20 | "name": "python", 21 | "version": "" 22 | }, 23 | "orig_nbformat": 4 24 | }, 25 | "nbformat": 4, 26 | "nbformat_minor": 2 27 | } -------------------------------------------------------------------------------- /sa-dsml-many-models/code/aml_prs/model_train.py: -------------------------------------------------------------------------------- 1 | # Licensed under the MIT license. 2 | 3 | import os 4 | from azureml.core import Run, Model 5 | import joblib 6 | from util.timeseries_utilities import ColumnDropper, SimpleLagger, SimpleCalendarFeaturizer, SimpleForecaster 7 | from sklearn.metrics import mean_squared_error, mean_absolute_error 8 | from sklearn.linear_model import LinearRegression 9 | import numpy as np 10 | import pandas as pd 11 | def init(): 12 | global ws 13 | current_run = Run.get_context() 14 | ws = current_run.experiment.workspace 15 | 16 | print("Init complete") 17 | 18 | 19 | def run(mini_batch): 20 | print(f'run method start: {__file__}, run({mini_batch})') 21 | target_column= 'Quantity' 22 | timestamp_column= 'WeekStarting' 23 | drop_columns=['Revenue', 'Store', 'Brand'] 24 | #Get the store and brand. They are unique from the group so just the first value is sufficient 25 | store = str(mini_batch['Store'].iloc[0]) 26 | brand = str(mini_batch['Brand'].iloc[0]) 27 | 28 | model_name="prs_"+store+"_"+brand 29 | test_size=20 30 | # 1.0 Format the input data from group by, put the time in the index 31 | data = mini_batch \ 32 | .set_index('WeekStarting') \ 33 | .sort_index(ascending=True) 34 | 35 | # 2.0 Split the data into train and test sets 36 | train = data[:-test_size] 37 | test = data[-test_size:] 38 | 39 | # 3.0 Create and fit the forecasting pipeline 40 | # The pipeline will drop unhelpful features, make a calendar feature, and make lag features 41 | lagger = SimpleLagger(target_column, lag_orders=[1, 2, 3, 4]) 42 | transform_steps = [('column_dropper', ColumnDropper(drop_columns)), 43 | ('calendar_featurizer', SimpleCalendarFeaturizer()), ('lagger', lagger)] 44 | forecaster = SimpleForecaster(transform_steps, LinearRegression(), target_column, timestamp_column) 45 | forecaster.fit(train) 46 | 47 | # 4.0 Get predictions on test set 48 | forecasts = forecaster.forecast(test) 49 | compare_data = test.assign(forecasts=forecasts).dropna() 50 | 51 | # 5.0 Calculate accuracy metrics for the fit 52 | mse = mean_squared_error(compare_data[target_column], compare_data['forecasts']) 53 | rmse = np.sqrt(mse) 54 | mae = mean_absolute_error(compare_data[target_column], compare_data['forecasts']) 55 | actuals = compare_data[target_column].values 56 | preds = compare_data['forecasts'].values 57 | mape = np.mean(np.abs((actuals - preds) / actuals) * 100) 58 | 59 | # 7.0 Train model with full dataset 60 | forecaster.fit(data) 61 | 62 | # 8.0 Save the pipeline and register model to AML 63 | joblib.dump(forecaster, model_name)# 64 | model = Model.register(workspace=ws, model_name=model_name, model_path=model_name, tags={'mse':str(mse), 'mape': str(mape), 'rmse': str(rmse)}) 65 | result =pd.DataFrame({'Store':[store],'Brand':[brand], 'mse':[mse], 'mape': [mape], 'rmse': [rmse], 'model_name':[model_name]}) 66 | 67 | return result 68 | 69 | -------------------------------------------------------------------------------- /sa-dsml-many-models/code/aml_prs/prediction.py: -------------------------------------------------------------------------------- 1 | # Licensed under the MIT license. 2 | 3 | import os 4 | from azureml.core import Run, Model 5 | import joblib 6 | import util.timeseries_utilities 7 | 8 | def init(): 9 | global ws 10 | current_run = Run.get_context() 11 | ws = current_run.experiment.workspace 12 | 13 | print("Init complete") 14 | 15 | 16 | def run(mini_batch): 17 | print(f'run method start: {__file__}, run({mini_batch})') 18 | 19 | timestamp_column= 'WeekStarting' 20 | 21 | timeseries_id_columns= [ 'Store', 'Brand'] 22 | data = mini_batch \ 23 | .set_index(timestamp_column) \ 24 | .sort_index(ascending=True) 25 | #Prepare loading model from Azure ML, get the latest model by default 26 | model_name="prs_"+str(data['Store'].iloc[0])+"_"+str(data['Brand'].iloc[0]) 27 | model = Model(ws, model_name) 28 | model.download(exist_ok =True) 29 | forecaster = joblib.load(model_name) 30 | 31 | # Get predictions 32 | #This is to append the store and brand column to the result 33 | ts_id_dict = {id_col: str(data[id_col].iloc[0]) for id_col in timeseries_id_columns} 34 | forecasts=forecaster.forecast(data) 35 | prediction_df = forecasts.to_frame(name='Prediction') 36 | prediction_df =prediction_df.reset_index().assign(**ts_id_dict) 37 | 38 | 39 | return prediction_df 40 | -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/1-sai-create-endpoint.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://azuremlsdk2.blob.core.windows.net/latest/managedOnlineEndpoint.schema.json 2 | name: my-sai-endpoint 3 | type: online 4 | auth_mode: key 5 | traffic: 6 | blue: 80 7 | 8 | deployments: 9 | #blue deployment 10 | - name: blue 11 | code_configuration: 12 | code: 13 | local_path: ../ 14 | scoring_script: deployment/score.py 15 | environment: 16 | name: many_models_environment 17 | version: 2 18 | path: . 19 | conda_file: file:./conda.yml 20 | docker: 21 | image: mcr.microsoft.com/azureml/openmpi3.1.2-ubuntu18.04:20210301.v1 22 | 23 | instance_type: Standard_F2s_v2 24 | scale_settings: 25 | scale_type: manual 26 | instance_count: 1 27 | min_instances: 1 28 | max_instances: 2 -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/azureml_cli.txt: -------------------------------------------------------------------------------- 1 | 2 | az account set --subscription 0e9bace8-7a81-4922-83b5-d995ff706507 3 | az configure --defaults workspace=ws01ent group=azureml 4 | 5 | set ENDPOINT_NAME=many_model 6 | set WORKSPACE=ws01ent 7 | set LOCATION=westus2 8 | set ENDPOINT_NAME=many-model-sal 9 | az ml endpoint create --name %ENDPOINT_NAME% -f 1-sai-create-endpoint.yml 10 | 11 | az ml endpoint show --name %ENDPOINT_NAME% 12 | SET system_identity=8c7f9ce7-3ca3-4c56-bbfc-1746e0409564 13 | az ml endpoint show --name %ENDPOINT_NAME% --query "identity.principal_id" -o tsv 14 | set WS_ID=/subscriptions/0e9bace8-7a81-4922-83b5-d995ff706507/resourceGroups/azureml/providers/Microsoft.MachineLearningServices/workspaces/ws01ent 15 | az role assignment create --assignee %system_identity% --role "Reader" --scope %WS_ID% 16 | 17 | 18 | az ml endpoint update -n %ENDPOINT_NAME% -f 1-sai-create-endpoint.yml 19 | 20 | #Creating a custom role 21 | az role definition update --role-definition custom_role.json --subscription 0e9bace8-7a81-4922-83b5-d995ff706507 22 | 23 | az role definition list --subscription 0e9bace8-7a81-4922-83b5-d995ff706507 --custom-role-only true 24 | az role assignment create --assignee %system_identity% --role "AML SAL" --scope %WS_ID% -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/conda.yml: -------------------------------------------------------------------------------- 1 | name: many_models_environment 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.7 6 | - numpy 7 | - pip 8 | - scikit-learn==0.24.2 9 | - pandas 10 | - pip: 11 | - azureml-defaults 12 | - inference-schema[numpy-support] 13 | - joblib 14 | - numpy 15 | - adal 16 | -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/custom_role.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "AML SAL", 3 | "IsCustom": true, 4 | "Description": "Can obtain scoring token.", 5 | "Actions": [ 6 | 7 | "Microsoft.MachineLearningServices/workspaces/onlineEndpoints/score/action", 8 | "Microsoft.MachineLearningServices/workspaces/onlineEndpoints/token/action", 9 | "Microsoft.MachineLearningServices/workspaces/onlineEndpoints/listkeys/action" 10 | ], 11 | "NotActions": [ 12 | "Microsoft.MachineLearningServices/workspaces/*/delete", 13 | "Microsoft.MachineLearningServices/workspaces/write", 14 | "Microsoft.MachineLearningServices/workspaces/computes/*/write", 15 | "Microsoft.MachineLearningServices/workspaces/computes/*/delete", 16 | "Microsoft.Authorization/*/write" 17 | 18 | ], 19 | "AssignableScopes": [ 20 | "/subscriptions/0e9bace8-7a81-4922-83b5-d995ff706507/resourceGroups/azureml", 21 | "/subscriptions/0e9bace8-7a81-4922-83b5-d995ff706507/resourceGroups/azureml/providers/Microsoft.MachineLearningServices/workspaces/ws01ent" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/main_deployment.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "source": [ 6 | "Follow the steps in https://docs.microsoft.com/en-us/azure/machine-learning/how-to-deploy-managed-online-endpoints to deploy the scoring function to Azure.\r\n", 7 | "This scoring is on demand, i.e. it only loads the model when the client requests." 8 | ], 9 | "metadata": {} 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "source": [ 15 | "import urllib.request\r\n", 16 | "import requests\r\n", 17 | "import pandas as pd\r\n", 18 | "import json\r\n", 19 | "for file_name in ['test_data_1000_dominicks.csv','test_data_1002_tropicana.csv']:\r\n", 20 | " sample_data= pd.read_csv(file_name)\r\n", 21 | " sample_data.drop(['Unnamed: 0'], axis=1, inplace=True)\r\n", 22 | " #Use the below version of URL and header in case you test with remote web service (AKS)\r\n", 23 | " url ='https://many-model.westus2.inference.ml.azure.com/score'\r\n", 24 | " api_key = 'K8It9Dq12BpQu8ryt6VRXZB1AmCzfsZu' # Replace this with the API key for the web service\r\n", 25 | " headers = {'Content-Type':'application/json', 'Authorization':('Bearer '+ api_key)}\r\n", 26 | "\r\n", 27 | "\r\n", 28 | "\r\n", 29 | " data = {\"Inputs\":sample_data.to_json() }\r\n", 30 | " body = str.encode(json.dumps(data))\r\n", 31 | " resp = requests.post(url, data=body, headers=headers)\r\n", 32 | " print(resp.text)\r\n" 33 | ], 34 | "outputs": [ 35 | { 36 | "output_type": "stream", 37 | "name": "stderr", 38 | "text": [ 39 | "C:\\Users\\janguy\\Anaconda3\\envs\\dlresearch\\lib\\site-packages\\numpy\\_distributor_init.py:30: UserWarning: loaded more than 1 DLL from .libs:\n", 40 | "C:\\Users\\janguy\\Anaconda3\\envs\\dlresearch\\lib\\site-packages\\numpy\\.libs\\libopenblas.NOIJJG62EMASZI6NYURL6JBKM4EVBGM7.gfortran-win_amd64.dll\n", 41 | "C:\\Users\\janguy\\Anaconda3\\envs\\dlresearch\\lib\\site-packages\\numpy\\.libs\\libopenblas.PYQHXLVVQ7VESDPUVUADXEVJOBGHJPAY.gfortran-win_amd64.dll\n", 42 | " warnings.warn(\"loaded more than 1 DLL from .libs:\\n%s\" %\n" 43 | ] 44 | }, 45 | { 46 | "output_type": "stream", 47 | "name": "stdout", 48 | "text": [ 49 | "key_auth_access_denied\n", 50 | "key_auth_access_denied\n" 51 | ] 52 | } 53 | ], 54 | "metadata": {} 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "source": [], 60 | "outputs": [], 61 | "metadata": {} 62 | } 63 | ], 64 | "metadata": { 65 | "interpreter": { 66 | "hash": "f7f364c9551711cd4699acda32e0312c3edab483ae246bf330de758088cecccb" 67 | }, 68 | "kernelspec": { 69 | "name": "python3", 70 | "display_name": "Python 3.8.5 64-bit ('dlresearch': conda)" 71 | }, 72 | "language_info": { 73 | "codemirror_mode": { 74 | "name": "ipython", 75 | "version": 3 76 | }, 77 | "file_extension": ".py", 78 | "mimetype": "text/x-python", 79 | "name": "python", 80 | "nbconvert_exporter": "python", 81 | "pygments_lexer": "ipython3", 82 | "version": "3.8.5" 83 | }, 84 | "orig_nbformat": 4 85 | }, 86 | "nbformat": 4, 87 | "nbformat_minor": 2 88 | } -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/many_model.yml: -------------------------------------------------------------------------------- 1 | $schema: https://azuremlsdk2.blob.core.windows.net/latest/managedOnlineEndpoint.schema.json 2 | name: my-endpoint 3 | type: online 4 | auth_mode: key 5 | traffic: 6 | blue: 80 7 | green:20 8 | 9 | deployments: 10 | #blue deployment 11 | - name: blue 12 | code_configuration: 13 | code: 14 | local_path: ../ 15 | scoring_script: deployment/score.py 16 | environment: 17 | name: many_models_environment 18 | version: 2 19 | path: . 20 | conda_file: file:./conda.yml 21 | docker: 22 | image: mcr.microsoft.com/azureml/openmpi3.1.2-ubuntu18.04:20210301.v1 23 | 24 | instance_type: Standard_F2s_v2 25 | scale_settings: 26 | scale_type: manual 27 | instance_count: 1 28 | min_instances: 1 29 | max_instances: 2 -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/prs_1000_dominicks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/sa-dsml-many-models/code/deployment/prs_1000_dominicks -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/test_data.csv: -------------------------------------------------------------------------------- 1 | ,WeekStarting,Store,Brand,Quantity,Advert,Price,Revenue 2 | 0,1990-06-14,1000,dominicks,12003,1,2.59,31087.769999999997 3 | 1,1990-06-21,1000,dominicks,10239,1,2.39,24471.210000000003 4 | 2,1990-06-28,1000,dominicks,17917,1,2.48,44434.159999999996 5 | 3,1990-07-05,1000,dominicks,14218,1,2.33,33127.94 6 | 4,1990-07-12,1000,dominicks,15925,1,2.01,32009.249999999996 7 | 5,1990-07-19,1000,dominicks,17850,1,2.17,38734.5 8 | 6,1990-07-26,1000,dominicks,10576,1,1.97,20834.72 9 | 7,1990-08-02,1000,dominicks,9912,1,2.26,22401.12 10 | 8,1990-08-09,1000,dominicks,9571,1,2.11,20194.809999999998 11 | 9,1990-08-16,1000,dominicks,15748,1,2.42,38110.159999999996 12 | -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/test_data_1000_dominicks.csv: -------------------------------------------------------------------------------- 1 | ,WeekStarting,Store,Brand,Quantity,Advert,Price,Revenue 2 | 0,1990-06-14,1000,dominicks,12003,1,2.59,31087.769999999997 3 | 1,1990-06-21,1000,dominicks,10239,1,2.39,24471.210000000003 4 | 2,1990-06-28,1000,dominicks,17917,1,2.48,44434.159999999996 5 | 3,1990-07-05,1000,dominicks,14218,1,2.33,33127.94 6 | 4,1990-07-12,1000,dominicks,15925,1,2.01,32009.249999999996 7 | 5,1990-07-19,1000,dominicks,17850,1,2.17,38734.5 8 | 6,1990-07-26,1000,dominicks,10576,1,1.97,20834.72 9 | 7,1990-08-02,1000,dominicks,9912,1,2.26,22401.12 10 | 8,1990-08-09,1000,dominicks,9571,1,2.11,20194.809999999998 11 | 9,1990-08-16,1000,dominicks,15748,1,2.42,38110.159999999996 12 | -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/test_data_1002_tropicana.csv: -------------------------------------------------------------------------------- 1 | ,WeekStarting,Store,Brand,Quantity,Advert,Price,Revenue 2 | 0,1991-01-24,1002,tropicana,9715,1,2.16,20984.4 3 | 1,1991-01-31,1002,tropicana,18991,1,2.27,43109.57 4 | 2,1991-02-07,1002,tropicana,13250,1,2.42,32065.0 5 | 3,1991-02-14,1002,tropicana,11520,1,2.6,29952.0 6 | 4,1991-02-21,1002,tropicana,16200,1,2.09,33858.0 7 | 5,1991-02-28,1002,tropicana,19683,1,2.63,51766.29 8 | 6,1991-03-07,1002,tropicana,11195,1,2.21,24740.95 9 | 7,1991-03-14,1002,tropicana,16313,1,2.38,38824.939999999995 10 | 8,1991-03-21,1002,tropicana,9924,1,2.55,25306.199999999997 11 | 9,1991-03-28,1002,tropicana,11892,1,2.19,26043.48 12 | 10,1991-04-04,1002,tropicana,9783,1,2.59,25337.969999999998 13 | 11,1991-04-11,1002,tropicana,14064,1,2.06,28971.84 14 | 12,1991-04-18,1002,tropicana,12070,1,2.36,28485.199999999997 15 | 13,1991-04-25,1002,tropicana,12416,1,2.08,25825.280000000002 16 | 14,1991-05-02,1002,tropicana,11680,1,2.03,23710.399999999998 17 | 15,1991-05-09,1002,tropicana,17474,1,2.25,39316.5 18 | 16,1991-05-16,1002,tropicana,19523,1,2.02,39436.46 19 | 17,1991-05-23,1002,tropicana,14042,1,2.16,30330.72 20 | 18,1991-05-30,1002,tropicana,13203,1,2.6,34327.8 21 | 19,1991-06-06,1002,tropicana,13242,1,1.94,25689.48 22 | -------------------------------------------------------------------------------- /sa-dsml-many-models/code/deployment/test_deployment.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 159, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "upstream connect error or disconnect/reset before headers. reset reason: connection failure\n" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "import urllib.request\r\n", 18 | "import requests\r\n", 19 | "import pandas as pd\r\n", 20 | "import json\r\n", 21 | "for file_name in ['test_data_1000_dominicks.csv','test_data_1002_tropicana.csv']:\r\n", 22 | " sample_data= pd.read_csv(file_name)\r\n", 23 | " sample_data.drop(['Unnamed: 0'], axis=1, inplace=True)\r\n", 24 | " #Use the below version of URL and header in case you test with remote web service (AKS)\r\n", 25 | " url ='https://many-model2.westus2.inference.ml.azure.com/score'\r\n", 26 | " api_key = '9Qnjp0tSwMUrFJtMk4q8fqbDprq9fUsv' # Replace this with the API key for the web service\r\n", 27 | " headers = {'Content-Type':'application/json', 'Authorization':('Bearer '+ api_key)}\r\n", 28 | "\r\n", 29 | "\r\n", 30 | "\r\n", 31 | " data = {\"Inputs\":sample_data.to_json() }\r\n", 32 | " body = str.encode(json.dumps(data))\r\n", 33 | " resp = requests.post(url, data=body, headers=headers)\r\n", 34 | " print(resp.text)\r\n" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [] 43 | } 44 | ], 45 | "metadata": { 46 | "interpreter": { 47 | "hash": "01395c2bf5351d89929a188eaee14092f5abeb2f2ed0be057aa3ed6e35212c40" 48 | }, 49 | "kernelspec": { 50 | "display_name": "Python 3.6.12 64-bit ('mlflow-1a43b1fcadd2a32c00a1972c5464ff374bda3f6b': conda)", 51 | "name": "python3" 52 | }, 53 | "language_info": { 54 | "codemirror_mode": { 55 | "name": "ipython", 56 | "version": 3 57 | }, 58 | "file_extension": ".py", 59 | "mimetype": "text/x-python", 60 | "name": "python", 61 | "nbconvert_exporter": "python", 62 | "pygments_lexer": "ipython3", 63 | "version": "3.8.5" 64 | }, 65 | "orig_nbformat": 4 66 | }, 67 | "nbformat": 4, 68 | "nbformat_minor": 2 69 | } -------------------------------------------------------------------------------- /sa-dsml-many-models/images/aml_many_models_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/sa-dsml-many-models/images/aml_many_models_arch.png -------------------------------------------------------------------------------- /sa-dsml-many-models/images/pandas_udf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/sa-dsml-many-models/images/pandas_udf.png -------------------------------------------------------------------------------- /sa-dsml-many-models/images/spark_many_models_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/sa-dsml-many-models/images/spark_many_models_arch.png -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/AzurePolicy/audit/Audit-Managed-Disks.json: -------------------------------------------------------------------------------- 1 | { 2 | "policyRule": { 3 | "if": { 4 | "anyOf": [ 5 | { 6 | "allOf": [ 7 | { 8 | "field": "type", 9 | "equals": "Microsoft.Compute/virtualMachines" 10 | }, 11 | { 12 | "field": "Microsoft.Compute/virtualMachines/osDisk.uri", 13 | "exists": true 14 | } 15 | ] 16 | }, 17 | { 18 | "allOf": [ 19 | { 20 | "field": "type", 21 | "equals": "Microsoft.Compute/VirtualMachineScaleSets" 22 | }, 23 | { 24 | "anyOf": [ 25 | { 26 | "field": "Microsoft.Compute/VirtualMachineScaleSets/osDisk.vhdContainers", 27 | "exists": true 28 | }, 29 | { 30 | "field": "Microsoft.Compute/VirtualMachineScaleSets/osdisk.imageUrl", 31 | "exists": true 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | ] 38 | }, 39 | "then": { 40 | "effect": "audit" 41 | } 42 | }, 43 | "parameters": {}, 44 | "metadata": { 45 | "category": "Compute" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/AzurePolicy/audit/Audit-PublicIP.json: -------------------------------------------------------------------------------- 1 | { 2 | "policyRule": { 3 | "if": { 4 | "anyOf": [ 5 | { 6 | "source": "action", 7 | "like": "Microsoft.Network/PublicIPAddresses/*" 8 | } 9 | ] 10 | }, 11 | "then": { 12 | "effect": "audit" 13 | } 14 | }, 15 | "parameters": {}, 16 | "metadata": { 17 | "category": "Network" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/AzurePolicy/audit/Audit-Resource-Tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "if": { 3 | "not": { 4 | "field": "tags", 5 | "containsKey": "costCenter" 6 | } 7 | }, 8 | "then": { 9 | "effect": "audit" 10 | } 11 | } -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/AzurePolicy/deny/Allowed-Locations.json: -------------------------------------------------------------------------------- 1 | { 2 | "policyRule": { 3 | "if": { 4 | "not": { 5 | "field": "location", 6 | "in": [ 7 | "eastus", 8 | "eastus2", 9 | "westus", 10 | "westus2", 11 | "westeurope" 12 | ] 13 | } 14 | }, 15 | "then": { 16 | "effect": "deny" 17 | } 18 | }, 19 | "parameters": {}, 20 | "metadata": { 21 | "category": "Build" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/AzurePolicy/deny/Prevent-Unmanaged-Disk-Creation.json: -------------------------------------------------------------------------------- 1 | { 2 | "policyRule": { 3 | "if": { 4 | "anyOf": [ 5 | { 6 | "allOf": [ 7 | { 8 | "field": "type", 9 | "equals": "Microsoft.Compute/virtualMachines" 10 | }, 11 | { 12 | "field": "Microsoft.Compute/virtualMachines/osDisk.uri", 13 | "exists": true 14 | } 15 | ] 16 | }, 17 | { 18 | "allOf": [ 19 | { 20 | "field": "type", 21 | "equals": "Microsoft.Compute/VirtualMachineScaleSets" 22 | }, 23 | { 24 | "anyOf": [ 25 | { 26 | "field": "Microsoft.Compute/VirtualMachineScaleSets/osDisk.vhdContainers", 27 | "exists": true 28 | }, 29 | { 30 | "field": "Microsoft.Compute/VirtualMachineScaleSets/osdisk.imageUrl", 31 | "exists": true 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | ] 38 | }, 39 | "then": { 40 | "effect": "deny" 41 | } 42 | }, 43 | "parameters": {}, 44 | "metadata": { 45 | "category": "Compute" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/AzurePolicy/deny/denyPIP.json: -------------------------------------------------------------------------------- 1 | "policyRule": { 2 | "if": { 3 | "allOf": [ 4 | { 5 | "field": "type", 6 | "equals": "Microsoft.Network/networkInterfaces" 7 | }, 8 | { 9 | "field": "Microsoft.Network/networkInterfaces/ipconfigurations[*].publicIpAddress.id", 10 | "exists": true 11 | } 12 | ] 13 | }, 14 | "then": { 15 | "effect": "deny" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/JsonExamples/businessAnalysts.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Business Analysts", 3 | "IsCustom": true, 4 | "Description": "Allows for a number of read operations + a few operations roles that allow business analysts to perform job duties.", 5 | "Actions": [ 6 | "Microsoft.Resources/subscriptions/resourceGroups/read", 7 | "Microsoft.Storage/storageAccounts/read", 8 | "Microsoft.Network/virtualNetworks/read", 9 | "Microsoft.ResourceHealth/availabilityStatuses/read", 10 | "Microsoft.Network/virtualNetworks/subnets/read", 11 | "Microsoft.Resources/subscriptions/resourceGroups/read", 12 | "Microsoft.resources/deployments/*", 13 | "Microsoft.Compute/*/read", 14 | "Microsoft.Compute/virtualMachines/restart/action", 15 | "Microsoft.Compute/virtualMachineScaleSets/restart/action", 16 | "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/restart/action", 17 | "Microsoft.Sql/*/read" 18 | ], 19 | "NotActions": [ 20 | 21 | ], 22 | "DataActions": [ 23 | 24 | ], 25 | "NotDataActions": [ 26 | 27 | ], 28 | "AssignableScopes": [ 29 | "/subscriptions/00000000-0000-0000-0000-000000000000" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/JsonExamples/cloudDevelopers.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Cloud Developers", 3 | "IsCustom": true, 4 | "Description": "Allows for developer permissions within an Azure environment.", 5 | "Actions": [ 6 | "Microsoft.Resources/subscriptions/resourceGroups/write", 7 | "Microsoft.Resources/subscriptions/resourceGroups/read", 8 | "Microsoft.Storage/storageaccounts/write", 9 | "Microsoft.Storage/storageAccounts/read", 10 | "Microsoft.Network/virtualNetworks/read", 11 | "Microsoft.Network/virtualNetworks/subnets/read", 12 | "Microsoft.Network/virtualNetworks/subnets/join/action", 13 | "Microsoft.Network/networkInterfaces/*", 14 | "Microsoft.resources/deployments/*", 15 | "Microsoft.Compute/*", 16 | "Microsoft.Sql/*", 17 | "Microsoft.Web/sites/*", 18 | "Microsoft.Insights/*" 19 | ], 20 | "NotActions": [ 21 | 22 | ], 23 | "DataActions": [ 24 | 25 | ], 26 | "NotDataActions": [ 27 | 28 | ], 29 | "AssignableScopes": [ 30 | "/subscriptions/00000000-0000-0000-0000-000000000000" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/JsonExamples/cloudOpsAdmins.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Cloud Ops Administrators", 3 | "IsCustom": true, 4 | "Description": "Allows for administrator permissions within an Azure environment.", 5 | "Actions": [ 6 | "Microsoft.Resources/subscriptions/resourceGroups/write", 7 | "Microsoft.Resources/subscriptions/resourceGroups/read", 8 | "Microsoft.Storage/storageaccounts/write", 9 | "Microsoft.Storage/storageAccounts/read", 10 | "Microsoft.Network/virtualNetworks/read", 11 | "Microsoft.Network/virtualNetworks/subnets/read", 12 | "Microsoft.Network/virtualNetworks/subnets/join/action", 13 | "Microsoft.Network/networkInterfaces/*", 14 | "Microsoft.resources/deployments/*", 15 | "Microsoft.Compute/*", 16 | "Microsoft.Sql/*" 17 | ], 18 | "NotActions": [ 19 | 20 | ], 21 | "DataActions": [ 22 | 23 | ], 24 | "NotDataActions": [ 25 | 26 | ], 27 | "AssignableScopes": [ 28 | "/subscriptions/00000000-0000-0000-0000-000000000000" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/JsonExamples/cloudOpsEngLead.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Cloud Ops Engineering Lead", 3 | "IsCustom": true, 4 | "Description": "Allows for a lot of control over Azure environments. Does not allow ability to delete or create virtual networks", 5 | "Actions": [ 6 | "*" 7 | ], 8 | "NotActions": [ 9 | "Microsoft.Network/*/delete", 10 | "Microsoft.Network/*/write" 11 | ], 12 | "DataActions": [ 13 | 14 | ], 15 | "NotDataActions": [ 16 | 17 | ], 18 | "AssignableScopes": [ 19 | "/subscriptions/00000000-0000-0000-0000-000000000000" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/JsonExamples/digitalSecurity.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Digital Security", 3 | "IsCustom": true, 4 | "Description": "Allows for security engineering functions related to Azure environments.", 5 | "Actions": [ 6 | "*/read", 7 | "Microsoft.Resources/subscriptions/resourceGroups/write", 8 | "Microsoft.Storage/storageaccounts/write", 9 | "Microsoft.resources/deployments/*", 10 | "Microsoft.EventHub/namespaces/eventhubs/*" 11 | ], 12 | "NotActions": [ 13 | 14 | ], 15 | "DataActions": [ 16 | 17 | ], 18 | "NotDataActions": [ 19 | 20 | ], 21 | "AssignableScopes": [ 22 | "/subscriptions/00000000-0000-0000-0000-000000000000" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/JsonExamples/networkAdmins.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Network Administrators", 3 | "IsCustom": true, 4 | "Description": "Allows for networking functions related to Azure environments.", 5 | "Actions": [ 6 | "*/read", 7 | "Microsoft.Network/*", 8 | "Microsoft.Resources/subscriptions/resourceGroups/write", 9 | "Microsoft.Storage/storageaccounts/write", 10 | "Microsoft.Storage/storageAccounts/read", 11 | "Microsoft.resources/deployments/*" 12 | ], 13 | "NotActions": [ 14 | 15 | ], 16 | "DataActions": [ 17 | 18 | ], 19 | "NotDataActions": [ 20 | 21 | ], 22 | "AssignableScopes": [ 23 | "/subscriptions/00000000-0000-0000-0000-000000000000" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/JsonExamples/suppReaderTickets.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "Reader Support Tickets", 3 | "IsCustom": true, 4 | "Description": "View everything in the subscription and also open support tickets.", 5 | "Actions": [ 6 | "*/read", 7 | "Microsoft.Support/*" 8 | ], 9 | "NotActions": [], 10 | "DataActions": [], 11 | "NotDataActions": [], 12 | "AssignableScopes": [ 13 | "/subscriptions/00000000-0000-0000-0000-000000000000" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShell/AzResourceProviderOperations.ps1: -------------------------------------------------------------------------------- 1 | # Exports a list of all Azure Provider Operations to a CSV file. 2 | Get-AzResourceProviderOperation -OperationSearchString * | Select Operation,OperationName,ProviderNamespace,Description ` 3 | | Export-Csv c:\Scripts\CustomRBAC\resourceprovideractions.csv -nti 4 | 5 | # Working examples to drill deeper into specific provider namespaces. 6 | Get-AzResourceProvider | Select ProviderNameSpace | Export-Csv C:\Scripts\CustomRBAC\ResourceProviders.csv -nti 7 | 8 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.Automation/* ` 9 | | Select Operation,OperationName,Description | Export-Csv C:\Scripts\CustomRBAC\Automation.csv -nti 10 | 11 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.Compute/* ` 12 | | Select Operation,OperationName,Description | Export-Csv C:\Scripts\CustomRBAC\Compute.csv -nti 13 | 14 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.HDInsight/* ` 15 | | Select Operation,OperationName,Description | Export-Csv CC:\Scripts\CustomRBAC\HDInsight.csv -nti 16 | 17 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.insights/* ` 18 | | Select Operation,OperationName,Description | Export-Csv C:\Scripts\CustomRBAC\insights.csv -nti 19 | 20 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.Network/* ` 21 | | Select Operation,OperationName,Description | Export-Csv C:\Scripts\CustomRBAC\Network.csv -nti 22 | 23 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.OperationalInsights/* ` 24 | | Select Operation,OperationName,Description | Export-Csv C:\Scripts\CustomRBAC\OpsInsights.csv -nti 25 | 26 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.OperationsManagement/* ` 27 | | Select Operation,OperationName,Description | Export-Csv CC:\Scripts\CustomRBAC\OpsMgmt.csv -nti 28 | 29 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.Sql/* ` 30 | | Select Operation,OperationName,Description | Export-Csv C:\Scripts\CustomRBAC\SQL.csv -nti 31 | 32 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.Storage/* ` 33 | | Select Operation,OperationName.Description | Export-Csv C:\Scripts\CustomRBAC\Storage.csv -nti 34 | 35 | Get-AzResourceProviderOperation -OperationSearchString Microsoft.Resources/* ` 36 | | Select Operation,OperationName,Description | Export-Csv C:\Scripts\CustomRBAC\Resources.csv -nti 37 | 38 | Get-AzResourceProviderOperation -OperationSearchString '*' | ? { $_.Operation -like 'Microsoft.Network/*' } ` 39 | | select Operation,OperationName,Description | Out-File azure-network-permissions.txt 40 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShell/Built-InRoles.ps1: -------------------------------------------------------------------------------- 1 | # Grabs all Azure Resource Manager builtin role definitions, selects the name + description, then exports the results as a csv. 2 | Get-AzRoleDefinition | Select-Object Name, Description | Export-Csv c:\Scripts\allArmRoles.csv -NTI 3 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShell/CustomRoleAssignment.ps1: -------------------------------------------------------------------------------- 1 | # Retrieve the role definition for the "Virtual Machine Contributor" built-in role 2 | $role = Get-AzRoleDefinition "Virtual Machine Contributor" 3 | # Set the role Id to null as this will be automatically generated when creating a custom role 4 | $role.Id = $null 5 | # Give the role a name and description 6 | $role.Name = "Limited VM Operator" 7 | $role.Description = "Users can monitor, stop, deallocate, and restart virtual machines." 8 | # Remove all actions in this case as we want to start fresh 9 | # If you wanted to grant the default permissions for this role and add new permissions, this step can be skipped. 10 | $role.Actions.Clear() 11 | 12 | $role.Actions.Add("Microsoft.Authorization/*/read") 13 | $role.Actions.Add("Microsoft.Resources/subscriptions/resourceGroups/read") 14 | $role.Actions.Add("Microsoft.Compute/*/read") 15 | $role.Actions.Add("Microsoft.Compute/virtualMachines/start/action") 16 | $role.Actions.Add("Microsoft.Compute/virtualMachines/restart/action") 17 | $role.Actions.Add("Microsoft.Compute/virtualMachines/powerOff/action") 18 | $role.Actions.Add("Microsoft.Compute/virtualMachines/deallocate/action") 19 | $role.Actions.Add("Microsoft.Network/networkInterfaces/read") 20 | $role.Actions.Add("Microsoft.Compute/disks/read") 21 | $role.Actions.Add("Microsoft.Insights/alertRules/read") 22 | $role.Actions.Add("Microsoft.Insights/diagnosticSettings/read") 23 | 24 | # Clear the scopes that this applies to 25 | $role.AssignableScopes.Clear() 26 | # Apply it to a specific resource group, note you would need to replace with the actual subscription id 27 | $role.AssignableScopes.Add("/subscriptions/subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx/resourceGroups/MyResourceGroup") 28 | 29 | New-AzRoleDefinition -Role $role 30 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShell/New-AzureRmDef-w-JSON.ps1: -------------------------------------------------------------------------------- 1 | # Creates a new Azure Resource Manager custom role based upon an input file. 2 | New-AzRoleDefinition -InputFile C:\Users\flore\Desktop\CustomRole.json 3 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShell/RoleTemplate.ps1: -------------------------------------------------------------------------------- 1 | # Provides a baseline template to work off with an existing builtin role for the custom role. 2 | Get-AzRoleDefinition -Name "Network Contributor" | ConvertTo-Json | Out-File "C:\ScriptOutputs\NetworkContributor.json" 3 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShellRoleExamples/RBACBASA.ps1: -------------------------------------------------------------------------------- 1 | $ADGroup = "Business Analysts" 2 | $ADGroupSearch = Get-AzADGroup -SearchString $ADGroup 3 | 4 | #Scope should be subscription ID in the form of "subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx" : 5 | #Can only add one subscription in this form 6 | 7 | $scope = "" 8 | 9 | $role = Get-AzRoleDefinition "Reader" 10 | $role.id = $null 11 | $role.name = "BA SA" 12 | $role.Description = "Business Analyst Role" 13 | $role.Actions.Clear() 14 | $role.NotActions.Clear() 15 | $role.Actions.Add("Microsoft.Resources/subscriptions/resourceGroups/read") 16 | $role.Actions.Add("Microsoft.Storage/storageAccounts/read") 17 | $role.Actions.Add("Microsoft.Network/virtualNetworks/read") 18 | $role.Actions.Add("Microsoft.Network/virtualNetworks/subnets/read") 19 | $role.Actions.Add("Microsoft.resources/deployments/*") 20 | $role.Actions.Add("Microsoft.Compute/*/read") 21 | $role.Actions.Add("Microsoft.Compute/virtualMachines/restart/action") 22 | $role.Actions.Add("Microsoft.Compute/virtualMachineScaleSets/restart/action") 23 | $role.Actions.Add("Microsoft.Compute/virtualMachineScaleSets/virtualMachines/restart/action") 24 | $role.Actions.Add("Microsoft.Sql/*/read") 25 | $role.AssignableScopes.Clear() 26 | $role.AssignableScopes.Add($scope) 27 | 28 | New-AzRoleDefinition -Role $role 29 | 30 | New-AzRoleAssignment -ObjectId $ADGroupSearch.Id.Guid -RoleDefinitionName $role.name -Scope $scope 31 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShellRoleExamples/RBACCloudOpsLead.ps1: -------------------------------------------------------------------------------- 1 | $ADGroup = "Engineering" 2 | $ADGroupSearch = Get-AzureRmADGroup -SearchString $ADGroup 3 | 4 | #Scope should be subscription ID in the form of "subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx" : 5 | #Can only add one subscription in this form 6 | 7 | $scope = "" 8 | 9 | $role = Get-AzRoleDefinition "Reader" 10 | $role.id = $null 11 | $role.name = "CloudOps Lead" 12 | $role.Description = "Cloud Engineering Lead" 13 | $role.Actions.Clear() 14 | $role.NotActions.Clear() 15 | $role.Actions.Add("*") 16 | $role.NotActions.Add("Microsoft.Network/*/delete") 17 | $role.NotActions.Add("Microsoft.Network/*/write") 18 | $role.AssignableScopes.Clear() 19 | $role.AssignableScopes.Add($scope) 20 | 21 | New-AzureRmRoleDefinition -Role $role 22 | 23 | New-AzureRmRoleAssignment -ObjectId $ADGroupSearch.Id.Guid -RoleDefinitionName $role.name -Scope $scope 24 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShellRoleExamples/RBACCloudOpsNonLead.ps1: -------------------------------------------------------------------------------- 1 | $ADGroup = "Administrators" 2 | $ADGroupSearch = Get-AzADGroup -SearchString $ADGroup 3 | 4 | #Scope should be subscription ID in form of "subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx" : 5 | #Can only add one subscription in this form 6 | 7 | $scope = "" 8 | 9 | $role = Get-AzRoleDefinition "Reader" 10 | $role.id = $null 11 | $role.name = "CloudOps Administrators" 12 | $role.Description = "Cloud Administrators" 13 | $role.Actions.Clear() 14 | $role.NotActions.Clear() 15 | $role.Actions.Add("Microsoft.Resources/subscriptions/resourceGroups/write") 16 | $role.Actions.Add("Microsoft.Resources/subscriptions/resourceGroups/read") 17 | $role.Actions.Add("Microsoft.Storage/storageaccounts/write") 18 | $role.Actions.Add("Microsoft.Storage/storageAccounts/read") 19 | $role.Actions.Add("Microsoft.Network/virtualNetworks/read") 20 | $role.Actions.Add("Microsoft.Network/virtualNetworks/subnets/read") 21 | $role.Actions.Add("Microsoft.Network/virtualNetworks/subnets/join/action") 22 | $role.Actions.Add("Microsoft.Network/networkInterfaces/*") 23 | $role.Actions.Add("Microsoft.resources/deployments/*") 24 | $role.Actions.Add("Microsoft.Compute/*") 25 | $role.Actions.Add("Microsoft.Sql/*") 26 | $role.AssignableScopes.Clear() 27 | $role.AssignableScopes.Add($scope) 28 | 29 | New-AzRoleDefinition -Role $role 30 | 31 | New-AzRoleAssignment -ObjectId $ADGroupSearch.Id.Guid -RoleDefinitionName $role.name -Scope $scope 32 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShellRoleExamples/RBACDeveloper.ps1: -------------------------------------------------------------------------------- 1 | $ADGroup = "Developers" 2 | $ADGroupSearch = Get-AzADGroup -SearchString $ADGroup 3 | 4 | #Scope should be subscription ID in form of "subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx" : 5 | #Can only add one subscription in this form 6 | 7 | $scope = "" 8 | 9 | $role = Get-AzRoleDefinition "Reader" 10 | $role.id = $null 11 | $role.name = "Developer" 12 | $role.Description = "Cloud Developer Role" 13 | $role.Actions.Clear() 14 | $role.NotActions.Clear() 15 | $role.Actions.Add("Microsoft.Resources/subscriptions/resourceGroups/write") 16 | $role.Actions.Add("Microsoft.Resources/subscriptions/resourceGroups/read") 17 | $role.Actions.Add("Microsoft.Storage/storageaccounts/write") 18 | $role.Actions.Add("Microsoft.Storage/storageAccounts/read") 19 | $role.Actions.Add("Microsoft.Network/virtualNetworks/read") 20 | $role.Actions.Add("Microsoft.Network/virtualNetworks/subnets/read") 21 | $role.Actions.Add("Microsoft.Network/virtualNetworks/subnets/join/action") 22 | $role.Actions.Add("Microsoft.Network/networkInterfaces/*") 23 | $role.Actions.Add("Microsoft.resources/deployments/*") 24 | $role.Actions.Add("Microsoft.Compute/*") 25 | $role.Actions.Add("Microsoft.Sql/*") 26 | $role.Actions.Add("Microsoft.Web/sites/*") 27 | $role.Actions.Add("Microsoft.Insights/*") 28 | $role.AssignableScopes.Clear() 29 | $role.AssignableScopes.Add($scope) 30 | 31 | New-AzRoleDefinition -Role $role 32 | 33 | New-AzRoleAssignment -ObjectId $ADGroupSearch.Id.Guid -RoleDefinitionName $role.name -Scope $scope 34 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShellRoleExamples/RBACDigitalSecurity.ps1: -------------------------------------------------------------------------------- 1 | $ADGroup = "Digital Security" 2 | $ADGroupSearch = Get-AzADGroup -SearchString $ADGroup 3 | 4 | #Scope should be subscription ID in form of "subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx" : 5 | #Can only add one subscription in this form 6 | 7 | $scope = "" 8 | 9 | $role = Get-AzRoleDefinition "Reader" 10 | $role.id = $null 11 | $role.name = "Digital Security" 12 | $role.Description = "Digital Security Role" 13 | $role.Actions.Clear() 14 | $role.NotActions.Clear() 15 | $role.Actions.Add("*/read") 16 | $role.Actions.Add("Microsoft.Resources/subscriptions/resourceGroups/write") 17 | $role.Actions.Add("Microsoft.Storage/storageaccounts/write") 18 | $role.Actions.Add("Microsoft.resources/deployments/*") 19 | $role.Actions.Add("Microsoft.EventHub/namespaces/eventhubs/*") 20 | $role.AssignableScopes.Clear() 21 | $role.AssignableScopes.Add($scope) 22 | 23 | New-AzRoleDefinition -Role $role 24 | 25 | New-AzRoleAssignment -ObjectId $ADGroupSearch.Id.Guid -RoleDefinitionName $role.name -Scope $scope 26 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShellRoleExamples/RBACNetworking.ps1: -------------------------------------------------------------------------------- 1 | $ADGroup = "NetworkAdmins" 2 | $ADGroupSearch = Get-AzADGroup -SearchString $ADGroup 3 | 4 | #Scope should be subscription ID in form of "subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx" : 5 | #Can only add one subscription in this form 6 | 7 | $scope = "" 8 | 9 | $role = Get-AzRoleDefinition "Reader" 10 | $role.id = $null 11 | $role.name = "NetworkingTest" 12 | $role.Description = "Networking Role" 13 | $role.Actions.Clear() 14 | $role.NotActions.Clear() 15 | $role.Actions.Add("*/read") 16 | $role.Actions.Add("Microsoft.Network/*") 17 | $role.Actions.Add("Microsoft.Resources/subscriptions/resourceGroups/write") 18 | $role.Actions.Add("Microsoft.Storage/storageaccounts/write") 19 | $role.Actions.Add("Microsoft.Storage/storageAccounts/read") 20 | $role.Actions.Add("Microsoft.resources/deployments/*") 21 | $role.AssignableScopes.Clear() 22 | $role.AssignableScopes.Add($scope) 23 | 24 | New-AzRoleDefinition -Role $role 25 | 26 | New-AzRoleAssignment -ObjectId $ADGroupSearch.Id.Guid -RoleDefinitionName $role.name -Scope $scope 27 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/CustomRBAC/PowerShellRoleExamples/README.md: -------------------------------------------------------------------------------- 1 | # PowerShell Role Examples: 2 | These provide PoSH role templates to use for custom RBAC assignments in Azure. 3 | -------------------------------------------------------------------------------- /sample-AzGuardRails-Governance/README.md: -------------------------------------------------------------------------------- 1 | # AzGuardRails 2 | 3 | Azure Policy, PowerShell, and JSON Examples of guard rails for Azure environments. 4 | 5 | © 2019 Microsoft Corporation. 6 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 7 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 8 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 9 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 10 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 11 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 12 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 13 | documentation, even if Microsoft has been advised of the possibility of such damages. 14 | -------------------------------------------------------------------------------- /sample-EasyAuth-ClassicASP/EasyAuthClassicASP/default.asp: -------------------------------------------------------------------------------- 1 | <% @ language="VBScript" %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Classic ASP Auth Test 10 | 11 | 24 | 25 | 26 |
27 | 56 |
57 |
58 | Session: 59 | 60 | <%For Each X in Session.Contents%> 61 | 62 | 63 | 64 | 65 | <%Next%> 66 |
<%=X%><%=Session.Contents(x)%>
67 | 68 | <% 'WriteServerVariables() %> 69 | 70 |
71 | 72 | -------------------------------------------------------------------------------- /sample-EasyAuth-ClassicASP/EasyAuthClassicASP/dockerfile: -------------------------------------------------------------------------------- 1 | # escape=` 2 | 3 | FROM mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019 4 | SHELL ["powershell", "-command"] 5 | 6 | RUN Install-WindowsFeature Web-ASP; ` 7 | Install-WindowsFeature Web-CGI; ` 8 | Install-WindowsFeature Web-ISAPI-Ext; ` 9 | Install-WindowsFeature Web-ISAPI-Filter; ` 10 | Install-WindowsFeature Web-Includes; ` 11 | Install-WindowsFeature Web-HTTP-Errors; ` 12 | Install-WindowsFeature Web-Common-HTTP; ` 13 | Install-WindowsFeature Web-Performance; ` 14 | Install-WindowsFeature WAS; ` 15 | Import-module IISAdministration; 16 | 17 | RUN md c:/msi; 18 | 19 | RUN Invoke-WebRequest 'https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_en-US.msi' -OutFile c:/msi/urlrewrite2.msi; ` 20 | Start-Process 'c:/msi/urlrewrite2.msi' '/qn' -PassThru | Wait-Process; 21 | 22 | EXPOSE 80 23 | 24 | RUN Remove-Website -Name 'Default Web Site'; ` 25 | md c:\mywebsite; ` 26 | New-IISSite -Name "mywebsite" ` 27 | -PhysicalPath 'c:\mywebsite' ` 28 | -BindingInformation "*:80:"; 29 | 30 | RUN & c:\windows\system32\inetsrv\appcmd.exe ` 31 | unlock config ` 32 | /section:system.webServer/asp 33 | 34 | RUN & c:\windows\system32\inetsrv\appcmd.exe ` 35 | unlock config ` 36 | /section:system.webServer/handlers 37 | 38 | RUN & c:\windows\system32\inetsrv\appcmd.exe ` 39 | unlock config ` 40 | /section:system.webServer/modules 41 | 42 | 43 | ADD . c:\mywebsite -------------------------------------------------------------------------------- /sample-EasyAuth-ClassicASP/EasyAuthClassicASP/global.asa: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sample-EasyAuth-ClassicASP/EasyAuthClassicASP/web.config: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sample-EasyAuth-ClassicASP/README.md: -------------------------------------------------------------------------------- 1 | ## Easy Auth for Classic ASP 2 | 3 | This is a hacked-up PoC showing how Classic ASP can take advantage of the Easy Auth feature of Azure App Services. 4 | Easy Auth makes the id_token from Azure AD available in a header after authentication; this parses the token 5 | (App Services will have already validated it and logged you in), and sets the claims in session state. 6 | 7 | If you are interested in deploying this in a Windows Container in App Service, a dockerfile is also 8 | available in the solution directory. 9 | 10 | ## Contributing 11 | 12 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 13 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 14 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 15 | 16 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 17 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 18 | provided by the bot. You will only need to do this once across all repos using our CLA. 19 | 20 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 21 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 22 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 23 | -------------------------------------------------------------------------------- /sample-Python-KeyVault-Function/README.md: -------------------------------------------------------------------------------- 1 | A simple example - accessing Key Vault from a [python http triggered function](https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-function-python). Originally wanted to do this with MSI, but sadly, [no MSI support just yet](https://github.com/Azure/Azure-Functions/issues/1066). When MSI is available it should look something like [this](https://github.com/Microsoft/csa-misc-utils/blob/master/sample-Python-KeyVault-Function/init-with-msi.py). I don't do much python so if looking at this makes your insides hurt, please let me know [Twitter](https://twitter.com/azureandchill) [GitHub](https://github.com/jpda). 2 | 3 | ## to publish: 4 | because certain dependencies are binary, you have to build in a docker container - `--build-native-deps` does this for you during publish 5 | 6 | to create a service principal for rbac assignment to KV secrets, use `az ad sp create-for-rbac --name 'a-recognizable-name' --skip-assignment` 7 | 8 | to publish from local, login to azure cli `az login` and set your subscription to the one containing your function, then 9 | 10 | `func azure functionapp publish --build-native-deps` 11 | -------------------------------------------------------------------------------- /sample-Python-KeyVault-Function/init-manual-service-principal.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import azure.functions as func 3 | 4 | from azure.keyvault import KeyVaultClient, KeyVaultAuthentication 5 | from azure.common.credentials import ServicePrincipalCredentials 6 | # see https://docs.microsoft.com/en-us/python/api/overview/azure/key-vault?view=azure-python 7 | 8 | def auth_callback(server, resource, scope): 9 | credentials = ServicePrincipalCredentials( 10 | client_id = '', 11 | secret = '', 12 | tenant = '', 13 | resource = "https://vault.azure.net" 14 | ) 15 | token = credentials.token 16 | return token['token_type'], token['access_token'] 17 | 18 | client = KeyVaultClient(KeyVaultAuthentication(auth_callback)) 19 | 20 | def main(req: func.HttpRequest) -> func.HttpResponse: 21 | logging.info('Python HTTP trigger function processed a request.') 22 | secret_bundle = client.get_secret("https://.vault.azure.net/", "", "") 23 | return func.HttpResponse(f"{secret_bundle.value}!") -------------------------------------------------------------------------------- /sample-Python-KeyVault-Function/init-with-msi.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import azure.functions as func 3 | from azure.keyvault import KeyVaultClient 4 | from msrestazure.azure_active_directory import MSIAuthentication, ServicePrincipalCredentials 5 | 6 | credentials = MSIAuthentication( 7 | resource='https://vault.azure.net' 8 | ) 9 | client = KeyVaultClient( 10 | credentials 11 | ) 12 | 13 | def main(req: func.HttpRequest) -> func.HttpResponse: 14 | logging.info('Python HTTP trigger function processed a request.') 15 | secret_bundle = client.get_secret("https://.vault.azure.net/", "", "") 16 | return func.HttpResponse(f"{secret_bundle.value}!") -------------------------------------------------------------------------------- /sample-Python-KeyVault-Function/requirements.txt: -------------------------------------------------------------------------------- 1 | adal==1.2.1 2 | asn1crypto==0.24.0 3 | astroid==2.1.0 4 | azure-common==1.1.17 5 | azure-functions==1.0.0a5 6 | azure-functions-worker==1.0.0a6 7 | azure-keyvault==1.1.0 8 | azure-nspkg==3.0.2 9 | certifi==2023.7.22 10 | cffi==1.11.5 11 | chardet==3.0.4 12 | colorama==0.4.1 13 | cryptography==41.0.6 14 | grpcio==1.53.0 15 | grpcio-tools==1.14.2 16 | idna==2.8 17 | isodate==0.6.0 18 | isort==4.3.4 19 | lazy-object-proxy==1.3.1 20 | mccabe==0.6.1 21 | msrest==0.6.4 22 | msrestazure==0.6.0 23 | oauthlib==3.0.1 24 | protobuf==3.18.3 25 | pycparser==2.19 26 | PyJWT==2.4.0 27 | pylint==2.2.2 28 | python-dateutil==2.7.5 29 | requests==2.31.0 30 | requests-oauthlib==1.2.0 31 | six==1.12.0 32 | typed-ast==1.2.0 33 | urllib3>=1.24.2 34 | wrapt==1.11.1 -------------------------------------------------------------------------------- /sample-UpdateManagement/Kusto/Compliance/UncompliantHosts.csl: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------------- 2 | // Created 3 | // 2018.12.05 4 | // Shannon Kuehn 5 | // Last Updated 6 | // 7 | // © 2018 Microsoft Corporation. 8 | // All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 9 | // or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 10 | // warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular 11 | // purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. 12 | // In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts 13 | // be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business 14 | // interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the 15 | // sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages. 16 | // 17 | // Update Management 18 | // 19 | // Uncompliant Servers - Lists all servers that are not compliant via Update Management. 20 | //----------------------------------------------------------------------------------- 21 | 22 | Update 23 | | where UpdateState == "Needed" and (Classification == "Security Updates" or Classification == "Critical Updates") 24 | | summarize count() by Computer 25 | -------------------------------------------------------------------------------- /sample-UpdateManagement/Kusto/Post Analysis/Host-w-PatchAmountApplied.csl: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------------- 2 | // Created 3 | // 2019.01.16 4 | // Shannon Kuehn 5 | // Last Updated 6 | // 7 | // © 2018 Microsoft Corporation. 8 | // All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 9 | // or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 10 | // warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular 11 | // purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. 12 | // In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts 13 | // be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business 14 | // interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the 15 | // sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages. 16 | //----------------------------------------------------------------------------------- 17 | 18 | UpdateRunProgress 19 | | where InstallationStatus == 'Succeeded' 20 | | where TimeGenerated > now(-30d) 21 | | distinct Computer 22 | , Title 23 | | summarize count(Title) by Computer 24 | | sort by Computer asc 25 | -------------------------------------------------------------------------------- /sample-UpdateManagement/Kusto/Post Analysis/LinWin-PostAnalysis.csl: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------------- 2 | // Created 3 | // 2018.11.20 4 | // Shannon Kuehn 5 | // Last Updated 6 | // 7 | // © 2018 Microsoft Corporation. 8 | // All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 9 | // or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 10 | // warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular 11 | // purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. 12 | // In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts 13 | // be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business 14 | // interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the 15 | // sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages. 16 | // 17 | // Update Management 18 | // 19 | // Patching post-analysis query sample - Windows and Linux VMs 20 | // 21 | // Grabs patch details for patch group and builds a post analysis report for regulatory compliance. 22 | //----------------------------------------------------------------------------------- 23 | 24 | let group1 = dynamic (['hostname1','hostname2','hostname3','hostname4','hostname5','hostname6','hostname7','hostname8']); 25 | UpdateRunProgress 26 | | where UpdateRunName == "group1" 27 | | where InstallationStatus == 'Succeeded' 28 | | where TimeGenerated > now(-60d) 29 | | project Computer 30 | , TimeGenerated 31 | , SourceComputerId 32 | , InstallationStatus 33 | , Product 34 | , Title 35 | , KBID 36 | , UpdateId 37 | , ErrorResult 38 | , UpdateRunName 39 | | sort by Computer asc 40 | -------------------------------------------------------------------------------- /sample-UpdateManagement/Kusto/Pre-Analysis/Linux-PreAnalysis.csl: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------------- 2 | // Created 3 | // 2018.11.06 4 | // Shannon Kuehn 5 | // Last Updated 6 | // 7 | // © 2018 Microsoft Corporation. 8 | // All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 9 | // or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 10 | // warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular 11 | // purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. 12 | // In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts 13 | // be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business 14 | // interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the 15 | // sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages. 16 | // 17 | // Update Management 18 | // 19 | // Patching pre-analysis query sample - Linux VMs 20 | // 21 | // Grabs patch details for patch group and builds a pre-analysis report for regulatory compliance. 22 | //----------------------------------------------------------------------------------- 23 | 24 | let group1 = dynamic (['hostname1','hostname2','hostname3','hostname4','hostname5','hostname6','hostname7','hostname8']); 25 | Update 26 | | where Computer in (group1) 27 | | where TimeGenerated>ago(30d) and OSType=="Linux" and SourceComputerId in ((Heartbeat 28 | | where TimeGenerated>ago(30d) and OSType=="Linux" and notempty(Computer) 29 | | summarize arg_max(TimeGenerated, Solutions) by SourceComputerId 30 | | where Solutions has "updates" 31 | | distinct SourceComputerId)) 32 | | summarize hint.strategy=partitioned arg_max(TimeGenerated, *) by Computer, SourceComputerId, Product, ProductArch 33 | | where UpdateState=~"Needed" 34 | | where Classification=~"Critical Updates" 35 | | project Computer 36 | , TimeGenerated 37 | , Product 38 | , Classification 39 | , UpdateState 40 | , OSType 41 | , PackageRepository 42 | , OSName 43 | , OSVersion 44 | | sort by Computer asc 45 | , Product asc 46 | -------------------------------------------------------------------------------- /sample-UpdateManagement/Kusto/Pre-Analysis/Windows-PreAnalysis.csl: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------------- 2 | // Created 3 | // 2018.11.06 4 | // Shannon Kuehn 5 | // Last Updated 6 | // 7 | // © 2018 Microsoft Corporation. 8 | // All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 9 | // or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 10 | // warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular 11 | // purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. 12 | // In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts 13 | // be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business 14 | // interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the 15 | // sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages. 16 | // 17 | // Update Management 18 | // 19 | // Patching pre-analysis query sample - Windows VMs 20 | // 21 | // Grabs patch details for patch group and builds a pre-analysis report for regulatory compliance. 22 | //----------------------------------------------------------------------------------- 23 | 24 | let group1 = dynamic (['hostname1','hostname2','hostname3','hostname4','hostname5','hostname6','hostname7','hostname8']); 25 | Update 26 | | where Computer in (group1) 27 | | where TimeGenerated>ago(30d) and OSType!="Linux" and (Optional==false or Classification has "Critical" or Classification has "Security") and SourceComputerId in ((Heartbeat 28 | | where TimeGenerated>ago(30d) and OSType=~"Windows" and notempty(Computer) 29 | | summarize arg_max(TimeGenerated, Solutions) by SourceComputerId 30 | | where Solutions has "updates" 31 | | distinct SourceComputerId)) 32 | | summarize hint.strategy=partitioned arg_max(TimeGenerated, *) by Computer, SourceComputerId, UpdateID 33 | | where UpdateState=~"Needed" and Approved!=false 34 | | project Computer 35 | , TimeGenerated 36 | , PublishedDate 37 | , KBID 38 | , Product 39 | , Title 40 | , UpdateState 41 | , Optional 42 | , RebootBehavior 43 | | sort by Computer asc 44 | , PublishedDate 45 | -------------------------------------------------------------------------------- /sample-UpdateManagement/Kusto/README.md: -------------------------------------------------------------------------------- 1 | Kusto Queries - Detailed Explanations: 2 | 1) CVE Numbers are only listed for Linux within the underlying database engine for Kusto, except not every Linux server 3 | patch contains a CVE Number. 4 | 2) For the pre and post analysis queries, TimeGenerated refers to when the patch applies. In order to ensure you grab 5 | data for the past month, the TimeGenerated needs to be in the initial part of the Kusto query. 6 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/ADDS/01-InitialTask-ADDSSecurityGroups.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2018.08.24 4 | Shannon Kuehn 5 | Last Updated 6 | 7 | © 2018 Microsoft Corporation. 8 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 9 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 10 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 11 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 12 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 13 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 14 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 15 | documentation, even if Microsoft has been advised of the possibility of such damages. 16 | #> 17 | 18 | 19 | ## Initial Security Group Assignment # 20 | ######################################################################################################### 21 | ## 22 | ## Script performs the following tasks: 23 | ## 1) Imports ActiveDirectory PoSH module. 24 | ## 2) Stores all AD DS computers as a variable to loop through. 25 | ## 3) Loops through each recently created server to determine if a metadata json file is on the server. 26 | ## 4) If server has a json file saved, server name will be stored as a variable. 27 | ## 5) Loops through server name and extracts SamAccountName. SamAccountName is necessary for AD Security 28 | ## Group assignment. 29 | ## 6) Formulates a patch schedule based upon json file for each server. 30 | ## Notes: 31 | ## a. Run on server with AD DS role installed or on a server with RSAT tools. 32 | ## b. Test permissions to run (best with a service account): requires permissions to query server 33 | ## objects in AD DS and ability to assign servers to Security Groups. 34 | ######################################################################################################### 35 | 36 | Import-Module ActiveDirectory 37 | $servers = Get-ADComputer -Filter * | Select-Object -ExpandProperty Name 38 | ForEach($server in $servers){ 39 | $path = Test-Path "\\$server\C$\filepath\base.json" 40 | If($path -eq $true){ 41 | $patchSchedule = Get-Content -Raw -Path "\\$server\c$\filepath\base.json" | ConvertFrom-Json | Select-Object -ExpandProperty patch_schedule 42 | $SAMAccountName = Get-ADComputer -Identity $server | Select-Object -ExpandProperty SamAccountName 43 | $patchScheduleName = "" 44 | Switch ($patchSchedule){ 45 | '01'{$patchScheduleName = "patch_schedule_01"} 46 | '02'{$patchScheduleName = "patch_schedule_02"} 47 | '03'{$patchScheduleName = "patch_schedule_03"} 48 | '04'{$patchScheduleName = "patch_schedule_04"} 49 | } 50 | Add-ADGroupMember -Identity $patchScheduleName -Members $SAMAccountName 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/ADDS/README.md: -------------------------------------------------------------------------------- 1 | PowerShell Scripts - Explained 2 |

1) 01-InitialTask-ADDSSecurityGroups.ps1 - Initially imports ActiveDirectory PowerShell module, iterates through 3 | all servers that are domain joined, and groups based upon a patch schedule listed inside a json file sitting in a 4 | specific directory. 5 |

2) 02-SchedTask-ADDSSecurityGroups.ps1 - As a scheduled task, imports ActiveDirectory PowerShell module, iterates through 6 | new servers that are domin joined, and groups based upon a patch schedule listed inside a json file sitting in a specific 7 | directory. 8 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AutomatedComplianceReporting/01-BearerToken.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2018.12.20 4 | Shannon Kuehn 5 | Last Updated 6 | © 2018 Microsoft Corporation. 7 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 8 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 9 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 10 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 11 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 12 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 13 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 14 | documentation, even if Microsoft has been advised of the possibility of such damages. 15 | #> 16 | # Adapted from: 17 | # https://www.codeisahighway.com/how-to-easily-and-silently-obtain-accesstoken-bearer-from-an-existing-azure-powershell-session/ 18 | 19 | function Get-AzureRmCachedAccessToken() 20 | { 21 | $ErrorActionPreference = 'Stop' 22 | 23 | if(-not (Get-Module AzureRm.Profile)) { 24 | Import-Module AzureRm.Profile 25 | } 26 | $azureRmProfileModuleVersion = (Get-Module AzureRm.Profile).Version 27 | # refactoring performed in AzureRm.Profile v3.0 or later 28 | if($azureRmProfileModuleVersion.Major -ge 3) { 29 | $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile 30 | if(-not $azureRmProfile.Accounts.Count) { 31 | Write-Error "Ensure you have logged in before calling this function." 32 | } 33 | } else { 34 | # AzureRm.Profile < v3.0 35 | $azureRmProfile = [Microsoft.WindowsAzure.Commands.Common.AzureRmProfileProvider]::Instance.Profile 36 | if(-not $azureRmProfile.Context.Account.Count) { 37 | Write-Error "Ensure you have logged in before calling this function." 38 | } 39 | } 40 | 41 | $currentAzureContext = Get-AzureRmContext 42 | $profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile) 43 | Write-Debug ("Getting access token for tenant" + $currentAzureContext.Subscription.TenantId) 44 | $token = $profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId) 45 | $token.AccessToken 46 | } 47 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AutomatedComplianceReporting/02-PostAnalysisAutomation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2019.01.25 4 | Shannon Kuehn 5 | Last Updated 6 | 7 | © 2019 Microsoft Corporation. 8 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 9 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 10 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 11 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 12 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 13 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 14 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 15 | documentation, even if Microsoft has been advised of the possibility of such damages. 16 | #> 17 | 18 | #Required parameters 19 | param( 20 | [Parameter(Mandatory=$true)] 21 | [string]$subscriptionId, 22 | [Parameter(Mandatory=$true)] 23 | [array]$rG, 24 | [Parameter(Mandatory=$true)] 25 | [string]$workspaceName 26 | ) 27 | 28 | #Generate bearer token and use token + header for the API call. Ensure the header has more information to grab the records 29 | #correctly. The body needs to be passed as shown below in order to run a successful Kusto Query against the environment. 30 | $bearer = Get-AzCachedAccessToken 31 | $header = @{"Authorization"="Bearer $bearer";"Content-Type"="application/json";"Prefer"="response-v1=true"} 32 | $apiCall = "https://management.azure.com/subscriptions/"+$subscriptionId+"/resourceGroups/"+$rG+"/providers/Microsoft.OperationalInsights/workspaces/"+$workspaceName+"/api/query?api-version=2017-01-01-preview" 33 | $body = @" 34 | {"query": "UpdateRunProgress | where InstallationStatus == 'Succeeded' | where TimeGenerated > now(-30d) | project Computer , TimeGenerated , SourceComputerId , InstallationStatus , Product , Title , KBID , UpdateId , ErrorResult , UpdateRunName | sort by Computer asc"} 35 | "@ 36 | 37 | #Run the script, which extracts columns and rows as objects, then adds them to a data table, which can be extracted as a csv. 38 | $response = Invoke-WebRequest -Uri $apiCall -Headers $header -Method Post -Body $body 39 | $jsonResponses = $response.Content | ConvertFrom-Json 40 | $ScriptBlock = .{ 41 | $tableObj = New-Object System.Data.DataTable "Post-Analysis" 42 | $jsonResponse.tables.columns | ForEach { 43 | $newcol = New-Object System.Data.DataColumn 44 | $newcol.ColumnName=$_.Name 45 | $newcol.DataType=$_.Type 46 | $tableObj.Columns.Add($newcol) 47 | } 48 | 49 | $jsonResponse.tables.rows | ForEach { 50 | $tableObj.Rows.Add($_) 51 | } 52 | 53 | $tableObj | Export-Csv \\server\share\ComplianceReporting\PostAnalysis_2019-01-19.csv -NTI 54 | } 55 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AutomatedComplianceReporting/04-PreAnalysisAutomation-Windows.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2019.01.25 4 | Shannon Kuehn 5 | Last Updated 6 | 7 | © 2019 Microsoft Corporation. 8 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 9 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 10 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 11 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 12 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 13 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 14 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 15 | documentation, even if Microsoft has been advised of the possibility of such damages. 16 | #> 17 | 18 | #Required parameters. 19 | param( 20 | [Parameter(Mandatory=$true)] 21 | [string]$subscriptionId, 22 | [Parameter(Mandatory=$true)] 23 | [array]$rG, 24 | [Parameter(Mandatory=$true)] 25 | [string]$workspaceName 26 | ) 27 | 28 | #Generate bearer token and use token + header for the API call. Ensure the header has more information to grab the records. 29 | $bearer = Get-AzureRmCachedAccessToken 30 | $header = @{"Authorization"="Bearer $bearer";"Content-Type"="application/json";"Prefer"="response-v1=true"} 31 | $apiCall = "https://management.azure.com/subscriptions/"+$subscriptionId+"/resourceGroups/"+$rG+"/providers/Microsoft.OperationalInsights/workspaces/"+$workspaceName+"/api/query?api-version=2017-01-01-preview" 32 | $body = @" 33 | {"query": "Update | where TimeGenerated>ago(30d) and OSType!='Linux' and (Optional==false or Classification has 'Critical' or Classification has 'Security') and SourceComputerId in ((Heartbeat | where TimeGenerated>ago(30d) and OSType=~'Windows' and notempty(Computer) | summarize arg_max(TimeGenerated, Solutions) by SourceComputerId | where Solutions has 'updates' | distinct SourceComputerId)) | summarize hint.strategy=partitioned arg_max(TimeGenerated, *) by Computer, SourceComputerId, UpdateID | where UpdateState=~'Needed' and Approved!=false | project Computer , TimeGenerated , PublishedDate , KBID , Product , Title , UpdateState , Optional , RebootBehavior | sort by Computer asc , PublishedDate"} 34 | "@ 35 | 36 | #Run the script, which extracts columns and rows as objects, then adds them to a data table, which can be extracted as a csv. 37 | $response = Invoke-WebRequest -Uri $apiCall -Headers $header -Method Post -Body $body 38 | $jsonResponses = $response.Content | ConvertFrom-Json 39 | $ScriptBlock = .{ 40 | $tableObj = New-Object System.Data.DataTable "Post-Analysis" 41 | $jsonResponse.tables.columns | ForEach { 42 | $newcol = New-Object System.Data.DataColumn 43 | $newcol.ColumnName=$_.Name 44 | $newcol.DataType=$_.Type 45 | $tableObj.Columns.Add($newcol) 46 | } 47 | 48 | $jsonResponse.tables.rows | ForEach { 49 | $tableObj.Rows.Add($_) 50 | } 51 | 52 | $tableObj | Export-Csv \\server\share\ComplianceReporting\PreAnalysisWindows_2019-01-19.csv -NTI 53 | } 54 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AutomatedComplianceReporting/README.md: -------------------------------------------------------------------------------- 1 | Automated Compliance Reporting: 2 | 1) Run the 01-BearerToken.ps1 while logged into Azure PowerShell, with an active account pointed at the working sub. 3 | 2) The 01-BearerToken.ps1 script loads the actual Bearer Token into memory. This token gets called upon in the each of the automation scripts by way of using the Get-AzureRmCachedAccessToken function. The bearer token allows the logged in user to hit the API directly to query information. 4 | 3) Each script will run a Log Analytics query against the Update Management environment, whether it be for pre-analysis or post analysis. 5 | 4) In addition, the output of each script will export to a csv file that can be used for regulatory records of patch compliance. 6 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AzureAutomationRunbooks/3rdPartyPatching/7Zip.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2019.02.07 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2019 Microsoft Corporation. 10 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 11 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 12 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 13 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 14 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 15 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 16 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 17 | documentation, even if Microsoft has been advised of the possibility of such damages. 18 | #> 19 | 20 | #Required parameters to run on a schedule or on-demand. 21 | param( 22 | [Parameter(Mandatory = $true)] 23 | [string]$sourcePath, 24 | [Parameter(Mandatory = $true)] 25 | [string]$workspaceId, 26 | [Parameter(Mandatory = $true)] 27 | [string]$query 28 | ) 29 | 30 | #Specify the Azure Automation connection. 31 | $Conn = Get-AutomationConnection -Name AzureRunAsConnection 32 | Connect-AzAccount -ServicePrincipal -Tenant $Conn.TenantID ` 33 | -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint 34 | Set-AzContext -SubscriptionId $Conn.SubscriptionID 35 | 36 | #Required Parameters to test with AzureAutomationAuthoringToolkit. Comment out with # if running inside Azure on a schedule. 37 | $sourcePath = '\\server\share\AppPatching\7-Zip\' 38 | $workspaceId = "Log Analytics workspace ID goes here" 39 | $query = "ConfigurationData | where ConfigDataType == 'Software' | where SoftwareName == '7-Zip 18.01' | distinct Computer" 40 | $queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceId -Query $query 41 | $computername = $queryResults.Results | Select-Object -ExpandProperty Computer 42 | 43 | #Run the installation script. 44 | ForEach($computer in $computername){ 45 | Copy-Item -Path $sourcePath -Destination \\$computer\c$\ -Recurse -Force 46 | $session = New-PSSession -ComputerName $computer 47 | Invoke-Command -Session $session -ScriptBlock { 48 | $process = New-Object System.Diagnostics.Process 49 | $process.StartInfo.FileName = "C:\7-Zip\7z1806-x64.exe" 50 | $process.StartInfo.Arguments = " /s" 51 | $process.StartInfo.Verb = "RunAs" 52 | $process.Start() 53 | } 54 | Enter-PSSession -Session $session 55 | Exit-PSSession 56 | } 57 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AzureAutomationRunbooks/3rdPartyPatching/JRE.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2019.02.07 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2019 Microsoft Corporation. 10 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 11 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 12 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 13 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 14 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 15 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 16 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 17 | documentation, even if Microsoft has been advised of the possibility of such damages. 18 | #> 19 | 20 | #Required parameters to run on a schedule or on-demand. 21 | param( 22 | [Parameter(Mandatory = $true)] 23 | [string]$sourcePath, 24 | [Parameter(Mandatory = $true)] 25 | [string]$workspaceId, 26 | [Parameter(Mandatory = $true)] 27 | [string]$query 28 | ) 29 | 30 | #Specify the Azure Automation connection. 31 | $Conn = Get-AutomationConnection -Name AzureRunAsConnection 32 | Connect-AzAccount -ServicePrincipal -Tenant $Conn.TenantID ` 33 | -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint 34 | Set-AzContext -SubscriptionId $Conn.SubscriptionID 35 | 36 | #Required Parameters to test with AzureAutomationAuthoringToolkit. Comment out with # if running inside Azure on a schedule. 37 | $sourcePath = '\\server\share\AppPatching\Java\' 38 | $workspaceId = "Log Analytics workspace goes here" 39 | $query = 'ConfigurationData | where ConfigDataType == "Software" | where SoftwareName == "Java 8 Update 201 (64-bit)" | distinct Computer' 40 | $queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceId -Query $query 41 | $computername = $queryResults.Results | Select-Object -ExpandProperty Computer 42 | 43 | #Run the installation script. 44 | ForEach($computer in $computername){ 45 | Copy-Item -Path $sourcePath -Destination \\$computer\c$\ -Recurse -Force 46 | $session = New-PSSession -ComputerName $computer 47 | Invoke-Command -Session $session -ScriptBlock { 48 | $process = New-Object System.Diagnostics.Process 49 | $process.StartInfo.FileName = "C:\java\jre-8u202-windows-x64.exe" 50 | $process.StartInfo.Arguments = " /s" 51 | $process.StartInfo.Verb = "RunAs" 52 | $process.Start() 53 | } 54 | Enter-PSSession -Session $session 55 | Exit-PSSession 56 | } 57 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AzureAutomationRunbooks/3rdPartyPatching/VNC.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2019.02.07 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2019 Microsoft Corporation. 10 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 11 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 12 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 13 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 14 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 15 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 16 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 17 | documentation, even if Microsoft has been advised of the possibility of such damages. 18 | #> 19 | 20 | #Required parameters to run on a schedule or on-demand. 21 | param( 22 | [Parameter(Mandatory = $true)] 23 | [string]$sourcePath, 24 | [Parameter(Mandatory = $true)] 25 | [string]$workspaceId, 26 | [Parameter(Mandatory = $true)] 27 | [string]$query 28 | ) 29 | 30 | #Specify the Azure Automation connection. 31 | $Conn = Get-AutomationConnection -Name AzureRunAsConnection 32 | Connect-AzAccount -ServicePrincipal -Tenant $Conn.TenantID ` 33 | -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint 34 | Set-AzContext -SubscriptionId $Conn.SubscriptionID 35 | 36 | #Required Parameters to test with AzureAutomationAuthoringToolkit. Comment out with # if running inside Azure on a schedule. 37 | $sourcePath = '\\server\share\AppPatching\RealVNC\' 38 | $workspaceId = "Log Analytics workspace ID goes here" 39 | $query = 'ConfigurationData | where ConfigDataType == "Software" | where SoftwareName == "VNC Server 6.0.0" | distinct Computer' 40 | $queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceId -Query $query 41 | $computername = $queryResults.Results | Select-Object -ExpandProperty Computer 42 | 43 | #Run the installation script. 44 | ForEach($computer in $computername){ 45 | Copy-Item -Path $sourcePath -Destination \\$computer\c$\ -Recurse -Force 46 | $session = New-PSSession -ComputerName $computer 47 | Invoke-Command -Session $session -ScriptBlock { 48 | $process = New-Object System.Diagnostics.Process 49 | $process.StartInfo.FileName = "msiexec" 50 | $process.StartInfo.Arguments = " /i `"C:\RealVNC\VNC-Server-6.4.0-Windows-en-64bit.msi`" /qn" 51 | $process.StartInfo.Verb = "RunAs" 52 | $process.Start() 53 | } 54 | Enter-PSSession -Session $session 55 | Exit-PSSession 56 | } 57 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AzureAutomationRunbooks/3rdPartyPatching/WinZip.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2019.02.07 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2019 Microsoft Corporation. 10 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 11 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 12 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 13 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 14 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 15 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 16 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 17 | documentation, even if Microsoft has been advised of the possibility of such damages. 18 | #> 19 | 20 | #Required parameters to run on a schedule or on-demand. 21 | param( 22 | [Parameter(Mandatory = $true)] 23 | [string]$sourcePath, 24 | [Parameter(Mandatory = $true)] 25 | [string]$workspaceId, 26 | [Parameter(Mandatory = $true)] 27 | [string]$query 28 | ) 29 | 30 | #Specify the Azure Automation connection. 31 | $Conn = Get-AutomationConnection -Name AzureRunAsConnection 32 | Connect-AzAccount -ServicePrincipal -Tenant $Conn.TenantID ` 33 | -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint 34 | Set-AzContext -SubscriptionId $Conn.SubscriptionID 35 | 36 | #Required Parameters to test with AzureAutomationAuthoringToolkit. Comment out with # if running inside Azure on a schedule. 37 | $sourcePath = '\\server\share\AppPatching\WinZip\' 38 | $workspaceId = "Log Analytics workspace ID goes here" 39 | $query = 'ConfigurationData | where ConfigDataType == "Software" | where SoftwareName == "WinZip 21.0" | distinct Computer' 40 | $queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceId -Query $query 41 | $computername = $queryResults.Results | Select-Object -ExpandProperty Computer 42 | 43 | #Run the installation script. 44 | ForEach($computer in $computername){ 45 | Copy-Item -Path $sourcePath -Destination \\$computer\c$\ -Recurse -Force 46 | $session = New-PSSession -ComputerName $computer 47 | Invoke-Command -Session $session -ScriptBlock { 48 | $process = New-Object System.Diagnostics.Process 49 | $process.StartInfo.FileName = "msiexec" 50 | $process.StartInfo.Arguments = " /i `"C:\WinZip\winzip230-64.msi`" /qn" 51 | $process.StartInfo.Verb = "RunAs" 52 | $process.Start() 53 | } 54 | Enter-PSSession -Session $session 55 | Exit-PSSession 56 | } 57 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AzureAutomationRunbooks/3rdPartyPatching/Wireshark.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2019.02.07 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2019 Microsoft Corporation. 10 | 11 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 12 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 13 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 14 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 15 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 16 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 17 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 18 | documentation, even if Microsoft has been advised of the possibility of such damages. 19 | #> 20 | 21 | #Required parameters to run on a schedule or on-demand. 22 | param( 23 | [Parameter(Mandatory = $true)] 24 | [string]$sourcePath, 25 | [Parameter(Mandatory = $true)] 26 | [string]$workspaceId, 27 | [Parameter(Mandatory = $true)] 28 | [string]$query 29 | ) 30 | 31 | #Specify the Azure Automation connection. 32 | $Conn = Get-AutomationConnection -Name AzureRunAsConnection 33 | Connect-AzAccount -ServicePrincipal -Tenant $Conn.TenantID ` 34 | -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint 35 | Set-AzContext -SubscriptionId $Conn.SubscriptionID 36 | 37 | #Required Parameters to test with AzureAutomationAuthoringToolkit. Comment out with # if running inside Azure on a schedule. 38 | $sourcePath = '\\server\share\AppPatching\Wireshark' 39 | $workspaceId = "Log Analytics workspace ID goes here" 40 | $query = "ConfigurationData | where ConfigDataType == 'Software' | where SoftwareName == 'Wireshark 2.6.3 64-bit' | distinct Computer" 41 | $queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceId -Query $query 42 | $computername = $queryResults.Results | Select-Object -ExpandProperty Computer 43 | 44 | #Run the installation script. 45 | ForEach($computer in $computername){ 46 | Copy-Item -Path $sourcePath -Destination \\$computer\c$\ -Recurse -Force 47 | $session = New-PSSession -ComputerName $computer 48 | Invoke-Command -Session $session -ScriptBlock { 49 | $process = New-Object System.Diagnostics.Process 50 | $process.StartInfo.FileName = "C:\Wireshark\Wireshark-win64-2.6.5.exe" 51 | $process.StartInfo.Arguments = " /S" 52 | $process.StartInfo.Verb = "RunAs" 53 | $process.Start() 54 | } 55 | Enter-PSSession -Session $session 56 | Exit-PSSession 57 | } 58 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AzureAutomationRunbooks/Export-RunAsCertificateToHybridWorker.ps1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | https://www.powershellgallery.com/packages/Export-RunAsCertificateToHybridWorker/1.0/Content/Export-RunAsCertificateToHybridWorker.ps1 3 | 4 | .VERSION 1.0 5 | .GUID 3a796b9a-623d-499d-86c8-c249f10a6986 6 | .AUTHOR Azure Automation Team 7 | .COMPANYNAME Microsoft 8 | .COPYRIGHT 9 | .TAGS Azure Automation 10 | .LICENSEURI 11 | .PROJECTURI 12 | .ICONURI 13 | .EXTERNALMODULEDEPENDENCIES 14 | .REQUIREDSCRIPTS 15 | .EXTERNALSCRIPTDEPENDENCIES 16 | .RELEASENOTES 17 | #> 18 | 19 | <# 20 | .SYNOPSIS 21 | Exports the Run As certificate from an Azure Automation account to a hybrid worker in that account. 22 | 23 | .DESCRIPTION 24 | This runbook exports the Run As certificate from an Azure Automation account to a hybrid worker in that account. 25 | Run this runbook in the hybrid worker where you want the certificate installed. 26 | This allows the use of the AzureRunAsConnection to authenticate to Azure and manage Azure resources from runbooks running in the hybrid worker. 27 | 28 | .EXAMPLE 29 | .\Export-RunAsCertificateToHybridWorker 30 | 31 | .NOTES 32 | AUTHOR: Azure Automation Team 33 | LASTEDIT: 2016.10.13 34 | #> 35 | 36 | [OutputType([string])] 37 | 38 | # Set the password used for this certificate 39 | $Password = "YourStrongPasswordForTheCert" 40 | 41 | # Stop on errors 42 | $ErrorActionPreference = 'stop' 43 | 44 | # Get the management certificate that will be used to make calls into Azure Service Management resources 45 | $RunAsCert = Get-AutomationCertificate -Name "AzureRunAsCertificate" 46 | 47 | # location to store temporary certificate in the Automation service host 48 | $CertPath = Join-Path $env:temp "AzureRunAsCertificate.pfx" 49 | 50 | # Save the certificate 51 | $Cert = $RunAsCert.Export("pfx",$Password) 52 | Set-Content -Value $Cert -Path $CertPath -Force -Encoding Byte | Write-Verbose 53 | 54 | Write-Output ("Importing certificate into local machine root store from " + $CertPath) 55 | $SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force 56 | Import-PfxCertificate -FilePath $CertPath -CertStoreLocation Cert:\LocalMachine\My -Password $SecurePassword -Exportable | Write-Verbose 57 | 58 | # Test that authentication to Azure ARM is working 59 | $RunAsConnection = Get-AutomationConnection -Name "AzureRunAsConnection" 60 | 61 | Add-AzureRmAccount ` 62 | -ServicePrincipal ` 63 | -TenantId $RunAsConnection.TenantId ` 64 | -ApplicationId $RunAsConnection.ApplicationId ` 65 | -CertificateThumbprint $RunAsConnection.CertificateThumbprint | Write-Verbose 66 | 67 | Select-AzureRmSubscription -SubscriptionId $RunAsConnection.SubscriptionID | Write-Verbose 68 | 69 | # List automation accounts to confirm ARM calls are working 70 | Get-AzureRmAutomationAccount | Select AutomationAccountName 71 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AzureAutomationRunbooks/README.md: -------------------------------------------------------------------------------- 1 | Get-AutomationConnection Explained: 2 |
Get-AutomationConnection is an internal cmdlet to Azure Automation that allows you to get a relative connection 3 | from the automation account without going through the Azure Resource Manager APIs. Note that the cmdlet is not 4 | Get-AzureAutomationConnection (which is the old service management cmdlet) or Get-AzureRMAutomationConnection 5 | (which is the newer resource manager cmdlet). This interal cmdlet is necessary to ensure you can get a Run As 6 | Connection to pass to the Connect-AzureRMAccount cmdlet to authenticate to Azure and manage other resources. 7 | There are other internal cmdlets that are used to retrieve assets, like Get-AutomationVariable, etc. 8 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AzureAutomationRunbooks/RollbackPatches/WindowsRollback.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2019.01.25 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2019 Microsoft Corporation. 10 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 11 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 12 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 13 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 14 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 15 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 16 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 17 | documentation, even if Microsoft has been advised of the possibility of such damages. 18 | #> 19 | 20 | #Required parameters to run on a schedule or on-demand. 21 | param( 22 | [Parameter(Mandatory=$true)] 23 | [string]$query, 24 | [Parameter(Mandatory = $true)] 25 | [string]$workspaceId, 26 | [Parameter(Mandatory = $true)] 27 | [string]$KB 28 | ) 29 | 30 | #Specify the Azure Automation connection. 31 | $RunAsConnection = Get-AutomationConnection -Name AzureRunAsConnection 32 | Connect-AzAccount -CertificateThumbprint $RunAsConnection.CertificateThumbprint ` 33 | -ApplicationId $RunAsConnection.ApplicationID -Tenant $RunAsConnection.TenantID -ServicePrincipal 34 | Set-AzContext -SubscriptionId $RunAsConnection.SubscriptionID 35 | 36 | #Required Parameters to test with AzureAutomationAuthoringToolkit. Comment out with # if running inside Azure on a schedule. 37 | $workspaceId = "Log Analytics workspace ID goes here" 38 | $KB = 'List KB to uninstall with just numbers, no KB in front' 39 | $query = 'ConfigurationData | where ConfigDataType == "Software" | where SoftwareName == "Security Update for Windows Server 2012 R2 (KB3177186)" | project Computer | distinct Computer' 40 | 41 | #Azure Automation Runbook script. 42 | $queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceId -Query $query 43 | $computers = $queryResults.Results | Select-Object -ExpandProperty Computer 44 | foreach ($computer in $computers) 45 | { 46 | Invoke-Command -ComputerName $computer -ScriptBlock {C:\Windows\System32\wusa.exe /kb:$KB /uninstall /quiet /norestart} -Verbose 47 | } 48 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/AzureAutomationRunbooks/RollbackPatches/linRollBack.py: -------------------------------------------------------------------------------- 1 | # 2 | # Created 3 | # 2019.02.27 4 | # Shannon Kuehn 5 | # Last Updated 6 | # 7 | # © 2019 Microsoft Corporation. 8 | # All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 9 | # or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 10 | # warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 11 | # The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 12 | # shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 13 | # any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 14 | # business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 15 | # documentation, even if Microsoft has been advised of the possibility of such damages. 16 | 17 | 18 | import os 19 | from azure.mgmt.compute import ComputeManagementClient 20 | import azure.mgmt.resource 21 | import automationassets 22 | 23 | def get_automation_runas_credential(runas_connection): 24 | from OpenSSL import crypto 25 | import binascii 26 | from msrestazure import azure_active_directory 27 | import adal 28 | 29 | # Get the Azure Automation RunAs service principal certificate 30 | cert = automationassets.get_automation_certificate("AzureRunAsCertificate") 31 | pks12_cert = crypto.load_pkcs12(cert) 32 | pem_pkey = crypto.dump_privatekey(crypto.FILETYPE_PEM,pks12_cert.get_privatekey()) 33 | 34 | # Get run as connection information for the Azure Automation service principal 35 | application_id = runas_connection["ApplicationId"] 36 | thumbprint = runas_connection["CertificateThumbprint"] 37 | tenant_id = runas_connection["TenantId"] 38 | 39 | # Authenticate with service principal certificate 40 | resource ="https://management.core.windows.net/" 41 | authority_url = ("https://login.microsoftonline.com/"+tenant_id) 42 | context = adal.AuthenticationContext(authority_url) 43 | return azure_active_directory.AdalAuthentication( 44 | lambda: context.acquire_token_with_client_certificate( 45 | resource, 46 | application_id, 47 | pem_pkey, 48 | thumbprint) 49 | ) 50 | 51 | # Authenticate to Azure using the Azure Automation RunAs service principal 52 | runas_connection = automationassets.get_automation_connection("AzureRunAsConnection") 53 | azure_credential = get_automation_runas_credential(runas_connection) 54 | 55 | # Initialize the compute management client with the RunAs credential and specify the subscription to work against. 56 | compute_client = ComputeManagementClient( 57 | azure_credential, 58 | str(runas_connection["SubscriptionId"]) 59 | ) 60 | 61 | list_of_servers = ["host1", "host2", "host3", "host4", "host5"] 62 | 63 | for server in list_of_servers: 64 | subprocess.call(['yum remove package_name'], shell=True) 65 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/GroupSchedule/01-SvrGrping.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2018.11.26 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2018 Microsoft Corporation. 10 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 11 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 12 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 13 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 14 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 15 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 16 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 17 | documentation, even if Microsoft has been advised of the possibility of such damages. 18 | #> 19 | 20 | $workspaceId = "Log Analytics workspace ID goes here" 21 | $scriptBlock = .{ 22 | $serverinfo = Get-Content -Path "C:\temp\servergrouping.csv" | ConvertFrom-Csv 23 | $query = "Heartbeat | summarize arg_max(TimeGenerated, *) by SourceComputerId | top 500000 by Computer asc" 24 | $queryResults = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceId -Query $query 25 | $queryResults.Results 26 | $servers = $queryResults.Results | Select-Object -ExpandProperty Computer 27 | } 28 | $group1 = @() 29 | $group2 = @() 30 | $group3 = @() 31 | 32 | ForEach($server in $serverinfo) 33 | { 34 | Switch ($server.patchSchedule){ 35 | 'patch_schedule01' { 36 | $new1 = $servers | ? {$_ -eq $server.servername} | Select-Object -First 1 37 | $group1+=$new1 38 | } 39 | 'patch_schedule02' { 40 | $new2 = $servers | ? {$_ -eq $server.servername} | Select-Object -First 1 41 | $group2+=$new2 42 | } 43 | 'patch_schedule03' { 44 | $new3 = $servers | ? {$_ -eq $server.servername} | Select-Object -First 1 45 | $group3+=$new3 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/GroupSchedule/02-WinSvrSched.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2018.11.26 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2018 Microsoft Corporation. 10 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 11 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 12 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 13 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 14 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 15 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 16 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 17 | documentation, even if Microsoft has been advised of the possibility of such damages. 18 | #> 19 | 20 | function SetSchedule { 21 | param( 22 | [Parameter(Mandatory=$true)] 23 | [string]$startTime, 24 | [Parameter(Mandatory=$true)] 25 | [array]$group, 26 | [Parameter(Mandatory=$true)] 27 | [string]$resourceGroup, 28 | [Parameter(Mandatory=$true)] 29 | [string]$automationAccount, 30 | [Parameter(Mandatory=$true)] 31 | [string]$DaysofMonth, 32 | [Parameter(Mandatory=$true)] 33 | [int]$durationHours, 34 | [Parameter(Mandatory=$false)] 35 | [int]$monthInterval, 36 | [Parameter(Mandatory=$true)] 37 | [string]$scheduleName 38 | ) 39 | 40 | $duration = New-TimeSpan -Hours $durationHours 41 | $schedule = New-AzAutomationSchedule -ResourceGroupName $resourceGroup ` 42 | -AutomationAccountName $automationAccount ` 43 | -Name $scheduleName ` 44 | -StartTime $startTime ` 45 | -DaysOfMonth $DaysofMonth ` 46 | -MonthInterval $monthInterval ` 47 | -ForUpdateConfiguration 48 | 49 | New-AzAutomationSoftwareUpdateConfiguration -ResourceGroupName $resourceGroup ` 50 | -AutomationAccountName $automationAccount ` 51 | -Schedule $schedule ` 52 | -Windows ` 53 | -NonAzureComputer $group ` 54 | -IncludedUpdateClassification Critical ` 55 | -Duration $duration 56 | } 57 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/GroupSchedule/03-LinuxSvrSched.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2018.11.26 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2018 Microsoft Corporation. 10 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 11 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 12 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 13 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 14 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 15 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 16 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 17 | documentation, even if Microsoft has been advised of the possibility of such damages. 18 | #> 19 | 20 | function SetSchedule { 21 | param( 22 | [Parameter(Mandatory=$true)] 23 | [string]$startTime, 24 | [Parameter(Mandatory=$true)] 25 | [array]$group, 26 | [Parameter(Mandatory=$true)] 27 | [string]$resourceGroup, 28 | [Parameter(Mandatory=$true)] 29 | [string]$automationAccount, 30 | [Parameter(Mandatory=$true)] 31 | [string]$DaysofMonth, 32 | [Parameter(Mandatory=$true)] 33 | [int]$durationHours, 34 | [Parameter(Mandatory=$false)] 35 | [int]$monthInterval, 36 | [Parameter(Mandatory=$true)] 37 | [string]$scheduleName 38 | ) 39 | 40 | $duration = New-TimeSpan -Hours $durationHours 41 | $schedule = New-AzAutomationSchedule -ResourceGroupName $resourceGroup ` 42 | -AutomationAccountName $automationAccount ` 43 | -Name $scheduleName ` 44 | -StartTime $startTime ` 45 | -DaysOfMonth $DaysofMonth ` 46 | -MonthInterval $monthInterval ` 47 | -ForUpdateConfiguration 48 | 49 | New-AzAutomationSoftwareUpdateConfiguration -ResourceGroupName $resourceGroup ` 50 | -AutomationAccountName $automationAccount ` 51 | -Schedule $schedule ` 52 | -Linux ` 53 | -NonAzureComputer $group ` 54 | -IncludedPackageClassification Critical ` 55 | -Duration $duration 56 | } 57 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/GroupSchedule/README.md: -------------------------------------------------------------------------------- 1 | PowerShell Scripts - Explained 2 |

1) 01-SvrGrping.ps1 - An example script of how to group servers based upon a .csv input. Potentially helpful in the event 3 | of a company not using WSUS or ADDS for server grouping. 4 |

2) 02-WinSvrSched.ps1 - Using variables from the 01-SvrGrping.ps1 script, schedule an Azure Automation runbook job with a 5 | corresponding patch schedule for Windows Servers using the new PowerShell preview cmdlets 6 | (New-AzureRmAutomationSoftwareUpdateConfiguration). 7 |

3) 03-LinuxSvrSched.ps1 - Using variables from the 01-SvrGrping.ps1 script, schedule an Azure Automation runbook job with a 8 | corresponding patch schedule for Linux servers, using the new PowerShell preview cmdlets 9 | (New-AzureRmAutomationSoftwareUpdateConfiguration). 10 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/UpdateAgentReadiness/01-BearerToken.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2018.12.20 4 | Shannon Kuehn 5 | Last Updated 6 | © 2018 Microsoft Corporation. 7 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 8 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 9 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 10 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 11 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 12 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 13 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 14 | documentation, even if Microsoft has been advised of the possibility of such damages. 15 | #> 16 | # Adapted from: 17 | # https://www.codeisahighway.com/how-to-easily-and-silently-obtain-accesstoken-bearer-from-an-existing-azure-powershell-session/ 18 | 19 | function Get-AzureRmCachedAccessToken() 20 | { 21 | $ErrorActionPreference = 'Stop' 22 | 23 | if(-not (Get-Module AzureRm.Profile)) { 24 | Import-Module AzureRm.Profile 25 | } 26 | $azureRmProfileModuleVersion = (Get-Module AzureRm.Profile).Version 27 | # refactoring performed in AzureRm.Profile v3.0 or later 28 | if($azureRmProfileModuleVersion.Major -ge 3) { 29 | $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile 30 | if(-not $azureRmProfile.Accounts.Count) { 31 | Write-Error "Ensure you have logged in before calling this function." 32 | } 33 | } else { 34 | # AzureRm.Profile < v3.0 35 | $azureRmProfile = [Microsoft.WindowsAzure.Commands.Common.AzureRmProfileProvider]::Instance.Profile 36 | if(-not $azureRmProfile.Context.Account.Count) { 37 | Write-Error "Ensure you have logged in before calling this function." 38 | } 39 | } 40 | 41 | $currentAzureContext = Get-AzureRmContext 42 | $profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile) 43 | Write-Debug ("Getting access token for tenant" + $currentAzureContext.Subscription.TenantId) 44 | $token = $profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId) 45 | $token.AccessToken 46 | } 47 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/UpdateAgentReadiness/02-UpdateAgentReadiness.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Created 3 | 2018.12.20 4 | Shannon Kuehn 5 | 6 | Last Updated 7 | 2019.07.08 8 | 9 | © 2018 Microsoft Corporation. 10 | All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 11 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 12 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 13 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 14 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 15 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 16 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 17 | documentation, even if Microsoft has been advised of the possibility of such damages. 18 | #> 19 | 20 | # Fill in local path, Azure subscription ID, resource group, and automation account. 21 | $path = "Local or server file path goes here" 22 | $subId = "Azure subscription ID goes here" 23 | $rg = "Resource Group name goes here" 24 | $automationAccount = "{automation account}" 25 | $bearer = Get-AzureRmCachedAccessToken 26 | $header = @{"Authorization"="Bearer $bearer"} 27 | $hybridWorkerGroups = Get-AzureRmAutomationHybridWorkerGroup -ResourceGroupName $rg -AutomationAccountName $automationAccount 28 | $csv = "MachineName,State,LastSeenTime`r`n" 29 | $file = New-Object -ComObject Scripting.FileSystemObject 30 | $csvFile = $file.CreateTextFile($path,$true) 31 | $csvFile.Write($csv) 32 | $csvFile.Close() 33 | 34 | ForEach($hybridworkerGroup in $hybridWorkerGroups){ 35 | $apiCall = "https://management.azure.com/subscriptions/"+$subId+"/resourceGroups/"+$rg+"/providers/Microsoft.Automation/automationAccounts/"+$automationAccount+"/hybridRunbookWorkerGroups/"+$hybridWorkerGroup.Name+"?api-version=2015-10-31" 36 | $invokeStatus = (Invoke-WebRequest -Uri $apiCall -Headers $header -Method Get) 37 | $state = "" 38 | $lastSeenTime = "" 39 | if($invokeStatus.StatusCode -eq 200) 40 | { 41 | $invokeVar = Invoke-RestMethod -Uri $apiCall -Headers $header -Method Get 42 | $lastSeenDate = Get-Date -Date $invokeVar.hybridRunbookWorkers[0].lastSeenDateTime 43 | $diffTimeSpan = (Get-Date) - $lastSeenDate 44 | if($diffTimeSpan.Hours -gt 1) 45 | { 46 | $state = "disconnected" 47 | } 48 | else { 49 | $state = "ready" 50 | } 51 | } 52 | elseif($invokeStatus.StatusCode -eq 404) 53 | { 54 | $state = "not configured" 55 | } 56 | else 57 | { 58 | $state = "error" 59 | } 60 | $lastSeenTime = $hybridworkerGroup.RunbookWorker.LastSeenDateTime.LocalDateTime.ToShortDateString()+ " " + $hybridworkerGroup.RunbookWorker.LastSeenDateTime.LocalDateTime.ToLongTimeString() 61 | Add-Content $path "$($hybridworkerGroup.RunbookWorker.Name),$state,$lastSeenTime" 62 | } 63 | -------------------------------------------------------------------------------- /sample-UpdateManagement/PowerShell/UpdateAgentReadiness/README.md: -------------------------------------------------------------------------------- 1 | Update Agent Readiness - Explained: 2 |
In the portal, the Update Agent Readiness column data is lazy-loaded. Azure checks readiness of every machine individually using the following REST API. 3 |

Update Agent Readiness - GET Calls: 4 |
The update readiness metric solely checks if the patch agent (System HybridWorker) is registered and actively pinging. Each response code (Ready, Disconnected, Not configured) denotes a specific readiness state within the column. 5 |
1) Not Configured - the GET call resolves to 404. 6 |
2) Disconnected - The GET call resolves to 200, but lastSeen property value (related to ping time) is older than an hour ago. 7 |
3) Ready - The GET call resolves to 200 and the lastSeen property is less than an hour ago. 8 |

UpdateAgentReadiness Instructions: 9 |
1) Run the 01-BearerToken.ps1 while logged into Azure PowerShell with an active account and pointed at the working sub. 10 |
2) The 01-BearerToken.ps1 loads the actual Bearer Token into memory. This gets called upon in the UpdateAgentReadiness.ps1 11 | script by way of using Get-AzureRmCachedAccessToken. 12 |
3) The 02-UpdateAgentReadiness.ps1 script will output all servers checked into Update Management inside 1 .csv file. Having 13 | this information will be helpful if grouping servers within a patch group. If the server reports as "Not configured" or 14 | "Disconnected," the patching fails. 15 | -------------------------------------------------------------------------------- /sample-UpdateManagement/README.md: -------------------------------------------------------------------------------- 1 | # Update Management 2 | 3 | Sample Code and Documentation to Assist Deployment and Management of On-Premises and AWS Servers 4 | 5 | Microsoft Documentation: 6 |
Azure Rm Automation Schedule 7 |
Azure Rm Automation Software Update Configuration 8 |

General Troubleshooting:
1) Ensure Azure Az PowerShell module is completely up to date.
2) Troubleshoot Update Agent Readiness: Not Configured 9 |

Troubleshooting MMA Agent: 10 |
Windows Troubleshooting 11 |
Linux Troubleshooting 12 |
Troubleshoot Hybrid Runbook Worker 13 |
Windows Troubleshooter Tool 14 |
Linux Troubleshooter Tool 15 |

General Information: 16 |
1) As of January 2019, patch groups are limited to 500 servers. If there are more than 500 servers, customers will need to divide into multiple groups. 17 |
2) If WSUS is involved with the deployment, Windows looks to WSUS as the control plane with excluded and included patches. 18 |
3) Standalone WSUS works well, even if there is a substantial amount of Windows machines. 19 |
4) Pay attention to the total number of nodes if using an OMS Gateway. If servers cannot be assessed in the portal underneath Update Management and telneting to the machines over appropriate ports (default is 8081) does not work, plan to build out another OMS Gateway and use a load balancer. The easiest way to course correct is to assign a new static IP address to the first OMS Gateway, assign a new static IP address to the second OMS Gateway, and use the static IP of the first OMS Gateway as your VIP on the load balancer. You will not need to adjust the deployment on all servers' MMA configurations this way. 20 | 21 | -------------------------------------------------------------------------------- /sample-UpdateManagement/WSUS Deployment/README.md: -------------------------------------------------------------------------------- 1 | Additional WSUS Deployment Reference Documentation: 2 |

Deploy Windows Server Update Services 3 |
Plan Your WSUS Deployment 4 | -------------------------------------------------------------------------------- /sample-UpdateManagement/WSUS Deployment/WSUS30SP2DeployGuide.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/sample-UpdateManagement/WSUS Deployment/WSUS30SP2DeployGuide.doc -------------------------------------------------------------------------------- /sample-UpdateManagement/WSUS Deployment/WSUS30SP2DeployGuide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/csa-misc-utils/6108d2fcbc9025555929d8ff4dde36b9ebd44d96/sample-UpdateManagement/WSUS Deployment/WSUS30SP2DeployGuide.pdf -------------------------------------------------------------------------------- /sample-VMSS-DomainJoin-Arm/README.md: -------------------------------------------------------------------------------- 1 | ## Azure ARM Samples -- VMSS with a domain join 2 | 3 | I was recently working with a customer who needed to create VMs on-demand that were domain joined to complete HPC compute operations. This is an example template highlighting how to create VM Scale Set instances from a template pointing to a 'golden image' as a source and domain joining each VM spun up. 4 | 5 | Note: The source image must reside in the same region as the VM Scale Sets for this to function. The source image can reside in a different resource group; as there are parameters for both the source image and resource group as well as the name of an existing Virtual Network and subnet to deploy each instance into. 6 | 7 | 8 | * [VMSS Example](./WindowsVirtualMachineScaleSet_DomainJoin.json). 9 | The example ARM template 10 | 11 | -------------------------------------------------------------------------------- /sample-VMSS-DomainJoin-Arm/WindowsVirtualMachineScaleSet.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "vmssName": { 6 | "value": null 7 | }, 8 | "instanceCount": { 9 | "value": 1 10 | }, 11 | "adminUsername": { 12 | "value": "user" 13 | }, 14 | "adminPassword": { 15 | "value": "Pass" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /sample-k8sRefArch/README.md: -------------------------------------------------------------------------------- 1 | Created 2 |
2019.07.05 3 |
Shannon Kuehn 4 |

5 | © 2019 Microsoft Corporation. 6 |
All rights reserved. Sample scripts/code provided herein are not supported under any Microsoft standard support program 7 | or service. The sample scripts/code are provided AS IS without warranty of any kind. Microsoft disclaims all implied 8 | warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 9 | The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 10 | shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for 11 | any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of 12 | business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or 13 | documentation, even if Microsoft has been advised of the possibility of such damages.
14 | 15 | # Infrastructure as Code 16 | Reference Architecture 17 |
Azure Kubernetes Service (AKS) - Azure CNI Plugin 18 |

Files/Folders for this Repository: 19 |
1) **README.md** - markdown file that contains all information for repo (files, folders, steps). 20 |
2) **setup folder** - general setup information (Azure CLI, PowerShell, generating ssh keys, setting up Service Principal) 21 |
3) **keyVault folder** - code to set up a Key Vault for public SSH keys, Service Principal secret, and SSL cert password. 22 |
4) **k8s folder** - base template and yaml files for secure, managed K8s cluster on Azure. 23 |
5) **appGw folder** - takes the k8s configuration files and adds an Application Gateway. 24 |
6) **apim-appGw folder** - takes the k8s configuration files and adds an API Management Gateway + Application Gateway. 25 | -------------------------------------------------------------------------------- /sample-k8sRefArch/apim-appGw/README.md: -------------------------------------------------------------------------------- 1 | # Application Gateway and API Management Gateway 2 |
Getting to this folder means you started with the setup, keyVault, and k8s folders. By the point you reach this folder, you should have a public SSH key stored in Key Vault, a Service Principal with it's secret stored in Key Vault, a VNet, and a K8s cluster with an internal ingress controller. In the event you do not have that set up, please ensure you start with the setup and keyVault folders. The K8s template and yaml files within this folder will also generate the same AKS setup so you can add an API Management Gateway and Application Gateway. The API Management Gateway will publish a health check API that serves as an echo service to itself so the Application Gateway backend health can report as "healthy." 3 | 4 | **Deployment Notes** 5 |

There are a few additional files in this folder: 6 |
1) **appGwApimDeploy.json** - this is the deployment ARM template for both the Application Gateway and the API Management Gateway. The APIM is set as internal and the healthcheck API code is configured as an API, an API operation, and an API policy. The end configuration has a custom healthcheck API exposed on the APIM that allows the Application Gateway's backends to report as healthy. 7 |
2) **appGwApimParams.json** - these are the parameters for the Application Gateway and APIM deployment. 8 | 9 | **Information on K8s Configuration** 10 |

Within this folder, you will find the following files: 11 | 12 | 1) **aksDeploy.json** - ARM template that deploys the Kubernetes cluster. This file declares all the Kubernetes cluster, which comprises of deploying nodes (VMs), nics, the Availability Set, and the NSG. Additional comments: 13 |
- This deployment does not deploy a public load balancer. 14 |
- This deployment reports to an existing Log Analytics workspace.
15 | 16 | 2) **aksParams.json** - this is the parameters file for the ARM template deployment. Future deployments can be conducted by editing these parameters first adn re-deploying. 17 | 18 | 3) **kubectl-helm-commands.md** - this file guides you on how to connect to your Kubernetes cluster, create a namespace, create a Tiller service account for helm, install helm, and create an internal ingress controller for your Kubernetes cluster. 19 | 20 | 4) **helm-rbac.yaml** - this is the file to run while connected to your Kubernetes cluster so you can create the Tiller service account for helm. 21 | 22 | 5) **ingress-internal.yaml** - this is the file to use for your larger helm command to install an nginx internal ingress controller. 23 | 24 | -------------------------------------------------------------------------------- /sample-k8sRefArch/apim-appGw/aksParams.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "spAppId": { 6 | "value": "e1ce23e5-4c4e-4aa0-ad39-7d945f6695c3" 7 | }, 8 | "spObjectId": { 9 | "value": "eb8e6c40-2735-43a7-a68e-0e127a14d80b" 10 | }, 11 | "spSecret": { 12 | "reference": { 13 | "keyVault": { 14 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 15 | }, 16 | "secretName": "spPw" 17 | } 18 | }, 19 | "rgName": { 20 | "value": "k8s" 21 | }, 22 | "vnetName": { 23 | "value": "k8sVnet" 24 | }, 25 | "vnetTag": { 26 | "value": "AKS vNet" 27 | }, 28 | "vnetAddrPrefix": { 29 | "value": "10.10.0.0/16" 30 | }, 31 | "sub1Name": { 32 | "value": "k8sCluster" 33 | }, 34 | "sub1Prefix": { 35 | "value": "10.10.1.0/24" 36 | }, 37 | "sub2Name": { 38 | "value": "k8sServices" 39 | }, 40 | "sub2Prefix": { 41 | "value": "10.10.2.0/24" 42 | }, 43 | "logAnalyticsWorkspaceId": { 44 | "value": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourcegroups/sbk-la/providers/microsoft.operationalinsights/workspaces/sbk-log-analytics-25924" 45 | }, 46 | "aksClusterName": { 47 | "value": "k8s" 48 | }, 49 | "dnsPrefix": { 50 | "value": "k8ssbk" 51 | }, 52 | "k8sVersion": { 53 | "value": "1.12.8" 54 | }, 55 | "nodeCount": { 56 | "value": 3 57 | }, 58 | "nodeVmSize": { 59 | "value": "Standard_DS1_v2" 60 | }, 61 | "osType": { 62 | "value": "Linux" 63 | }, 64 | "osAdminUser": { 65 | "value": "sbkadmin" 66 | }, 67 | "sshPubKey": { 68 | "reference": { 69 | "keyVault": { 70 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 71 | }, 72 | "secretName": "k8sSsh" 73 | } 74 | }, 75 | "maxPods": { 76 | "value": 31 77 | }, 78 | "enableHttpApplicationRouting": { 79 | "value": false 80 | }, 81 | "networkPlugin": { 82 | "value": "azure" 83 | }, 84 | "enableRBAC": { 85 | "value": true 86 | }, 87 | "serviceCidr": { 88 | "value": "10.0.0.0/16" 89 | }, 90 | "dnsServiceIP": { 91 | "value": "10.0.0.10" 92 | }, 93 | "dockerBridgeCidr": { 94 | "value": "172.17.0.1/16" 95 | }, 96 | "aksEnv": { 97 | "value": "k8s" 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /sample-k8sRefArch/apim-appGw/appGwApimParams.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "existingVNETName": { 6 | "value": "k8sVnet" 7 | }, 8 | "newSubnetName1": { 9 | "value": "appGw" 10 | }, 11 | "newSubnetAddressPrefix1": { 12 | "value": "10.10.3.0/24" 13 | }, 14 | "newSubnetName2": { 15 | "value": "apim" 16 | }, 17 | "newSubnetAddressPrefix2": { 18 | "value": "10.10.4.0/24" 19 | }, 20 | "publisherEmail": { 21 | "value": "Shannon.Kuehn@microsoft.com" 22 | }, 23 | "publisherName": { 24 | "value": "Microsoft" 25 | }, 26 | "frontendCertData": { 27 | "reference": { 28 | "keyVault": { 29 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 30 | }, 31 | "secretName": "wildcardsslcert" 32 | } 33 | }, 34 | "sslCertificatePassword": { 35 | "reference": { 36 | "keyVault": { 37 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 38 | }, 39 | "secretName": "sslcertpw" 40 | } 41 | }, 42 | "appGtwyName": { 43 | "value": "k8s" 44 | }, 45 | "appGtwySize": { 46 | "value": "WAF_Medium" 47 | }, 48 | "appGtwyCapacity": { 49 | "value": 1 50 | }, 51 | "appGatewayRuleName1": { 52 | "value": "appGwRule1" 53 | }, 54 | "appGatewayRuleName2": { 55 | "value": "appGwRule2" 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sample-k8sRefArch/apim-appGw/helm-rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tiller 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: tiller 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: tiller 18 | namespace: kube-system 19 | -------------------------------------------------------------------------------- /sample-k8sRefArch/apim-appGw/ingress-internal.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | service: 3 | loadBalancerIP: 10.10.1.200 4 | annotations: 5 | service.beta.kubernetes.io/azure-load-balancer-internal: "true" 6 | -------------------------------------------------------------------------------- /sample-k8sRefArch/apim-appGw/kubectl-helm-commands.md: -------------------------------------------------------------------------------- 1 | ## kubectl and helm commands 2 | 3 | 4 | Connect and Configure Internal Ingress Controller for K8s: 5 | 6 | az aks get-credentials --name k8sSecure --resource-group k8sSecure 7 | Merged "k8sSecure" as current context in /Users/username/.kube/config 8 | 9 | kubectl apply -f ingress-internal-l.yaml 10 | kubectl get service secure-k8s 11 | 12 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 13 | internal-app LoadBalancer 10.0.248.59 10.10.1.200 80:30555/TCP 2m 14 | 15 | Create namespace internal-ingress 16 | 17 | kubectl create namespace internal-ingress 18 | 19 | Install Helm Locally or via Cloud Shell (then use helm-rbac.yaml file from repo for next step) 20 | 21 | kubectl apply -f helm-rbac.yaml 22 | helm init \ 23 | --service-account tiller \ 24 | --node-selectors "beta.kubernetes.io/os"="linux" 25 | 26 | Use Helm to Deploy a NGINX Ingress Controller (use ingress-internal.yaml file in repo) 27 | 28 | helm install stable/nginx-ingress \ 29 | --namespace internal-ingress \ 30 | -f ingress-internal.yaml \ 31 | --set controller.replicaCount=2 \ 32 | --set controller.nodeSelector."beta\.kubernetes\.io/os"=linux \ 33 | --set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux 34 | 35 | kubectl get service -l app=nginx-ingress --namespace internal-ingress 36 | 37 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORTS AGE 38 | alternating-coral-nginx-ingress-controller LoadBalancer 10.0.248.59 10.10.1.200 80:31507/TCP,443:30707/TCP 1m 39 | alternating-coral-nginx-ingress-default-backend ClusterIP 10.0.134.66 80/TCP 1m 40 | 41 | -------------------------------------------------------------------------------- /sample-k8sRefArch/appGw/README.md: -------------------------------------------------------------------------------- 1 | # Application Gateway 2 |
Getting to this folder means you started with the setup, keyVault, and k8s folders. By the point you reach this folder, you should have a public SSH key stored in Key Vault, a Service Principal with secret stored in Key Vault, a VNet, and a K8s cluster with an internal ingress controller. In the event you do not have that set up, please ensure you start with the setup and keyVault folders. The files K8s within this folder will also generate the same AKS setup so you can add an Application Gateway. The difference between this folder and the k8s folder is you will create an echo api service on your cluster. 3 | 4 | **Deployment Notes** 5 |

There are a few additional files in this folder: 6 |
1) **echo-api.yaml** - once the K8s cluster is deployed, apply this yaml file to the K8s cluster by running kubectl apply -f echo-api.yaml (as is, no adjustments). The yaml file has already been adjusted from an internal ingress GitHub sample repo, located here. The major tweaks to this yaml file are related to removing the auth headers from the original. Deploying the echo service allows the Application Gateway to use a custom probe for backend health reporting. 7 |
2) **appGwDeploy.json** - this is the ARM template that adds an Application Gateway to the final deployment. The SSL cert password needs to be passed as a string vs. a secure string. There is a custom probe that leans on an echo service you deploy prior to deploying the Application Gateway. 8 |
3) **appGwParams.json** - this is the parameters file for the ARM template deployment. 9 |

Deploying all 3 files will add an Application Gateway to your existing internal K8s cluster. The Application Gateway will have a public IP address, have WAF enabled, and WAF rules are configured for deployment. All configurations can be adjusted to fit your deployment requirements. 10 | 11 | **Information on K8s Configuration** 12 |

Within this folder, you will find the following files: 13 | 14 | 1) **aksDeploy.json** - ARM template that deploys the Kubernetes cluster. This file declares all the Kubernetes cluster, which comprises of deploying nodes (VMs), nics, the Availability Set, and the NSG. Additional comments: 15 |
- This deployment does not deploy a public load balancer. 16 |
- This deployment reports to an existing Log Analytics workspace.
17 | 18 | 2) **aksParams.json** - this is the parameters file for the ARM template deployment. Future deployments can be conducted by editing these parameters first adn re-deploying. 19 | 20 | 3) **kubectl-helm-commands.md** - this file guides you on how to connect to your Kubernetes cluster, create a namespace, create a Tiller service account for helm, install helm, and create an internal ingress controller for your Kubernetes cluster. 21 | 22 | 4) **helm-rbac.yaml** - this is the file to run while connected to your Kubernetes cluster so you can create the Tiller service account for helm. 23 | 24 | 5) **ingress-internal.yaml** - this is the file to use for your larger helm command to install an nginx internal ingress controller. 25 | 26 | -------------------------------------------------------------------------------- /sample-k8sRefArch/appGw/aksParams.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "spAppId": { 6 | "value": "e1ce23e5-4c4e-4aa0-ad39-7d945f6695c3" 7 | }, 8 | "spObjectId": { 9 | "value": "eb8e6c40-2735-43a7-a68e-0e127a14d80b" 10 | }, 11 | "spSecret": { 12 | "reference": { 13 | "keyVault": { 14 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 15 | }, 16 | "secretName": "spPw" 17 | } 18 | }, 19 | "rgName": { 20 | "value": "k8s" 21 | }, 22 | "vnetName": { 23 | "value": "k8sVnet" 24 | }, 25 | "vnetTag": { 26 | "value": "AKS vNet" 27 | }, 28 | "vnetAddrPrefix": { 29 | "value": "10.10.0.0/16" 30 | }, 31 | "sub1Name": { 32 | "value": "k8sCluster" 33 | }, 34 | "sub1Prefix": { 35 | "value": "10.10.1.0/24" 36 | }, 37 | "sub2Name": { 38 | "value": "k8sServices" 39 | }, 40 | "sub2Prefix": { 41 | "value": "10.10.2.0/24" 42 | }, 43 | "logAnalyticsWorkspaceId": { 44 | "value": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourcegroups/sbk-la/providers/microsoft.operationalinsights/workspaces/sbk-log-analytics-25924" 45 | }, 46 | "aksClusterName": { 47 | "value": "k8s" 48 | }, 49 | "dnsPrefix": { 50 | "value": "k8ssbk" 51 | }, 52 | "k8sVersion": { 53 | "value": "1.12.8" 54 | }, 55 | "nodeCount": { 56 | "value": 3 57 | }, 58 | "nodeVmSize": { 59 | "value": "Standard_DS1_v2" 60 | }, 61 | "osType": { 62 | "value": "Linux" 63 | }, 64 | "osAdminUser": { 65 | "value": "sbkadmin" 66 | }, 67 | "sshPubKey": { 68 | "reference": { 69 | "keyVault": { 70 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 71 | }, 72 | "secretName": "k8sSsh" 73 | } 74 | }, 75 | "maxPods": { 76 | "value": 31 77 | }, 78 | "enableHttpApplicationRouting": { 79 | "value": false 80 | }, 81 | "networkPlugin": { 82 | "value": "azure" 83 | }, 84 | "enableRBAC": { 85 | "value": true 86 | }, 87 | "serviceCidr": { 88 | "value": "10.0.0.0/16" 89 | }, 90 | "dnsServiceIP": { 91 | "value": "10.0.0.10" 92 | }, 93 | "dockerBridgeCidr": { 94 | "value": "172.17.0.1/16" 95 | }, 96 | "aksEnv": { 97 | "value": "k8s" 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /sample-k8sRefArch/appGw/appGwParams.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "existingVNETName": { 6 | "value": "k8sVnet" 7 | }, 8 | "newSubnetName": { 9 | "value": "appGw" 10 | }, 11 | "newSubnetAddressPrefix": { 12 | "value": "10.10.3.0/24" 13 | }, 14 | "frontendCertData": { 15 | "reference": { 16 | "keyVault": { 17 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 18 | }, 19 | "secretName": "wildcardsslcert" 20 | } 21 | }, 22 | "sslCertificatePassword": { 23 | "reference": { 24 | "keyVault": { 25 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 26 | }, 27 | "secretName": "sslcertpw" 28 | } 29 | }, 30 | "appGtwyName": { 31 | "value": "k8s" 32 | }, 33 | "appGtwySize": { 34 | "value": "WAF_Medium" 35 | }, 36 | "appGtwyCapacity": { 37 | "value": 1 38 | }, 39 | "appGatewayRuleName1": { 40 | "value": "appGwRule1" 41 | }, 42 | "appGatewayRuleName2": { 43 | "value": "appGwRule2" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sample-k8sRefArch/appGw/echo-api.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: demo-echo-service 5 | labels: 6 | k8s-app: demo-echo-service 7 | namespace: default 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | k8s-app: demo-echo-service 13 | template: 14 | metadata: 15 | labels: 16 | k8s-app: demo-echo-service 17 | spec: 18 | terminationGracePeriodSeconds: 60 19 | containers: 20 | - name: echo-service 21 | image: electroma/ingress-demo-echosvc-amd64:0.1 22 | ports: 23 | - containerPort: 8080 24 | resources: 25 | limits: 26 | cpu: 10m 27 | memory: 20Mi 28 | requests: 29 | cpu: 10m 30 | memory: 20Mi 31 | --- 32 | apiVersion: v1 33 | kind: Service 34 | metadata: 35 | name: demo-echo-service 36 | labels: 37 | k8s-app: demo-echo-service 38 | namespace: default 39 | spec: 40 | ports: 41 | - port: 80 42 | targetPort: 8080 43 | selector: 44 | k8s-app: demo-echo-service 45 | --- 46 | apiVersion: extensions/v1beta1 47 | kind: Ingress 48 | metadata: 49 | name: public-demo-echo-service 50 | namespace: default 51 | spec: 52 | rules: 53 | - host: public-demo-echo-service.kube.local 54 | http: 55 | paths: 56 | - backend: 57 | serviceName: demo-echo-service 58 | servicePort: 80 59 | path: / 60 | --- 61 | apiVersion: extensions/v1beta1 62 | kind: Ingress 63 | metadata: 64 | name: secure-demo-echo-service 65 | namespace: default 66 | spec: 67 | rules: 68 | - host: secure-demo-echo-service.kube.local 69 | http: 70 | paths: 71 | - backend: 72 | serviceName: demo-echo-service 73 | servicePort: 80 74 | path: / 75 | -------------------------------------------------------------------------------- /sample-k8sRefArch/appGw/helm-rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tiller 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: tiller 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: tiller 18 | namespace: kube-system 19 | -------------------------------------------------------------------------------- /sample-k8sRefArch/appGw/ingress-internal.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | service: 3 | loadBalancerIP: 10.10.1.200 4 | annotations: 5 | service.beta.kubernetes.io/azure-load-balancer-internal: "true" 6 | -------------------------------------------------------------------------------- /sample-k8sRefArch/appGw/kubectl-helm-commands.md: -------------------------------------------------------------------------------- 1 | ## kubectl and helm commands 2 | 3 | 4 | Connect and Configure Internal Ingress Controller for K8s: 5 | 6 | az aks get-credentials --name k8sSecure --resource-group k8sSecure 7 | Merged "k8sSecure" as current context in /Users/username/.kube/config 8 | 9 | kubectl apply -f ingress-internal-l.yaml 10 | kubectl get service secure-k8s 11 | 12 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 13 | internal-app LoadBalancer 10.0.248.59 10.10.1.200 80:30555/TCP 2m 14 | 15 | Create namespace internal-ingress 16 | 17 | kubectl create namespace internal-ingress 18 | 19 | Install Helm Locally or via Cloud Shell (then use helm-rbac.yaml file from repo for next step) 20 | 21 | kubectl apply -f helm-rbac.yaml 22 | helm init \ 23 | --service-account tiller \ 24 | --node-selectors "beta.kubernetes.io/os"="linux" 25 | 26 | Use Helm to Deploy a NGINX Ingress Controller (use ingress-internal.yaml file in repo) 27 | 28 | helm install stable/nginx-ingress \ 29 | --namespace internal-ingress \ 30 | -f ingress-internal.yaml \ 31 | --set controller.replicaCount=2 \ 32 | --set controller.nodeSelector."beta\.kubernetes\.io/os"=linux 33 | --set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux 34 | 35 | kubectl get service -l app=nginx-ingress --namespace internal-ingress 36 | 37 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORTS AGE 38 | alternating-coral-nginx-ingress-controller LoadBalancer 10.0.248.59 10.10.1.200 80:31507/TCP,443:30707/TCP 1m 39 | alternating-coral-nginx-ingress-default-backend ClusterIP 10.0.134.66 80/TCP 1m 40 | 41 | -------------------------------------------------------------------------------- /sample-k8sRefArch/k8s/README.md: -------------------------------------------------------------------------------- 1 | # Deploying a Kubernetes Cluster in Azure 2 |
Getting to this folder means you started with the setup and keyVault folders. In order to deploy this cluster, you need a service principal in AAD and a public SSH key. Between the setup and keyVault folder, you should have generated a service principal, generated a public SSH key, created a Key Vault, and stored both of those secrets to reference within the template. 3 | 4 | **Information on K8s Configuration** 5 |

Within this folder, you will find the following files: 6 | 7 | 1) **aksDeploy.json** - ARM template that deploys the Kubernetes cluster. This file declares all the Kubernetes cluster, which comprises of deploying nodes (VMs), nics, the Availability Set, and the NSG. Additional comments: 8 |
- This deployment deploys a private load balancer as an nginx ingress controller. 9 |
- This deployment reports to an existing Log Analytics workspace.
10 | 11 | 2) **aksParams.json** - this is the parameters file for the ARM template deployment. Future deployments can be conducted by editing these parameters first and then re-deploying. 12 | 13 | 3) **kubectl-helm-commands.md** - this file guides you on how to connect to your Kubernetes cluster, create a namespace, create a Tiller service account for helm, install helm, and create an internal ingress controller for your Kubernetes cluster. 14 | 15 | 4) **helm-rbac.yaml** - this is the file to run while connected to your Kubernetes cluster so you can create the Tiller service account for helm. 16 | 17 | 5) **ingress-internal.yaml** - this is the file to use for your larger helm command to install an nginx internal ingress controller. 18 | 19 | -------------------------------------------------------------------------------- /sample-k8sRefArch/k8s/aksParams.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "spAppId": { 6 | "value": "e1ce23e5-4c4e-4aa0-ad39-7d945f6695c3" 7 | }, 8 | "spObjectId": { 9 | "value": "eb8e6c40-2735-43a7-a68e-0e127a14d80b" 10 | }, 11 | "spSecret": { 12 | "reference": { 13 | "keyVault": { 14 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 15 | }, 16 | "secretName": "spPw" 17 | } 18 | }, 19 | "rgName": { 20 | "value": "k8s" 21 | }, 22 | "vnetName": { 23 | "value": "k8sVnet" 24 | }, 25 | "vnetTag": { 26 | "value": "AKS vNet" 27 | }, 28 | "vnetAddrPrefix": { 29 | "value": "10.10.0.0/16" 30 | }, 31 | "sub1Name": { 32 | "value": "k8sCluster" 33 | }, 34 | "sub1Prefix": { 35 | "value": "10.10.1.0/24" 36 | }, 37 | "sub2Name": { 38 | "value": "k8sServices" 39 | }, 40 | "sub2Prefix": { 41 | "value": "10.10.2.0/24" 42 | }, 43 | "logAnalyticsWorkspaceId": { 44 | "value": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourcegroups/sbk-la/providers/microsoft.operationalinsights/workspaces/sbk-log-analytics-25924" 45 | }, 46 | "aksClusterName": { 47 | "value": "k8s" 48 | }, 49 | "dnsPrefix": { 50 | "value": "k8ssbk" 51 | }, 52 | "k8sVersion": { 53 | "value": "1.12.8" 54 | }, 55 | "nodeCount": { 56 | "value": 3 57 | }, 58 | "nodeVmSize": { 59 | "value": "Standard_DS1_v2" 60 | }, 61 | "osType": { 62 | "value": "Linux" 63 | }, 64 | "osAdminUser": { 65 | "value": "sbkadmin" 66 | }, 67 | "sshPubKey": { 68 | "reference": { 69 | "keyVault": { 70 | "id": "/subscriptions/9c29eddd-8897-46b5-b8ca-503e7ba5b463/resourceGroups/keyVault/providers/Microsoft.KeyVault/vaults/kv-6e6dnpt2p5fxi" 71 | }, 72 | "secretName": "k8sSsh" 73 | } 74 | }, 75 | "maxPods": { 76 | "value": 31 77 | }, 78 | "enableHttpApplicationRouting": { 79 | "value": false 80 | }, 81 | "networkPlugin": { 82 | "value": "azure" 83 | }, 84 | "enableRBAC": { 85 | "value": true 86 | }, 87 | "serviceCidr": { 88 | "value": "10.0.0.0/16" 89 | }, 90 | "dnsServiceIP": { 91 | "value": "10.0.0.10" 92 | }, 93 | "dockerBridgeCidr": { 94 | "value": "172.17.0.1/16" 95 | }, 96 | "aksEnv": { 97 | "value": "k8s" 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /sample-k8sRefArch/k8s/helm-rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tiller 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: tiller 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: tiller 18 | namespace: kube-system 19 | -------------------------------------------------------------------------------- /sample-k8sRefArch/k8s/ingress-internal.yaml: -------------------------------------------------------------------------------- 1 | controller: 2 | service: 3 | loadBalancerIP: 10.10.1.200 4 | annotations: 5 | service.beta.kubernetes.io/azure-load-balancer-internal: "true" 6 | -------------------------------------------------------------------------------- /sample-k8sRefArch/k8s/kubectl-helm-commands.md: -------------------------------------------------------------------------------- 1 | ## kubectl and helm commands 2 | 3 | 4 | Connect and Configure Internal Ingress Controller for K8s: 5 | 6 | az aks get-credentials --name k8sSecure --resource-group k8sSecure 7 | Merged "k8sSecure" as current context in /Users/username/.kube/config 8 | 9 | kubectl apply -f ingress-internal-l.yaml 10 | kubectl get service secure-k8s 11 | 12 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 13 | internal-app LoadBalancer 10.0.248.59 10.10.1.200 80:30555/TCP 2m 14 | 15 | Create namespace internal-ingress 16 | 17 | kubectl create namespace internal-ingress 18 | 19 | Install Helm Locally or via Cloud Shell (then use helm-rbac.yaml file from repo for next step) 20 | 21 | kubectl apply -f helm-rbac.yaml 22 | helm init \ 23 | --service-account tiller \ 24 | --node-selectors "beta.kubernetes.io/os"="linux" 25 | 26 | Use Helm to Deploy a NGINX Ingress Controller (use ingress-internal.yaml file in repo) 27 | 28 | helm install stable/nginx-ingress \ 29 | --namespace internal-ingress \ 30 | -f ingress-internal.yaml \ 31 | --set controller.replicaCount=2 \ 32 | --set controller.nodeSelector."beta\.kubernetes\.io/os"=linux 33 | --set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux 34 | 35 | kubectl get service -l app=nginx-ingress --namespace internal-ingress 36 | 37 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORTS AGE 38 | alternating-coral-nginx-ingress-controller LoadBalancer 10.0.248.59 10.10.1.200 80:31507/TCP,443:30707/TCP 1m 39 | alternating-coral-nginx-ingress-default-backend ClusterIP 10.0.134.66 80/TCP 1m 40 | 41 | -------------------------------------------------------------------------------- /sample-k8sRefArch/keyVault/PoSH/createKeyVault.ps1: -------------------------------------------------------------------------------- 1 | # Script Notes 2 | # $subscriptionName is the name of the Azure subscription to install the Key Vault into 3 | # $resourceGroupName is the resource group that will contain the Key Vault to create to contain Key Vault secrets, passwords, and certificates 4 | # $keyVaultName is the name of the Key Vault you are deploying 5 | # $location is the region your Key Vault is deploying to 6 | # $keyVaultAdminUser - Azure AD users or groups that have access to Key Vault 7 | 8 | param( 9 | [Parameter(Mandatory=$true)] 10 | [string] $subscriptionName, 11 | [Parameter(Mandatory=$true)] 12 | [string] $resourceGroupName, 13 | [Parameter(Mandatory=$true)] 14 | [string] $keyVaultName, 15 | [Parameter(Mandatory=$true)] 16 | [string] $location, 17 | [Parameter(Mandatory=$true)] 18 | [string] $keyVaultAdminUser 19 | ) 20 | 21 | # Login to Azure 22 | Login-AzAccount 23 | 24 | # Select the appropriate subscription 25 | Select-AzSubscription -SubscriptionName $subscriptionName 26 | 27 | # Make the Key Vault provider is available (commented out - if not registered, uncomment the line below and run in PowerShell): 28 | # Register-AzureRmResourceProvider -ProviderNamespace Microsoft.KeyVault 29 | 30 | # Create the Resource Group 31 | New-AzResourceGroup -Name $resourceGroupName -Location $location 32 | 33 | # Create the Key Vault (enabling it for Disk Encryption, Deployment and Template Deployment) 34 | New-AzKeyVault -VaultName $keyVaultName -ResourceGroupName $resourceGroupName -Location $location ` 35 | -EnabledForDiskEncryption -EnabledForDeployment -EnabledForTemplateDeployment 36 | 37 | # Add the Administrator policies to the Key Vault 38 | $ObjectId = (Get-AzADUser -UserPrincipalName $keyVaultAdminUser).Id 39 | 40 | # Set access policy for Key Vault 41 | Set-AzKeyVaultAccessPolicy -VaultName $keyVaultName -ResourceGroupName $resourceGroupName -ObjectId $ObjectId ` 42 | -PermissionsToKeys decrypt,encrypt,unwrapKey,wrapKey,verify,sign,get,list,update,create,import,delete,backup,restore,recover,purge ` 43 | –PermissionsToSecrets get,list,set,delete,backup,restore,recover,purge ` 44 | –PermissionsToCertificates get,list,delete,create,import,update,managecontacts,getissuers,listissuers,setissuers,deleteissuers,manageissuers,recover,purge,backup,restore ` 45 | -PermissionsToStorage get,list,delete,set,update,regeneratekey,getsas,listsas,deletesas,setsas,recover,backup,restore,purge 46 | -------------------------------------------------------------------------------- /sample-k8sRefArch/keyVault/PoSH/generateSecret.ps1: -------------------------------------------------------------------------------- 1 | # Adds an individual secret to Key Vault 2 | 3 | param( 4 | [Parameter(Mandatory=$true)] 5 | [string] $keyVaultName 6 | [Parameter(Mandatory=$true)] 7 | [string] $secretName 8 | ) 9 | 10 | Set-AzKeyVaultSecret -VaultName $keyVaultName -Name $secretName -SecretValue (Get-Credential).Password 11 | -------------------------------------------------------------------------------- /sample-k8sRefArch/keyVault/README.md: -------------------------------------------------------------------------------- 1 | # Key Vault Folder Read Me 2 | The files in these folders walk you through how to set up a Key Vault, either through PowerShel or an ARM Template. In addition, the PoSH folder contains code on how to place a secret into your Key Vault. The purpose of these files is to stand up a Key Vault and add the public SSH key (K8s cluster), Service Principal password, and the SSL cert password (Application Gateway). Building a Key Vault means you are not storing credentials in code and is best practice for any development team. 3 | -------------------------------------------------------------------------------- /sample-k8sRefArch/keyVault/armTemplate/createKeyVaultParams.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "keyVaultSku": { 6 | "value": "Standard" 7 | }, 8 | "enabledForDeployment": { 9 | "value": true 10 | }, 11 | "enabledForTemplateDeployment": { 12 | "value": true 13 | }, 14 | "enabledForDiskEncryption": { 15 | "value": true 16 | }, 17 | "keyPermissions": { 18 | "value": [ 19 | "get", 20 | "list", 21 | "update", 22 | "create", 23 | "import", 24 | "delete", 25 | "recover", 26 | "backup", 27 | "restore", 28 | "decrypt", 29 | "encrypt", 30 | "unwrapKey", 31 | "wrapKey", 32 | "verify", 33 | "sign", 34 | "purge" 35 | ] 36 | }, 37 | "secretPermissions": { 38 | "value": [ 39 | "get", 40 | "list", 41 | "set", 42 | "delete", 43 | "recover", 44 | "backup", 45 | "restore", 46 | "purge" 47 | ] 48 | }, 49 | "certificatePermissions": { 50 | "value": [ 51 | "get", 52 | "list", 53 | "set", 54 | "delete", 55 | "recover", 56 | "backup", 57 | "restore", 58 | "purge" 59 | ] 60 | }, 61 | "tenantId": { 62 | "value": "72f988bf-86f1-41af-91ab-2d7cd011db47" 63 | }, 64 | "objectId": { 65 | "value": "67a00545-bee5-4a8e-85ec-9a6595f7f0e0" 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sample-k8sRefArch/setup/README.md: -------------------------------------------------------------------------------- 1 | # Setup Folder Read Me 2 | The files in this folder correlate to the overall setup that preps a Kubernetes cluster build. You will walk through how to 3 | generate a Service Principal and generate a public SSH key to build the K8s cluster securely. 4 | -------------------------------------------------------------------------------- /sample-k8sRefArch/setup/generateSshKeys.md: -------------------------------------------------------------------------------- 1 | **Mac Instructions:** 2 | 3 | Type the following at a terminal prompt: 4 | 5 | ssh-keykgen -t rsa -b 2048 6 | 7 | This starts the key generation process. When you execute this command, the ssh-keygen utility prompts you to indicate where 8 | to store the key. Press the ENTER key to accept the default location. The ssh-keygen utility prompts you for a passphrase. Type in a passphrase. You can also hit the ENTER key to accept the default (no passphrase). However, this is not recommended. 9 | 10 | Your identification has been saved in /Users/yourmacusername/.ssh/id_rsa. 11 | Your public key has been saved in /Users/yourmacusername/.ssh/id_rsa.pub. 12 | The key fingerprint is: 13 | ae:89:72:0b:85:da:5a:f4:7c:1f:c2:43:fd:c6:44:38 yourmacusername@yourmac.local 14 | The key's randomart image is: 15 | +--[ RSA 2048]----+ 16 | | | 17 | | . | 18 | | E . | 19 | | . . o | 20 | | o . . S . | 21 | | + + o . + | 22 | |. + o = o + | 23 | | o...o * o | 24 | |. oo.o . | 25 | +-----------------+ 26 | 27 | Access your public SSH key by running the following command: 28 | 29 | cat ~/.ssh/id_rsa.pub 30 | 31 | You will save this public SSH key into your Key Vault and use at the time of deployment for your K8s cluster. 32 | 33 | **Windows Instructions:** 34 | 35 | 1) Download PuTTYgen: https://www.ssh.com/ssh/putty/windows/puttygen 36 | 2) Open the PuTTYgen program. 37 | 3) For Type of key to generate, select SSH-2 RSA. 38 | 4) Click the Generate button. 39 | 5) Move your mouse in the area below the progress bar. When the progress bar is full, PuTTYgen generates your key pair. 40 | 6) Type a passphrase in the Key passphrase field. Type the same passphrase in the Confirm passphrase field. You can use a key without a passphrase, but this is not recommended. 41 | 7) Right-click in the text field labeled Public key for pasting into OpenSSH authorized_keys file and choose Select All. 42 | 8) Right-click again in the same text field and choose Copy. 43 | 9) Save this into your Key Vault to use at the time of deployment for your K8s cluster. 44 | --------------------------------------------------------------------------------