-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Terraform templates v1.0.0 for AWS, GCP & Azure
- Loading branch information
0 parents
commit 1009082
Showing
26 changed files
with
1,767 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*.terraform* | ||
*.tfstate* | ||
gcp.yaml | ||
outputs.tf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Example: Creation of a virtual scanner using existing resources, a marketplace AMI, a security group, ignore AMI changes set to true. | ||
|
||
# General Configuration | ||
aws_region = "us-east-1" | ||
availability_zone = "us-east-1b" | ||
scanner_instance_type = "t2.micro" | ||
scanner_name = "terraform-test" | ||
ignore_ami_changes = true | ||
vm_count = 2 # Number of VMs to create | ||
instance_state = "running" | ||
|
||
# Network and Security Configuration | ||
vpc_name = "existing_vpc_name" | ||
virtual_subnet_name = "existing_subnet_name" | ||
default_security_group = false | ||
security_group = "existing_security_group_name" | ||
|
||
# IP Address Configuration | ||
assign_public_ip = true | ||
assign_ipv6_public_ip = false | ||
|
||
# Marketplace Image and QualysGuard Configuration | ||
ami = "global_marketplace" | ||
friendly_name = "qvsa" # Friendly name for the scanner to be created in qweb | ||
qualysguard_url = "qualysguard.qualys.com" # URL for QualysGuard platform | ||
proxy_url = "user:[email protected]:8080" # Proxy URL for the scanner |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#!/bin/bash | ||
|
||
# Check if required environment variables are set | ||
for var in QUALYSGUARD_LOGIN QUALYSGUARD_PASSWORD AWS_ACCESS_KEY_ID AWS_DEFAULT_REGION AWS_SECRET_ACCESS_KEY; do | ||
[ -z "${!var}" ] && echo "Error: $var is missing." && exit 1 | ||
done | ||
|
||
# File with the configuration | ||
config_file=${1} | ||
|
||
if [ ! -f "${config_file}" ]; then | ||
echo "Error: Configuration file '${config_file}' not found." | ||
exit 1 | ||
fi | ||
|
||
# Read the file and export variables | ||
while IFS='=' read -r key value; do | ||
# Skip lines that are comments or empty | ||
if [[ "${key}" =~ ^\s*# ]] || [[ -z "${key}" ]]; then | ||
continue | ||
fi | ||
|
||
# Remove leading/trailing spaces from key and value | ||
key=$(echo "${key}" | sed "s/ //g") | ||
value=$(echo "${value}" | sed "s/ //g") | ||
|
||
# Replace spaces around '=' and handle the value in quotes | ||
if [[ "${value}" == \"*\" ]]; then | ||
value=$(echo "${value}" | sed -e 's/.*"\([^"]*\)".*/\1/') | ||
fi | ||
|
||
# Remove inline comments | ||
value=$(echo "${value}" | sed 's/^[^"]*"\([^"]*\)".*/\1/') | ||
|
||
# Skip if the value is empty after processing | ||
if [[ -z "${value}" ]]; then | ||
continue | ||
fi | ||
# Export variable | ||
export "${key}=${value}" | ||
done < "${config_file}" | ||
|
||
user_prefix="${friendly_name}-$(date +%s)" # Creating user_prefix with same timestamp | ||
|
||
# Get the count of existing userdata files | ||
mkdir -p userdata # This would be no-op if userdata dir is already present | ||
existing_count=$(ls userdata/userdata_*.txt 2>/dev/null | wc -l) | ||
|
||
# Check if VM count matches the existing userdata files count | ||
if [ "${existing_count}" -eq "${vm_count}" ]; then | ||
echo "VM count matches the number of userdata files. Skipping further execution." | ||
exit 0 | ||
fi | ||
|
||
# Loop starting from the next available index | ||
for ((i = existing_count; i < vm_count; i++)); do | ||
sleep 3 | ||
qvsa_name="${user_prefix}-${i}" | ||
USER_DATA="userdata/userdata_${i}.txt" | ||
|
||
perscode=$(curl --insecure -s \ | ||
--request POST --url "https://${qualysguard_url}/api/2.0/fo/appliance/" \ | ||
--user "${QUALYSGUARD_LOGIN}:${QUALYSGUARD_PASSWORD}" \ | ||
--header "X-Requested-With: Curl" \ | ||
--data "action=create&echo_request=1&name=${qvsa_name}" | \ | ||
grep "ACTIVATION_CODE" | cut -d">" -f2 | cut -d"<" -f1) | ||
|
||
if [ $? -eq 0 ] && [ -n "${perscode}" ]; then | ||
echo "PERSCODE=${perscode}" >> ${USER_DATA} | ||
else | ||
echo "Unable to fetch Activation code" | ||
exit 1 | ||
fi | ||
|
||
if [ -n "${proxy_url}" ]; then | ||
echo "PROXY_URL=${proxy_url}" >> ${USER_DATA} | ||
fi | ||
|
||
done | ||
echo "Userdata file generation completed successfully!" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
locals { | ||
isGlobalMktImage = contains(["global_marketplace"], var.ami) ? 1 : 0 | ||
vpc_name = var.vpc_name | ||
subnet_name = var.virtual_subnet_name | ||
selected_ami = local.isGlobalMktImage == 1 ? data.aws_ami.global_marketplace_ami[0].id : var.ami | ||
security_group = var.default_security_group ? aws_security_group.default_security_group[0].id : data.aws_security_group.existing[0].id | ||
security_group_name = var.default_security_group ? "default_security_group" : var.security_group | ||
templates = [for i in range(var.vm_count) : file("./userdata/userdata_${i}.txt")] | ||
proxy_port = regex(":(\\d+)$", var.proxy_url)[0] | ||
is_proxy_defined = length(var.proxy_url) > 0 | ||
} | ||
|
||
data "aws_vpc" "existing_vpc" { | ||
filter { | ||
name = "tag:Name" | ||
values = [local.vpc_name] | ||
} | ||
} | ||
|
||
data "aws_subnet" "existing_subnet" { | ||
filter { | ||
name = "tag:Name" | ||
values = [local.subnet_name] | ||
} | ||
} | ||
|
||
resource "aws_security_group" "default_security_group" { | ||
count = var.default_security_group ? 1 : 0 | ||
name = local.security_group_name | ||
vpc_id = data.aws_vpc.existing_vpc.id | ||
# Ingress rules: Allow inbound traffic only if a proxy is defined | ||
dynamic "ingress" { | ||
for_each = local.is_proxy_defined ? [1] : [] | ||
content { | ||
from_port = tonumber(local.proxy_port) | ||
to_port = tonumber(local.proxy_port) | ||
protocol = "tcp" | ||
cidr_blocks = [var.proxy_cidr_block] | ||
ipv6_cidr_blocks = [var.proxy_ipv6_cidr_blocks] | ||
} | ||
} | ||
# Egress rules: Allow only HTTPS (port 443) | ||
egress { | ||
from_port = 443 | ||
to_port = 443 | ||
protocol = "tcp" | ||
cidr_blocks = ["0.0.0.0/0"] | ||
ipv6_cidr_blocks = ["::/0"] | ||
|
||
} | ||
tags = { | ||
Name = local.security_group_name | ||
} | ||
} | ||
|
||
data "aws_security_group" "existing" { | ||
count = var.default_security_group ? 0 : 1 | ||
filter { | ||
name = "tag:Name" | ||
values = [local.security_group_name] | ||
} | ||
} | ||
|
||
data "aws_ami" "global_marketplace_ami" { | ||
count = local.isGlobalMktImage == 1 ? 1 : 0 | ||
most_recent = true | ||
owners = ["aws-marketplace"] | ||
filter { | ||
name = "name" | ||
values = ["qVSA-AWS.x86*"] | ||
} | ||
} | ||
|
||
resource "aws_instance" "vm_instance_ignore_ami_change" { | ||
count = var.ignore_ami_changes ? var.vm_count : 0 | ||
ami = local.selected_ami | ||
instance_type = var.scanner_instance_type | ||
subnet_id = data.aws_subnet.existing_subnet.id | ||
monitoring = true | ||
ipv6_address_count = var.assign_ipv6_public_ip ? 1 : 0 | ||
associate_public_ip_address = var.assign_public_ip ? true : false | ||
vpc_security_group_ids = [local.security_group] | ||
lifecycle { | ||
ignore_changes = [ami, ] | ||
} | ||
tags = { | ||
Name = "${var.scanner_name}-${count.index}" | ||
} | ||
user_data = base64encode(local.templates[count.index]) | ||
} | ||
|
||
resource "aws_instance" "vm_instance" { | ||
count = var.ignore_ami_changes ? 0 : var.vm_count | ||
ami = local.selected_ami | ||
instance_type = var.scanner_instance_type | ||
subnet_id = data.aws_subnet.existing_subnet.id | ||
monitoring = true | ||
ipv6_address_count = var.assign_ipv6_public_ip ? 1 : 0 | ||
associate_public_ip_address = var.assign_public_ip ? true : false | ||
vpc_security_group_ids = [local.security_group] | ||
tags = { | ||
Name = "${var.scanner_name}-${count.index}" | ||
} | ||
user_data = base64encode(local.templates[count.index]) | ||
} | ||
|
||
resource "aws_ec2_instance_state" "instance_status" { | ||
count = var.vm_count | ||
instance_id = var.ignore_ami_changes ? aws_instance.vm_instance_ignore_ami_change[count.index].id : aws_instance.vm_instance[count.index].id | ||
state = var.instance_state | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
provider "aws" { | ||
region = var.aws_region | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Deploy Qualys Virtual Scanner Appliance on AWS Cloud | ||
|
||
## AWS Template Files | ||
|
||
- `main.tf` | ||
- `provider.tf` | ||
- `variables.tf` | ||
- `version.tf` | ||
|
||
## Deploy using Terraform | ||
|
||
### Pre-requisite | ||
|
||
1. **For Windows Users**: Install Windows Subsystem for Linux (WSL). For detailed installation steps, refer to the [official documentation](https://learn.microsoft.com/en-us/windows/wsl/install). | ||
2. **Terraform Installation**: Install Terraform by following the instructions provided in the [official terraform documentation](https://developer.hashicorp.com/terraform/install). | ||
|
||
### STEP 1 | ||
|
||
#### Export Environment Variables for QualysGuard and AWS Authentication | ||
|
||
```shell | ||
export QUALYSGUARD_LOGIN="your_qualysguard_username" | ||
export QUALYSGUARD_PASSWORD="your__qualysguard_password" | ||
export AWS_ACCESS_KEY_ID="your_AWS_ACCESS_KEY_ID" | ||
export AWS_SECRET_ACCESS_KEY="your_AWS_SECRET_ACCESS_KEY" | ||
export AWS_DEFAULT_REGION="your_AWS_DEFAULT_REGION" | ||
``` | ||
|
||
Execute the following script: | ||
|
||
./get_activation_token.sh <PATH_TO_TFVARS_FILE> | ||
|
||
Example: | ||
./get_activation_token.sh example/existing_resource.tfvars | ||
|
||
### STEP 2 | ||
|
||
#### See parameter file examples in the 'example' directory | ||
|
||
```shell | ||
terraform init | ||
terraform plan -var-file=<PATH_TO_TFVARS_FILE> | ||
terraform apply -var-file=<PATH_TO_TFVARS_FILE> | ||
``` | ||
|
||
### Parameters | ||
|
||
| Parameter | Input Value | Description | | ||
| ----------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `aws_region` | Region Identifier | Region identifier for deployment (e.g., `us-east-1`). [Learn more](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html) | | ||
| `availability_zone` | Availability Zones | Zone identifier for deployment (e.g., `us-east-1b`). [Learn more](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html#Concepts.RegionsAndAvailabilityZones.AvailabilityZones) | | ||
| `scanner_instance_type` | Instance Type | Any from mentioned series (e.g., `t2.micro`) [Learn more](https://aws.amazon.com/ec2/instance-types/) | | ||
| `scanner_name` | Virtual Scanner Name | VM name on AWS can be 1-63 characters long and may contain alphanumerics, underscores, periods, and hyphens but cannot contain special characters. It should match the regex `^[a-z]([-a-z0-9]*[a-z0-9])?`. | | ||
| `vm_count` | Number of scanner VMs to create | N/A | | ||
| `instance_state` |`running`/`stopped` | Desired state of Scanner VM's | | ||
| `vpc_name` | VPC Name | Name of existing VPC. | | ||
| `virtual_subnet_name` | Subnet Name | Name of existing subnet. | | ||
| `default_security_group` | true/false |Attatch default Security Group to scanner VMs. Default value is true. | | ||
| `security_group` | Security group name |Existing Security group name. Provide default_security_group = false while using existing security group. | | ||
| `ami` | `global_marketplace` or AMI ID | AMI for the scanner VM. Provide the AMI ID stored in the region or `"global_marketplace"` to use the latest marketplace image. | | ||
| `ignore_ami_changes` | `true`/`false` | For `ignore_ami_changes=true`, Terraform will ignore any new AMI ID updates. This allows you to update the configurations of the existing VM(s) that were created with the previous AMI. For `ignore_ami_changes=false`, Terraform will detect a new AMI ID, triggering the creation of new VM(s) with the updated AMI and desired variables(during terraform plan/apply). | | ||
| `friendly_name` | Friendly name for scanners | Assign a friendly name to each scanner created on QWeb. The friendly_name will be a combination of a user-defined name (up to 19 characters) and a 13-character string consisting of the current Unix timestamp and the VM's vm_count index. Since QWeb has a 32-character limit for the friendly_name, the user-defined portion can be up to 19 characters. Example: qvsa-1234567890-0 | | ||
| `proxy_url` | Optional Variable | Valid proxy, if applicable. | | ||
| `proxy_cidr_block` | Valid cidr range |Valid proxy cidr range for security/firewall rules.Default value is "0.0.0.0/0" | | ||
| `proxy_ipv6_cidr_blocks` | Valid IPv6 cidr range |Valid proxy IPv6 cidr range for security/firewall rules.Default value is "::/0" | | ||
| `assign_public_ip` | true/false | This parameter specifies whether to assign a public IPv4 address to the scanner VM (`true` for yes, `false` for no).Default value is false. | | ||
| `assign_ipv6_public_ip` | true/false | This parameter specifies whether to assign a public IPv6 address to the scanner VM (`true` for yes, `false` for no). Default value is false. | | ||
|
||
### Note | ||
|
||
There is an open bug which doesn't prevent IPv6 assignment even though `ipv6_address_count=0` is set: <https://github.com/hashicorp/terraform-provider-aws/issues/20025> |
Oops, something went wrong.