├── .gitignore ├── CODE_OF_CONDUCT ├── CONTRIBUTING ├── LICENSE ├── README ├── restore-sql-server-transaction-logs ├── .editorconfig ├── Function │ ├── LICENSE │ ├── main.py │ ├── main_test.py │ └── requirements.txt ├── README.md ├── scheduled-upload │ ├── HelperFunctions │ │ └── HelperFunctions.psm1 │ ├── LICENSE │ ├── upload-script.Tests.ps1 │ └── upload-script.ps1 └── tlog-restore-cloudsql.png ├── save-sqlaudit-files ├── Function │ ├── Function.cs │ ├── LICENSE │ ├── README.md │ └── SqlAuditToCloudLog.csproj └── Run │ ├── .csharpierrc.yaml │ ├── .dockerignore │ ├── AspNetCoreWebApi6.csproj │ ├── Controllers │ └── AuditFileController.cs │ ├── Dockerfile │ ├── LICENSE │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── README.md │ ├── SQLAuditEvent.cs │ ├── Utils.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── env.yml ├── single-project-managed-ad-cloud-sql ├── README ├── cloudsql.tf ├── compute.tf ├── dns.tf ├── firewall.tf ├── hierarchy.tf ├── iam.tf ├── locals.tf ├── managed-ad.tf ├── nat-gateway.tf ├── network.tf ├── outputs.tf ├── providers.tf ├── secrets.tf ├── service_accounts.tf ├── sleepers.tf ├── templatefiles │ ├── jumpbox.tpl │ ├── mgmt-vm-script.tpl │ └── on-prem-dc.tpl ├── terraform.tfvars └── variables.tf ├── sql-server-tempDB-local-ssd-provisioning-script ├── READ.ME └── create-local-ssd-stripe-set.txt ├── sqlserver-dnn ├── README.md ├── compute.tf ├── dns.tf ├── files │ ├── .DS_Store │ ├── ContosoUniversity.bak │ └── samplewebapp.zip ├── firewall.tf ├── iam.tf ├── images │ ├── .DS_Store │ ├── AG_fail_SSMS.png │ ├── AG_properties_failover_mode.png │ ├── Architecture.png │ ├── RDP_MacOS.png │ ├── RDP_Windows.png │ └── WebApp.png ├── ipaddress.tf ├── locals.tf ├── natgw.tf ├── network.tf ├── output.tf ├── project-api.tf ├── providers.tf ├── secrets.tf ├── serviceaccounts.tf ├── sleeptimers.tf ├── templatefiles │ ├── dc1.tpl │ ├── jumpbox.tpl │ ├── sql1.tpl │ ├── sql2.tpl │ ├── web1.tpl │ └── witness.tpl ├── terraform.tfvars └── variables.tf └── windows-cold-dr-async-pd ├── .DS_Store ├── .gitignore ├── README.md ├── dr ├── dr-vms.tf ├── locals.tf ├── output.tf ├── stage-failback-async-boot-disks.tf.dr ├── stage-failback-async-rep.tf.dr ├── terraform.tfvars.sample └── variables.tf ├── failback ├── .terraform.lock.hcl ├── failback-vms.tf ├── locals.tf ├── output.tf ├── restage-dr-async-boot-disks.tf.failback ├── restage-dr-async-rep.tf.failback ├── terraform.tfvars.sample └── variables.tf ├── images ├── .DS_Store └── Windows Cold DR Architecture.png └── setup ├── .terraform.lock.hcl ├── locals.tf ├── output.tf ├── prod-async-boot-disks.tf ├── prod-async-rep.tf ├── prod-vms.tf ├── templatefiles ├── ad-join.tpl └── gcloud_template ├── terraform.tfvars.sample └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] -------------------------------------------------------------------------------- /CODE_OF_CONDUCT: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, education, socio-economic status, nationality, personal appearance, 10 | race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | This Code of Conduct also applies outside the project spaces when the Project 56 | Steward has a reasonable belief that an individual's behavior may have a 57 | negative impact on the project or its community. 58 | 59 | ## Conflict Resolution 60 | 61 | We do not believe that all conflict is bad; healthy debate and disagreement 62 | often yield positive results. However, it is never okay to be disrespectful or 63 | to engage in behavior that violates the project’s code of conduct. 64 | 65 | If you see someone violating the code of conduct, you are encouraged to address 66 | the behavior directly with those involved. Many issues can be resolved quickly 67 | and easily, and this gives people more control over the outcome of their 68 | dispute. If you are unable to resolve the matter for any reason, or if the 69 | behavior is threatening or harassing, report it. We are dedicated to providing 70 | an environment where participants feel welcome and safe. 71 | 72 | Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the 73 | Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to 74 | receive and address reported violations of the code of conduct. They will then 75 | work with a committee consisting of representatives from the Open Source 76 | Programs Office and the Google Open Source Strategy team. If for any reason you 77 | are uncomfortable reaching out to the Project Steward, please email 78 | opensource@google.com. 79 | 80 | We will investigate every complaint, but you may not receive a direct response. 81 | We will use our discretion in determining when and how to follow up on reported 82 | incidents, which may range from not taking action to permanent expulsion from 83 | the project and project-sponsored spaces. We will notify the accused of the 84 | report and provide them an opportunity to discuss it before any action is taken. 85 | The identity of the reporter will be omitted from the details of the report 86 | supplied to the accused. In potentially harmful situations, such as ongoing 87 | harassment or threats to anyone's safety, we may take action without notice. 88 | 89 | ## Attribution 90 | 91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 92 | available at 93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 94 | 95 | Note: A version of this file is also available in the 96 | [New Project repo](https://github.com/google/new-project/blob/master/docs/code-of-conduct.md). -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our Community Guidelines 22 | 23 | This project follows 24 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code Reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use GitHub pull requests for this purpose. Consult 32 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 33 | information on using pull requests. -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # msft-on-gcp-code-samples 2 | 3 | Init 4 | -------------------------------------------------------------------------------- /restore-sql-server-transaction-logs/.editorconfig: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # EditorConfig: http://EditorConfig.org 16 | root = false 17 | 18 | # Unix-style newlines at the bottom of every file 19 | [*] 20 | end_of_line = lf 21 | charset = utf-8 22 | 23 | # Tab indentation 24 | indent_style = space 25 | indent_size = 4 26 | 27 | # Make sure every file has a blank line at the end 28 | insert_final_newline = true 29 | 30 | # Remove any whitespace characters preceding newline characters 31 | trim_trailing_whitespace = true 32 | 33 | # Give operators breathing room, but not brackets 34 | spaces_around_operators = true 35 | spaces_around_brackets = false 36 | 37 | # Don't check indentation for those pre-formatted files 38 | [LICENSE] 39 | indent_size = unset -------------------------------------------------------------------------------- /restore-sql-server-transaction-logs/Function/requirements.txt: -------------------------------------------------------------------------------- 1 | functions-framework==3.5.0 2 | google-api-python-client==2.110.0 3 | google-cloud-storage==2.14.0 4 | google-cloud-error-reporting==1.10.0 5 | pytest==8.0.1 -------------------------------------------------------------------------------- /restore-sql-server-transaction-logs/scheduled-upload/HelperFunctions/HelperFunctions.psm1: -------------------------------------------------------------------------------- 1 | # Define the logic to set the content metadata the functions below. Exposed in functions for visibility and convenience. 2 | function Get-CloudSqlInstanceMetadataTag { 3 | param () 4 | 5 | #Fill in with the destination Cloud SQL for SQL Server instance name 6 | return "" 7 | } 8 | 9 | function Get-DatabaseNameMetadataTag { 10 | param ($file) 11 | return $file.Directory.Name 12 | } 13 | 14 | function Get-BackupTypeMetadataTag { 15 | param ($file) 16 | 17 | If ($file.Name -like "*full*") {return "FULL"} 18 | If ($file.Name -like "*diff*") {return "DIFF"} 19 | return "TLOG" 20 | } 21 | 22 | function Get-RecoveryMetadataTag { 23 | param () 24 | # Change to True in case of restore with recovery 25 | return "False" 26 | } 27 | 28 | function Get-LogObject { 29 | param ($LogFile) 30 | 31 | If (Test-Path -Path $LogFile -PathType Leaf) { 32 | $log = Get-Content $LogFile -Raw | ConvertFrom-Json 33 | } 34 | else { 35 | $log = New-Object -TypeName psobject 36 | $log | Add-Member -MemberType NoteProperty -Name LastRunMaxWriteDateTimeUtc -Value '' 37 | $log | Add-Member -MemberType NoteProperty -Name LastRunTimeUtc -Value '' 38 | $log | Add-Member -MemberType NoteProperty -Name LastRunUploadedObjectsNumber -Value 0 39 | } 40 | return $log 41 | } 42 | 43 | function Get-LastMaxDateTimeUtc { 44 | param ($log, $DateTimeFormat) 45 | 46 | $lastMaxDateTimeUtcString = if ($log.LastRunMaxWriteDateTimeUtc) {$log.LastRunMaxWriteDateTimeUtc} else {[DateTime]::MinValue.ToString($DateTimeFormat)} 47 | $lastMaxDateTimeUtc=[datetime]::ParseExact($lastMaxDateTimeUtcString,$DateTimeFormat,$null) 48 | return $lastMaxDateTimeUtc 49 | } 50 | 51 | function Sync-LogData { 52 | param ($NewMaxDateTimeUtc, $UploadedObjectsNumber, $CurrentDateTimeUtc, $DateTimeFormat, $LogFile) 53 | 54 | $logset = New-Object -TypeName psobject 55 | $logset | Add-Member -MemberType NoteProperty -Name LastRunMaxWriteDateTimeUtc -Value '' 56 | $logset | Add-Member -MemberType NoteProperty -Name LastRunTimeUtc -Value '' 57 | $logset | Add-Member -MemberType NoteProperty -Name LastRunUploadedObjectsNumber -Value 0 58 | 59 | $logset.LastRunMaxWriteDateTimeUtc = $NewMaxDateTimeUtc.ToString($DateTimeFormat) 60 | $logset.LastRunUploadedObjectsNumber = $UploadedObjectsNumber 61 | $logset.LastRunTimeUtc = $CurrentDateTimeUtc.ToString($DateTimeFormat) 62 | $logset | ConvertTo-Json -Depth 1 | Out-File $LogFile 63 | 64 | return $logset 65 | } 66 | 67 | function Get-Auth { 68 | param ($AccountKeyFile) 69 | gcloud auth activate-service-account --key-file $AccountKeyFile --quiet 70 | return 71 | } 72 | 73 | function UploadNewGCSObject { 74 | param ($BucketName, $UploadDestination, $Metadata, $FileName) 75 | New-GcsObject -Bucket $BucketName -ObjectName $UploadDestination -Metadata $Metadata -File $FileName -Force 76 | return 77 | } 78 | 79 | function UploadToCloudBucket { 80 | param ($LocalPathForBackupFiles, $GoogleAccountKeyFile, $BucketName, $DateTimeFormat, $LogFile) 81 | 82 | $uploadedObjectsNumber=0 83 | $currentDateTimeUtc = (Get-Date).ToUniversalTime() 84 | 85 | $log = Get-LogObject $LogFile 86 | $lastMaxDateTimeUtc = Get-LastMaxDateTimeUtc $log $DateTimeFormat 87 | $newMaxDateTimeUtc = $lastMaxDateTimeUtc 88 | 89 | # filter out existing files with an older timestamp than the reference data of the last run 90 | $recentlyAddedFiles = Get-ChildItem -Path $LocalPathForBackupFiles -Attributes !Directory *.* -Recurse | 91 | Where-Object {[datetime]::ParseExact($_.LastWriteTimeUtc.ToString($DateTimeFormat),$DateTimeFormat,$null) -gt $lastMaxDateTimeUtc} 92 | 93 | if ($recentlyAddedFiles.Count -gt 0) { 94 | 95 | Get-Auth $GoogleAccountKeyFile 96 | 97 | Foreach ($file in $recentlyAddedFiles) 98 | { 99 | try 100 | { 101 | $CloudSqlInstance = Get-CloudSqlInstanceMetadataTag 102 | $DatabaseName = Get-DatabaseNameMetadataTag $file 103 | $BackupType = Get-BackupTypeMetadataTag $file 104 | $Recovery = Get-RecoveryMetadataTag 105 | 106 | $metadata = @{CloudSqlInstance = $CloudSqlInstance; DatabaseName = $DatabaseName; BackupType=$BackupType ; Recovery=$Recovery} 107 | 108 | $uploadDestination = $file.Name 109 | 110 | UploadNewGCSObject -BucketName $BucketName -UploadDestination $uploadDestination -Metadata $metadata -FileName $file.FullName 111 | 112 | $uploadedObjectsNumber++; 113 | $newMaxDateTimeUtc = if ($file.LastWriteTimeUtc -gt $newMaxDateTimeUtc) {$file.LastWriteTimeUtc} else {$newMaxDateTimeUtc} 114 | 115 | # persist reference data for next run 116 | Sync-LogData -NewMaxDateTimeUtc $newMaxDateTimeUtc -UploadedObjectsNumber $uploadedObjectsNumber -CurrentDateTimeUtc $currentDateTimeUtc -DateTimeFormat $DateTimeFormat -LogFile $LogFile 117 | 118 | } 119 | catch 120 | { 121 | throw $_.Exception.GetType().FullName,$_.Exception.Message 122 | exit 1 123 | } 124 | } 125 | 126 | } 127 | 128 | return 129 | } -------------------------------------------------------------------------------- /restore-sql-server-transaction-logs/scheduled-upload/upload-script.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Import the Pester module 2 | using module Pester 3 | 4 | # Import the module containing your functions 5 | Import-Module $PSScriptRoot\HelperFunctions 6 | 7 | # Set the path for test files 8 | $TestFilesPath = Join-Path -Path $PSScriptRoot -ChildPath "TestFiles" 9 | 10 | # Create a test directory if it doesn't exist 11 | if (-not (Test-Path -Path $TestFilesPath -PathType Container)) { 12 | New-Item -ItemType Directory -Path $TestFilesPath | Out-Null 13 | } 14 | 15 | # Helper function to create test files 16 | function Invoke-FileCreation { 17 | param ( 18 | $FileName, 19 | $Content = "" 20 | ) 21 | 22 | $filePath = Join-Path -Path $TestFilesPath -ChildPath $FileName 23 | $Content | Out-File -FilePath $filePath -Force 24 | } 25 | 26 | # BeforeEach block to create test files 27 | BeforeAll { 28 | 29 | # Create test files 30 | Invoke-FileCreation -FileName "FullBackupFile.bak" -Content "" 31 | Invoke-FileCreation -FileName "DiffBackupFile.bak" -Content "" 32 | Invoke-FileCreation -FileName "BackupFile.bak" -Content "" 33 | Invoke-FileCreation -FileName "OtherFile.txt" -Content "" 34 | } 35 | 36 | 37 | # Describe block for Get-DatabaseNameMetadataTag function 38 | Describe "Get-DatabaseNameMetadataTag" { 39 | It "Returns the directory name" { 40 | $file = Get-Item -Path "$TestFilesPath\FullBackupFile.bak" 41 | $result = Get-DatabaseNameMetadataTag -file $file 42 | $result | Should -Be $file.Directory.Name 43 | } 44 | } 45 | 46 | # Describe block for Get-BackupTypeMetadataTag function 47 | Describe "Get-BackupTypeMetadataTag" { 48 | It "Returns 'FULL' for a full backup file" { 49 | $file = Get-Item -Path "$TestFilesPath\FullBackupFile.bak" 50 | $result = Get-BackupTypeMetadataTag -file $file 51 | $result | Should -Be "FULL" 52 | } 53 | 54 | 55 | It "Returns 'DIFF' for a differential backup file" { 56 | $file = Get-Item -Path "$TestFilesPath\DiffBackupFile.bak" 57 | $result = Get-BackupTypeMetadataTag -file $file 58 | $result | Should -Be "DIFF" 59 | } 60 | 61 | It "Returns 'TLOG' for a transaction log backup file" { 62 | $file = Get-Item -Path "$TestFilesPath\BackupFile.bak" 63 | $result = Get-BackupTypeMetadataTag -file $file 64 | $result | Should -Be "TLOG" 65 | } 66 | } 67 | 68 | # Describe block for Get-LogObject function 69 | Describe "Get-LogObject" { 70 | It "Returns a valid log object" { 71 | $LogFile = "LogFile.json" 72 | $result = Get-LogObject -LogFile $LogFile 73 | $result | Should -Not -BeNullOrEmpty 74 | } 75 | } 76 | 77 | # Describe block for Sync-LogData function 78 | Describe "Sync-LogData" { 79 | It "Creates a log object with correct properties" { 80 | $newMaxDateTimeUtc = Get-Date 81 | $uploadedObjectsNumber = 10 82 | $currentDateTimeUtc = Get-Date 83 | $DateTimeFormat = "yyyy-MM-dd HH:mm:ss" 84 | $LogFile = "TestLogFile.json" 85 | Sync-LogData -newMaxDateTimeUtc $newMaxDateTimeUtc -uploadedObjectsNumber $uploadedObjectsNumber -currentDateTimeUtc $currentDateTimeUtc -DateTimeFormat $DateTimeFormat -LogFile $LogFile 86 | $log = Get-Content $LogFile | ConvertFrom-Json 87 | $log.LastRunMaxWriteDateTimeUtc | Should -Be $newMaxDateTimeUtc.ToString($DateTimeFormat) 88 | $log.LastRunUploadedObjectsNumber | Should -Be $uploadedObjectsNumber 89 | $log.LastRunTimeUtc | Should -Be $currentDateTimeUtc.ToString($DateTimeFormat) 90 | } 91 | } 92 | 93 | # Describe block for UploadToCloudBucket function 94 | Describe "UploadToCloudBucket" { 95 | # You may need to mock some commands or use Pester's -MockWith parameter to test specific behaviors 96 | It "Does not throw an exception for valid inputs" { 97 | 98 | # Mock the upload and auth commands 99 | Mock -CommandName 'Get-Auth' -MockWith { return } -ModuleName HelperFunctions 100 | Mock -CommandName 'UploadNewGCSObject' -MockWith {return } -ModuleName HelperFunctions 101 | Mock -CommandName 'Sync-LogData' -MockWith { return } -ModuleName HelperFunctions 102 | 103 | $LocalPathForBackupFiles = $TestFilesPath 104 | $GoogleAccountKeyFile = "NotExistingKeyFile.json" 105 | $BucketName = "" 106 | $DateTimeFormat = "yyyy-MM-dd HH:mm:ss" 107 | $LogFile = "NotExistingLogFile.json" 108 | { UploadToCloudBucket -LocalPathForBackupFiles $LocalPathForBackupFiles -GoogleAccountKeyFile $GoogleAccountKeyFile -BucketName $BucketName -DateTimeFormat $DateTimeFormat -LogFile $LogFile } | Should -Not -Throw 109 | } 110 | } 111 | 112 | # AfterAll block to clean up test files 113 | AfterAll { 114 | # Remove test files 115 | if (Test-Path $TestFilesPath) {Remove-Item -Path $TestFilesPath -Recurse -Force} 116 | if (Test-Path "TestLogFile.json") {Remove-Item -Path "TestLogFile.json"} 117 | } -------------------------------------------------------------------------------- /restore-sql-server-transaction-logs/scheduled-upload/upload-script.ps1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\HelperFunctions 2 | 3 | # Constants - fill with necessary information: 4 | #The folder where the backup files are created. The script goes into subfolders as well. 5 | New-Variable -Name LocalPathForBackupFiles -Value "" -Option Constant 6 | 7 | #The bucket name where the backup files will be uploaded 8 | New-Variable -Name BucketName -Value "" 9 | 10 | #The full path to your google accout json key file. It is used by the script to authenticate against the bucket. 11 | New-Variable -Name GoogleAccountKeyFile -Value "" -Option Constant 12 | 13 | New-Variable -Name LogFile -Value "log.json" -Option Constant 14 | New-Variable -Name DateTimeFormat -Value "yyyy-MM-dd HH:mm:ss.fff" -Option Constant 15 | 16 | 17 | # Validations 18 | If (-Not (Test-Path $LocalPathForBackupFiles)) { 19 | throw "The provided value for LocalPathForBackupFiles $LocalPathForBackupFiles is not a valid path." 20 | } 21 | 22 | If (-Not ($BucketName)) { 23 | throw "The value of the BucketName variable must not be empty." 24 | } 25 | 26 | If (-Not (Test-Path $GoogleAccountKeyFile -PathType Leaf)) { 27 | throw "The file for GoogleAccountKeyFile $GoogleAccountKeyFile does not exist." 28 | } 29 | 30 | UploadToCloudBucket $LocalPathForBackupFiles $GoogleAccountKeyFile $BucketName $DateTimeFormat $LogFile -------------------------------------------------------------------------------- /restore-sql-server-transaction-logs/tlog-restore-cloudsql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/restore-sql-server-transaction-logs/tlog-restore-cloudsql.png -------------------------------------------------------------------------------- /save-sqlaudit-files/Function/README.md: -------------------------------------------------------------------------------- 1 | # Save SQL Server .Sqlaudit Files to Cloud Log 2 | 3 | This repository contains a .net cloud function implementation that can parse SQL Server 2017/2019 .sqlaudit files and save the audit events inside those files to google cloud log for further investigation and/processing 4 | 5 | ## Parsing library 6 | 7 | The parsing of the events is done using the XELite cross platform library. It can read XEvents from XEL/sqlaudit files or live SQL streams: https://www.nuget.org/packages/Microsoft.SqlServer.XEvent.XELite/2021.12.12.2 8 | 9 | ## Main workflow 10 | 11 | The process starts when SQL Server audit files are being uploaded to a cloud bucket for archiving or audit purposes. These files may come from a SQL Server stand alone instance or CloudSQL for SQL Server. 12 | A cloud function is triggered by the upload event and parses the contents of the files and stores them in a structured way in cloud log. Optionally these can be also published to a PubSub topic for further distribution. 13 | 14 | The structure of the fields that are being logged is harmonized with the pg_audit extension for Cloud SQL for PostgreSQL. The set is extended with some other SQL Server specific fields. The storing of these fields in the log can be controlled through the config/mySettings.json file that is located on the trigger source bucket. Every time the function runs, this json file is parsed and taken into account as an active configuration. The default structure of this file is described below. 15 | 16 | When calling the write to cloud log api, there is the recommendation to batch the log entries that are written there. If the batches are too small, the write performance might be degraded because of the multitude of api calls. If the batches are too big, a call might take too long to finish. The default batch size is 1000 log entries per api call. 17 | 18 | The function should have defined a set of environment variables. They are described below, in the constraints section. 19 | 20 | ## References 21 | 22 | * [Cloud storage triggers](https://cloud.google.com/functions/docs/calling/storage) 23 | 24 | * [Writing structured logs to Cloud Log](https://cloud.google.com/logging/docs/samples/logging-write-log-entry) 25 | 26 | * [AuditLog .net reference](https://cloud.google.com/dotnet/docs/reference/Google.Cloud.Audit/latest/Google.Cloud.Audit.AuditLog) 27 | 28 | * [LogEntry .net reference](https://cloud.google.com/dotnet/docs/reference/Google.Cloud.Logging.V2/latest/Google.Cloud.Logging.V2.LogEntry) 29 | 30 | * [MSSQL Server 2022 sqlaudit reference](https://learn.microsoft.com/en-us/sql/relational-databases/system-functions/sys-fn-get-audit-file-transact-sql?view=sql-server-ver16) 31 | 32 | * [CloudSQL pg_audit](https://cloud.google.com/sql/docs/postgres/pg-audit) 33 | 34 | * [PGAudit reference](https://access.crunchydata.com/documentation/pgaudit/1.2.0/) 35 | 36 | * [Limitations of Log Entries writes](https://cloud.google.com/logging/quotas) 37 | 38 | ## Constraints 39 | 40 | There are some working assumptions: 41 | 42 | 1. The sqlaudit files are not updated (there is no process that opens existing files on the cloud bucket and updates them by appending content); 43 | 44 | 1. The files are supposed to be named like CloudSQL for Sql Server names them natively: 45 | __Audit-_UID.sqlaudit 46 | 47 | 1. There should be a config/mySettings.json file on the source trigger bucket. The config folder is mandatory. This file decides what fields are included in the payload to save to the cloud bucket or not. The default file should look as follows 48 | 49 | ```json 50 | { 51 | "auditClass": true, 52 | "object": true, 53 | "database": true, 54 | "databaseSessionId": true, 55 | "statement": true, 56 | "substatementId": true, 57 | "user": true, 58 | "transactionId": true, 59 | "objectId": true, 60 | "databasePrincipalName": true, 61 | "serverInstanceName": true, 62 | "applicationName": true, 63 | "durationMilliseconds": true, 64 | "schemaName": true, 65 | "succeeded": true, 66 | "actionId": true, 67 | "connectionId": true, 68 | "hostName": true 69 | } 70 | ``` 71 | 72 | The name "mySettings.json" can be changed, but the change should be done in the environment variables (see below) 73 | 74 | 1. There are some Cloud log limitations. Please check https://cloud.google.com/logging/quotas 75 | 76 | 1. The Cloud functions needs to have defined the following environment variables: 77 | 78 | | Name | Definition | 79 | | ---- | ---- | 80 | | `PROJECT_ID` | the name of project that contains the Cloud Logging and optionally the PubSub instance. | 81 | | `LOG_ID` | the name of your log; can be something like "sql-audit-log" | 82 | | `SETTINGS_FILENAME` | the name of the settings file| 83 | | `TOPIC_ID` | optional; the name of the PubSub topic where logs should be written. | 84 | | `DO_PUBSUB_PUBLISH` | boolean flag telling if the function should write to PubSub or not | 85 | | `BATCH_SIZE` | integer, the batch size of log entries that are written in one cloud log write api call. | 86 | 87 | Example of default environment variables: 88 | 89 | ```sh 90 | PROJECT_ID=my-cloud-sql-project 91 | LOG_ID=sqlaudit-log 92 | SETTINGS_FILENAME=mySettings.json 93 | TOPIC_ID=my-cloud-pubsub-topic 94 | DO_PUBSUB_PUBLISH=false 95 | BATCH_SIZE=1000 96 | ``` 97 | -------------------------------------------------------------------------------- /save-sqlaudit-files/Function/SqlAuditToCloudLog.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/.csharpierrc.yaml: -------------------------------------------------------------------------------- 1 | printWidth: 100 # Specify at what point the printer will wrap content 2 | useTabs: false # Indent lines with tabs instead of spaces. 3 | tabWidth: 4 #Specify the number of spaces used per indentation level 4 | preprocessorSymbolSets: # Preprocessor Symbol Sets like #if FIRST #elif SECOND 5 | - "" 6 | - "DEBUG" 7 | - "DEBUG,CODE_STYLE" -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | README.md 3 | **/obj/ 4 | **/bin/ -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/AspNetCoreWebApi6.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use Microsoft's official lightweight .NET images. 2 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 3 | WORKDIR /app 4 | 5 | # Install production dependencies. 6 | # Copy csproj and restore as distinct layers. 7 | COPY *.csproj ./ 8 | RUN dotnet restore 9 | 10 | # Copy local code to the container image. 11 | COPY . ./ 12 | 13 | # Build a release artifact. 14 | RUN dotnet publish -c Release -o out 15 | 16 | # Run the web service on container startup in a lean production image. 17 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 18 | WORKDIR /app 19 | COPY --from=build /app/out . 20 | 21 | # Start the .dll (will have the same name as your .csproj file) 22 | ENTRYPOINT ["dotnet", "AspNetCoreWebApi6.dll"] -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/Program.cs: -------------------------------------------------------------------------------- 1 | namespace AspNetCoreWebApi6 2 | { 3 | public class Program 4 | { 5 | public static void Main(string[] args) 6 | { 7 | var builder = WebApplication.CreateBuilder(args); 8 | builder.Services.AddControllers(); 9 | builder.Services.AddHttpContextAccessor(); 10 | builder.Services.AddEndpointsApiExplorer(); 11 | builder.Services.AddSwaggerGen(); 12 | 13 | var port = Environment.GetEnvironmentVariable("PORT") ?? "8080"; 14 | var url = $"http://0.0.0.0:{port}"; 15 | 16 | var app = builder.Build(); 17 | 18 | // Configure the HTTP request pipeline. 19 | if (app.Environment.IsDevelopment()) 20 | { 21 | app.UseSwagger(); 22 | app.UseSwaggerUI(); 23 | } 24 | 25 | app.UseHttpsRedirection(); 26 | 27 | app.UseAuthorization(); 28 | 29 | app.MapControllers(); 30 | 31 | app.Run(url); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:42361", 8 | "sslPort": 44363 9 | } 10 | }, 11 | "profiles": { 12 | "AspNetCoreWebApi6": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://localhost:7192;http://localhost:5192", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/README.md: -------------------------------------------------------------------------------- 1 | # Save SQL Server .Sqlaudit Files to Cloud Log 2 | 3 | This repository contains the implementation of an ASP.NET Core MVC application that can parse SQL Server 2017/2019 .sqlaudit files and save the audit events inside those files to google cloud log for further investigation and/processing 4 | 5 | ## Parsing library 6 | 7 | The parsing of the events is done using the XELite cross platform library. It can read XEvents from XEL/sqlaudit files or live SQL streams: https://www.nuget.org/packages/Microsoft.SqlServer.XEvent.XELite/2021.12.12.2 8 | 9 | ## Main workflow 10 | 11 | The process starts when SQL Server audit files are being uploaded to a cloud bucket for archiving or audit purposes. These files may come from a SQL Server stand alone instance or CloudSQL for SQL Server. 12 | An EventArc trigger is fired by the upload event and it calls the asp.net application deployed to Cloud Run. The application parses the contents of the files and stores them in a structured way in cloud log. Optionally these can be also published to a PubSub topic for further distribution. 13 | 14 | The structure of the fields that are being logged is harmonized with the pg_audit extension for Cloud SQL for PostgreSQL. The set is extended with some other SQL Server specific fields. The storing of these fields in the log can be controlled through the config/mySettings.json file that is located on the trigger source bucket. Every time the application receives a request, this json file is parsed and taken into account as an active configuration. The default structure of this file is described below. 15 | 16 | When calling the write to cloud log api, there is the recommendation to batch the log entries that are written there. If the batches are too small, the write performance might be degraded because of the multitude of api calls. If the batches are too big, a call might take too long to finish. The default batch size is 1000 log entries per api call. 17 | 18 | The application should have defined a set of environment variables. They are described below, in the constraints section. 19 | 20 | ## References 21 | 22 | * [Eventarc triggers](https://cloud.google.com/functions/docs/calling/eventarc) 23 | 24 | * [Deploy a .NET service to Cloud Run](https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-dotnet-service) 25 | 26 | * [Writing structured logs to Cloud Log](https://cloud.google.com/logging/docs/samples/logging-write-log-entry) 27 | 28 | * [AuditLog .net reference](https://cloud.google.com/dotnet/docs/reference/Google.Cloud.Audit/latest/Google.Cloud.Audit.AuditLog) 29 | 30 | * [LogEntry .net reference](https://cloud.google.com/dotnet/docs/reference/Google.Cloud.Logging.V2/latest/Google.Cloud.Logging.V2.LogEntry) 31 | 32 | * [MSSQL Server 2022 sqlaudit reference](https://learn.microsoft.com/en-us/sql/relational-databases/system-functions/sys-fn-get-audit-file-transact-sql?view=sql-server-ver16) 33 | 34 | * [CloudSQL pg_audit](https://cloud.google.com/sql/docs/postgres/pg-audit) 35 | 36 | * [PGAudit reference](https://access.crunchydata.com/documentation/pgaudit/1.2.0/) 37 | 38 | * [Limitations of Log Entries writes](https://cloud.google.com/logging/quotas) 39 | 40 | ## Constraints 41 | 42 | There are some working assumptions: 43 | 44 | 1. The sqlaudit files are not updated (there is no process that opens existing files on the cloud bucket and updates them by appending content); 45 | 46 | 1. The files are supposed to be named like CloudSQL for Sql Server names them natively: 47 | __Audit-_UID.sqlaudit 48 | 49 | 1. There should be a config/mySettings.json file on the source trigger bucket. The config folder is mandatory. This file decides what fields are included in the payload to save to the cloud bucket or not. The default file should look as follows 50 | 51 | ```json 52 | { 53 | "auditClass": true, 54 | "object": true, 55 | "database": true, 56 | "databaseSessionId": true, 57 | "statement": true, 58 | "substatementId": true, 59 | "user": true, 60 | "transactionId": true, 61 | "objectId": true, 62 | "databasePrincipalName": true, 63 | "serverInstanceName": true, 64 | "applicationName": true, 65 | "durationMilliseconds": true, 66 | "schemaName": true, 67 | "succeeded": true, 68 | "actionId": true, 69 | "connectionId": true, 70 | "hostName": true 71 | } 72 | ``` 73 | 74 | The name "mySettings.json" can be changed, but the change should be done in the environment variables (see below) 75 | 76 | 1. There are some Cloud log limitations. Please check https://cloud.google.com/logging/quotas 77 | 78 | 1. The Cloud Run service needs to have defined the following environment variables: 79 | 80 | | Name | Definition | 81 | | ---- | ---- | 82 | | `PROJECT_ID` | the name of project that contains the Cloud Logging and optionally the PubSub instance. | 83 | | `LOG_ID` | the name of your log; can be something like "sql-audit-log" | 84 | | `SETTINGS_FILENAME` | the name of the settings file| 85 | | `TOPIC_ID` | optional; the name of the PubSub topic where logs should be written. | 86 | | `DO_PUBSUB_PUBLISH` | boolean flag telling if the application should write to PubSub or not | 87 | | `BATCH_SIZE` | integer, the batch size of log entries that are written in one cloud log write api call. | 88 | 89 | Example of default environment variables: 90 | 91 | ```sh 92 | PROJECT_ID=my-cloud-sql-project 93 | LOG_ID=sqlaudit-log 94 | SETTINGS_FILENAME=mySettings.json 95 | TOPIC_ID=my-cloud-pubsub-topic 96 | DO_PUBSUB_PUBLISH=false 97 | BATCH_SIZE=1000 98 | ``` 99 | 100 | The environment variables can be defined upfront in the env.yml file. The application can then be deployed to Cloud Run using the following command: 101 | gcloud run deploy sql-server-audit-file-reader --env-vars-file=env.yml -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/SQLAuditEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AspNetCoreWebApi6 4 | { 5 | public class SqlAuditEvent 6 | { 7 | public string? ClassType { get; set; } 8 | public string? ObjectName { get; set; } 9 | public string? DatabaseName { get; set; } 10 | public int? SessionId { get; set; } 11 | public string? Statement { get; set; } 12 | public string? SequenceNumber { get; set; } 13 | public string? ServerPrincipalName { get; set; } 14 | public Int64? TransactionId { get; set; } 15 | public int? ObjectId { get; set; } 16 | public string? DatabasePrincipalName { get; set; } 17 | public string? ServerInstanceName { get; set; } 18 | public string? ApplicationName { get; set; } 19 | public Int64? DurationMilliseconds { get; set; } 20 | public DateTime EventTime { get; set; } 21 | public string? SchemaName { get; set; } 22 | public bool? Succeeded { get; set; } 23 | public string? ActionId { get; set; } 24 | public string? ConnectionId { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "GOOGLE_APPLICATION_CREDENTIALS": "" 9 | } 10 | -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /save-sqlaudit-files/Run/env.yml: -------------------------------------------------------------------------------- 1 | PROJECT_ID: "" 2 | LOG_ID: "" 3 | SETTINGS_FILENAME: "mySettings.json" 4 | DO_PUBSUB_PUBLISH: "False" 5 | DO_PARQUET_GCS_SAVE: "True" 6 | PARQUET_GCS_BUCKETNAME: "" 7 | DO_LOG_SAVE: "False" 8 | BATCH_SIZE: "1000" -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/README: -------------------------------------------------------------------------------- 1 | Init 2 | -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/cloudsql.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### CLOUD SQL SERVER INSTANCE CONFIG ### 18 | 19 | module "cloudsql_instance" { 20 | project_id = var.provider_project 21 | source = "GoogleCloudPlatform/sql-db/google//modules/mssql" 22 | version = "13.0.1" 23 | deletion_protection = false 24 | depends_on = [ 25 | google_service_networking_connection.cloud_sql_service_networking, 26 | google_active_directory_domain.managed_ad_domain, 27 | google_project_iam_binding.cloudsql_service_account_managed_ad 28 | ] 29 | active_directory_config = { 30 | domain = var.managed_ad_domain_name 31 | } 32 | name = var.cloudsql_instance_name 33 | availability_type = "ZONAL" 34 | database_version = "SQLSERVER_2019_STANDARD" 35 | zone = var.default_zone 36 | ip_configuration = { 37 | allocated_ip_range = google_compute_global_address.cloud_sql_service_networking.name 38 | authorized_networks = [] 39 | ipv4_enabled = false 40 | private_network = module.network.network_id 41 | require_ssl = false 42 | } 43 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/compute.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### COMPUTE ENGINE INSTANCES CONFIG ### 18 | 19 | resource "google_compute_instance" "dc1" { 20 | name = var.dc1_hostname 21 | machine_type = "e2-standard-2" 22 | zone = var.default_zone 23 | 24 | depends_on = [ 25 | google_secret_manager_secret_version.local_admin, 26 | google_secret_manager_secret_version.adds_dsrm, 27 | google_secret_manager_secret_version.adds_trust_password, 28 | google_dns_policy.inbound_policy 29 | ] 30 | 31 | tags = ["rdp", "dc"] 32 | 33 | boot_disk { 34 | initialize_params { 35 | image = "windows-2022" 36 | size = 50 37 | type = "pd-balanced" 38 | } 39 | device_name = var.dc1_hostname 40 | } 41 | 42 | network_interface { 43 | network = module.network.network_id 44 | subnetwork = module.network.subnets_ids[0] 45 | } 46 | 47 | metadata = { 48 | domain = var.on_prem_ad_domain_name 49 | sysprep-specialize-script-ps1 = templatefile("./templatefiles/on-prem-dc.tpl", { 50 | project_name = var.provider_project 51 | dc1_os_hostname = var.dc1_hostname 52 | dc1_zone = var.default_zone 53 | on_prem_ad_domain_name = var.on_prem_ad_domain_name 54 | on_prem_ad_domain_netbios = local.on_prem_ad_netbios 55 | managed_ad_domain_name = var.managed_ad_domain_name 56 | managed_ad_domain_netbios = local.managed_ad_netbios 57 | local_admin_secret_id = google_secret_manager_secret.local_admin.secret_id 58 | adds_dsrm_secret_id = google_secret_manager_secret.adds_dsrm.secret_id 59 | user_password_secret_id = google_secret_manager_secret.on_prem_ad_user_password.secret_id 60 | on_prem_ad_admin_secret_id = google_secret_manager_secret.on_prem_ad_admin.secret_id 61 | managed_ad_admin_secret_id = google_secret_manager_secret.managed_ad_admin.secret_id 62 | adds_trust_password_secret_id = google_secret_manager_secret.adds_trust_password.secret_id 63 | }) 64 | } 65 | 66 | shielded_instance_config { 67 | enable_secure_boot = true 68 | enable_vtpm = true 69 | enable_integrity_monitoring = true 70 | } 71 | 72 | service_account { 73 | email = google_service_account.demo_compute.email 74 | scopes = ["cloud-platform"] 75 | } 76 | 77 | allow_stopping_for_update = true 78 | } 79 | 80 | resource "google_compute_instance" "mgmt_vm" { 81 | name = var.mgmt_vm_hostname 82 | machine_type = "e2-standard-2" 83 | zone = var.default_zone 84 | 85 | depends_on = [ 86 | google_secret_manager_secret_version.local_admin 87 | ] 88 | 89 | tags = ["rdp", "mgmt-vm"] 90 | 91 | boot_disk { 92 | initialize_params { 93 | image = "windows-2022" 94 | size = 50 95 | type = "pd-balanced" 96 | } 97 | device_name = var.mgmt_vm_hostname 98 | } 99 | 100 | network_interface { 101 | network = module.network.network_id 102 | subnetwork = module.network.subnets_ids[0] 103 | } 104 | 105 | metadata = { 106 | domain = var.managed_ad_domain_name 107 | sysprep-specialize-script-ps1 = templatefile("./templatefiles/mgmt-vm-script.tpl", { 108 | project_name = var.provider_project 109 | mgmt_vm_os_hostname = var.mgmt_vm_hostname 110 | mgmt_vm_zone = var.default_zone 111 | managed_ad_domain_name = var.managed_ad_domain_name 112 | local_admin_secret_id = google_secret_manager_secret.local_admin.secret_id 113 | managed_ad_admin_secret_id = google_secret_manager_secret.managed_ad_admin.secret_id 114 | }) 115 | } 116 | 117 | shielded_instance_config { 118 | enable_secure_boot = true 119 | enable_vtpm = true 120 | enable_integrity_monitoring = true 121 | } 122 | 123 | service_account { 124 | email = google_service_account.demo_compute.email 125 | scopes = ["cloud-platform"] 126 | } 127 | 128 | allow_stopping_for_update = true 129 | } 130 | 131 | resource "google_compute_instance" "jumpbox" { 132 | name = var.jumpbox_hostname 133 | machine_type = "e2-standard-2" 134 | zone = var.default_zone 135 | 136 | tags = ["rdp", "jumpbox"] 137 | 138 | boot_disk { 139 | initialize_params { 140 | image = "windows-2022" 141 | size = 50 142 | type = "pd-balanced" 143 | } 144 | device_name = var.jumpbox_hostname 145 | } 146 | 147 | network_interface { 148 | network = module.network.network_id 149 | subnetwork = module.network.subnets_ids[0] 150 | access_config { 151 | 152 | } 153 | } 154 | 155 | metadata = { 156 | sysprep-specialize-script-ps1 = templatefile("./templatefiles/jumpbox.tpl", { 157 | jumpbox_os_hostname = var.jumpbox_hostname 158 | jumpbox_vm_zone = var.default_zone 159 | dc1_ip_address = google_compute_instance.dc1.network_interface[0].network_ip 160 | mgmt_vm_ip_address = google_compute_instance.mgmt_vm.network_interface[0].network_ip 161 | on_prem_ad_domain_netbios = local.on_prem_ad_netbios 162 | managed_ad_domain_netbios = local.managed_ad_netbios 163 | on_prem_ad_admin_secret_id = google_secret_manager_secret.on_prem_ad_admin.secret_id 164 | managed_ad_admin_secret_id = google_secret_manager_secret.managed_ad_admin.secret_id 165 | local_admin_secret_id = google_secret_manager_secret.local_admin.secret_id 166 | }) 167 | } 168 | 169 | shielded_instance_config { 170 | enable_secure_boot = true 171 | enable_vtpm = true 172 | enable_integrity_monitoring = true 173 | } 174 | 175 | service_account { 176 | email = google_service_account.demo_compute.email 177 | scopes = ["cloud-platform"] 178 | } 179 | 180 | allow_stopping_for_update = true 181 | } 182 | -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/dns.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### DNS CONFIG ### 18 | 19 | resource "google_dns_policy" "inbound_policy" { 20 | name = "inbound-policy" 21 | enable_inbound_forwarding = true 22 | 23 | enable_logging = true 24 | 25 | networks { 26 | network_url = module.network.network_id 27 | } 28 | } 29 | 30 | resource "google_dns_managed_zone" "on_prem_ad_zone" { 31 | name = replace("${var.on_prem_ad_domain_name}", ".", "-") 32 | dns_name = "${var.on_prem_ad_domain_name}." 33 | description = "${var.on_prem_ad_domain_name} Private Cloud DNS Zone" 34 | 35 | visibility = "private" 36 | 37 | private_visibility_config { 38 | networks { 39 | network_url = module.network.network.network_id 40 | } 41 | } 42 | 43 | forwarding_config { 44 | target_name_servers { 45 | ipv4_address = google_compute_instance.dc1.network_interface[0].network_ip 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/firewall.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### FIREWALL CONFIG ### 18 | 19 | module "network_firewall-rules" { 20 | project_id = var.provider_project 21 | source = "terraform-google-modules/network/google//modules/firewall-rules" 22 | version = "6.0.0" 23 | network_name = module.network.network_name 24 | 25 | rules = [{ 26 | name = "allow-rdp-to-jumpbox" 27 | description = null 28 | direction = "INGRESS" 29 | priority = 1000 30 | ranges = ["74.196.117.186/32"] 31 | source_tags = null 32 | source_service_accounts = null 33 | target_tags = ["rdp"] 34 | target_service_accounts = null 35 | allow = [{ 36 | protocol = "tcp" 37 | ports = ["3389"] 38 | }] 39 | deny = [] 40 | log_config = { 41 | metadata = "INCLUDE_ALL_METADATA" 42 | } 43 | }, 44 | { 45 | name = "allow-iap-tcp-forwarding" 46 | description = "Allow Google Cloud IAP TCP Forwarding" 47 | direction = "INGRESS" 48 | priority = 1000 49 | ranges = ["35.235.240.0/20"] 50 | source_tags = null 51 | source_service_accounts = null 52 | target_tags = null 53 | target_service_accounts = null 54 | allow = [{ 55 | protocol = "tcp" 56 | ports = ["22", "3389"] 57 | }] 58 | deny = [] 59 | log_config = { 60 | metadata = "INCLUDE_ALL_METADATA" 61 | } 62 | }, 63 | { 64 | name = "allow-managed-ad-to-on-prem-ad" 65 | description = "Allow AD Ports from ${var.managed_ad_domain_name} to ${var.on_prem_ad_domain_name}" 66 | direction = "INGRESS" 67 | priority = 1000 68 | ranges = ["${var.managed_ad_cidr}"] 69 | source_tags = null 70 | source_service_accounts = null 71 | target_tags = ["dc"] 72 | target_service_accounts = null 73 | allow = [ 74 | { 75 | protocol = "tcp" 76 | ports = ["53", "88", "135", "389", "445", "464", "49152-65535"] 77 | }, 78 | { 79 | protocol = "udp" 80 | ports = ["53", "88", "123", "389", "445", "464"] 81 | } 82 | ] 83 | deny = [] 84 | log_config = { 85 | metadata = "INCLUDE_ALL_METADATA" 86 | } 87 | }, 88 | { 89 | name = "allow-google-dns-proxy" 90 | description = null 91 | direction = "INGRESS" 92 | priority = 1000 93 | ranges = ["35.199.192.0/19"] 94 | source_tags = null 95 | source_service_accounts = null 96 | target_tags = ["dc"] 97 | target_service_accounts = null 98 | allow = [ 99 | { 100 | protocol = "tcp" 101 | ports = ["53"] 102 | }, 103 | { 104 | protocol = "udp" 105 | ports = ["53"] 106 | } 107 | ] 108 | deny = [] 109 | log_config = { 110 | metadata = "INCLUDE_ALL_METADATA" 111 | } 112 | }, 113 | { 114 | name = "allow-internal" 115 | description = null 116 | direction = "INGRESS" 117 | priority = 1000 118 | ranges = ["${module.network.subnets_ips[0]}"] 119 | source_tags = null 120 | source_service_accounts = null 121 | target_tags = null 122 | target_service_accounts = null 123 | allow = [ 124 | { 125 | protocol = "all" 126 | ports = [] 127 | } 128 | ] 129 | deny = [] 130 | log_config = { 131 | metadata = "INCLUDE_ALL_METADATA" 132 | } 133 | } 134 | ] 135 | } 136 | 137 | -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/hierarchy.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### RESOURCE HIERARCHY CONFIG ### 18 | 19 | resource "google_project_service" "single_project_services" { 20 | for_each = toset(local.project_services) 21 | service = each.value 22 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/iam.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### IAM CONFIG ### 18 | 19 | resource "google_project_iam_binding" "cloudsql_service_account_managed_ad" { 20 | project = var.provider_project 21 | depends_on = [ 22 | google_project_service_identity.cloudsql_service_account 23 | ] 24 | role = "roles/managedidentities.sqlintegrator" 25 | 26 | members = [ 27 | "serviceAccount:${google_project_service_identity.cloudsql_service_account.email}", 28 | ] 29 | } 30 | 31 | resource "google_project_iam_member" "compute_service_account_project_IAM" { 32 | project = var.provider_project 33 | member = google_service_account.demo_compute.member 34 | for_each = toset(local.project_iam_roles) 35 | role = each.value 36 | } 37 | 38 | resource "google_secret_manager_secret_iam_member" "compute_service_account_secretAccessor" { 39 | for_each = toset(local.secrets_ids_accessor) 40 | secret_id = each.value 41 | role = "roles/secretmanager.secretAccessor" 42 | member = google_service_account.demo_compute.member 43 | } 44 | 45 | resource "google_secret_manager_secret_iam_member" "compute_service_account_secretVersionAdder" { 46 | for_each = toset(local.secrets_ids_adder) 47 | secret_id = each.value 48 | role = "roles/secretmanager.secretVersionAdder" 49 | member = google_service_account.demo_compute.member 50 | } 51 | 52 | resource "google_project_iam_custom_role" "update_metadata" { 53 | role_id = "update_metadata" 54 | title = "Metadata Updater" 55 | description = "Users in this role are granted the compute.instances.setMetadata permission to update Instance metadata" 56 | permissions = ["compute.instances.setMetadata"] 57 | } 58 | 59 | resource "google_compute_instance_iam_member" "update_instance_metadata" { 60 | depends_on = [ 61 | google_project_iam_custom_role.update_metadata 62 | ] 63 | zone = var.default_zone 64 | for_each = toset(local.instances_for_metadata_updater) 65 | instance_name = each.value 66 | role = google_project_iam_custom_role.update_metadata.name 67 | member = google_service_account.demo_compute.member 68 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/locals.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | ### LOCALS ### 19 | 20 | locals { 21 | 22 | project_services = [ 23 | "secretmanager.googleapis.com", 24 | "compute.googleapis.com", 25 | "dns.googleapis.com", 26 | "managedidentities.googleapis.com", 27 | "orgpolicy.googleapis.com", 28 | "servicenetworking.googleapis.com", 29 | "sqladmin.googleapis.com" 30 | ] 31 | 32 | on_prem_ad_netbios = split(".", "${var.on_prem_ad_domain_name}")[0] 33 | 34 | managed_ad_netbios = split(".", "${var.managed_ad_domain_name}")[0] 35 | 36 | secrets_ids_accessor = [ 37 | google_secret_manager_secret.local_admin.secret_id, 38 | google_secret_manager_secret.adds_dsrm.secret_id, 39 | google_secret_manager_secret.managed_ad_admin.secret_id, 40 | google_secret_manager_secret.on_prem_ad_user_password.secret_id, 41 | google_secret_manager_secret.adds_trust_password.secret_id, 42 | google_secret_manager_secret.cloudsql_admin_password.secret_id, 43 | google_secret_manager_secret.on_prem_ad_admin.secret_id 44 | ] 45 | 46 | secrets_ids_adder = [ 47 | google_secret_manager_secret.managed_ad_admin.secret_id, 48 | google_secret_manager_secret.on_prem_ad_admin.secret_id 49 | ] 50 | 51 | project_iam_roles = [ 52 | "roles/managedidentities.admin", 53 | "roles/managedidentities.domainAdmin", 54 | "roles/compute.networkViewer", 55 | "roles/iam.serviceAccountUser" 56 | ] 57 | 58 | instances_for_metadata_updater = [ 59 | google_compute_instance.dc1.name, 60 | google_compute_instance.mgmt_vm.name, 61 | google_compute_instance.jumpbox.name 62 | ] 63 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/managed-ad.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### MANAGED ACTIVE DIRECTORY CONFIG ### 18 | 19 | resource "google_active_directory_domain" "managed_ad_domain" { 20 | domain_name = var.managed_ad_domain_name 21 | locations = ["${var.default_region}"] 22 | reserved_ip_range = var.managed_ad_cidr 23 | authorized_networks = ["${module.network.network_id}"] 24 | } 25 | 26 | resource "google_active_directory_domain_trust" "ad-domain-trust" { 27 | depends_on = [ 28 | google_active_directory_domain.managed_ad_domain, 29 | google_compute_instance.dc1, 30 | time_sleep.wait_5_minutes 31 | ] 32 | domain = var.managed_ad_domain_name 33 | target_domain_name = var.on_prem_ad_domain_name 34 | target_dns_ip_addresses = ["${google_compute_instance.dc1.network_interface[0].network_ip}"] 35 | trust_direction = "OUTBOUND" 36 | trust_type = "FOREST" 37 | trust_handshake_secret = google_secret_manager_secret_version.adds_trust_password.secret_data 38 | } 39 | -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/nat-gateway.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### NAT GATEWAY CONFIG ### 18 | 19 | resource "google_compute_router" "natgw_router" { 20 | name = "natgw-router" 21 | region = var.default_region 22 | network = module.network.network_id 23 | } 24 | 25 | resource "google_compute_router_nat" "natgw" { 26 | name = "natgw" 27 | router = google_compute_router.natgw_router.name 28 | region = google_compute_router.natgw_router.region 29 | nat_ip_allocate_option = "AUTO_ONLY" 30 | source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" 31 | 32 | log_config { 33 | enable = true 34 | filter = "ERRORS_ONLY" 35 | } 36 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/network.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### IAM CONFIG ### 18 | 19 | module "network" { 20 | project_id = var.provider_project 21 | depends_on = [ 22 | google_project_service.single_project_services 23 | ] 24 | source = "terraform-google-modules/network/google" 25 | version = "6.0.0" 26 | mtu = 1500 27 | network_name = var.vpc_network_name 28 | subnets = var.vpc_subnets 29 | } 30 | 31 | resource "google_compute_global_address" "cloud_sql_service_networking" { 32 | name = "cloud-sql-service-networking" 33 | purpose = "VPC_PEERING" 34 | address_type = "INTERNAL" 35 | address = var.cloud_sql_service_networking_cidr 36 | prefix_length = 24 37 | network = module.network.network_id 38 | } 39 | 40 | resource "google_service_networking_connection" "cloud_sql_service_networking" { 41 | network = module.network.network_id 42 | service = "servicenetworking.googleapis.com" 43 | reserved_peering_ranges = [google_compute_global_address.cloud_sql_service_networking.name] 44 | } 45 | 46 | resource "google_compute_network_peering_routes_config" "peering_routes" { 47 | peering = google_service_networking_connection.cloud_sql_service_networking.peering 48 | network = module.network.network_name 49 | 50 | import_custom_routes = true 51 | export_custom_routes = true 52 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### OUTPUTS ### 18 | 19 | output "project_name" { 20 | description = "Generated value of the Project" 21 | value = var.provider_project 22 | } 23 | 24 | output "jumpbox_info" { 25 | description = "Connectivity Info for Jumpbox" 26 | value = < Security > Secrets Manager > ${google_secret_manager_secret.local_admin.secret_id} 30 | 31 | Once logged in, you will be able to access the On-Prem Domain Controller and the Management VM for Managed AD. 32 | There are RDP connections already configured on the Desktop for when you login, along with the instructions to 33 | retrieve the passwords. 34 | 35 | The Management VM has SQL Server Management Studio already installed so you can connect to Cloud SQL Server. 36 | JUMPBOX 37 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/providers.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### PROVIDER CONFIG ### 18 | 19 | terraform { 20 | required_providers { 21 | google = { 22 | source = "hashicorp/google" 23 | version = "~> 4.47" 24 | } 25 | random = { 26 | source = "hashicorp/random" 27 | version = "3.4.3" 28 | } 29 | google-beta = { 30 | source = "hashicorp/google-beta" 31 | version = ">= 4.28.0, < 5.0" 32 | } 33 | null = { 34 | source = "hashicorp/null" 35 | version = "3.2.1" 36 | } 37 | external = { 38 | source = "hashicorp/external" 39 | version = "2.2.3" 40 | } 41 | time = { 42 | source = "hashicorp/time" 43 | version = "0.9.1" 44 | } 45 | } 46 | } 47 | 48 | provider "google" { 49 | project = var.provider_project 50 | region = var.default_region 51 | zone = var.default_zone 52 | } 53 | 54 | provider "google-beta" { 55 | project = var.provider_project 56 | region = var.default_region 57 | zone = var.default_zone 58 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/secrets.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### SECRETS CONFIG ### 18 | 19 | resource "random_password" "local_admin" { 20 | length = 15 21 | special = true 22 | override_special = "!#$%&*()-_=+[]{}<>:?" 23 | } 24 | 25 | resource "random_password" "adds_dsrm" { 26 | length = 15 27 | special = true 28 | override_special = "!#$%&*()-_=+[]{}<>:?" 29 | } 30 | 31 | resource "random_password" "on_prem_ad_user_password" { 32 | length = 11 33 | special = true 34 | override_special = "!#$%&*()-_=+[]{}<>:?" 35 | } 36 | 37 | resource "random_password" "adds_trust_password" { 38 | length = 15 39 | special = true 40 | override_special = "!#$%&*()-_=+[]{}<>:?" 41 | } 42 | 43 | resource "google_secret_manager_secret" "local_admin" { 44 | depends_on = [ 45 | time_sleep.wait_90_seconds 46 | ] 47 | secret_id = "local-admin" 48 | 49 | labels = { 50 | label = "local-administrator" 51 | } 52 | 53 | replication { 54 | automatic = true 55 | } 56 | } 57 | 58 | resource "google_secret_manager_secret_version" "local_admin" { 59 | secret = google_secret_manager_secret.local_admin.id 60 | secret_data = random_password.local_admin.result 61 | } 62 | 63 | resource "google_secret_manager_secret" "managed_ad_admin" { 64 | depends_on = [ 65 | time_sleep.wait_90_seconds 66 | ] 67 | secret_id = "${replace("${var.managed_ad_domain_name}", ".", "-")}-admin" 68 | 69 | labels = { 70 | label = "${replace("${var.managed_ad_domain_name}", ".", "-")}-admin" 71 | } 72 | 73 | replication { 74 | automatic = true 75 | } 76 | } 77 | 78 | resource "google_secret_manager_secret" "on_prem_ad_admin" { 79 | depends_on = [ 80 | time_sleep.wait_90_seconds 81 | ] 82 | secret_id = "${replace("${var.on_prem_ad_domain_name}", ".", "-")}-admin" 83 | 84 | labels = { 85 | label = "${replace("${var.on_prem_ad_domain_name}", ".", "-")}-admin" 86 | } 87 | 88 | replication { 89 | automatic = true 90 | } 91 | } 92 | 93 | resource "google_secret_manager_secret" "adds_dsrm" { 94 | depends_on = [ 95 | time_sleep.wait_90_seconds 96 | ] 97 | secret_id = "${replace("${var.on_prem_ad_domain_name}", ".", "-")}-dsrm" 98 | 99 | labels = { 100 | label = "${replace("${var.on_prem_ad_domain_name}", ".", "-")}-dsrm" 101 | } 102 | 103 | replication { 104 | automatic = true 105 | } 106 | } 107 | 108 | resource "google_secret_manager_secret_version" "adds_dsrm" { 109 | secret = google_secret_manager_secret.adds_dsrm.id 110 | secret_data = random_password.adds_dsrm.result 111 | } 112 | 113 | resource "google_secret_manager_secret" "on_prem_ad_user_password" { 114 | depends_on = [ 115 | time_sleep.wait_90_seconds 116 | ] 117 | secret_id = "${replace("${var.on_prem_ad_domain_name}", ".", "-")}-user" 118 | 119 | labels = { 120 | label = "${replace("${var.on_prem_ad_domain_name}", ".", "-")}-user" 121 | } 122 | 123 | replication { 124 | automatic = true 125 | } 126 | } 127 | 128 | resource "google_secret_manager_secret_version" "on_prem_ad_user_password" { 129 | secret = google_secret_manager_secret.on_prem_ad_user_password.id 130 | secret_data = random_password.on_prem_ad_user_password.result 131 | } 132 | 133 | resource "google_secret_manager_secret" "adds_trust_password" { 134 | depends_on = [ 135 | time_sleep.wait_90_seconds 136 | ] 137 | secret_id = "adds-trust-password" 138 | 139 | labels = { 140 | label = "adds-trust-password" 141 | } 142 | 143 | replication { 144 | automatic = true 145 | } 146 | } 147 | 148 | resource "google_secret_manager_secret_version" "adds_trust_password" { 149 | secret = google_secret_manager_secret.adds_trust_password.id 150 | secret_data = random_password.adds_trust_password.result 151 | } 152 | 153 | resource "google_secret_manager_secret" "cloudsql_admin_password" { 154 | depends_on = [ 155 | time_sleep.wait_90_seconds 156 | ] 157 | secret_id = "cloudsql-admin" 158 | 159 | labels = { 160 | label = "cloudsql-admin" 161 | } 162 | 163 | replication { 164 | automatic = true 165 | } 166 | } 167 | 168 | resource "google_secret_manager_secret_version" "cloudsql_admin_password" { 169 | secret = google_secret_manager_secret.cloudsql_admin_password.id 170 | secret_data = module.cloudsql_instance.root_password 171 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/service_accounts.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### SERVICE ACCOUNTS CONFIG ### 18 | 19 | resource "google_service_account" "demo_compute" { 20 | account_id = "demo-compute" 21 | display_name = "Service Account for Compute" 22 | } 23 | 24 | resource "google_project_service_identity" "cloudsql_service_account" { 25 | provider = google-beta 26 | service = "sqladmin.googleapis.com" 27 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/sleepers.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### SLEEPERS CONFIG ### 18 | 19 | resource "time_sleep" "wait_5_minutes" { 20 | depends_on = [ 21 | google_active_directory_domain.managed_ad_domain 22 | ] 23 | create_duration = "5m" 24 | } 25 | 26 | resource "time_sleep" "wait_90_seconds" { 27 | create_duration = "90s" 28 | } -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/templatefiles/jumpbox.tpl: -------------------------------------------------------------------------------- 1 | <#** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *#> 16 | 17 | ### JUMPBOX CONFIGURATION SCRIPT ### 18 | 19 | #Generate Stage 1 Script 20 | 21 | ' 22 | $ErrorActionPreference = "SilentlyContinue" 23 | 24 | #Disable Scheduled Task for Stage 1 25 | Disable-ScheduledTask -TaskName "Jumpbox VM Bootstrap - Stage 1" 26 | 27 | #Getting Local Admin password and enabling account 28 | $local_admin_password = ConvertTo-SecureString -String (gcloud secrets versions access latest --secret=${local_admin_secret_id}) -AsPlainText -Force 29 | Get-LocalUser -Name "Administrator" | Set-LocalUser -Password $local_admin_password 30 | Get-LocalUser -Name "Administrator" | Enable-LocalUser 31 | 32 | #Unregister Scheduled Task for Stage 1 33 | Unregister-ScheduledTask -TaskName "Jumpbox VM Bootstrap - Stage 1" -Confirm:$false 34 | 35 | #Add Instance metadata to mark as ready 36 | gcloud compute instances add-metadata ${jumpbox_os_hostname} --zone=${jumpbox_vm_zone} --metadata="status"="ready" 37 | 38 | ' | Out-File C:\stage1.ps1 39 | 40 | $stage1_taskName = "Management VM Bootstrap - Stage 1" 41 | $s1action = New-ScheduledTaskAction -Execute 'PowerShell.exe' ` 42 | -Argument "C:\stage1.ps1" 43 | $s1trigger = New-ScheduledTaskTrigger -AtStartup 44 | 45 | $s1principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount 46 | Register-ScheduledTask -Action $s1action -Trigger $s1trigger -Principal $s1principal -TaskName $stage1_taskName 47 | 48 | #Generate instructions to retrieve passwords 49 | ' 50 | ######################### 51 | ### P A S S W O R D S ### 52 | ######################### 53 | 54 | On-Prem Environment 55 | =================== 56 | Connection Name: on-prem-dc (RDP file on Desktop) 57 | Password : (see below) 58 | Do one of the following to retrieve the password 59 | - Option #1: Navigate to Cloud Console > Security > Secrets Manager > ${on_prem_ad_admin_secret_id} 60 | - Option #2: Run the following command from Powershell to see the Credentials 61 | > gcloud secrets versions access latest --secret=${on_prem_ad_admin_secret_id} 62 | 63 | Managed AD Environment 64 | ====================== 65 | Connection Name: mgmt-vm-managed-ad (RDP file on Desktop) 66 | Password : (see below) 67 | Do one of the following to retrieve the password 68 | - Option #1: Navigate to Cloud Console > Security > Secrets Manager > ${managed_ad_admin_secret_id} 69 | - Option #2: Run the following command from Powershell to see the Credentials 70 | > gcloud secrets versions access latest --secret=${managed_ad_admin_secret_id} 71 | ' | Out-File C:\Users\Public\Desktop\Passwords-Readme.txt 72 | 73 | 74 | #Generate RDP file for dc1 75 | ' 76 | screen mode id:i:2 77 | use multimon:i:0 78 | desktopwidth:i:800 79 | desktopheight:i:600 80 | session bpp:i:32 81 | winposstr:s:0,3,0,0,800,600 82 | compression:i:1 83 | keyboardhook:i:2 84 | audiocapturemode:i:0 85 | videoplaybackmode:i:1 86 | connection type:i:7 87 | networkautodetect:i:1 88 | bandwidthautodetect:i:1 89 | displayconnectionbar:i:1 90 | username:s:${on_prem_ad_domain_netbios}\administrator 91 | enableworkspacereconnect:i:0 92 | disable wallpaper:i:0 93 | allow font smoothing:i:0 94 | allow desktop composition:i:0 95 | disable full window drag:i:1 96 | disable menu anims:i:1 97 | disable themes:i:0 98 | disable cursor setting:i:0 99 | bitmapcachepersistenable:i:1 100 | full address:s:${dc1_ip_address} 101 | audiomode:i:0 102 | redirectprinters:i:1 103 | redirectcomports:i:0 104 | redirectsmartcards:i:1 105 | redirectwebauthn:i:1 106 | redirectclipboard:i:1 107 | redirectposdevices:i:0 108 | autoreconnection enabled:i:1 109 | authentication level:i:2 110 | prompt for credentials:i:0 111 | negotiate security layer:i:1 112 | remoteapplicationmode:i:0 113 | alternate shell:s: 114 | shell working directory:s: 115 | gatewayhostname:s: 116 | gatewayusagemethod:i:4 117 | gatewaycredentialssource:i:4 118 | gatewayprofileusagemethod:i:0 119 | promptcredentialonce:i:0 120 | gatewaybrokeringtype:i:0 121 | use redirection server name:i:0 122 | rdgiskdcproxy:i:0 123 | kdcproxyname:s: 124 | enablerdsaadauth:i:0 125 | ' | Out-File C:\Users\Public\Desktop\on-prem-dc.rdp 126 | 127 | #Generate RDP file for mgmt-vm 128 | ' 129 | screen mode id:i:2 130 | use multimon:i:0 131 | desktopwidth:i:800 132 | desktopheight:i:600 133 | session bpp:i:32 134 | winposstr:s:0,3,0,0,800,600 135 | compression:i:1 136 | keyboardhook:i:2 137 | audiocapturemode:i:0 138 | videoplaybackmode:i:1 139 | connection type:i:7 140 | networkautodetect:i:1 141 | bandwidthautodetect:i:1 142 | displayconnectionbar:i:1 143 | username:s:${managed_ad_domain_netbios}\setupadmin 144 | enableworkspacereconnect:i:0 145 | disable wallpaper:i:0 146 | allow font smoothing:i:0 147 | allow desktop composition:i:0 148 | disable full window drag:i:1 149 | disable menu anims:i:1 150 | disable themes:i:0 151 | disable cursor setting:i:0 152 | bitmapcachepersistenable:i:1 153 | full address:s:${mgmt_vm_ip_address} 154 | audiomode:i:0 155 | redirectprinters:i:1 156 | redirectcomports:i:0 157 | redirectsmartcards:i:1 158 | redirectwebauthn:i:1 159 | redirectclipboard:i:1 160 | redirectposdevices:i:0 161 | autoreconnection enabled:i:1 162 | authentication level:i:2 163 | prompt for credentials:i:0 164 | negotiate security layer:i:1 165 | remoteapplicationmode:i:0 166 | alternate shell:s: 167 | shell working directory:s: 168 | gatewayhostname:s: 169 | gatewayusagemethod:i:4 170 | gatewaycredentialssource:i:4 171 | gatewayprofileusagemethod:i:0 172 | promptcredentialonce:i:0 173 | gatewaybrokeringtype:i:0 174 | use redirection server name:i:0 175 | rdgiskdcproxy:i:0 176 | kdcproxyname:s: 177 | enablerdsaadauth:i:0 178 | ' | Out-File C:\Users\Public\Desktop\mgmt-vm-managed-ad.rdp 179 | -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/templatefiles/mgmt-vm-script.tpl: -------------------------------------------------------------------------------- 1 | <#** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *#> 16 | 17 | ### MANAGEMENT VM CONFIGURATION SCRIPT ### 18 | 19 | #Generate Stage 1 Script 20 | 21 | ' 22 | $ErrorActionPreference = "SilentlyContinue" 23 | 24 | #Disable Scheduled Task for Stage 1 25 | Disable-ScheduledTask -TaskName "Management VM Bootstrap - Stage 1" 26 | 27 | #Install RSAT Tools 28 | Install-WindowsFeature RSAT-AD-Tools,RSAT-DNS-Server,GPMC 29 | 30 | #Getting Local Admin password and enabling account 31 | $local_admin_password = ConvertTo-SecureString -String (gcloud secrets versions access latest --secret=${local_admin_secret_id}) -AsPlainText -Force 32 | Get-LocalUser -Name "Administrator" | Set-LocalUser -Password $local_admin_password 33 | Get-LocalUser -Name "Administrator" | Enable-LocalUser 34 | 35 | #Download and install SSMS 36 | (New-Object Net.WebClient).DownloadFile("https://aka.ms/ssmsfullsetup", "C:\SSMS-Setup-ENU.exe") 37 | C:\SSMS-Setup-ENU.exe /install /quiet /norestart 38 | 39 | #Wait for Managed AD Domain to come online and then reset Admin password 40 | $ad_status = ((gcloud active-directory domains describe ${managed_ad_domain_name} --format json) | ConvertFrom-Json)[0].state 41 | 42 | while ($ad_status -ne "READY") { 43 | Sleep(60) 44 | $ad_status = ((gcloud active-directory domains describe ${managed_ad_domain_name} --format json) | ConvertFrom-Json)[0].state 45 | } 46 | 47 | #Getting Managed AD Admin Username and Password 48 | Sleep(60) 49 | $managed_ad_admin_user = "${managed_ad_domain_name}" + "\" + ((gcloud active-directory domains describe ${managed_ad_domain_name} --format json) | ConvertFrom-Json)[0].admin 50 | $managed_ad_admin_password = ((((gcloud secrets versions access latest --secret=${managed_ad_admin_secret_id}) -Split "\n")[1]) -Split " ")[2] 51 | $managed_ad_admin_password = ConvertTo-SecureString -String $managed_ad_admin_password -AsPlainText -Force 52 | $domain_credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $managed_ad_admin_user, $managed_ad_admin_password 53 | 54 | #Unregister Scheduled Task for Stage 1 55 | Unregister-ScheduledTask -TaskName "Management VM Bootstrap - Stage 1" -Confirm:$false 56 | 57 | #Add Instance metadata to mark as ready 58 | gcloud compute instances add-metadata ${mgmt_vm_os_hostname} --zone=${mgmt_vm_zone} --metadata="status"="ready" 59 | 60 | #Add SQL Server Management Studio shortcut to Desktop 61 | New-Item -ItemType SymbolicLink -Path "C:\Users\Public\Desktop" -Name "Microsoft SQL Server Management Studio 18.lnk" -Value "C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\Ssms.exe" 62 | 63 | #Joining Server to ${managed_ad_domain_name} and restarting 64 | Add-Computer -Domain ${managed_ad_domain_name} -Credential $domain_credential -Restart -Force 65 | 66 | ' | Out-File C:\stage1.ps1 67 | 68 | $stage1_taskName = "Management VM Bootstrap - Stage 1" 69 | $s1action = New-ScheduledTaskAction -Execute 'PowerShell.exe' ` 70 | -Argument "C:\stage1.ps1" 71 | $s1trigger = New-ScheduledTaskTrigger -AtStartup 72 | 73 | $s1principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount 74 | Register-ScheduledTask -Action $s1action -Trigger $s1trigger -Principal $s1principal -TaskName $stage1_taskName 75 | -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/terraform.tfvars: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### INPUT OVERRIDE VARIABLES CONFIG ### 18 | 19 | provider_project = "REPLACE_WITH_PROJECT_ID" -------------------------------------------------------------------------------- /single-project-managed-ad-cloud-sql/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### VARIABLES CONFIG ### 18 | 19 | variable "provider_project" { 20 | description = "The Google Project defined in the Google provider configuration" 21 | } 22 | 23 | variable "default_region" { 24 | description = "The default Google Region for resources" 25 | default = "us-central1" 26 | } 27 | 28 | variable "default_zone" { 29 | description = "The default Google Region Zone for resources" 30 | default = "us-central1-a" 31 | } 32 | 33 | variable "demos_folder_name" { 34 | description = "The name of the Folder created under the Organization to house the demo" 35 | default = "demos" 36 | } 37 | 38 | variable "modifier" { 39 | description = "A user provided value to help with Project uniqueness" 40 | default = "demo" 41 | } 42 | 43 | variable "vpc_network_name" { 44 | description = "The name of the custom mode VPC in the Project" 45 | default = "tf-demo-vpc" 46 | } 47 | 48 | variable "vpc_subnets" { 49 | description = "The list of subnets to be created in the VPC" 50 | default = [ 51 | { 52 | subnet_name = "demo-vpc-subnet-us-central1" 53 | subnet_ip = "10.145.0.0/24" 54 | subnet_region = "us-central1" 55 | subnet_private_access = "true" 56 | } 57 | ] 58 | } 59 | 60 | variable "allowed_ips_for_rdp_to_jumpbox" { 61 | description = "The list of IPs allowed direct RDP access to the Jumpbox" 62 | default = ["0.0.0.0/0"] 63 | } 64 | 65 | variable "cloud_sql_service_networking_cidr" { 66 | description = "The /24 CIDR block used for Cloud SQL Server Service Networking. DO NOT SPECIFY MASK" 67 | default = "10.20.10.0" 68 | } 69 | 70 | variable "jumpbox_hostname" { 71 | description = "OS Hostname for Jumpbox Server" 72 | default = "jumpbox" 73 | } 74 | 75 | variable "dc1_hostname" { 76 | description = "OS Hostname for DC1" 77 | default = "dc1" 78 | } 79 | 80 | variable "mgmt_vm_hostname" { 81 | description = "OS Hostname for mgmt-vm" 82 | default = "mgmt-vm" 83 | } 84 | 85 | variable "on_prem_ad_domain_name" { 86 | description = "Active Directory Domain Name configured on DC1" 87 | default = "example.com" 88 | } 89 | 90 | variable "managed_ad_domain_name" { 91 | description = "Managed Service for Microsoft Active Directory Domain Name" 92 | default = "gcpexample.com" 93 | } 94 | 95 | variable "managed_ad_cidr" { 96 | description = "The /24 CIDR block used by Managed AD" 97 | default = "10.10.10.0/24" 98 | } 99 | 100 | variable "cloudsql_instance_name" { 101 | description = "Instance name for Cloud SQL Server Instance" 102 | default = "test-cloudsql" 103 | } -------------------------------------------------------------------------------- /sql-server-tempDB-local-ssd-provisioning-script/READ.ME: -------------------------------------------------------------------------------- 1 | ##Original author: Anibal Santiago @anibals 2 | 3 | drive.web-frontend_20230222.00_p0 4 | create-local-ssd-stripe-set-README.txt 5 | ## Commands to test the startup script that configures all Local SSDs 6 | ## as a RAID-0 (stripe set) 7 | 8 | # List of SQL Server images 9 | # gcloud compute images list | grep sql 10 | 11 | ## Create an instance with a boot disk of 250GB and 4 Local SSDs 12 | ## NOTE: File "create-local-ssd-stripe-set.ps1" must exist in current folder 13 | gcloud compute instances create sqlserver-test \ 14 | --machine-type n1-highmem-32 \ 15 | --boot-disk-type pd-ssd \ 16 | --boot-disk-size 250GB \ 17 | --local-ssd interface=nvme \ 18 | --local-ssd interface=nvme \ 19 | --local-ssd interface=nvme \ 20 | --local-ssd interface=nvme \ 21 | --image-project windows-sql-cloud \ 22 | --image-family sql-std-2019-win-2019 \ 23 | --zone us-east1-b \ 24 | --network default \ 25 | --metadata-from-file windows-startup-script-ps1="create-local-ssd-stripe-set.ps1" 26 | 27 | 28 | ## Wait for instance to be created by checking the serial port output 29 | ## Look for the text "Finished running startup scripts" 30 | ## Then press Ctrl-C to exit this command 31 | gcloud compute instances tail-serial-port-output sqlserver-test --zone us-east1-b 32 | 33 | 34 | ## Generate a Windows password 35 | gcloud compute reset-windows-password sqlserver-test --zone us-east1-b --quiet 36 | 37 | 38 | ## Connect to the VM using an RDP client 39 | 1) Connect to SQL Server and validate the Tempdb is in the LocalSSD stripe set 40 | 2) Change SQL Server service to "manual" start to let the startup script be the one that starts SQL Server 41 | This is needed as SQL Server may try to start before the startup script creates the Z:\SQLData 42 | and Z:\SQLLog folders. 43 | 3) Shutdown the VM by running this command as Administrator: shutdown /s /f 44 | Wait until the instance show as stopped in the GCP console 45 | 46 | 47 | # You can now Edit the VM and change the startup script 48 | # It is under "Custom metadata". You will see the "windows-startup-script-ps1" 49 | # The section that moves the Tempdb does not need to run every time and can be commented 50 | # You may want to keep the command that starts SQL Server 51 | 52 | 53 | ## Start the VM again 54 | gcloud compute instances start sqlserver-test --zone us-east1-b --quiet 55 | 56 | 57 | ## Wait again for the text "Finished running startup scripts" in the serial port 58 | ## output, then press Ctrl-C to stop this command 59 | gcloud compute instances tail-serial-port-output sqlserver-test --zone us-east1-b 60 | 61 | 62 | ## Connect to the VM again using an RDP client 63 | 1) Validate that the Tempdb is once again created in the LocalSSD stripe set 64 | You can run the command: sqlcmd -S. -E -Q "exec sp_helpdb Tempdb" 65 | 66 | 67 | ## Delete the VM once you are done testing 68 | # gcloud compute instances delete sqlserver-test --zone us-east1-b --quiet 69 | -------------------------------------------------------------------------------- /sql-server-tempDB-local-ssd-provisioning-script/create-local-ssd-stripe-set.txt: -------------------------------------------------------------------------------- 1 | ##Original author: Anibal Santiago @anibals 2 | # 3 | # Copyright(c) 2021 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | # use this file except in compliance with the License. You may obtain a copy of 7 | # the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | 17 | 18 | # Script to configure the Local SSDs in a VM. Use as the startup script 19 | # Filename: create-local-ssd-stripe-set.ps1 20 | # 21 | # The script will do the following: 22 | # 1) Create a RAID-0 using all the Local SSDs 23 | # 2) Create folders Z:\SQLData and Z:\SQLLog for the Tempdb 24 | # 3) Move the Tempdb database to the Z: drive. Notice that this should only be done once. 25 | # You can comment that section after the first time the VM has run 26 | # 27 | # Note: The script assumes that Z: is the drive letter for the Local SSDs. Change it if 28 | # using a different letter for the Local SSDs 29 | 30 | 31 | # AllocationUnitSize to format the disk: 64K=65536 32 | $AllocationUnitSize = 65536 33 | 34 | ### Create a Raid-0 in all available Local SSDs ### 35 | # Create storage pool with all available Local SSDs NVMe 36 | $diskCount = (Get-PhysicalDisk -CanPool $true -FriendlyName 'NVMe*' | Measure-Object).Count; 37 | 38 | if ($diskCount -gt 0) { 39 | New-StoragePool -FriendlyName "LocalSSD" ` 40 | -StorageSubSystemUniqueId (Get-StorageSubSystem).uniqueID ` 41 | -PhysicalDisks (Get-PhysicalDisk -CanPool $true -FriendlyName 'NVMe*'); 42 | 43 | # Create a new virtual disk as a stripe set 44 | New-VirtualDisk -FriendlyName "TempDB" ` 45 | -StoragePoolFriendlyName "LocalSSD" -Interleave 65536 ` 46 | -NumberOfColumns $diskCount -ProvisioningType Fixed -ResiliencySettingName "Simple" -UseMaximumSize; 47 | Initialize-Disk -PartitionStyle GPT -VirtualDisk (Get-VirtualDisk -FriendlyName "TempDB"); 48 | 49 | # Format the disk and assign letter Z: 50 | $diskNumber = ((Get-VirtualDisk -FriendlyName "TempDB" | Get-Disk).Number); 51 | New-Partition -DiskNumber $diskNumber -DriveLetter Z -UseMaximumSize; 52 | Format-Volume -DriveLetter Z -FileSystem NTFS -AllocationUnitSize $AllocationUnitSize -NewFileSystemLabel "TempDB" -Confirm:$false -Force; 53 | } 54 | 55 | ## If Z: drive exist, use it for Tempdb. Otherwise we assume the VM does not have Local SSDs. 56 | if (Test-Path "Z:\"){ 57 | ## Create a folder for the TempDB database. Make sure you specify the folder location as configured in SQL Server. 58 | ## Note: Every time the VM is stop and then restarted, we need to create the Tempdb folder in the LocalSSD as it is not 59 | ## recreated automatically. 60 | if (!(Test-Path "Z:\SQLData")) { New-Item -Path "Z:\SQLData" -ItemType Directory }; 61 | if (!(Test-Path "Z:\SQLLog")) { New-Item -Path "Z:\SQLLog" -ItemType Directory }; 62 | 63 | ## Move Tempdb to Local SSD. This can be commented after the first run as it is saved in the SQL Server system tables. 64 | ## NOTE: COMMENT AFTER FIRST RUN OF VM 65 | sqlcmd -S. -E -Q "ALTER DATABASE tempdb MODIFY FILE (NAME = tempdev, FILENAME = 'Z:\SQLData\tempdev.mdf', SIZE = 1024MB, FILEGROWTH = 1024MB)" 66 | sqlcmd -S. -E -Q "ALTER DATABASE tempdb MODIFY FILE (NAME = templog, FILENAME = 'Z:\SQLLog\templog.ldf', SIZE = 1024MB, FILEGROWTH = 1024MB)" 67 | sqlcmd -S. -E -Q "ALTER DATABASE tempdb MODIFY FILE (NAME = temp2, FILENAME = 'Z:\SQLData\temp2.ndf', SIZE = 1024MB, FILEGROWTH = 1024MB)" 68 | sqlcmd -S. -E -Q "ALTER DATABASE tempdb MODIFY FILE (NAME = temp3, FILENAME = 'Z:\SQLData\temp3.ndf', SIZE = 1024MB, FILEGROWTH = 1024MB)" 69 | sqlcmd -S. -E -Q "ALTER DATABASE tempdb MODIFY FILE (NAME = temp4, FILENAME = 'Z:\SQLData\temp4.ndf', SIZE = 1024MB, FILEGROWTH = 1024MB)" 70 | 71 | # Stop SQL Server to use the new location of the Tempdb. Can be commented after the first run. 72 | ## NOTE: COMMENT AFTER FIRST RUN OF VM 73 | Stop-Service -Force MSSQLSERVER 74 | 75 | # Start SQL Server. Don't uncomment this to make sure SQL Server is always restarted. 76 | # It may fail to start if Windows starts the service before the Z: is formatted. Alternatively you can change the SQL Server service 77 | # to start manually and make this command the one that start SQL Server every time. 78 | Start-Service MSSQLSERVER 79 | } -------------------------------------------------------------------------------- /sqlserver-dnn/dns.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### DNS CONFIG ### 18 | 19 | resource "google_dns_managed_zone" "prod_dns_zone" { 20 | project = var.provider_project 21 | name = replace("${var.ad_domain_name}", ".", "-") 22 | dns_name = "${var.ad_domain_name}." 23 | description = "${var.ad_domain_name} Private Cloud DNS Zone" 24 | 25 | visibility = "private" 26 | 27 | private_visibility_config { 28 | networks { 29 | network_url = module.prod_vpc.network.network_id 30 | } 31 | } 32 | 33 | forwarding_config { 34 | target_name_servers { 35 | ipv4_address = google_compute_instance.dc1.network_interface[0].network_ip 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /sqlserver-dnn/files/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/files/.DS_Store -------------------------------------------------------------------------------- /sqlserver-dnn/files/ContosoUniversity.bak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/files/ContosoUniversity.bak -------------------------------------------------------------------------------- /sqlserver-dnn/files/samplewebapp.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/files/samplewebapp.zip -------------------------------------------------------------------------------- /sqlserver-dnn/firewall.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### FIREWALL CONFIG ### 18 | 19 | module "prod_firewall_rules" { 20 | source = "terraform-google-modules/network/google//modules/firewall-rules" 21 | version = "6.0.0" 22 | project_id = var.provider_project 23 | network_name = module.prod_vpc.network_name 24 | 25 | rules = [{ 26 | name = "allow-iap-tcp-forwarding-prod-vpc" 27 | description = "Allow Google Cloud IAP TCP Forwarding" 28 | direction = "INGRESS" 29 | priority = 1000 30 | ranges = ["35.235.240.0/20"] 31 | source_tags = null 32 | source_service_accounts = null 33 | target_tags = null 34 | target_service_accounts = null 35 | allow = [{ 36 | protocol = "tcp" 37 | ports = ["22", "3389"] 38 | }] 39 | deny = [] 40 | log_config = { 41 | metadata = "INCLUDE_ALL_METADATA" 42 | } 43 | }, 44 | { 45 | name = "allow-google-dns-proxy-prod-vpc" 46 | description = "Allow Google DNS to forward DNS queries to Domain Controller" 47 | direction = "INGRESS" 48 | priority = 1000 49 | ranges = ["35.199.192.0/19"] 50 | source_tags = null 51 | source_service_accounts = null 52 | target_tags = ["dc"] 53 | target_service_accounts = null 54 | allow = [ 55 | { 56 | protocol = "tcp" 57 | ports = ["53"] 58 | }, 59 | { 60 | protocol = "udp" 61 | ports = ["53"] 62 | } 63 | ] 64 | deny = [] 65 | log_config = { 66 | metadata = "INCLUDE_ALL_METADATA" 67 | } 68 | }, 69 | { 70 | name = "allow-internal-prod-vpc" 71 | description = "Allow all internal communication" 72 | direction = "INGRESS" 73 | priority = 1000 74 | ranges = ["${module.prod_vpc.subnets_ips[0]}"] 75 | source_tags = null 76 | source_service_accounts = null 77 | target_tags = null 78 | target_service_accounts = null 79 | allow = [ 80 | { 81 | protocol = "all" 82 | ports = [] 83 | } 84 | ] 85 | deny = [] 86 | log_config = { 87 | metadata = "INCLUDE_ALL_METADATA" 88 | } 89 | } 90 | ] 91 | } -------------------------------------------------------------------------------- /sqlserver-dnn/iam.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### IAM CONFIG ### 18 | 19 | resource "google_secret_manager_secret_iam_member" "prod_compute_service_account" { 20 | project = var.provider_project 21 | for_each = toset(local.secrets_ids_accessor) 22 | secret_id = each.value 23 | role = "roles/secretmanager.secretAccessor" 24 | member = google_service_account.prod_compute_service_account.member 25 | } 26 | 27 | resource "google_secret_manager_secret_iam_member" "prod_compute_service_account_secretVersionAdder" { 28 | project = var.provider_project 29 | for_each = toset(local.secrets_ids_adder) 30 | secret_id = each.value 31 | role = "roles/secretmanager.secretVersionAdder" 32 | member = google_service_account.prod_compute_service_account.member 33 | } 34 | 35 | resource "google_service_account_iam_member" "prod_compute_service_account_serviceAccountUser" { 36 | service_account_id = google_service_account.prod_compute_service_account.name 37 | role = "roles/iam.serviceAccountUser" 38 | member = google_service_account.prod_compute_service_account.member 39 | } 40 | 41 | resource "google_project_iam_custom_role" "metadata_manager" { 42 | depends_on = [ 43 | time_sleep.api_cool_down_timer 44 | ] 45 | project = var.provider_project 46 | role_id = "metadata_manager" 47 | title = "Metadata Manager" 48 | description = "Custom Role to allow Read and Update of Instance Metadata" 49 | permissions = [ 50 | "compute.instances.setMetadata", 51 | "compute.instances.get" 52 | ] 53 | } 54 | 55 | resource "google_compute_instance_iam_member" "update_instance_metadata" { 56 | depends_on = [ 57 | google_project_iam_custom_role.metadata_manager 58 | ] 59 | project = var.provider_project 60 | zone = var.prod_zone 61 | for_each = toset(local.instances_for_metadata_manager) 62 | instance_name = each.value 63 | role = google_project_iam_custom_role.metadata_manager.name 64 | member = google_service_account.prod_compute_service_account.member 65 | } 66 | 67 | resource "google_project_iam_custom_role" "reset_gce_vm" { 68 | depends_on = [ 69 | time_sleep.api_cool_down_timer 70 | ] 71 | project = var.provider_project 72 | role_id = "reset_gce_vm" 73 | title = "GCE Reset" 74 | description = "Custom Role to allow resetting a GCE VM" 75 | permissions = [ 76 | "compute.instances.reset", 77 | "compute.instances.stop", 78 | "compute.instances.start" 79 | ] 80 | } 81 | 82 | resource "google_compute_instance_iam_member" "reset_gce_vm" { 83 | depends_on = [ 84 | google_project_iam_custom_role.reset_gce_vm 85 | ] 86 | project = var.provider_project 87 | zone = var.prod_zone 88 | for_each = toset(local.instances_for_reset_gce_vm) 89 | instance_name = each.value 90 | role = google_project_iam_custom_role.reset_gce_vm.name 91 | member = google_service_account.prod_compute_service_account.member 92 | } -------------------------------------------------------------------------------- /sqlserver-dnn/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/images/.DS_Store -------------------------------------------------------------------------------- /sqlserver-dnn/images/AG_fail_SSMS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/images/AG_fail_SSMS.png -------------------------------------------------------------------------------- /sqlserver-dnn/images/AG_properties_failover_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/images/AG_properties_failover_mode.png -------------------------------------------------------------------------------- /sqlserver-dnn/images/Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/images/Architecture.png -------------------------------------------------------------------------------- /sqlserver-dnn/images/RDP_MacOS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/images/RDP_MacOS.png -------------------------------------------------------------------------------- /sqlserver-dnn/images/RDP_Windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/images/RDP_Windows.png -------------------------------------------------------------------------------- /sqlserver-dnn/images/WebApp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/sqlserver-dnn/images/WebApp.png -------------------------------------------------------------------------------- /sqlserver-dnn/ipaddress.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### VPC IP RESERVATIONS ### 18 | 19 | resource "google_compute_address" "wsfc_cluster_ip" { 20 | name = "wsfc-cluster-ip" 21 | subnetwork = module.prod_vpc.subnets_ids[0] 22 | address_type = "INTERNAL" 23 | region = var.prod_region 24 | } 25 | -------------------------------------------------------------------------------- /sqlserver-dnn/locals.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | ### LOCALS ### 19 | 20 | locals { 21 | 22 | project_services = [ 23 | "secretmanager.googleapis.com", 24 | "compute.googleapis.com", 25 | "iam.googleapis.com", 26 | "dns.googleapis.com", 27 | "cloudresourcemanager.googleapis.com" 28 | ] 29 | 30 | ad_domain_netbios = split(".", "${var.ad_domain_name}")[0] 31 | 32 | secrets_ids_accessor = [ 33 | google_secret_manager_secret.local_admin.secret_id, 34 | google_secret_manager_secret.adds_dsrm.secret_id, 35 | google_secret_manager_secret.ad_user_password.secret_id, 36 | google_secret_manager_secret.ad_admin.secret_id, 37 | google_secret_manager_secret.sql_service.secret_id, 38 | google_secret_manager_secret.web_service.secret_id 39 | ] 40 | 41 | secrets_ids_adder = [ 42 | google_secret_manager_secret.ad_admin.secret_id 43 | ] 44 | 45 | instances_for_metadata_manager = [ 46 | google_compute_instance.dc1.name, 47 | google_compute_instance.sql1.name, 48 | google_compute_instance.sql2.name, 49 | google_compute_instance.web1.name, 50 | google_compute_instance.witness.name, 51 | google_compute_instance.jumpbox.name 52 | ] 53 | 54 | instances_for_reset_gce_vm = [ 55 | google_compute_instance.sql1.name, 56 | google_compute_instance.sql2.name, 57 | google_compute_instance.web1.name 58 | ] 59 | } -------------------------------------------------------------------------------- /sqlserver-dnn/natgw.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### NAT GATEWAY CONFIG ### 18 | 19 | resource "google_compute_router" "prod_nat_router" { 20 | project = var.provider_project 21 | name = "prod-natgw-router" 22 | network = module.prod_vpc.network_self_link 23 | region = var.prod_region 24 | } 25 | 26 | module "prod_vpc_cloud_nat" { 27 | source = "terraform-google-modules/cloud-nat/google" 28 | version = "~> 1.2" 29 | router = google_compute_router.prod_nat_router.name 30 | project_id = var.provider_project 31 | name = "prod-natgw" 32 | region = var.prod_region 33 | } -------------------------------------------------------------------------------- /sqlserver-dnn/network.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### NETWORK CONFIG ### 18 | 19 | module "prod_vpc" { 20 | depends_on = [ 21 | google_project_service.provider_project_apis 22 | ] 23 | 24 | source = "terraform-google-modules/network/google" 25 | version = "6.0.0" 26 | project_id = var.provider_project 27 | mtu = 1500 28 | network_name = var.prod_network_name 29 | subnets = var.prod_subnets 30 | } 31 | -------------------------------------------------------------------------------- /sqlserver-dnn/output.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### OUTPUT ### 18 | 19 | output "instructions_to_access_environment" { 20 | description = "Instructions to access environment" 21 | value = "\n\tLogin to the Jumpbox Server using IAP.\n\n\tCredentials\n\t\tRun this command> gcloud secrets versions access latest --secret=${google_secret_manager_secret.ad_admin.secret_id} --project=${var.provider_project}\n\n\tIAP Tunnel\n\t\tRun this command> gcloud compute start-iap-tunnel ${google_compute_instance.jumpbox.name} 3389 --local-host-port=localhost:50001 --zone=${var.prod_zone} --project=${var.provider_project}\n\n\t\tAfter the Tunnel is established, use any RDP Client to connect to localhost:50001 and use the Credentials to authenticate to the Jumpbox." 22 | } -------------------------------------------------------------------------------- /sqlserver-dnn/project-api.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### ENABLE PROJECT APIs ### 18 | 19 | resource "google_project_service" "provider_project_apis" { 20 | for_each = toset(local.project_services) 21 | service = each.value 22 | } -------------------------------------------------------------------------------- /sqlserver-dnn/providers.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_providers { 19 | google = { 20 | source = "hashicorp/google" 21 | version = "~> 4.47" 22 | } 23 | random = { 24 | source = "hashicorp/random" 25 | version = "3.4.3" 26 | } 27 | time = { 28 | source = "hashicorp/time" 29 | version = "0.9.1" 30 | } 31 | } 32 | } 33 | 34 | provider "google" { 35 | project = var.provider_project 36 | } -------------------------------------------------------------------------------- /sqlserver-dnn/secrets.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ## GENERATE RANDOM PASSWORDS ### 18 | 19 | resource "random_password" "local_admin" { 20 | length = 15 21 | special = true 22 | override_special = "!#$%&*()-_=+[]{}<>:?" 23 | } 24 | 25 | resource "random_password" "adds_dsrm" { 26 | length = 15 27 | special = true 28 | override_special = "!#$%&*()-_=+[]{}<>:?" 29 | } 30 | 31 | resource "random_password" "ad_user_password" { 32 | length = 11 33 | special = true 34 | override_special = "!#$%&*()-_=+[]{}<>:?" 35 | } 36 | 37 | resource "random_password" "sql_service_password" { 38 | length = 15 39 | special = true 40 | override_special = "!#$%&*()-_=+[]{}<>:?" 41 | } 42 | 43 | resource "random_password" "web_service_password" { 44 | length = 15 45 | special = true 46 | override_special = "!#$%&*()-_=+[]{}<>:?" 47 | } 48 | 49 | ### STORE PASSWORDS IN SECRET MANAGER ### 50 | 51 | resource "google_secret_manager_secret" "local_admin" { 52 | project = var.provider_project 53 | depends_on = [ 54 | time_sleep.api_cool_down_timer 55 | ] 56 | secret_id = "local-admin" 57 | 58 | labels = { 59 | label = "local-administrator" 60 | } 61 | 62 | replication { 63 | auto { 64 | 65 | } 66 | } 67 | } 68 | 69 | resource "google_secret_manager_secret_version" "local_admin" { 70 | secret = google_secret_manager_secret.local_admin.id 71 | secret_data = random_password.local_admin.result 72 | } 73 | 74 | resource "google_secret_manager_secret" "ad_admin" { 75 | project = var.provider_project 76 | depends_on = [ 77 | time_sleep.api_cool_down_timer 78 | ] 79 | secret_id = "${replace("${var.ad_domain_name}", ".", "-")}-admin" 80 | 81 | labels = { 82 | label = "${replace("${var.ad_domain_name}", ".", "-")}-admin" 83 | } 84 | 85 | replication { 86 | auto { 87 | 88 | } 89 | } 90 | } 91 | 92 | resource "google_secret_manager_secret" "adds_dsrm" { 93 | project = var.provider_project 94 | depends_on = [ 95 | time_sleep.api_cool_down_timer 96 | ] 97 | secret_id = "${replace("${var.ad_domain_name}", ".", "-")}-dsrm" 98 | 99 | labels = { 100 | label = "${replace("${var.ad_domain_name}", ".", "-")}-dsrm" 101 | } 102 | 103 | replication { 104 | auto { 105 | 106 | } 107 | } 108 | } 109 | 110 | resource "google_secret_manager_secret_version" "adds_dsrm" { 111 | secret = google_secret_manager_secret.adds_dsrm.id 112 | secret_data = random_password.adds_dsrm.result 113 | } 114 | 115 | resource "google_secret_manager_secret" "ad_user_password" { 116 | project = var.provider_project 117 | depends_on = [ 118 | time_sleep.api_cool_down_timer 119 | ] 120 | secret_id = "${replace("${var.ad_domain_name}", ".", "-")}-user" 121 | 122 | labels = { 123 | label = "${replace("${var.ad_domain_name}", ".", "-")}-user" 124 | } 125 | 126 | replication { 127 | auto { 128 | 129 | } 130 | } 131 | } 132 | 133 | resource "google_secret_manager_secret_version" "ad_user_password" { 134 | secret = google_secret_manager_secret.ad_user_password.id 135 | secret_data = random_password.ad_user_password.result 136 | } 137 | 138 | resource "google_secret_manager_secret" "sql_service" { 139 | project = var.provider_project 140 | depends_on = [ 141 | time_sleep.api_cool_down_timer 142 | ] 143 | secret_id = "sql-service" 144 | 145 | labels = { 146 | label = "sql-service" 147 | } 148 | 149 | replication { 150 | auto { 151 | 152 | } 153 | } 154 | } 155 | 156 | resource "google_secret_manager_secret_version" "sql_service" { 157 | secret = google_secret_manager_secret.sql_service.id 158 | secret_data = random_password.sql_service_password.result 159 | } 160 | 161 | resource "google_secret_manager_secret" "web_service" { 162 | project = var.provider_project 163 | depends_on = [ 164 | time_sleep.api_cool_down_timer 165 | ] 166 | secret_id = "web-service" 167 | 168 | labels = { 169 | label = "web-service" 170 | } 171 | 172 | replication { 173 | auto { 174 | 175 | } 176 | } 177 | } 178 | 179 | resource "google_secret_manager_secret_version" "web_service" { 180 | secret = google_secret_manager_secret.web_service.id 181 | secret_data = random_password.web_service_password.result 182 | } 183 | -------------------------------------------------------------------------------- /sqlserver-dnn/serviceaccounts.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### SERVICE ACCOUNTS CONFIG ### 18 | 19 | resource "google_service_account" "prod_compute_service_account" { 20 | depends_on = [ 21 | time_sleep.api_cool_down_timer 22 | ] 23 | project = var.provider_project 24 | account_id = "prod-compute-service-account" 25 | display_name = "Service Account for Compute Engine Instances in Prod" 26 | } 27 | -------------------------------------------------------------------------------- /sqlserver-dnn/sleeptimers.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### TIMER CONFIG ### 18 | 19 | # Sleep timer to allow all required APIs to be enabled and ready to use 20 | resource "time_sleep" "api_cool_down_timer" { 21 | depends_on = [ 22 | google_project_service.provider_project_apis 23 | ] 24 | create_duration = "90s" 25 | } 26 | 27 | # Sleep timer to allow all post deployment configuration tasks to complete 28 | resource "time_sleep" "environment_configuration_timer" { 29 | depends_on = [ 30 | google_compute_instance_iam_member.update_instance_metadata 31 | ] 32 | create_duration = "15m" 33 | } -------------------------------------------------------------------------------- /sqlserver-dnn/templatefiles/dc1.tpl: -------------------------------------------------------------------------------- 1 | <#** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *#> 16 | 17 | ### DOMAIN CONTROLLER CONFIGURATION SCRIPT ### 18 | 19 | #Generate Stage 1 Script 20 | ' 21 | $ErrorActionPreference = "Stop" 22 | 23 | #Add Bootstrapping Metadata to GCE Instance Metadata 24 | gcloud compute instances add-metadata ${dc1_os_hostname} --zone=${dc1_zone} --metadata=STATUS=BOOTSTRAPPING 25 | 26 | #Disable Scheduled Task for Stage 1 27 | Disable-ScheduledTask -TaskName "Domain Controller Bootstrap - Stage 1" 28 | 29 | #Update Bootstrapping Metadata 30 | gcloud compute instances add-metadata ${dc1_os_hostname} --zone=${dc1_zone} --metadata=STATUS=INSTALLING_ADDS_ROLE 31 | 32 | #Install required Windows features 33 | Install-WindowsFeature AD-Domain-Services -IncludeManagementTools 34 | 35 | #Install Chrome 36 | Start-BitsTransfer -Source "https://dl.google.com/chrome/install/latest/chrome_installer.exe" -Destination "C:\chrome_installer.exe" 37 | Start-Process "C:\chrome_installer.exe" -ArgumentList "/silent /install" -Wait 38 | 39 | #Update Bootstrapping Metadata 40 | gcloud compute instances add-metadata ${dc1_os_hostname} --zone=${dc1_zone} --metadata=STATUS=SETTING_PASSWORDS 41 | 42 | $local_admin_password = ConvertTo-SecureString -String (gcloud secrets versions access latest --secret=${local_admin_secret_id}) -AsPlainText -Force 43 | Get-LocalUser -Name "Administrator" | Set-LocalUser -Password $local_admin_password 44 | Get-LocalUser -Name "Administrator" | Enable-LocalUser 45 | 46 | #Get DSRM Password 47 | $adds_dsrm_password = ConvertTo-SecureString -String (gcloud secrets versions access latest --secret=${adds_dsrm_secret_id}) -AsPlainText -Force 48 | 49 | #Enable Scheduled Task for Stage 2 50 | Enable-ScheduledTask -TaskName "Domain Controller Bootstrap - Stage 2" 51 | 52 | #Unregister Scheduled Task for Stage 1 53 | Unregister-ScheduledTask -TaskName "Domain Controller Bootstrap - Stage 1" -Confirm:$false 54 | 55 | #Add Admin credentials to Secret Manager 56 | $ad_admin_user = "administrator" 57 | $ad_admin_password = (gcloud secrets versions access latest --secret=${local_admin_secret_id}) 58 | $ad_admin_cred = ("Admin Username: "+ "${ad_domain_name}" + "\" + $ad_admin_user + " `nAdmin Password: " + $ad_admin_password) 59 | $ad_admin_cred | gcloud secrets versions add ${ad_admin_secret_id} --data-file=- 60 | 61 | #Update Bootstrapping Metadata 62 | gcloud compute instances add-metadata ${dc1_os_hostname} --zone=${dc1_zone} --metadata=STATUS=INSTALLING_DOMAIN 63 | 64 | #Install Domain 65 | Install-ADDSForest ` 66 | -CreateDnsDelegation:$false ` 67 | -DatabasePath "C:\Windows\NTDS" ` 68 | -DomainMode "WinThreshold" ` 69 | -DomainName "${ad_domain_name}" ` 70 | -DomainNetbiosName "${ad_domain_netbios}" ` 71 | -ForestMode "WinThreshold" ` 72 | -InstallDns:$true ` 73 | -LogPath "C:\Windows\NTDS" ` 74 | -NoRebootOnCompletion:$false ` 75 | -SysvolPath "C:\Windows\SYSVOL" ` 76 | -SafeModeAdministratorPassword $adds_dsrm_password ` 77 | -Force:$true 78 | 79 | ' | Out-File C:\stage1.ps1 80 | 81 | #Generate Stage 2 Script 82 | ' 83 | $ErrorActionPreference = "SilentlyContinue" 84 | 85 | #Disable Scheduled Task for Stage 2 86 | Disable-ScheduledTask -TaskName "Domain Controller Bootstrap - Stage 2" 87 | 88 | #Update Bootstrapping Metadata 89 | gcloud compute instances add-metadata ${dc1_os_hostname} --zone=${dc1_zone} --metadata=STATUS=CREATING_AD_OU 90 | 91 | #Wait for Domain Services to finish loading 92 | Start-Sleep (60) 93 | 94 | #Get Domain Information 95 | $Domain = (Get-ADDomain).DNSRoot 96 | $BaseDN = (Get-ADDomain).DistinguishedName 97 | 98 | #Create OU Structure 99 | New-ADOrganizationalUnit -Name "Google Cloud" -Path $BaseDN -ProtectedFromAccidentalDeletion $False 100 | $GCPDN = (Get-ADOrganizationalUnit -Filter ''Name -eq "Google Cloud"'').DistinguishedName 101 | New-ADOrganizationalUnit -Name "Users" -Path $GCPDN -ProtectedFromAccidentalDeletion $False 102 | New-ADOrganizationalUnit -Name "Groups" -Path $GCPDN -ProtectedFromAccidentalDeletion $False 103 | New-ADOrganizationalUnit -Name "Service Accounts" -Path $GCPDN -ProtectedFromAccidentalDeletion $False 104 | 105 | #Create Service Accounts 106 | $ServiceAccountsDN = (Get-ADOrganizationalUnit -Filter ''Name -eq "Service Accounts"'' -SearchBase $GCPDN).DistinguishedName 107 | $SQLServiceAccountPassword = ConvertTo-SecureString -String (gcloud secrets versions access latest --secret=${sql_service_account_secret_id}) -AsPlainText -Force 108 | $WebServiceAccountPassword = ConvertTo-SecureString -String (gcloud secrets versions access latest --secret=${web_service_account_secret_id}) -AsPlainText -Force 109 | New-ADUser -Name "${sql_service_account_name}" -Description "SQL Server Service Account" -Path $ServiceAccountsDN -AccountPassword $SQLServiceAccountPassword -Enabled $true -PasswordNeverExpires $true 110 | New-ADUser -Name "${web_service_account_name}" -Description "Web Server Service Account" -Path $ServiceAccountsDN -AccountPassword $WebServiceAccountPassword -Enabled $true -PasswordNeverExpires $true 111 | 112 | #Update Bootstrapping Metadata 113 | gcloud compute instances add-metadata ${dc1_os_hostname} --zone=${dc1_zone} --metadata=STATUS=READY 114 | 115 | #Unregister Scheduled Task for Stage 2 116 | Unregister-ScheduledTask -TaskName "Domain Controller Bootstrap - Stage 2" -Confirm:$false 117 | 118 | ' | Out-File C:\stage2.ps1 119 | 120 | $stage1_taskName = "Domain Controller Bootstrap - Stage 1" 121 | $s1action = New-ScheduledTaskAction -Execute 'PowerShell.exe' ` 122 | -Argument "C:\stage1.ps1" 123 | $s1trigger = New-ScheduledTaskTrigger -AtStartup 124 | 125 | $s1principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount 126 | Register-ScheduledTask -Action $s1action -Trigger $s1trigger -Principal $s1principal -TaskName $stage1_taskName 127 | 128 | $stage2_taskName = "Domain Controller Bootstrap - Stage 2" 129 | $s2action = New-ScheduledTaskAction -Execute 'PowerShell.exe' ` 130 | -Argument "C:\stage2.ps1" 131 | $s2trigger = New-ScheduledTaskTrigger -AtStartup 132 | 133 | $s2principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount 134 | Register-ScheduledTask -Action $s2action -Trigger $s2trigger -Principal $s2principal -TaskName $stage2_taskName 135 | Disable-ScheduledTask -TaskName "Domain Controller Bootstrap - Stage 2" 136 | 137 | #Disable Server Manager Startup 138 | Get-ScheduledTask -TaskName "ServerManager" | Disable-ScheduledTask -------------------------------------------------------------------------------- /sqlserver-dnn/templatefiles/sql2.tpl: -------------------------------------------------------------------------------- 1 | <#** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *#> 16 | 17 | ### SQL SERVER CONFIGURATION SCRIPT ### 18 | 19 | #Generate Stage 1 Script 20 | ' 21 | $ErrorActionPreference = "Stop" 22 | 23 | #Wait for OS to initialize 24 | Start-Sleep (60) 25 | 26 | #Add Bootstrapping Metadata to GCE Instance Metadata 27 | gcloud compute instances add-metadata ${sql2_os_hostname} --zone=${sql2_zone} --metadata=STATUS=BOOTSTRAPPING 28 | 29 | #Disable Scheduled Task for Stage 1 30 | Disable-ScheduledTask -TaskName "SQL Server Bootstrap - Stage 1" 31 | 32 | # Install required Windows features 33 | Install-WindowsFeature Failover-Clustering -IncludeManagementTools 34 | Install-WindowsFeature RSAT-AD-PowerShell 35 | 36 | #Open Windows Firewall Ports for SQL Server AlwaysOn 37 | netsh advfirewall firewall add rule name="Allow SQL Server" dir=in action=allow protocol=TCP localport=1433 38 | netsh advfirewall firewall add rule name="Allow SQL Server replication" dir=in action=allow protocol=TCP localport=5022 39 | netsh advfirewall firewall add rule name="Allow SQL Server DNN Listener" dir=in action=allow protocol=TCP localport=${dnn_listener_port} 40 | 41 | #Install Chrome 42 | Start-BitsTransfer -Source "https://dl.google.com/chrome/install/latest/chrome_installer.exe" -Destination "C:\chrome_installer.exe" 43 | Start-Process "C:\chrome_installer.exe" -ArgumentList "/silent /install" -Wait 44 | 45 | #Enable Local Administrator 46 | $local_admin_password = ConvertTo-SecureString -String (gcloud secrets versions access latest --secret=${local_admin_secret_id}) -AsPlainText -Force 47 | Get-LocalUser -Name "Administrator" | Set-LocalUser -Password $local_admin_password 48 | Get-LocalUser -Name "Administrator" | Enable-LocalUser 49 | 50 | #Enable Scheduled Task for Stage 2 51 | Enable-ScheduledTask -TaskName "SQL Server Bootstrap - Stage 2" 52 | 53 | #Unregister Scheduled Task for Stage 1 54 | Unregister-ScheduledTask -TaskName "SQL Server Bootstrap - Stage 1" -Confirm:$false 55 | 56 | #Update Bootstrapping Metadata 57 | gcloud compute instances add-metadata ${sql2_os_hostname} --zone=${sql2_zone} --metadata=STATUS=DOMAIN_READY_CHECK 58 | 59 | #Check if Domain Controller is ready and add this Server to Domain 60 | $DomainStatus = gcloud compute instances describe ${dc1_os_hostname} --zone=${dc1_zone} --format="value[](metadata.items.STATUS)" 61 | while($DomainStatus -ne "READY") 62 | { 63 | Start-Sleep(5) 64 | $DomainStatus = gcloud compute instances describe ${dc1_os_hostname} --zone=${dc1_zone} --format="value[](metadata.items.STATUS)" 65 | } 66 | 67 | #Update Bootstrapping Metadata 68 | gcloud compute instances add-metadata ${sql2_os_hostname} --zone=${sql2_zone} --metadata=STATUS=JOINING_DOMAIN 69 | 70 | $ad_admin_user = "administrator" 71 | $ad_admin_username = "${ad_domain_netbios}" + "\" + $ad_admin_user 72 | $domain_credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ad_admin_username, $local_admin_password 73 | Add-Computer -Domain ${ad_domain_name} -Credential $domain_credential -Restart -Force 74 | 75 | ' | Out-File C:\stage1.ps1 76 | 77 | #Generate Stage 2 Script 78 | ' 79 | #$ErrorActionPreference = "SilentlyContinue" 80 | 81 | #Wait for OS to initialize 82 | Start-Sleep (60) 83 | 84 | #Disable Scheduled Task for Stage 2 85 | Disable-ScheduledTask -TaskName "SQL Server Bootstrap - Stage 2" 86 | 87 | #Unregister Scheduled Task for Stage 2 88 | #Unregister-ScheduledTask -TaskName "SQL Server Bootstrap - Stage 2" -Confirm:$false 89 | 90 | #Set DNS Suffix and Register DNS Client 91 | Set-DNSClient -InterfaceAlias "Ethernet" -ConnectionSpecificSuffix "${ad_domain_name}" 92 | Register-DnsClient 93 | 94 | #Change SQL Server Service Logon Account 95 | pwsh C:\sqlsvc.ps1 96 | 97 | #Rename SQL Server Instance 98 | $renameinstance = "EXEC sp_dropserver @server=@@SERVERNAME; 99 | GO 100 | EXEC sp_addserver ''${sql2_os_hostname}'', local; 101 | GO" 102 | sqlcmd -Q $renameinstance 103 | Restart-Service -Name MSSQLSERVER 104 | 105 | #Add SQL Server Service Account to Local Administrators 106 | Add-LocalGroupMember -Group "Administrators" -Member "${ad_domain_netbios}\${sql_service_account}" 107 | 108 | #Update Bootstrapping Metadata 109 | gcloud compute instances add-metadata ${sql2_os_hostname} --zone=${sql2_zone} --metadata=STATUS=READY 110 | 111 | ' | Out-File C:\stage2.ps1 112 | 113 | $stage1_taskName = "SQL Server Bootstrap - Stage 1" 114 | $s1action = New-ScheduledTaskAction -Execute 'PowerShell.exe' ` 115 | -Argument "C:\stage1.ps1" 116 | $s1trigger = New-ScheduledTaskTrigger -AtStartup 117 | 118 | $s1principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount 119 | Register-ScheduledTask -Action $s1action -Trigger $s1trigger -Principal $s1principal -TaskName $stage1_taskName 120 | 121 | $stage2_taskName = "SQL Server Bootstrap - Stage 2" 122 | $s2action = New-ScheduledTaskAction -Execute 'PowerShell.exe' ` 123 | -Argument "C:\stage2.ps1" 124 | $s2trigger = New-ScheduledTaskTrigger -AtStartup 125 | 126 | $s2principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount 127 | Register-ScheduledTask -Action $s2action -Trigger $s2trigger -Principal $s2principal -TaskName $stage2_taskName 128 | Disable-ScheduledTask -TaskName "SQL Server Bootstrap - Stage 2" 129 | 130 | #Disable Server Manager Startup 131 | Get-ScheduledTask -TaskName "ServerManager" | Disable-ScheduledTask 132 | 133 | ' 134 | $sql_username = "${ad_domain_netbios}\${sql_service_account}" 135 | $sql_user_password = ConvertTo-SecureString -String (gcloud secrets versions access latest --secret=${sql_service_account_secret_id}) -AsPlainText -Force 136 | $sql_user_credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $sql_username, $sql_user_password 137 | Set-Service -Name MSSQLSERVER -Credential $sql_user_credential 138 | Restart-Service -Name MSSQLSERVER 139 | ' | Out-File C:\sqlsvc.ps1 -------------------------------------------------------------------------------- /sqlserver-dnn/templatefiles/witness.tpl: -------------------------------------------------------------------------------- 1 | <#** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *#> 16 | 17 | ### CLUSTER WITNESS SERVER CONFIGURATION SCRIPT ### 18 | 19 | #Generate Stage 1 Script 20 | ' 21 | $ErrorActionPreference = "Stop" 22 | 23 | #Wait for OS to initialize 24 | Start-Sleep (60) 25 | 26 | #Add Bootstrapping Metadata to GCE Instance Metadata 27 | gcloud compute instances add-metadata ${witness_os_hostname} --zone=${witness_zone} --metadata=STATUS=BOOTSTRAPPING 28 | 29 | #Disable Scheduled Task for Stage 1 30 | Disable-ScheduledTask -TaskName "Witness Server Bootstrap - Stage 1" 31 | 32 | #Install Chrome 33 | Start-BitsTransfer -Source "https://dl.google.com/chrome/install/latest/chrome_installer.exe" -Destination "C:\chrome_installer.exe" 34 | Start-Process "C:\chrome_installer.exe" -ArgumentList "/silent /install" -Wait 35 | 36 | #Enable Local Administrator 37 | $local_admin_password = ConvertTo-SecureString -String (gcloud secrets versions access latest --secret=${local_admin_secret_id}) -AsPlainText -Force 38 | Get-LocalUser -Name "Administrator" | Set-LocalUser -Password $local_admin_password 39 | Get-LocalUser -Name "Administrator" | Enable-LocalUser 40 | 41 | #Enable Scheduled Task for Stage 2 42 | Enable-ScheduledTask -TaskName "Witness Server Bootstrap - Stage 2" 43 | 44 | #Unregister Scheduled Task for Stage 1 45 | Unregister-ScheduledTask -TaskName "Witness Server Bootstrap - Stage 1" -Confirm:$false 46 | 47 | #Update Bootstrapping Metadata 48 | gcloud compute instances add-metadata ${witness_os_hostname} --zone=${witness_zone} --metadata=STATUS=DOMAIN_READY_CHECK 49 | 50 | #Check if Domain Controller is ready and add this Server to Domain 51 | $DomainStatus = gcloud compute instances describe ${dc1_os_hostname} --zone=${dc1_zone} --format="value[](metadata.items.STATUS)" 52 | while($DomainStatus -ne "READY") 53 | { 54 | Start-Sleep(5) 55 | $DomainStatus = gcloud compute instances describe ${dc1_os_hostname} --zone=${dc1_zone} --format="value[](metadata.items.STATUS)" 56 | } 57 | 58 | #Update Bootstrapping Metadata 59 | gcloud compute instances add-metadata ${witness_os_hostname} --zone=${witness_zone} --metadata=STATUS=JOINING_DOMAIN 60 | 61 | $ad_admin_user = "administrator" 62 | $ad_admin_username = "${ad_domain_netbios}" + "\" + $ad_admin_user 63 | $domain_credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ad_admin_username, $local_admin_password 64 | Add-Computer -Domain ${ad_domain_name} -Credential $domain_credential -Restart -Force 65 | 66 | ' | Out-File C:\stage1.ps1 67 | 68 | #Generate Stage 2 Script 69 | ' 70 | $ErrorActionPreference = "SilentlyContinue" 71 | 72 | #Disable Scheduled Task for Stage 2 73 | Disable-ScheduledTask -TaskName "Witness Server Bootstrap - Stage 2" 74 | 75 | #Unregister Scheduled Task for Stage 2 76 | #Unregister-ScheduledTask -TaskName "Witness Server Bootstrap - Stage 2" -Confirm:$false 77 | 78 | #Set DNS Suffix and Register DNS Client 79 | Set-DNSClient -InterfaceAlias "Ethernet" -ConnectionSpecificSuffix "${ad_domain_name}" 80 | Register-DnsClient 81 | 82 | #Update Bootstrapping Metadata 83 | gcloud compute instances add-metadata ${witness_os_hostname} --zone=${witness_zone} --metadata=STATUS=CREATING_FILESHARE 84 | 85 | #Create File Share for Cluster Quorum 86 | New-Item "C:\QWitness" -Type directory 87 | New-Item "C:\Backup" -Type directory 88 | icacls C:\QWitness\ /grant "Everyone:(OI)(CI)(M)" 89 | icacls C:\Backup\ /grant "Everyone:(OI)(CI)(M)" 90 | 91 | New-SmbShare -Name QWitness -Path "C:\QWitness" -Description "SQL File Share Witness" -FullAccess "Everyone" 92 | New-SmbShare -Name Backup -Path "C:\Backup" -Description "SQL Backup" -FullAccess "Everyone" 93 | 94 | #Update Bootstrapping Metadata 95 | gcloud compute instances add-metadata ${witness_os_hostname} --zone=${witness_zone} --metadata=STATUS=READY 96 | 97 | ' | Out-File C:\stage2.ps1 98 | 99 | $stage1_taskName = "Witness Server Bootstrap - Stage 1" 100 | $s1action = New-ScheduledTaskAction -Execute 'PowerShell.exe' ` 101 | -Argument "C:\stage1.ps1" 102 | $s1trigger = New-ScheduledTaskTrigger -AtStartup 103 | 104 | $s1principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount 105 | Register-ScheduledTask -Action $s1action -Trigger $s1trigger -Principal $s1principal -TaskName $stage1_taskName 106 | 107 | $stage2_taskName = "Witness Server Bootstrap - Stage 2" 108 | $s2action = New-ScheduledTaskAction -Execute 'PowerShell.exe' ` 109 | -Argument "C:\stage2.ps1" 110 | $s2trigger = New-ScheduledTaskTrigger -AtStartup 111 | 112 | $s2principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount 113 | Register-ScheduledTask -Action $s2action -Trigger $s2trigger -Principal $s2principal -TaskName $stage2_taskName 114 | Disable-ScheduledTask -TaskName "Witness Server Bootstrap - Stage 2" 115 | 116 | #Disable Server Manager Startup 117 | Get-ScheduledTask -TaskName "ServerManager" | Disable-ScheduledTask -------------------------------------------------------------------------------- /sqlserver-dnn/terraform.tfvars: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | provider_project = "YOUR_PROJECT_ID" -------------------------------------------------------------------------------- /sqlserver-dnn/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ### VARIABLES ### 18 | 19 | variable "provider_project" { 20 | description = "The Project ID that will be used for the demo" 21 | } 22 | 23 | variable "prod_region" { 24 | description = "The Prod Compute region for the demo" 25 | default = "us-central1" 26 | } 27 | 28 | variable "prod_zone" { 29 | description = "The Prod Compute zone for the demo" 30 | default = "us-central1-f" 31 | } 32 | 33 | variable "prod_network_name" { 34 | description = "The name for the Prod Network VPC" 35 | default = "prod-vpc" 36 | } 37 | 38 | variable "prod_subnets" { 39 | description = "The list of subnets to be created in the Prod VPC" 40 | default = [ 41 | { 42 | subnet_name = "prod-subnet-us-central1" 43 | subnet_ip = "10.145.0.0/24" 44 | subnet_region = "us-central1" 45 | subnet_private_access = "true" 46 | } 47 | ] 48 | } 49 | 50 | variable "dc1_hostname" { 51 | description = "Hostname for the Primary Domain Controller" 52 | default = "dc1" 53 | } 54 | 55 | variable "sql1_hostname" { 56 | description = "Hostname for the Primary SQL Server AlwaysOn Node" 57 | default = "sql1" 58 | } 59 | 60 | variable "sql2_hostname" { 61 | description = "Hostname for the Secondary SQL Server AlwaysOn Node" 62 | default = "sql2" 63 | } 64 | 65 | variable "web1_hostname" { 66 | description = "Hostname for the Web Server" 67 | default = "web1" 68 | } 69 | 70 | variable "witness_hostname" { 71 | description = "Hostname for the Cluster Witness Server" 72 | default = "witness" 73 | } 74 | 75 | variable "jumpbox_hostname" { 76 | description = "Hostname for the Jumpbox Server" 77 | default = "jumpbox" 78 | } 79 | 80 | variable "ad_domain_name" { 81 | description = "Active Directory Domain Name configured on DC1" 82 | default = "example.com" 83 | } 84 | 85 | variable "sql_service_account_name" { 86 | description = "SQL Service Account Username" 87 | default = "sql-svc" 88 | } 89 | 90 | variable "web_service_account_name" { 91 | description = "Web Server user for connection string" 92 | default = "web-user" 93 | } 94 | 95 | variable "aoag_name" { 96 | description = "AlwaysOn Availability Group Name" 97 | default = "aoag-dnn" 98 | } 99 | 100 | variable "dnn_listener_name" { 101 | description = "AlwaysOn Availability Group Distributed Network Name (DNN) Listerner Name" 102 | default = "dnn-listener" 103 | } 104 | 105 | variable "dnn_listener_port" { 106 | description = "Distributed Network Name (DNN) Listerner TCP Port - DO NOT USE TCP 1433" 107 | default = "6789" 108 | } 109 | 110 | variable "database_backup_url" { 111 | description = "The direct download URL for the Sample Database" 112 | default = "https://docs.google.com/uc?export=download&id=1jh8gm2raP2T5KI6SR5q2wXyR8PZfXv5C" 113 | } 114 | 115 | variable "website_url" { 116 | description = "The direct download URL for the Sample Web App" 117 | default = "https://docs.google.com/uc?export=download&id=1x7G2mpySty_9r5KaWu3LVLOLTmbYzB9t" 118 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/windows-cold-dr-async-pd/.DS_Store -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/dr/dr-vms.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_instance" "dr-vms" { 18 | depends_on = [google_compute_instance.dr-dc] 19 | for_each = local.app_dr_vms 20 | name = each.key 21 | machine_type = each.value.vm_size 22 | zone = each.value.zone 23 | project = var.app-dr-project 24 | 25 | # tags = ["foo", "bar"] 26 | 27 | boot_disk { 28 | source = "projects/${var.app-dr-project}/zones/${each.value.dr_boot_disk_zone}/disks/${each.value.dr_boot_disk}" 29 | } 30 | 31 | network_interface { 32 | subnetwork = var.app-dr-ip-subnet-self-link 33 | network_ip = data.terraform_remote_state.app_server_ip.outputs.app_server_ip[each.value.vm_order] 34 | } 35 | 36 | shielded_instance_config { 37 | enable_secure_boot = true 38 | enable_vtpm = true 39 | enable_integrity_monitoring = true 40 | } 41 | 42 | service_account { 43 | email = var.app-dr-service-account 44 | scopes = ["cloud-platform"] 45 | } 46 | 47 | allow_stopping_for_update = true 48 | 49 | } 50 | 51 | resource "google_compute_instance" "dr-dc" { 52 | count = var.use-domain-controller ? 1 : 0 53 | name = var.app-dc-gce-display-name 54 | machine_type = var.app-dc-machine-type 55 | zone = var.app-dr-dc-zone 56 | project = var.app-dr-project 57 | 58 | # tags = ["foo", "bar"] 59 | 60 | boot_disk { 61 | source = "projects/${var.app-dr-project}/zones/${var.app-dr-dc-zone}/disks/${var.app-dc-gce-display-name}-secboot" 62 | } 63 | 64 | network_interface { 65 | subnetwork = var.app-dr-ip-subnet-self-link 66 | network_ip = var.app-dr-dc-ip 67 | } 68 | 69 | shielded_instance_config { 70 | enable_secure_boot = true 71 | enable_vtpm = true 72 | enable_integrity_monitoring = true 73 | } 74 | 75 | service_account { 76 | email = var.app-dr-service-account 77 | scopes = ["cloud-platform"] 78 | } 79 | 80 | allow_stopping_for_update = true 81 | 82 | } 83 | 84 | resource "google_compute_instance" "dr-sql" { 85 | count = var.use-sql ? 1 : 0 86 | depends_on = [google_compute_instance.dr-dc] 87 | name = var.app-sql-gce-display-name 88 | machine_type = var.app-sql-machine-type 89 | zone = var.app-dr-sql-zone 90 | project = var.app-dr-project 91 | 92 | boot_disk { 93 | source = "projects/${var.app-dr-project}/zones/${var.app-dr-sql-zone}/disks/${var.app-sql-gce-display-name}-secboot" 94 | } 95 | 96 | attached_disk { 97 | source = "projects/${var.app-dr-project}/zones/${var.app-dr-sql-zone}/disks/${var.app-sql-data-disk-name}-secboot" 98 | } 99 | 100 | network_interface { 101 | subnetwork = var.app-dr-ip-subnet-self-link 102 | network_ip = var.app-dr-sql-ip 103 | } 104 | 105 | shielded_instance_config { 106 | enable_secure_boot = true 107 | enable_vtpm = true 108 | enable_integrity_monitoring = true 109 | } 110 | 111 | service_account { 112 | email = var.app-dr-service-account 113 | scopes = ["cloud-platform"] 114 | } 115 | 116 | allow_stopping_for_update = true 117 | 118 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/dr/locals.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | data "terraform_remote_state" "app_server_ip" { 18 | backend = "local" 19 | 20 | config = { 21 | path = "../setup/terraform.tfstate" 22 | } 23 | } 24 | 25 | locals { 26 | app_dr_vms = { 27 | "dr-app-001" = { vm_order = 0, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-001-secboot" }, 28 | "dr-app-002" = { vm_order = 1, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-002-secboot" }, 29 | "dr-app-003" = { vm_order = 2, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-003-secboot" }, 30 | "dr-app-004" = { vm_order = 3, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-004-secboot" }, 31 | "dr-app-005" = { vm_order = 4, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-005-secboot" }, 32 | "dr-app-006" = { vm_order = 5, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-006-secboot" }, 33 | "dr-app-007" = { vm_order = 6, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-007-secboot" }, 34 | "dr-app-008" = { vm_order = 7, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-008-secboot" }, 35 | "dr-app-009" = { vm_order = 8, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-009-secboot" }, 36 | "dr-app-010" = { vm_order = 9, vm_size = "e2-medium", zone = "us-central1-a", dr_boot_disk_zone = "us-central1-a", dr_boot_disk = "app-010-secboot" }, 37 | } 38 | 39 | sec_boot_disks_for_failback = { 40 | "app-001-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-001-secboot" }, 41 | "app-002-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-002-secboot" }, 42 | "app-003-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-003-secboot" }, 43 | "app-004-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-004-secboot" }, 44 | "app-005-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-005-secboot" }, 45 | "app-006-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-006-secboot" }, 46 | "app-007-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-007-secboot" }, 47 | "app-008-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-008-secboot" }, 48 | "app-009-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-009-secboot" }, 49 | "app-010-failback" = { disk_type = "pd-balanced", failback_zone = "us-east4-a", dr_disk_zone = "us-central1-a", dr_disk = "app-010-secboot" }, 50 | } 51 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/dr/output.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "app_server_ip" { 18 | value = values(google_compute_instance.dr-vms).*.network_interface.0.network_ip 19 | } 20 | -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/dr/stage-failback-async-boot-disks.tf.dr: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_disk" "failback-sec-boot-disks-for-app-vms" { 18 | depends_on = [google_compute_instance.dr-vms] 19 | for_each = local.sec_boot_disks_for_failback 20 | name = each.key 21 | type = each.value.disk_type 22 | zone = each.value.failback_zone 23 | project = var.app-prod-project 24 | 25 | guest_os_features { 26 | type = "UEFI_COMPATIBLE" 27 | } 28 | 29 | guest_os_features { 30 | type = "MULTI_IP_SUBNET" 31 | } 32 | 33 | guest_os_features { 34 | type = "VIRTIO_SCSI_MULTIQUEUE" 35 | } 36 | 37 | guest_os_features { 38 | type = "GVNIC" 39 | } 40 | 41 | guest_os_features { 42 | type = "WINDOWS" 43 | } 44 | 45 | licenses = ["projects/windows-cloud/global/licenses/windows-server-2022-dc"] 46 | 47 | async_primary_disk { 48 | disk = "projects/${var.app-dr-project}/zones/${each.value.dr_disk_zone}/disks/${each.value.dr_disk}" 49 | } 50 | 51 | physical_block_size_bytes = 4096 52 | } 53 | 54 | resource "google_compute_disk" "failback-sec-boot-disk-for-dc" { 55 | count = var.use-domain-controller ? 1 : 0 56 | depends_on = [google_compute_instance.dr-dc] 57 | name = "${var.app-dc-gce-display-name}-failback" 58 | type = var.app-dc-disk-type 59 | zone = var.app-prod-dc-zone 60 | project = var.app-prod-project 61 | 62 | guest_os_features { 63 | type = "UEFI_COMPATIBLE" 64 | } 65 | 66 | guest_os_features { 67 | type = "MULTI_IP_SUBNET" 68 | } 69 | 70 | guest_os_features { 71 | type = "VIRTIO_SCSI_MULTIQUEUE" 72 | } 73 | 74 | guest_os_features { 75 | type = "GVNIC" 76 | } 77 | 78 | guest_os_features { 79 | type = "WINDOWS" 80 | } 81 | 82 | licenses = ["projects/windows-cloud/global/licenses/windows-server-2022-dc"] 83 | 84 | async_primary_disk { 85 | disk = "projects/${var.app-dr-project}/zones/${var.app-dr-dc-zone}/disks/${var.app-dc-gce-display-name}-secboot" 86 | } 87 | 88 | physical_block_size_bytes = 4096 89 | } 90 | 91 | resource "google_compute_disk" "failback-sec-boot-disk-for-sql" { 92 | count = var.use-sql ? 1 : 0 93 | depends_on = [google_compute_instance.dr-sql] 94 | name = "${var.app-sql-gce-display-name}-failback" 95 | type = var.app-sql-disk-type 96 | zone = var.app-prod-sql-zone 97 | project = var.app-prod-project 98 | 99 | async_primary_disk { 100 | disk = "projects/${var.app-dr-project}/zones/${var.app-dr-sql-zone}/disks/${var.app-sql-gce-display-name}-secboot" 101 | } 102 | 103 | physical_block_size_bytes = 4096 104 | } 105 | 106 | resource "google_compute_disk" "failback-sec-data-disk-for-sql" { 107 | count = var.use-sql ? 1 : 0 108 | depends_on = [google_compute_instance.dr-sql] 109 | name = "${var.app-sql-data-disk-name}-failback" 110 | type = var.app-sql-disk-type 111 | zone = var.app-prod-sql-zone 112 | project = var.app-prod-project 113 | 114 | async_primary_disk { 115 | disk = "projects/${var.app-dr-project}/zones/${var.app-dr-sql-zone}/disks/${var.app-sql-data-disk-name}-secboot" 116 | } 117 | 118 | physical_block_size_bytes = 4096 119 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/dr/stage-failback-async-rep.tf.dr: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_disk_async_replication" "failback-async-replication-for-app-servers" { 18 | for_each = local.sec_boot_disks_for_failback 19 | depends_on = [google_compute_disk.failback-sec-boot-disks-for-app-vms] 20 | primary_disk = "projects/${var.app-dr-project}/zones/${each.value.dr_disk_zone}/disks/${each.value.dr_disk}" 21 | secondary_disk { 22 | disk = google_compute_disk.failback-sec-boot-disks-for-app-vms[each.key].id 23 | } 24 | } 25 | 26 | resource "google_compute_disk_async_replication" "failback-async-replication-for-dc" { 27 | count = var.use-domain-controller ? 1 : 0 28 | depends_on = [google_compute_disk.failback-sec-boot-disk-for-dc] 29 | primary_disk = "projects/${var.app-dr-project}/zones/${var.app-dr-dc-zone}/disks/${var.app-dc-gce-display-name}-secboot" 30 | secondary_disk { 31 | disk = google_compute_disk.failback-sec-boot-disk-for-dc[count.index].id 32 | } 33 | } 34 | 35 | resource "google_compute_disk_async_replication" "failback-async-replication-for-sql-boot" { 36 | count = var.use-sql ? 1 : 0 37 | depends_on = [google_compute_disk.failback-sec-boot-disk-for-sql] 38 | primary_disk = "projects/${var.app-dr-project}/zones/${var.app-dr-sql-zone}/disks/${var.app-sql-gce-display-name}-secboot" 39 | secondary_disk { 40 | disk = google_compute_disk.failback-sec-boot-disk-for-sql[count.index].id 41 | } 42 | } 43 | 44 | resource "google_compute_disk_async_replication" "failback-async-replication-for-sql-data" { 45 | count = var.use-sql ? 1 : 0 46 | depends_on = [google_compute_disk.failback-sec-boot-disk-for-sql] 47 | primary_disk = "projects/${var.app-dr-project}/zones/${var.app-dr-sql-zone}/disks/${var.app-sql-data-disk-name}-secboot" 48 | secondary_disk { 49 | disk = google_compute_disk.failback-sec-data-disk-for-sql[count.index].id 50 | } 51 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/dr/terraform.tfvars.sample: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | ########################### 17 | ### Configure Variables ### 18 | ########################### 19 | 20 | # Set this variable first in your command line interface 21 | # Bash 22 | # > export app_dr_project=REPLACE_WITH_SERVICE_PROJECT_FOR_DR_PROJECT_ID 23 | # > export shared_vpc_host_project=REPLACE_WITH_YOUR_SHARED_VPC_PROJECT_ID 24 | 25 | #################### 26 | ### DR Variables ### 27 | #################### 28 | 29 | use-domain-controller = false 30 | # Default value is FALSE 31 | # Set value to TRUE if you plan to manually build a Domain Controller as part of the demo 32 | 33 | use-sql = false 34 | # Default value is FALSE 35 | # Set value to TRUE if you plan to manually build a SQL Server with two disks as part of the demo 36 | 37 | app-dr-project = "REPLACE_WITH_SERVICE_PROJECT_FOR_DR_PROJECT_ID" 38 | 39 | app-dr-ip-subnet-self-link = "REPLACE_WITH_IP_SUBNET_FOR_DR_SELF_LINK" 40 | # Run this gcloud command to get the self link 41 | # > gcloud compute networks subnets describe dr-app-us-central1 --region=us-central1 --project=$shared_vpc_host_project --format="value(selfLink)" 42 | 43 | app-dr-service-account = "REPLACE_WITH_COMPUTE_ENGINE_DEFAULT_SERVICE_ACCOUNT" 44 | # Run this gcloud command to get the Service Account 45 | # > gcloud iam service-accounts list --project=$app_dr_project 46 | 47 | app-dc-gce-display-name = "" 48 | # Leave the variable value as blank if you are not using a Domain Controller 49 | 50 | app-dc-disk-type = "" 51 | # Leave the variable value as blank if you are not using a Domain Controller 52 | # Default is pd-balanced 53 | 54 | app-dc-machine-type = "" 55 | # Leave the variable value as blank if you are not using a Domain Controller 56 | 57 | app-dr-dc-ip = "" 58 | # Leave the variable value as blank if you are not using a Domain Controller 59 | 60 | app-dr-dc-zone = "" 61 | # Leave the variable value as blank if you are not using a Domain Controller 62 | # Default is us-central1-a 63 | 64 | app-sql-gce-display-name = "" 65 | # Leave the variable value as blank if you are not using a SQL Server 66 | 67 | app-sql-disk-type = "" 68 | # Leave the variable value as blank if you are not using a SQL Server 69 | # Default is pd-balanced 70 | 71 | app-sql-machine-type = "" 72 | # Leave the variable value as blank if you are not using a SQL Server 73 | 74 | app-dr-sql-ip = "" 75 | # Leave the variable value as blank if you are not using a SQL Server 76 | 77 | app-dr-sql-zone = "" 78 | # Leave the variable value as blank if you are not using a SQL Server 79 | # Default is us-central1-a 80 | 81 | app-sql-data-disk-name = "" 82 | # Leave the variable value as blank if you are not using a SQL Server 83 | 84 | ##################################### 85 | ### Failback/Production Variables ### 86 | ##################################### 87 | app-prod-project = "REPLACE_WITH_SERVICE_PROJECT_FOR_PRODUCTION_PROJECT_ID" 88 | 89 | app-prod-dc-zone = "" 90 | # Leave the variable value as blank if you are not using a Domain Controller 91 | # Default is us-east4-a 92 | 93 | app-prod-sql-zone = "" 94 | # Leave the variable value as blank if you are not using a SQL Server 95 | # Default is us-east4-a -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/dr/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #################### 18 | ### DR Variables ### 19 | #################### 20 | 21 | variable "use-domain-controller" { 22 | type = bool 23 | description = "Set value to TRUE if you plan to manually build a Domain Controller as part of the demo. The default value will be set to FALSE" 24 | } 25 | 26 | variable "use-sql" { 27 | type = bool 28 | description = "Set value to TRUE if you plan to manually build a SQL Server with two disks as part of the demo. The default value will be set to FALSE" 29 | } 30 | 31 | variable "app-dr-project" { 32 | type = string 33 | description = "The DR project" 34 | } 35 | 36 | variable "app-dr-service-account" { 37 | type = string 38 | description = "The DR GCE service account" 39 | } 40 | 41 | variable "app-dc-gce-display-name" { 42 | type = string 43 | description = "The domain controller name in the GCE console" 44 | } 45 | 46 | variable "app-dc-disk-type" { 47 | type = string 48 | description = "The DR domain controller boot disk type" 49 | } 50 | 51 | variable "app-dc-machine-type" { 52 | type = string 53 | description = "The DR domain controller machine type" 54 | } 55 | 56 | variable "app-dr-ip-subnet-self-link" { 57 | type = string 58 | description = "The DR subnet" 59 | } 60 | 61 | variable "app-dr-dc-ip" { 62 | type = string 63 | description = "The DR domain controller IP" 64 | } 65 | 66 | variable "app-dr-dc-zone" { 67 | type = string 68 | description = "The DR zone to contain the domain controller" 69 | } 70 | 71 | variable "app-sql-gce-display-name" { 72 | type = string 73 | description = "The SQL Server name in the GCE console" 74 | } 75 | 76 | variable "app-sql-disk-type" { 77 | type = string 78 | description = "The DR SQL Server boot disk type" 79 | } 80 | 81 | variable "app-sql-machine-type" { 82 | type = string 83 | description = "The DR SQL Server machine type" 84 | } 85 | 86 | variable "app-dr-sql-ip" { 87 | type = string 88 | description = "The DR SQL Server IP" 89 | } 90 | 91 | variable "app-dr-sql-zone" { 92 | type = string 93 | description = "The DR zone to contain the SQL Server" 94 | } 95 | 96 | variable "app-sql-data-disk-name" { 97 | type = string 98 | description = "The failback/production SQL Server data disk name" 99 | } 100 | 101 | ##################################### 102 | ### Failback/Production Variables ### 103 | ##################################### 104 | variable "app-prod-project" { 105 | type = string 106 | description = "The failback/production project" 107 | } 108 | 109 | variable "app-prod-dc-zone" { 110 | type = string 111 | description = "The failback/production zone to contain the domain controller" 112 | } 113 | 114 | variable "app-prod-sql-zone" { 115 | type = string 116 | description = "The failback/production zone to contain the SQL Server" 117 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/failback/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/google" { 5 | version = "5.14.0" 6 | hashes = [ 7 | "h1:T6EW5HOI1IrE4zHzQ/5kLyul+U2ByEaIgqMu4Ja7JFI=", 8 | "zh:3927ef7417d9d8a56077e6655d76c99f4175f9746e39226a00ee0555f8c63f8f", 9 | "zh:4b4f521f0779a1797047a8c531afda093aade934b4a49c080fe8d38680b3a52f", 10 | "zh:7e880c5b72684fc8342e03180a1fbbec65c6afeb70511b9c16181d5e168269e6", 11 | "zh:81a7f2efc30e698f476d3e240ee2d82f14eda374852059429fe808ad77b6addd", 12 | "zh:826d4ea55b4afceefb332646f21c6b6dc590b39b16e8d9b5d4a4211beb91dc5e", 13 | "zh:865600ef669fcdd4ae77515c3fd12565fab0f2a263fa2a6dae562f6fe68ed093", 14 | "zh:8e933d1d10fd316e62340175667264f093e4d24457b63d5adf3c424cce22b495", 15 | "zh:bf261924f7350074a355e5b9337f3a8054efb20d316e9085f2b5766dfb5126c4", 16 | "zh:e28e67dcbd4bbd82798561baf86d3dd04f97e08bbf523dfb9f355564ef27d3d6", 17 | "zh:f33cdd3117af8a15f33d375dbe398a5e558730cf6a7a145a479ab68e77572c12", 18 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", 19 | "zh:f913a0e0708391ccd26fc3458158cc1e10d68dc621bef3a1583328c61a77225d", 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/failback/failback-vms.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_instance" "failback-dc" { 18 | count = var.use-domain-controller ? 1 : 0 19 | name = var.app-dc-gce-display-name 20 | machine_type = var.app-dc-machine-type 21 | zone = var.app-prod-dc-zone 22 | project = var.app-prod-project 23 | 24 | boot_disk { 25 | source = "projects/${var.app-prod-project}/zones/${var.app-prod-dc-zone}/disks/${var.app-dc-gce-display-name}-failback" 26 | } 27 | 28 | network_interface { 29 | subnetwork = var.app-prod-ip-subnet-self-link 30 | network_ip = var.app-prod-dc-ip 31 | } 32 | 33 | shielded_instance_config { 34 | enable_secure_boot = true 35 | enable_vtpm = true 36 | enable_integrity_monitoring = true 37 | } 38 | 39 | service_account { 40 | email = var.app-prod-service-account 41 | scopes = ["cloud-platform"] 42 | } 43 | 44 | allow_stopping_for_update = true 45 | 46 | } 47 | 48 | resource "google_compute_instance" "failback-vms" { 49 | depends_on = [google_compute_instance.failback-dc] 50 | for_each = local.app_failback_vms 51 | name = each.key 52 | machine_type = each.value.vm_size 53 | zone = each.value.failback_zone 54 | project = var.app-prod-project 55 | 56 | boot_disk { 57 | source = "projects/${var.app-prod-project}/zones/${each.value.failback_zone}/disks/${each.value.failback_boot_disk}" 58 | } 59 | 60 | network_interface { 61 | subnetwork = var.app-prod-ip-subnet-self-link 62 | network_ip = data.terraform_remote_state.app_server_ip.outputs.app_server_ip[each.value.vm_order] 63 | } 64 | 65 | shielded_instance_config { 66 | enable_secure_boot = true 67 | enable_vtpm = true 68 | enable_integrity_monitoring = true 69 | } 70 | 71 | service_account { 72 | email = var.app-prod-service-account 73 | scopes = ["cloud-platform"] 74 | } 75 | 76 | allow_stopping_for_update = true 77 | 78 | } 79 | 80 | resource "google_compute_instance" "failback-sql" { 81 | count = var.use-sql ? 1 : 0 82 | depends_on = [google_compute_instance.failback-dc] 83 | name = var.app-sql-gce-display-name 84 | machine_type = var.app-sql-machine-type 85 | zone = var.app-prod-sql-zone 86 | project = var.app-prod-project 87 | 88 | boot_disk { 89 | source = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-gce-display-name}-failback" 90 | } 91 | 92 | attached_disk { 93 | source = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-data-disk-name}-failback" 94 | } 95 | 96 | network_interface { 97 | subnetwork = var.app-prod-ip-subnet-self-link 98 | network_ip = var.app-prod-sql-ip 99 | } 100 | 101 | shielded_instance_config { 102 | enable_secure_boot = true 103 | enable_vtpm = true 104 | enable_integrity_monitoring = true 105 | } 106 | 107 | service_account { 108 | email = var.app-prod-service-account 109 | scopes = ["cloud-platform"] 110 | } 111 | 112 | allow_stopping_for_update = true 113 | 114 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/failback/locals.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | data "terraform_remote_state" "app_server_ip" { 18 | backend = "local" 19 | 20 | config = { 21 | path = "../dr/terraform.tfstate" 22 | } 23 | } 24 | 25 | locals { 26 | app_failback_vms = { 27 | "app-001" = { vm_order = 0, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-001-failback" }, 28 | "app-002" = { vm_order = 1, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-002-failback" }, 29 | "app-003" = { vm_order = 2, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-003-failback" }, 30 | "app-004" = { vm_order = 3, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-004-failback" }, 31 | "app-005" = { vm_order = 4, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-005-failback" }, 32 | "app-006" = { vm_order = 5, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-006-failback" }, 33 | "app-007" = { vm_order = 6, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-007-failback" }, 34 | "app-008" = { vm_order = 7, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-008-failback" }, 35 | "app-009" = { vm_order = 8, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-009-failback" }, 36 | "app-010" = { vm_order = 9, vm_size = "e2-medium", failback_zone = "us-east4-a", failback_boot_disk = "app-010-failback" }, 37 | } 38 | 39 | sec_boot_disks_for_dr = { 40 | "app-001-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-001-failback" }, 41 | "app-002-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-002-failback" }, 42 | "app-003-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-003-failback" }, 43 | "app-004-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-004-failback" }, 44 | "app-005-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-005-failback" }, 45 | "app-006-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-006-failback" }, 46 | "app-007-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-007-failback" }, 47 | "app-008-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-008-failback" }, 48 | "app-009-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-009-failback" }, 49 | "app-010-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-010-failback" }, 50 | } 51 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/failback/output.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "app_server_ip" { 18 | value = values(google_compute_instance.failback-vms).*.network_interface.0.network_ip 19 | } 20 | -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/failback/restage-dr-async-boot-disks.tf.failback: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_disk" "dr-sec-boot-disks-for-app-vms" { 18 | depends_on = [google_compute_instance.failback-vms] 19 | for_each = local.sec_boot_disks_for_dr 20 | name = each.key 21 | type = each.value.disk_type 22 | zone = each.value.dr_zone 23 | project = var.app-dr-project 24 | 25 | guest_os_features { 26 | type = "UEFI_COMPATIBLE" 27 | } 28 | 29 | guest_os_features { 30 | type = "MULTI_IP_SUBNET" 31 | } 32 | 33 | guest_os_features { 34 | type = "VIRTIO_SCSI_MULTIQUEUE" 35 | } 36 | 37 | guest_os_features { 38 | type = "GVNIC" 39 | } 40 | 41 | guest_os_features { 42 | type = "WINDOWS" 43 | } 44 | 45 | licenses = ["projects/windows-cloud/global/licenses/windows-server-2022-dc"] 46 | 47 | async_primary_disk { 48 | disk = "projects/${var.app-prod-project}/zones/${each.value.prim_disk_zone}/disks/${each.value.prim_disk}" 49 | } 50 | 51 | physical_block_size_bytes = 4096 52 | } 53 | 54 | resource "google_compute_disk" "dr-sec-boot-disk-for-dc" { 55 | depends_on = [google_compute_instance.failback-dc] 56 | count = var.use-domain-controller ? 1 : 0 57 | name = "${var.app-dc-gce-display-name}-secboot" 58 | type = var.app-dc-disk-type 59 | zone = var.app-dr-dc-zone 60 | project = var.app-dr-project 61 | 62 | guest_os_features { 63 | type = "UEFI_COMPATIBLE" 64 | } 65 | 66 | guest_os_features { 67 | type = "MULTI_IP_SUBNET" 68 | } 69 | 70 | guest_os_features { 71 | type = "VIRTIO_SCSI_MULTIQUEUE" 72 | } 73 | 74 | guest_os_features { 75 | type = "GVNIC" 76 | } 77 | 78 | guest_os_features { 79 | type = "WINDOWS" 80 | } 81 | 82 | licenses = ["projects/windows-cloud/global/licenses/windows-server-2022-dc"] 83 | 84 | async_primary_disk { 85 | disk = "projects/${var.app-prod-project}/zones/${var.app-prod-dc-zone}/disks/${var.app-dc-gce-display-name}-failback" 86 | } 87 | 88 | physical_block_size_bytes = 4096 89 | } 90 | 91 | resource "google_compute_disk" "dr-sec-boot-disk-for-sql" { 92 | depends_on = [google_compute_instance.failback-sql] 93 | count = var.use-sql ? 1 : 0 94 | name = "${var.app-sql-gce-display-name}-secboot" 95 | type = var.app-sql-disk-type 96 | zone = var.app-dr-sql-zone 97 | project = var.app-dr-project 98 | 99 | async_primary_disk { 100 | disk = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-gce-display-name}-failback" 101 | } 102 | 103 | physical_block_size_bytes = 4096 104 | } 105 | 106 | resource "google_compute_disk" "dr-sec-data-disk-for-sql" { 107 | depends_on = [google_compute_instance.failback-sql] 108 | count = var.use-sql ? 1 : 0 109 | name = "${var.app-sql-data-disk-name}-secboot" 110 | type = var.app-sql-disk-type 111 | zone = var.app-dr-sql-zone 112 | project = var.app-dr-project 113 | 114 | async_primary_disk { 115 | disk = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-data-disk-name}-failback" 116 | } 117 | 118 | physical_block_size_bytes = 4096 119 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/failback/restage-dr-async-rep.tf.failback: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_disk_async_replication" "dr-async-replication-for-app-servers" { 18 | for_each = local.sec_boot_disks_for_dr 19 | depends_on = [google_compute_disk.dr-sec-boot-disks-for-app-vms] 20 | primary_disk = "projects/${var.app-prod-project}/zones/${each.value.prim_disk_zone}/disks/${each.value.prim_disk}" 21 | secondary_disk { 22 | disk = google_compute_disk.dr-sec-boot-disks-for-app-vms[each.key].id 23 | } 24 | } 25 | 26 | resource "google_compute_disk_async_replication" "dr-async-replication-for-dc" { 27 | count = var.use-domain-controller ? 1 : 0 28 | depends_on = [google_compute_disk.dr-sec-boot-disk-for-dc] 29 | primary_disk = "projects/${var.app-prod-project}/zones/${var.app-prod-dc-zone}/disks/${var.app-dc-gce-display-name}-failback" 30 | secondary_disk { 31 | disk = google_compute_disk.dr-sec-boot-disk-for-dc[count.index].id 32 | } 33 | } 34 | 35 | resource "google_compute_disk_async_replication" "dr-async-replication-for-sql-boot" { 36 | count = var.use-sql ? 1 : 0 37 | depends_on = [google_compute_disk.dr-sec-boot-disk-for-sql] 38 | primary_disk = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-gce-display-name}-failback" 39 | secondary_disk { 40 | disk = google_compute_disk.dr-sec-boot-disk-for-sql[count.index].id 41 | } 42 | } 43 | 44 | resource "google_compute_disk_async_replication" "dr-async-replication-for-sql-data" { 45 | count = var.use-sql ? 1 : 0 46 | depends_on = [google_compute_disk.dr-sec-data-disk-for-sql] 47 | primary_disk = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-data-disk-name}-failback" 48 | secondary_disk { 49 | disk = google_compute_disk.dr-sec-data-disk-for-sql[count.index].id 50 | } 51 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/failback/terraform.tfvars.sample: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | ########################### 17 | ### Configure Variables ### 18 | ########################### 19 | 20 | # Set this variable first in your command line interface 21 | # Bash 22 | # > export app_prod_project=REPLACE_WITH_SERVICE_PROJECT_FOR_PRODUCTION_PROJECT_ID 23 | # > export shared_vpc_host_project=REPLACE_WITH_YOUR_SHARED_VPC_PROJECT_ID 24 | 25 | ##################################### 26 | ### Failback/Production Variables ### 27 | ##################################### 28 | 29 | use-domain-controller = false 30 | # Default value is FALSE 31 | # Set value to TRUE if you plan to manually build a Domain Controller as part of the demo 32 | 33 | use-sql = false 34 | # Default value is FALSE 35 | # Set value to TRUE if you plan to manually build a SQL Server with two disks as part of the demo 36 | 37 | app-prod-project = "REPLACE_WITH_SERVICE_PROJECT_FOR_PRODUCTION_PROJECT_ID" 38 | 39 | app-prod-ip-subnet-self-link = "REPLACE_WITH_IP_SUBNET_FOR_PROD_SELF_LINK" 40 | # Run this gcloud command to get the self link 41 | # > gcloud compute networks subnets describe prod-app-us-east4 --region=us-east4 --project=$shared_vpc_host_project --format="value(selfLink)" 42 | 43 | app-prod-service-account = "REPLACE_WITH_COMPUTE_ENGINE_DEFAULT_SERVICE_ACCOUNT" 44 | # Run this gcloud command to get the Service Account 45 | # > gcloud iam service-accounts list --project=$app_prod_project 46 | 47 | app-prod-dc-zone = "REPLACE_WITH_PRODUCTION_ZONE" 48 | # Default is us-east4-a 49 | 50 | app-dc-gce-display-name = "" 51 | # Leave the variable value as blank if you are not using a Domain Controller 52 | 53 | app-prod-dc-ip = "" 54 | # Leave the variable value as blank if you are not using a Domain Controller 55 | 56 | app-dc-disk-type = "" 57 | # Leave the variable value as blank if you are not using a Domain Controller 58 | # Default is pd-balanced 59 | 60 | app-dc-machine-type = "" 61 | # Leave the variable value as blank if you are not using a Domain Controller 62 | 63 | app-prod-sql-zone = "" 64 | # Default is us-east4-a 65 | 66 | app-sql-gce-display-name = "" 67 | # Leave the variable value as blank if you are not using a SQL Server 68 | 69 | app-prod-sql-ip = "" 70 | # Leave the variable value as blank if you are not using a SQL Server 71 | 72 | app-sql-disk-type = "" 73 | # Leave the variable value as blank if you are not using a SQL Server 74 | # Default is pd-balanced 75 | 76 | app-sql-machine-type = "" 77 | # Leave the variable value as blank if you are not using a SQL Server 78 | 79 | app-sql-data-disk-name = "" 80 | # Leave the variable value as blank if you are not using a SQL Server 81 | 82 | #################### 83 | ### DR Variables ### 84 | #################### 85 | app-dr-project = "REPLACE_WITH_SERVICE_PROJECT_FOR_DR_PROJECT_ID" 86 | 87 | app-dr-region = "REPLACE_WITH_DR_REGION" 88 | # Default is us-central1 89 | 90 | app-dr-dc-zone = "" 91 | # Leave the variable value as blank if you are not using a Domain Controller 92 | # Default is us-central1-a 93 | 94 | app-dr-sql-zone = "" 95 | # Leave the variable value as blank if you are not using a SQL Server 96 | # Default is us-central1-a -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/failback/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ##################################### 18 | ### Failback/Production Variables ### 19 | ##################################### 20 | variable "use-domain-controller" { 21 | type = bool 22 | description = "Set value to TRUE if you plan to manually build a Domain Controller as part of the demo. The default value will be set to FALSE" 23 | } 24 | 25 | variable "use-sql" { 26 | type = bool 27 | description = "Set value to TRUE if you plan to manually build a SQL Server with two disks as part of the demo. The default value will be set to FALSE" 28 | } 29 | 30 | variable "app-prod-project" { 31 | type = string 32 | description = "The failback/production project" 33 | } 34 | 35 | variable "app-prod-ip-subnet-self-link" { 36 | type = string 37 | description = "The failback/production subnet" 38 | } 39 | 40 | variable "app-prod-service-account" { 41 | type = string 42 | description = "The failback/production GCE service account" 43 | } 44 | 45 | variable "app-prod-dc-zone" { 46 | type = string 47 | description = "The failback/production zone to contain the domain controller" 48 | } 49 | 50 | variable "app-dc-gce-display-name" { 51 | type = string 52 | description = "The domain controller name in the GCE console" 53 | } 54 | 55 | variable "app-dc-disk-type" { 56 | type = string 57 | description = "The failback/production domain controller boot disk type" 58 | } 59 | 60 | variable "app-dc-machine-type" { 61 | type = string 62 | description = "The failback/production domain controller machine type" 63 | } 64 | 65 | variable "app-prod-dc-ip" { 66 | type = string 67 | description = "The failback/production domain controller IP address" 68 | } 69 | 70 | variable "app-prod-sql-zone" { 71 | type = string 72 | description = "The failback/production zone to contain the SQL Server" 73 | } 74 | 75 | variable "app-sql-gce-display-name" { 76 | type = string 77 | description = "The SQL Server name in the GCE console" 78 | } 79 | 80 | variable "app-sql-disk-type" { 81 | type = string 82 | description = "The failback/production SQL Server disk type" 83 | } 84 | 85 | variable "app-sql-machine-type" { 86 | type = string 87 | description = "The failback/production SQL Server machine type" 88 | } 89 | 90 | variable "app-prod-sql-ip" { 91 | type = string 92 | description = "The failback/production SQL Server IP address" 93 | } 94 | 95 | variable "app-sql-data-disk-name" { 96 | type = string 97 | description = "The failback/production SQL Server data disk name" 98 | } 99 | 100 | #################### 101 | ### DR Variables ### 102 | #################### 103 | variable "app-dr-project" { 104 | type = string 105 | description = "The DR project" 106 | } 107 | 108 | variable "app-dr-region" { 109 | type = string 110 | description = "The DR region" 111 | } 112 | 113 | variable "app-dr-dc-zone" { 114 | type = string 115 | description = "The DR zone to contain the domain controller" 116 | } 117 | 118 | variable "app-dr-sql-zone" { 119 | type = string 120 | description = "The DR zone to contain the SQL Server" 121 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/windows-cold-dr-async-pd/images/.DS_Store -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/images/Windows Cold DR Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/example-demos-for-msft-workloads/fdc191f78afe765e18bb47afa1d01c5f3d3be26b/windows-cold-dr-async-pd/images/Windows Cold DR Architecture.png -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/google" { 5 | version = "5.14.0" 6 | hashes = [ 7 | "h1:T6EW5HOI1IrE4zHzQ/5kLyul+U2ByEaIgqMu4Ja7JFI=", 8 | "zh:3927ef7417d9d8a56077e6655d76c99f4175f9746e39226a00ee0555f8c63f8f", 9 | "zh:4b4f521f0779a1797047a8c531afda093aade934b4a49c080fe8d38680b3a52f", 10 | "zh:7e880c5b72684fc8342e03180a1fbbec65c6afeb70511b9c16181d5e168269e6", 11 | "zh:81a7f2efc30e698f476d3e240ee2d82f14eda374852059429fe808ad77b6addd", 12 | "zh:826d4ea55b4afceefb332646f21c6b6dc590b39b16e8d9b5d4a4211beb91dc5e", 13 | "zh:865600ef669fcdd4ae77515c3fd12565fab0f2a263fa2a6dae562f6fe68ed093", 14 | "zh:8e933d1d10fd316e62340175667264f093e4d24457b63d5adf3c424cce22b495", 15 | "zh:bf261924f7350074a355e5b9337f3a8054efb20d316e9085f2b5766dfb5126c4", 16 | "zh:e28e67dcbd4bbd82798561baf86d3dd04f97e08bbf523dfb9f355564ef27d3d6", 17 | "zh:f33cdd3117af8a15f33d375dbe398a5e558730cf6a7a145a479ab68e77572c12", 18 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", 19 | "zh:f913a0e0708391ccd26fc3458158cc1e10d68dc621bef3a1583328c61a77225d", 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/locals.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | locals { 18 | app_vms = { 19 | "app-001" = { zone = "us-east4-a" }, 20 | "app-002" = { zone = "us-east4-a" }, 21 | "app-003" = { zone = "us-east4-a" }, 22 | "app-004" = { zone = "us-east4-a" }, 23 | "app-005" = { zone = "us-east4-a" }, 24 | "app-006" = { zone = "us-east4-a" }, 25 | "app-007" = { zone = "us-east4-a" }, 26 | "app-008" = { zone = "us-east4-a" }, 27 | "app-009" = { zone = "us-east4-a" }, 28 | "app-010" = { zone = "us-east4-a" }, 29 | } 30 | 31 | sec_boot_disks_for_dr = { 32 | "app-001-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-001" }, 33 | "app-002-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-002" }, 34 | "app-003-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-003" }, 35 | "app-004-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-004" }, 36 | "app-005-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-005" }, 37 | "app-006-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-006" }, 38 | "app-007-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-007" }, 39 | "app-008-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-008" }, 40 | "app-009-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-009" }, 41 | "app-010-secboot" = { disk_type = "pd-balanced", dr_zone = "us-central1-a", prim_disk_zone = "us-east4-a", prim_disk = "app-010" }, 42 | } 43 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/output.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "app_server_ip" { 18 | value = values(google_compute_instance_from_template.app_vms_from_tpl).*.network_interface.0.network_ip 19 | } 20 | -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/prod-async-boot-disks.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_disk" "dr-sec-boot-disks-for-app-vms" { 18 | depends_on = [google_compute_instance_from_template.app_vms_from_tpl] 19 | for_each = local.sec_boot_disks_for_dr 20 | name = each.key 21 | type = each.value.disk_type 22 | zone = each.value.dr_zone 23 | project = var.app-dr-project 24 | 25 | guest_os_features { 26 | type = "UEFI_COMPATIBLE" 27 | } 28 | 29 | guest_os_features { 30 | type = "MULTI_IP_SUBNET" 31 | } 32 | 33 | guest_os_features { 34 | type = "VIRTIO_SCSI_MULTIQUEUE" 35 | } 36 | 37 | guest_os_features { 38 | type = "GVNIC" 39 | } 40 | 41 | guest_os_features { 42 | type = "WINDOWS" 43 | } 44 | 45 | licenses = ["projects/windows-cloud/global/licenses/windows-server-2022-dc"] 46 | 47 | async_primary_disk { 48 | disk = "projects/${var.app-prod-project}/zones/${each.value.prim_disk_zone}/disks/${each.value.prim_disk}" 49 | } 50 | 51 | physical_block_size_bytes = 4096 52 | } 53 | 54 | resource "google_compute_disk" "dr-sec-boot-disk-for-dc" { 55 | count = var.use-domain-controller ? 1 : 0 56 | name = "${var.app-dc-gce-display-name}-secboot" 57 | type = var.app-dc-disk-type 58 | zone = var.app-dr-dc-zone 59 | project = var.app-dr-project 60 | 61 | guest_os_features { 62 | type = "UEFI_COMPATIBLE" 63 | } 64 | 65 | guest_os_features { 66 | type = "MULTI_IP_SUBNET" 67 | } 68 | 69 | guest_os_features { 70 | type = "VIRTIO_SCSI_MULTIQUEUE" 71 | } 72 | 73 | guest_os_features { 74 | type = "GVNIC" 75 | } 76 | 77 | guest_os_features { 78 | type = "WINDOWS" 79 | } 80 | 81 | licenses = ["projects/windows-cloud/global/licenses/windows-server-2022-dc"] 82 | 83 | async_primary_disk { 84 | disk = "projects/${var.app-prod-project}/zones/${var.app-prod-dc-zone}/disks/${var.app-dc-gce-display-name}" 85 | } 86 | 87 | physical_block_size_bytes = 4096 88 | } 89 | 90 | resource "google_compute_disk" "dr-sec-boot-disk-for-sql" { 91 | count = var.use-sql ? 1 : 0 92 | name = "${var.app-sql-gce-display-name}-secboot" 93 | type = var.app-sql-disk-type 94 | zone = var.app-dr-sql-zone 95 | project = var.app-dr-project 96 | 97 | guest_os_features { 98 | type = "UEFI_COMPATIBLE" 99 | } 100 | 101 | guest_os_features { 102 | type = "MULTI_IP_SUBNET" 103 | } 104 | 105 | guest_os_features { 106 | type = "VIRTIO_SCSI_MULTIQUEUE" 107 | } 108 | 109 | guest_os_features { 110 | type = "GVNIC" 111 | } 112 | 113 | guest_os_features { 114 | type = "WINDOWS" 115 | } 116 | 117 | licenses = ["projects/windows-sql-cloud/global/licenses/sql-server-2022-web","projects/windows-cloud/global/licenses/windows-server-2022-dc"] 118 | 119 | async_primary_disk { 120 | disk = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-gce-display-name}" 121 | } 122 | 123 | physical_block_size_bytes = 4096 124 | } 125 | 126 | resource "google_compute_disk" "dr-sec-data-disk-for-sql" { 127 | count = var.use-sql ? 1 : 0 128 | name = "${var.app-sql-data-disk-name}-secboot" 129 | type = var.app-sql-disk-type 130 | zone = var.app-dr-sql-zone 131 | project = var.app-dr-project 132 | 133 | async_primary_disk { 134 | disk = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-data-disk-name}" 135 | } 136 | 137 | physical_block_size_bytes = 4096 138 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/prod-async-rep.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_disk_async_replication" "dr-async-replication-for-app-servers" { 18 | for_each = local.sec_boot_disks_for_dr 19 | depends_on = [google_compute_disk.dr-sec-boot-disks-for-app-vms] 20 | primary_disk = "projects/${var.app-prod-project}/zones/${each.value.prim_disk_zone}/disks/${each.value.prim_disk}" 21 | secondary_disk { 22 | disk = google_compute_disk.dr-sec-boot-disks-for-app-vms[each.key].id 23 | } 24 | } 25 | 26 | resource "google_compute_disk_async_replication" "dr-async-replication-for-dc" { 27 | count = var.use-domain-controller ? 1 : 0 28 | depends_on = [google_compute_disk.dr-sec-boot-disk-for-dc] 29 | primary_disk = var.app-prod-dc-disk-selflink 30 | secondary_disk { 31 | disk = google_compute_disk.dr-sec-boot-disk-for-dc[count.index].id 32 | } 33 | } 34 | 35 | resource "google_compute_disk_async_replication" "dr-async-replication-for-sql-boot" { 36 | count = var.use-sql ? 1 : 0 37 | depends_on = [google_compute_disk.dr-sec-boot-disk-for-sql] 38 | primary_disk = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-gce-display-name}" 39 | secondary_disk { 40 | disk = google_compute_disk.dr-sec-boot-disk-for-sql[count.index].id 41 | } 42 | } 43 | 44 | resource "google_compute_disk_async_replication" "dr-async-replication-for-sql-data" { 45 | count = var.use-sql ? 1 : 0 46 | depends_on = [google_compute_disk.dr-sec-data-disk-for-sql] 47 | primary_disk = "projects/${var.app-prod-project}/zones/${var.app-prod-sql-zone}/disks/${var.app-sql-data-disk-name}" 48 | secondary_disk { 49 | disk = google_compute_disk.dr-sec-data-disk-for-sql[count.index].id 50 | } 51 | } -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/prod-vms.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_instance_from_template" "app_vms_from_tpl" { 18 | for_each = local.app_vms 19 | name = each.key 20 | zone = each.value.zone 21 | project = var.app-prod-project 22 | 23 | source_instance_template = var.app-prod-tpl-self-link 24 | 25 | service_account { 26 | email = var.app-prod-service-account 27 | scopes = ["cloud-platform"] 28 | } 29 | 30 | network_interface { 31 | subnetwork = var.app-prod-ip-subnet-self-link 32 | } 33 | 34 | shielded_instance_config { 35 | enable_secure_boot = true 36 | enable_vtpm = true 37 | enable_integrity_monitoring = true 38 | } 39 | 40 | metadata = { 41 | windows-startup-script-ps1 = templatefile("./templatefiles/ad-join.tpl",{}) 42 | } 43 | } 44 | 45 | resource "google_compute_address" "app_server_ips" { 46 | for_each = local.app_vms 47 | name = "${each.key}-ip" 48 | project = var.app-prod-project 49 | subnetwork = var.app-prod-ip-subnet-self-link 50 | address_type = "INTERNAL" 51 | address = google_compute_instance_from_template.app_vms_from_tpl[each.key].network_interface.0.network_ip 52 | region = var.app-prod-region 53 | } 54 | -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/templatefiles/ad-join.tpl: -------------------------------------------------------------------------------- 1 | <#** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *#> 16 | 17 | $domain = "" 18 | $password = "" | ConvertTo-SecureString -asPlainText -Force 19 | $username = "" 20 | $credential = New-Object System.Management.Automation.PSCredential($username,$password) 21 | Add-Computer -DomainName $domain -Credential $credential -Restart -Force -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/templatefiles/gcloud_template: -------------------------------------------------------------------------------- 1 | Tips 2 | === 3 | 1. To get the Subnet selfLink, run this command and replace the values accordingly 4 | > gcloud compute networks subnets describe prod-app-us-east4 --region=us-east4 --project=REPLACE_WITH_YOUR_SHARED_VPC_PROJECT_ID --format="value(selfLink)" 5 | 6 | 2. To get the Compute Engine Default Service Account, run the following command and replace the values accordingly 7 | > gcloud iam service-accounts list --project=REPLACE_WITH_SERVICE_PROJECT_FOR_PRODUCTION_PROJECT_ID 8 | 9 | Note: If there are multiple values returned, use the Service Account email associated with the "Compute Engine default service account" 10 | 11 | gcloud compute instance-templates create app-server-tpl \ 12 | --project=REPLACE_WITH_SERVICE_PROJECT_FOR_PRODUCTION_PROJECT_ID \ 13 | --machine-type=REPLACE_WITH_PREFERRED_MACHINE_TYPE \ 14 | --network-interface=subnet=REPLACE_WITH_SUBNET_SELF_LINK,no-address \ 15 | --metadata=enable-oslogin=false \ 16 | --maintenance-policy=MIGRATE \ 17 | --provisioning-model=STANDARD \ 18 | --service-account=REPLACE_WITH_COMPUTE_ENGINE_DEFAULT_SERVICE_ACCOUNT \ 19 | --scopes=https://www.googleapis.com/auth/cloud-platform \ 20 | --region=us-east4 \ 21 | --create-disk=auto-delete=yes,boot=yes,image=projects/windows-cloud/global/images/windows-server-2022-dc-v20231011,mode=rw,size=50,type=pd-balanced \ 22 | --reservation-affinity=any -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/terraform.tfvars.sample: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ########################### 18 | ### Configure Variables ### 19 | ########################### 20 | 21 | # Set this variable first in your command line interface 22 | # Bash 23 | # > export app_prod_project=REPLACE_WITH_SERVICE_PROJECT_FOR_PRODUCTION_PROJECT_ID 24 | # > export shared_vpc_host_project=REPLACE_WITH_YOUR_SHARED_VPC_PROJECT_ID 25 | 26 | ###################### 27 | ### Prod Variables ### 28 | ###################### 29 | 30 | use-domain-controller = false 31 | # Default value is FALSE 32 | # Set value to TRUE if you plan to manually build a Domain Controller as part of the demo 33 | 34 | use-sql = false 35 | # Default value is FALSE 36 | # Set value to TRUE if you plan to manually build a SQL Server as part of the demo 37 | 38 | app-prod-project = "REPLACE_WITH_SERVICE_PROJECT_FOR_PRODUCTION_PROJECT_ID" 39 | 40 | app-prod-region = "REPLACE_WITH_PRODUCTION_REGION" 41 | # Default is us-east4 42 | 43 | app-prod-tpl-self-link = "REPLACE_WITH_SELF_LINK_FOR_INSTANCE_TEMPLATE" 44 | # Run this gcloud command to get the self link 45 | # > gcloud compute instance-templates describe app-server-tpl --project=$app_prod_project --format="value(selfLink)" 46 | 47 | app-prod-service-account = "REPLACE_WITH_COMPUTE_ENGINE_DEFAULT_SERVICE_ACCOUNT" 48 | # Run this gcloud command to get the Service Account 49 | # > gcloud iam service-accounts list --project=$app_prod_project 50 | 51 | app-prod-ip-subnet-self-link = "REPLACE_WITH_IP_SUBNET_FOR_PROD_SELF_LINK" 52 | # Run this gcloud command to get the self link 53 | # > gcloud compute networks subnets describe prod-app-us-east4 --region=us-east4 --project=$shared_vpc_host_project --format="value(selfLink)" 54 | 55 | app-dc-gce-display-name = "" 56 | # Leave the variable value as blank if you are not using a Domain Controller 57 | 58 | app-prod-dc-disk-selflink = "" 59 | # Leave the variable value as blank if you are not using a Domain Controller 60 | # Run this gcloud command to get the self link 61 | # > gcloud compute disks describe --zone= --project=$app_prod_project --format="value(selfLink)" 62 | 63 | app-dc-disk-type = "" 64 | # Leave the variable value as blank if you are not using a Domain Controller 65 | # Default is pd-balanced 66 | 67 | app-prod-dc-zone = "" 68 | # Leave the variable value as blank if you are not using a Domain Controller 69 | # Default is us-east4-c 70 | 71 | app-sql-gce-display-name = "" 72 | # Leave the variable value as blank if you are not using a SQL Server 73 | 74 | app-prod-sql-disk-selflink = "" 75 | # Leave the variable value as blank if you are not using a SQL Server 76 | # Run this gcloud command to get the self link 77 | # > gcloud compute disks describe --zone= --project=$app_prod_project --format="value(selfLink)" 78 | 79 | app-sql-disk-type = "" 80 | # Leave the variable value as blank if you are not using a SQL Server 81 | # Default is pd-balanced 82 | 83 | app-prod-sql-zone = "" 84 | # Leave the variable value as blank if you are not using a SQL Server 85 | # Default is us-east4-c 86 | 87 | app-sql-data-disk-name = "" 88 | # Leave the variable value as blank if you are not using a SQL Server 89 | 90 | #################### 91 | ### DR Variables ### 92 | #################### 93 | app-dr-project = "REPLACE_WITH_SERVICE_PROJECT_FOR_DR_PROJECT_ID" 94 | 95 | app-dr-region = "REPLACE_WITH_DR_REGION" 96 | # Default is us-central1 97 | 98 | app-dr-dc-zone = "" 99 | # Leave the variable value as blank if you are not using a Domain Controller 100 | # Default is us-central1-a 101 | 102 | app-dr-sql-zone = "" 103 | # Leave the variable value as blank if you are not using a SQL Server 104 | # Default is us-central1-a -------------------------------------------------------------------------------- /windows-cold-dr-async-pd/setup/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | ###################### 18 | ### Prod Variables ### 19 | ###################### 20 | 21 | variable "use-domain-controller" { 22 | type = bool 23 | description = "Set value to TRUE if you plan to manually build a Domain Controller as part of the demo. The default value will be set to FALSE" 24 | } 25 | 26 | variable "use-sql" { 27 | type = bool 28 | description = "Set value to TRUE if you plan to manually build a SQL Server as part of the demo. The default value will be set to FALSE" 29 | } 30 | 31 | variable "app-prod-project" { 32 | type = string 33 | description = "The production project" 34 | } 35 | 36 | variable "app-prod-region" { 37 | type = string 38 | description = "The production region" 39 | } 40 | 41 | variable "app-prod-tpl-self-link" { 42 | type = string 43 | description = "The self link to the app server template" 44 | } 45 | 46 | variable "app-prod-service-account" { 47 | type = string 48 | description = "The email address of the production GCE service account" 49 | } 50 | 51 | variable "app-prod-ip-subnet-self-link" { 52 | type = string 53 | description = "The subnet to associate the production app servers with" 54 | } 55 | 56 | variable "app-dc-gce-display-name" { 57 | type = string 58 | description = "The production domain controller name in the GCE console" 59 | } 60 | 61 | variable "app-prod-dc-disk-selflink" { 62 | type = string 63 | description = "The production domain controller boot disk self link" 64 | } 65 | 66 | variable "app-dc-disk-type" { 67 | type = string 68 | description = "The production domain controller boot disk type" 69 | } 70 | 71 | variable "app-prod-dc-zone" { 72 | type = string 73 | description = "The production zone to contain the domain controller" 74 | } 75 | 76 | variable "app-sql-gce-display-name" { 77 | type = string 78 | description = "The GCE display name of the SQL Server instance" 79 | } 80 | 81 | variable "app-prod-sql-disk-selflink" { 82 | type = string 83 | description = "The selflink of the production SQL Server boot disk" 84 | } 85 | 86 | variable "app-sql-disk-type" { 87 | type = string 88 | description = "The persistent disk type of the SQL Server disks" 89 | } 90 | 91 | variable "app-prod-sql-zone" { 92 | type = string 93 | description = "The production zone for the SQL Server" 94 | } 95 | 96 | variable "app-sql-data-disk-name" { 97 | type = string 98 | description = "The failback/production SQL Server data disk name" 99 | } 100 | 101 | #################### 102 | ### DR Variables ### 103 | #################### 104 | variable "app-dr-project" { 105 | type = string 106 | description = "The DR project" 107 | } 108 | 109 | variable "app-dr-region" { 110 | type = string 111 | description = "The DR region" 112 | } 113 | 114 | variable "app-dr-dc-zone" { 115 | type = string 116 | description = "The DR zone to contain the domain controller boot disk" 117 | } 118 | 119 | variable "app-dr-sql-zone" { 120 | type = string 121 | description = "The DR zone to contain the SQL Server" 122 | } --------------------------------------------------------------------------------