Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sap_vm_provision: feat: aws security enhancements #4

Merged
merged 5 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions roles/sap_vm_provision/PLATFORM_GUIDANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,56 @@ aws iam attach-group-policy --group-name 'ag-sap-automation' --policy-arn arn:aw
aws iam attach-group-policy --group-name 'ag-sap-automation' --policy-arn arn:aws:iam::aws:policy/AmazonRoute53FullAccess
```

It is recommended to create new AWS IAM Policy with detailed actions to improve security.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:DescribeImages",
"ec2:DescribeInstances",
"ec2:DescribeTags",
"ec2:DescribeInstanceAttribute",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:RunInstances",
"ec2:CreateTags",
"ec2:DescribeInstanceStatus",
"ec2:ModifyInstanceAttribute",
"ec2:DescribeRouteTables",
"route53:ListHostedZones",
"route53:ListResourceRecordSets",
"route53:ChangeResourceRecordSets",
"route53:GetChange",
"ec2:DescribeVolumes",
"ec2:CreateVolume",
"ec2:DeleteVolume",
"ec2:AttachVolume",
"ec2:DetachVolume",
"ec2:TerminateInstances",
"ec2:CreateRoute",
"iam:GetRole",
"iam:CreateRole",
"iam:ListInstanceProfilesForRole",
"iam:CreateInstanceProfile",
"iam:AddRoleToInstanceProfile",
"iam:ListAttachedRolePolicies",
"iam:ListRoleTags",
"iam:PutRolePolicy",
"iam:GetInstanceProfile",
"iam:PassRole",
"ec2:AssociateIamInstanceProfile",
"ec2:ReplaceRoute"
],
"Resource": "*"
}
]
}
```

</details>

<details>
Expand Down
4 changes: 3 additions & 1 deletion roles/sap_vm_provision/tasks/common/set_ansible_vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
sap_id_user_password: "{{ sap_id_user_password | default('') }}"
sap_software_download_directory: "{{ sap_software_download_directory | default('/software') }}"
sap_install_media_detect_source_directory: "{{ sap_software_download_directory | default('/software') }}"
no_log: "{{ __sap_vm_provision_no_log }}"

# - name: Set facts for all hosts - use facts from localhost - Ansible only
# ansible.builtin.set_fact:
Expand Down Expand Up @@ -47,10 +48,11 @@
sap_ha_pacemaker_cluster_aws_region: "{{ sap_ha_pacemaker_cluster_aws_region }}"
sap_ha_pacemaker_cluster_aws_access_key_id: "{{ sap_ha_pacemaker_cluster_aws_access_key_id }}"
sap_ha_pacemaker_cluster_aws_secret_access_key: "{{ sap_ha_pacemaker_cluster_aws_secret_access_key }}"
sap_ha_pacemaker_cluster_aws_vip_update_rt: "{{ aws_vpc_subnet_rt_info.route_tables[0].route_table_id }}"
sap_ha_pacemaker_cluster_aws_vip_update_rt: "{{ __sap_vm_provision_task_vpc_subnet_rt_info.route_tables[0].route_table_id }}"
when:
- sap_ha_pacemaker_cluster_aws_region is defined
- sap_vm_provision_iac_platform == "aws_ec2_vs"
no_log: "{{ __sap_vm_provision_no_log }}"

# - name: Set facts for all hosts - use facts from localhost - HA/DR - GCP
# ansible.builtin.set_fact:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,34 @@

- name: Ansible Task block for looped provisioning of AWS EC2 instances
environment:
AWS_ACCESS_KEY_ID: "{{ sap_vm_provision_aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ sap_vm_provision_aws_secret_access_key }}"
AWS_REGION: "{{ sap_vm_provision_aws_region }}"
any_errors_fatal: true
block:

- name: Identify OS Image (AWS AMI)
register: register_aws_ami
register: __sap_vm_provision_task_aws_ami
amazon.aws.ec2_ami_info:
owners: ["aws-marketplace"]
filters:
name: "{{ lookup('ansible.builtin.vars', 'sap_vm_provision_' + sap_vm_provision_iac_platform + '_host_os_image_dictionary')[sap_vm_provision_aws_ec2_vs_host_os_image] }}"
access_key: "{{ sap_vm_provision_aws_access_key }}"
secret_key: "{{ sap_vm_provision_aws_secret_access_key }}"
no_log: "{{ __sap_vm_provision_no_log }}"

- name: Set fact to hold loop variables from include_tasks
ansible.builtin.set_fact:
register_provisioned_host_all: []

- name: Provision hosts to AWS
register: register_provisioned_hosts
register: __sap_vm_provision_task_register_provisioned_hosts
ansible.builtin.include_tasks:
file: "{{ 'platform_' + sap_vm_provision_iac_type }}/{{ sap_vm_provision_iac_platform }}/execute_provision.yml"
apply:
environment:
AWS_ACCESS_KEY_ID: "{{ sap_vm_provision_aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ sap_vm_provision_aws_secret_access_key }}"
AWS_REGION: "{{ sap_vm_provision_aws_region }}"
# apply:
# environment:
# AWS_REGION: "{{ sap_vm_provision_aws_region }}"

- name: Add hosts provisioned to the Ansible Inventory
register: register_add_hosts
register: __sap_vm_provision_task_register_add_hosts
ansible.builtin.add_host:
name: "{{ add_item[0].host_node }}"
groups: "{{ add_item[0].sap_system_type + '_' if (add_item[0].sap_system_type != '') }}{{ add_item[0].sap_host_type }}"
Expand All @@ -41,7 +41,7 @@
loop_control:
label: "{{ add_item[0].host_node }}"
loop_var: add_item

no_log: "{{ __sap_vm_provision_no_log }}"

# Cannot override any variables from extravars input, see https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#understanding-variable-precedence
# Ensure no default value exists for any prompted variable before execution of Ansible Playbook
Expand All @@ -50,7 +50,10 @@
amazon.aws.ec2_vpc_route_table_info:
filters:
association.subnet-id: "{{ sap_vm_provision_aws_vpc_subnet_id }}"
register: aws_vpc_subnet_rt_info
access_key: "{{ sap_vm_provision_aws_access_key }}"
secret_key: "{{ sap_vm_provision_aws_secret_access_key }}"
register: __sap_vm_provision_task_vpc_subnet_rt_info
no_log: "{{ __sap_vm_provision_no_log }}"

- name: Set fact to hold all inventory hosts in all groups
ansible.builtin.set_fact:
Expand All @@ -71,9 +74,35 @@
ttl: 7200
value: "{{ hostvars[inventory_hostname].ansible_host }}"
wait: true

# - ansible.builtin.debug:
# var: register_add_hosts.results
access_key: "{{ sap_vm_provision_aws_access_key }}"
secret_key: "{{ sap_vm_provision_aws_secret_access_key }}"
register: __sap_vm_provision_task_route53
no_log: "{{ __sap_vm_provision_no_log }}"

rescue:
# This requires no_log set on each Ansible Task, and not set on the Ansible Task Block
# This requires an Ansible Task Block containing the Ansible Tasks for calling
# Infrastructure Platform APIs (via Ansible Modules)
- name: Show errors in task outputs
ansible.builtin.fail:
msg: "{{ lookup('ansible.builtin.vars', loop_item) }}"
loop:
- __sap_vm_provision_task_aws_ami
- __sap_vm_provision_task_provisioned_host_single
- __sap_vm_provision_task_instance_info
- __sap_vm_provision_task_volume_provisioning
- __sap_vm_provision_task_register_provisioned_hosts
- __sap_vm_provision_task_register_add_hosts
- __sap_vm_provision_task_vpc_subnet_rt_info
- __sap_vm_provision_task_route53
loop_control:
loop_var: loop_item
index_var: loop_item_index
label: "{{ 'Variable No. ' + (loop_item_index | string) }}"
when:
- lookup('ansible.builtin.vars', loop_item, default='') | length > 0
- not lookup('ansible.builtin.vars', loop_item, default='') is skipped
- lookup('ansible.builtin.vars', loop_item, default='') is failed

- name: Ansible Task block to execute on target inventory hosts
delegate_to: "{{ inventory_hostname }}"
Expand Down Expand Up @@ -120,19 +149,51 @@
delegate_to: localhost
run_once: true
environment:
AWS_ACCESS_KEY_ID: "{{ sap_vm_provision_aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ sap_vm_provision_aws_secret_access_key }}"
AWS_REGION: "{{ sap_vm_provision_aws_region }}"
when:
- sap_ha_pacemaker_cluster_aws_region is defined
- (groups["hana_secondary"] is defined and (groups["hana_secondary"] | length>0)) or (groups["nwas_ers"] is defined and (groups["nwas_ers"] | length>0)) or (groups["anydb_secondary"] is defined and (groups["anydb_secondary"] | length>0))
any_errors_fatal: true
block:

- name: Provision High Availability resources for AWS EC2 hosts
ansible.builtin.include_tasks:
file: "{{ 'platform_' + sap_vm_provision_iac_type }}/{{ sap_vm_provision_iac_platform }}/execute_setup_ha.yml"
apply:
environment:
AWS_ACCESS_KEY_ID: "{{ sap_vm_provision_aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ sap_vm_provision_aws_secret_access_key }}"
AWS_REGION: "{{ sap_vm_provision_aws_region }}"
# apply:
# environment:
# AWS_REGION: "{{ sap_vm_provision_aws_region }}"

rescue:
# This requires no_log set on each Ansible Task, and not set on the Ansible Task Block
# This requires an Ansible Task Block containing the Ansible Tasks for calling
# Infrastructure Platform APIs (via Ansible Modules)
- name: Show errors in task outputs
ansible.builtin.fail:
msg: "{{ lookup('ansible.builtin.vars', loop_item) }}"
loop:
- __sap_vm_provision_task_aws_account_info
- __sap_vm_provision_task_vpc_subnet_rt_info
- __sap_vm_provision_task_vpc_subnet_rt_route_sap_hana
- __sap_vm_provision_task_route53_sap_hana
- __sap_vm_provision_task_vpc_subnet_rt_route_sap_anydb
- __sap_vm_provision_task_route53_sap_anydb
- __sap_vm_provision_task_vpc_subnet_rt_route_sap_netweaver_ascs
- __sap_vm_provision_task_route53_sap_netweaver_ascs
- __sap_vm_provision_task_vpc_subnet_rt_route_sap_netweaver_ers
- __sap_vm_provision_task_route53_sap_netweaver_ers
- __sap_vm_provision_task_iam_role_ha_pacemaker
- __sap_vm_provision_task_iam_policy_dataprovider
- __sap_vm_provision_task_iam_policy_overlayip
- __sap_vm_provision_task_iam_policy_stonith_saphana
- __sap_vm_provision_task_iam_policy_stonith_sapnwas
- __sap_vm_provision_task_iam_attach_role
- __sap_vm_provision_task_iam_attach_instance_saphana
- __sap_vm_provision_task_iam_attach_instance_sapnwas
loop_control:
loop_var: loop_item
index_var: loop_item_index
label: "{{ 'Variable No. ' + (loop_item_index | string) }}"
when:
- lookup('ansible.builtin.vars', loop_item, default='') | length > 0
- not lookup('ansible.builtin.vars', loop_item, default='') is skipped
- lookup('ansible.builtin.vars', loop_item, default='') is failed
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
- not inventory_hostname in lookup('ansible.builtin.vars', 'sap_vm_provision_' + sap_vm_provision_iac_platform + '_host_specifications_dictionary')[sap_vm_provision_host_specification_plan].keys()

- name: Provision AWS EC2 Virtual Server instance
register: register_provisioned_host_single
register: __sap_vm_provision_task_provisioned_host_single
amazon.aws.ec2_instance:
state: started
name: "{{ inventory_hostname }}"
image_id: "{{ (register_aws_ami.images | sort(attribute='creation_date') | last).image_id }}"
image_id: "{{ (__sap_vm_provision_task_aws_ami.images | sort(attribute='creation_date') | last).image_id }}"
instance_type: "{{ lookup('ansible.builtin.vars', 'sap_vm_provision_' + sap_vm_provision_iac_platform + '_host_specifications_dictionary')[sap_vm_provision_host_specification_plan][scaleout_origin_host_spec | default(inventory_hostname)].virtual_machine_profile }}"
key_name: "{{ sap_vm_provision_aws_key_pair_name_ssh_host_public_key }}"
security_groups: "{{ sap_vm_provision_aws_vpc_sg_names }}"
Expand All @@ -33,6 +33,9 @@
network:
assign_public_ip: false
source_dest_check: "{{ not lookup('ansible.builtin.vars', 'sap_vm_provision_' + sap_vm_provision_iac_platform + '_host_specifications_dictionary')[sap_vm_provision_host_specification_plan][scaleout_origin_host_spec | default(inventory_hostname)].disable_ip_anti_spoofing }}" # Disable the Anti IP Spoofing by setting Source/Destination Check to false
access_key: "{{ sap_vm_provision_aws_access_key }}"
secret_key: "{{ sap_vm_provision_aws_secret_access_key }}"
no_log: "{{ __sap_vm_provision_no_log }}"

- name: Set fact for storage volume letters calculations (max 25 volumes)
ansible.builtin.set_fact:
Expand All @@ -43,15 +46,18 @@
filters:
"tag:Name": "{{ inventory_hostname }}"
"instance-state-name": ["running"]
register: instance_info
access_key: "{{ sap_vm_provision_aws_access_key }}"
secret_key: "{{ sap_vm_provision_aws_secret_access_key }}"
register: __sap_vm_provision_task_instance_info
no_log: "{{ __sap_vm_provision_no_log }}"

- name: Set fact for available storage volume device names
ansible.builtin.set_fact:
available_volumes: |-
{% set letters = 'bcdefghijklmnopqrstuvwxyz' %}
{% set volumes = [] %}
{%- for letter in letters -%}
{% for device in instance_info.instances[0].block_device_mappings -%}
{% for device in __sap_vm_provision_task_instance_info.instances[0].block_device_mappings -%}
{% if '/dev/sd' + letter not in device.device_name -%}
{% set dev = volumes.append('/dev/sd' + letter) %}
{%- endif %}
Expand Down Expand Up @@ -95,11 +101,13 @@
- name: Provision AWS EBS volumes for AWS EC2 Virtual Server instance filesystems
amazon.aws.ec2_vol:
name: "{{ inventory_hostname }}-vol_{{ vol_item.name }}"
instance: "{{ register_provisioned_host_single.instance_ids[0] }}"
instance: "{{ __sap_vm_provision_task_provisioned_host_single.instance_ids[0] }}"
volume_type: "{{ vol_item.type }}"
volume_size: "{{ vol_item.size }}"
device_name: "{{ vol_item.device }}"
delete_on_termination: true
access_key: "{{ sap_vm_provision_aws_access_key }}"
secret_key: "{{ sap_vm_provision_aws_secret_access_key }}"
loop: "{{ filesystem_volume_map }}"
loop_control:
loop_var: vol_item
Expand All @@ -108,26 +116,30 @@
when:
- vol_item.fstype is defined
- vol_item.size > 0
register: volume_provisioning
register: __sap_vm_provision_task_volume_provisioning
no_log: "{{ __sap_vm_provision_no_log }}"

- name: Read AWS EC2 instance information
amazon.aws.ec2_instance_info:
filters:
"tag:Name": "{{ inventory_hostname }}"
register: instance_info
access_key: "{{ sap_vm_provision_aws_access_key }}"
secret_key: "{{ sap_vm_provision_aws_secret_access_key }}"
register: __sap_vm_provision_task_instance_info
no_log: "{{ __sap_vm_provision_no_log }}"

- name: Add host facts
ansible.builtin.set_fact:
filesystem_volume_map: "{{ filesystem_volume_map }}"
volume_provisioning: "{{ volume_provisioning }}"
instance_info: "{{ instance_info }}"
__sap_vm_provision_task_volume_provisioning: "{{ __sap_vm_provision_task_volume_provisioning }}"
instance_info: "{{ __sap_vm_provision_task_instance_info }}"
delegate_to: "{{ inventory_hostname }}"
delegate_facts: true

no_log: "{{ __sap_vm_provision_no_log }}"

- name: Create fact for delegate host IP
ansible.builtin.set_fact:
provisioned_private_ip: "{{ register_provisioned_host_single.instances[0].private_ip_address }}"
provisioned_private_ip: "{{ __sap_vm_provision_task_provisioned_host_single.instances[0].private_ip_address }}"


- name: Copy facts to delegate host
Expand All @@ -139,7 +151,7 @@
delegate_sap_vm_provision_bastion_ssh_port: "{{ sap_vm_provision_bastion_ssh_port }}"
delegate_sap_vm_provision_ssh_bastion_private_key_file_path: "{{ sap_vm_provision_ssh_bastion_private_key_file_path }}"
delegate_sap_vm_provision_ssh_host_private_key_file_path: "{{ sap_vm_provision_ssh_host_private_key_file_path }}"
delegate_private_ip: "{{ register_provisioned_host_single.instances[0].private_ip_address }}"
delegate_private_ip: "{{ __sap_vm_provision_task_provisioned_host_single.instances[0].private_ip_address }}"
delegate_hostname: "{{ inventory_hostname }}"
delegate_sap_vm_provision_dns_root_domain_name: "{{ sap_vm_provision_dns_root_domain }}"

Expand Down Expand Up @@ -181,8 +193,8 @@

- name: Append loop value to register
ansible.builtin.set_fact:
register_provisioned_host_single: "{{ register_provisioned_host_single | combine( { 'host_node' : inventory_hostname } , { 'sap_host_type' : lookup('ansible.builtin.vars', 'sap_vm_provision_' + sap_vm_provision_iac_platform + '_host_specifications_dictionary')[sap_vm_provision_host_specification_plan][scaleout_origin_host_spec | default(inventory_hostname)].sap_host_type } , { 'sap_system_type' : (lookup('ansible.builtin.vars', 'sap_vm_provision_' + sap_vm_provision_iac_platform + '_host_specifications_dictionary')[sap_vm_provision_host_specification_plan][scaleout_origin_host_spec | default(inventory_hostname)].sap_system_type | default('')) } ) }}"
__sap_vm_provision_task_provisioned_host_single: "{{ __sap_vm_provision_task_provisioned_host_single | combine( { 'host_node' : inventory_hostname } , { 'sap_host_type' : lookup('ansible.builtin.vars', 'sap_vm_provision_' + sap_vm_provision_iac_platform + '_host_specifications_dictionary')[sap_vm_provision_host_specification_plan][scaleout_origin_host_spec | default(inventory_hostname)].sap_host_type } , { 'sap_system_type' : (lookup('ansible.builtin.vars', 'sap_vm_provision_' + sap_vm_provision_iac_platform + '_host_specifications_dictionary')[sap_vm_provision_host_specification_plan][scaleout_origin_host_spec | default(inventory_hostname)].sap_system_type | default('')) } ) }}"

- name: Append output to merged register
ansible.builtin.set_fact:
register_provisioned_host_all: "{{ register_provisioned_host_all + [register_provisioned_host_single] }}"
register_provisioned_host_all: "{{ register_provisioned_host_all + [__sap_vm_provision_task_provisioned_host_single] }}"
Loading
Loading