├── .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 | }
--------------------------------------------------------------------------------