Add rover ignite initial templates

This commit is contained in:
lolorol 2022-01-13 05:17:45 +00:00
Родитель 06f56f0d1c
Коммит 4bfa1ed587
199 изменённых файлов: 9315 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,15 @@
# Cloud Adoption Framework landing zones for Terraform - Starter template for Azure Subscription Vending Machine (ASVM)
```bash
cd /tf/caf/templates/platform
rover ignite \
--playbook /tf/caf/starter/templates/asvm/ansible.yaml \
-e base_templates_folder=/tf/caf/starter/templates/landingzones \
-e resource_template_folder=/tf/caf/starter/templates/resources \
-e config_folder=/tf/caf/orgs/contoso/asvm \
-e platform_config_folder=/tf/caf/orgs/contoso/platform \
-e scenario=contoso
```

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,155 @@
#!/usr/bin/env python
"""
An Ansible action plugin to allow explicit merging of dict and list facts.
https://github.com/leapfrogonline/ansible-merge-vars/blob/master/LICENSE.md
"""
from ansible.plugins.action import ActionBase
from ansible.errors import AnsibleError
from ansible.utils.vars import isidentifier
# Funky import dance for Ansible backwards compatitility (not sure if we
# actually need to do this or not)
try:
from __main__ import display
except ImportError:
from ansible.utils.display import Display # pylint: disable=ungrouped-imports
display = Display()
class ActionModule(ActionBase):
"""
Merge all variables in context with a certain suffix (lists or dicts only)
and create a new variable that contains the result of this merge. These
initial suffixed variables can be definied anywhere in the inventory, or by
any other means; as long as they're in the context for the running play,
they'll be merged.
"""
def run(self, tmp=None, task_vars=None):
suffix_to_merge = self._task.args.get('suffix_to_merge', '')
merged_var_name = self._task.args.get('merged_var_name', '')
dedup = self._task.args.get('dedup', True)
expected_type = self._task.args.get('expected_type')
recursive_dict_merge = bool(self._task.args.get('recursive_dict_merge', False))
if 'cacheable' in self._task.args.keys():
display.deprecated(
"The `cacheable` option does not actually do anything, since Ansible 2.5. "
"No matter what, the variable set by this plugin will be set in the fact "
"cache if you have fact caching enabled. To get rid of this warning, "
"remove the `cacheable` argument from your merge_vars task. This warning "
"will be removed in a future version of this plugin."
)
# Validate args
if expected_type not in ['dict', 'list']:
raise AnsibleError("expected_type must be set ('dict' or 'list').")
if not merged_var_name:
raise AnsibleError("merged_var_name must be set")
if not isidentifier(merged_var_name):
raise AnsibleError("merged_var_name '%s' is not a valid identifier" % merged_var_name)
if not suffix_to_merge.endswith('__to_merge'):
raise AnsibleError("Merge suffix must end with '__to_merge', sorry!")
keys = sorted([key for key in task_vars.keys()
if key.endswith(suffix_to_merge)])
display.v("Merging vars in this order: {}".format(keys))
# We need to render any jinja in the merged var now, because once it
# leaves this plugin, ansible will cleanse it by turning any jinja tags
# into comments.
# And we need it done before merging the variables,
# in case any structured data is specified with templates.
merge_vals = [self._templar.template(task_vars[key]) for key in keys]
# Dispatch based on type that we're merging
if merge_vals == []:
if expected_type == 'list':
merged = []
else:
merged = {}
elif isinstance(merge_vals[0], list):
merged = merge_list(merge_vals, dedup)
elif isinstance(merge_vals[0], dict):
merged = merge_dict(merge_vals, dedup, recursive_dict_merge)
else:
raise AnsibleError(
"Don't know how to merge variables of type: {}".format(type(merge_vals[0]))
)
return {
'ansible_facts': {merged_var_name: merged},
'changed': False,
}
def merge_dict(merge_vals, dedup, recursive_dict_merge):
"""
To merge dicts, just update one with the values of the next, etc.
"""
check_type(merge_vals, dict)
merged = {}
for val in merge_vals:
if not recursive_dict_merge:
merged.update(val)
else:
# Recursive merging of dictionaries with overlapping keys:
# LISTS: merge with merge_list
# DICTS: recursively merge with merge_dict
# any other types: replace (same as usual behaviour)
for key in val.keys():
if not key in merged:
# first hit of the value - just assign
merged[key] = val[key]
elif isinstance(merged[key], list):
merged[key] = merge_list([merged[key], val[key]], dedup)
elif isinstance(merged[key], dict):
merged[key] = merge_dict([merged[key], val[key]], dedup, recursive_dict_merge)
else:
merged[key] = val[key]
return merged
def merge_list(merge_vals, dedup):
""" To merge lists, just concat them. Dedup if wanted. """
check_type(merge_vals, list)
merged = flatten(merge_vals)
if dedup:
merged = deduplicate(merged)
return merged
def check_type(mylist, _type):
""" Ensure that all members of mylist are of type _type. """
if not all(isinstance(item, _type) for item in mylist):
raise AnsibleError("All values to merge must be of the same type, either dict or list")
def flatten(list_of_lists):
"""
Flattens a list of lists:
>>> flatten([[1, 2] [3, 4]])
[1, 2, 3, 4]
I wish Python had this in the standard lib :(
"""
return list((x for y in list_of_lists for x in y))
def deduplicate(mylist):
"""
Just brute force it. This lets us keep order, and lets us dedup unhashable
things, like dicts. Hopefully you won't run into such big lists that
this will ever be a performance issue.
"""
deduped = []
for item in mylist:
if item not in deduped:
deduped.append(item)
return deduped

Просмотреть файл

@ -0,0 +1,80 @@
# Get Platform subscriptions
- name: "Get platform subscriptions tfstate details"
register: subscription_tfstate_file_name
shell: |
az storage account list \
--subscription {{ config.caf_terraform.launchpad.subscription_id }} \
--query "[?tags.caf_tfstate=='{{ config.tfstates["platform"].platform_subscriptions.level | default('level1') }}' && tags.caf_environment=='{{ config.caf_terraform.launchpad.caf_environment }}'].{name:name}[0]" -o json | jq -r .name
- debug:
msg: "{{ subscription_tfstate_file_name.stdout }}"
- name: "Download platform subscriptions tfstate details"
register: platform_subscription_tfstate_exists
shell: |
az storage blob download \
--name "{{ config.tfstates["platform"].platform_subscriptions.tfstate | default('platform_subscriptions.tfstate') }}" \
--account-name "{{ subscription_tfstate_file_name.stdout }}" \
--container-name "tfstate" \
--auth-mode "login" \
--file "{{ job_cache_base_path }}/{{ config.tfstates["platform"].platform_subscriptions.tfstate | default('platform_subscriptions.tfstate') }}"
- name: "Get platform_subscriptions details"
when: platform_subscription_tfstate_exists.rc == 0
shell: "cat {{ job_cache_base_path }}/{{ config.tfstates[\"platform\"].platform_subscriptions.tfstate | default('platform_subscriptions.tfstate') }}"
register: platform_subscriptions
- name: "Get platform_subscriptions json data"
when: platform_subscription_tfstate_exists.rc == 0
set_fact:
platform_sub_jsondata: "{{ platform_subscriptions.stdout | from_json }}"
- name: "Get subscriptions list"
when: platform_subscription_tfstate_exists.rc == 0
set_fact:
platform_subscriptions_details: "{{ platform_sub_jsondata | json_query(path) }}"
vars:
path: 'outputs.objects.value.{{ config.tfstates["platform"].platform_subscriptions.lz_key_name }}.subscriptions'
# Get Platform keyvaults
- name: "Get tfstate keyvaults account name"
register: launchpad_storage_account
ignore_errors: yes
shell: |
az storage account list \
--subscription {{ config.caf_terraform.launchpad.subscription_id }} \
--query "[?tags.caf_tfstate=='{{ config.tfstates["platform"].launchpad.level | default('level0') }}' && tags.caf_environment=='{{ config.caf_terraform.launchpad.caf_environment }}'].{name:name}[0]" -o json | jq -r .name
- debug:
msg: "{{launchpad_storage_account}}"
- name: "Get tfstate keyvaults details"
register: credentials_tfstate_exists
when: launchpad_storage_account.stderr == ""
ignore_errors: yes
shell: |
az storage blob download \
--name "{{ config.tfstates["platform"].launchpad_credentials.tfstate | default('launchpad_credentials.tfstate') }}" \
--account-name "{{ launchpad_storage_account.stdout }}" \
--container-name "{{ config.tfstates["platform"].launchpad.workspace | default('tfstate') }}" \
--auth-mode "login" \
--file "~/.terraform.cache/launchpad/{{ config.tfstates["platform"].launchpad_credentials.tfstate | default('launchpad_credentials.tfstate') }}"
- name: "Get launchpad_credentials details"
when: credentials_tfstate_exists is not skipped
shell: "cat ~/.terraform.cache/launchpad/{{ config.tfstates[\"platform\"].launchpad_credentials.tfstate | default('launchpad_credentials.tfstate') }}"
register: launchpad_credentials
- name: "Get launchpad_credentials json data"
when: credentials_tfstate_exists is not skipped
set_fact:
credjsondata: "{{ launchpad_credentials.stdout | from_json }}"
- name: "Set keyvaults variable"
when: credentials_tfstate_exists is not skipped
set_fact:
keyvaults: "{{ credjsondata | json_query(path) }}"
vars:
path: 'outputs.objects.value.launchpad_credentials_rotation.keyvaults'

Просмотреть файл

@ -0,0 +1,84 @@
- name: CAF Terraform - Generate configuration files
hosts: localhost
vars:
base_templates_folder: "{{ base_templates_folder }}/asvm"
resource_template_folder: "{{ base_templates_folder }}/resources"
level: level3
tasks:
- name: "Load variable for landingzones config"
include_vars:
name: asvm_config__to_merge
dir: "{{config_folder}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "config.asvm.yaml|tfstates.asvm.yaml|deployments.yaml"
- name: "Set base variables"
set_fact:
job_cache_base_path: "/home/vscode/.terraform.cache"
config: "{{asvm_config__to_merge}}"
- name: "Content of asvm_config__to_merge"
debug:
msg: "{{asvm_config__to_merge}}"
- name: "Load variable for platform config"
include_vars:
name: platform_config__to_merge
dir: "{{config_folder_platform | default(config_folder)}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "caf.platform.yaml|tfstates.caf.yaml|tfstates.yaml"
- name: "Content of platform_config__to_merge"
debug:
msg: "{{platform_config__to_merge}}"
- name: Merge asvm and platform variables
merge_vars:
suffix_to_merge: config__to_merge
merged_var_name: config
expected_type: 'dict'
recursive_dict_merge: True
- name: "Set base config variables"
set_fact:
config: "{{ ansible_facts.config }}"
- name: "Content of config"
debug:
msg: "{{config}}"
- name: "Creates cache directory"
file:
path: "{{ job_cache_base_path }}/launchpad"
state: directory
- name: "{{ level }} | Get platform details (requires '-e config_folder_platform=path to yamls' path to be set)"
include_tasks: "ansible-get-platform-details.yaml"
when: config_folder_platform is defined
#
# Level 3
#
# landingzones deployments
- name: "{{ level }} | landingzones"
include_tasks: "{{ level }}/ansible.yaml"
loop: "{{asvm_config__to_merge.deployments.keys()}}"
loop_control:
loop_var: asvm_long_folder
#
# Linters
#
- name: Terraform linter
shell: |
terraform fmt -recursive {{ destination_base_path }}

Просмотреть файл

@ -0,0 +1,66 @@
- name: "Load variable for subscriptions"
include_vars:
name: subscriptions
dir: "{{config_folder}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "subscriptions.asvm.yaml|subscription.asvm.yaml"
- name: "Content of subscriptions"
debug:
msg: "{{subscriptions}}"
- name: "[{{ level }}-{{ subscription_key }}] Get tfstate details"
register: subscription_tfstate_storage_account_name
shell: |
az storage account list \
--subscription {{ config.caf_terraform.launchpad.subscription_id }} \
--query "[?tags.caf_tfstate=='{{ config.tfstates['asvm'][subscription_key].level }}' && tags.caf_environment=='{{ config.caf_terraform.launchpad.caf_environment }}'].{name:name}[0]" -o json | jq -r .name
- debug:
msg: "{{ subscription_tfstate_storage_account_name.stdout }}"
- name: "[{{ level }}-{{ subscription_key }}] Download tfstate details"
register: subscription_tfstate_exists
ignore_errors: true
shell: |
az storage blob download \
--name "{{ config.tfstates['asvm'][subscription_key].subscriptions.tfstate }}" \
--account-name "{{ subscription_tfstate_storage_account_name.stdout }}" \
--container-name "{{ config.tfstates['asvm'][subscription_key].workspace }}" \
--auth-mode "login" \
--file "{{ job_cache_base_path }}/{{ config.tfstates['asvm'][subscription_key].subscriptions.tfstate }}"
- debug:
msg: "{{ subscription_tfstate_exists }}"
when: subscriptions.subscriptions[subscription_key] is defined
- name: "[{{ level }}-{{ subscription_key }}] Get landingzones_subscriptions details"
shell: "cat {{ job_cache_base_path }}/{{ config.tfstates['asvm'][subscription_key].subscriptions.tfstate }}"
register: platform_subscriptions
when:
- subscriptions.subscriptions[subscription_key] is defined
- subscription_tfstate_exists.rc == 0
- name: "[{{ level }}-{{ subscription_key }}] Get subscriptions data"
when:
- subscriptions.subscriptions[subscription_key] is defined
- subscription_tfstate_exists.rc == 0
set_fact:
asvm_subscriptions_details: "{{ platform_subscriptions.stdout | from_json | json_query(path) }}"
vars:
path: 'outputs.objects.value."{{ config.tfstates["asvm"][subscription_key].subscriptions.lz_key_name }}".subscriptions'
- name: "[{{ level }}-{{ subscription_key }}] cleanup"
when:
- subscriptions.subscriptions[subscription_key] is defined
- subscription_tfstate_exists.rc == 0
file:
path: "{{ job_cache_base_path }}/{{ config.tfstates['asvm'][subscription_key].subscriptions.tfstate }}"
state: absent
- debug:
msg: "Platform subscriptions - {{ asvm_subscriptions_details }}"
when:
- subscriptions.subscriptions[subscription_key] is defined
- subscription_tfstate_exists.rc == 0

Просмотреть файл

@ -0,0 +1,62 @@
- name: set destination paths
set_fact:
destination_path: "{{ destination_base_path }}/{{ subscription_key }}/subscription"
deployment: "subscriptions"
- name: "Clean-up directory - subscription - {{ destination_path }}"
file:
path: "{{ destination_path }}"
state: absent
- name: "Content of subscriptions' resources"
debug:
msg: "{{resources}}"
- name: "[{{ level }} {{ subscription_key }}] Creates directory"
file:
path: "{{ destination_path }}"
state: directory
#
# global_settings
#
- name: "[{{ level }} {{ subscription_key }}] - subscription - global_settings"
when: resources.subscriptions[subscription_key].global_settings is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/global_settings.tfvars.j2"
#
# landingzone
#
- name: "[{{ level }} {{ subscription_key }}] - subscription - landingzone"
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/landingzone.tfvars.j2"
#
# subscription
#
- name: "[{{ level }} {{ subscription_key }}] - subscription - subscription"
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/subscriptions.tfvars.j2"
#
# Readme
#
- name: "[{{ level }}-{{ subscription_key }}] - subscription - *.md"
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ base_templates_folder }}/{{ level }}/subscription/*.md"

Просмотреть файл

@ -0,0 +1,58 @@
- name: set asvm context
set_fact:
asvm_folder: "{{ asvm_long_folder if 'path' not in asvm_long_folder else asvm_long_folder.path | regex_search('[^\/]+(?=\/$|$)') }}"
- name: "[{{ level }}-{{ asvm_folder }}] Set cache folder"
set_fact:
# job_cache_base_path: "/home/vscode/.terraform.cache"
subscription_key: "{{ asvm_folder }}"
- name: "Load variable for deployments"
include_vars:
name: deployments
dir: "{{config_folder}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "deployments.asvm.yaml|deployments.yaml"
- debug:
msg: "{{deployments}}"
### Generate remote state storage containers
- name: "[{{ level }} {{ subscription_key }}] - remote state container"
include_tasks: "{{ level }}/storage_containers/ansible.yaml"
when:
- deployments.deployments[subscription_key].storage_containers is defined
#### Get subscription_id
- name: "[{{ level }} {{ subscription_key }}] - subscription"
include_tasks: "{{ level }}/ansible-subscription-id.yaml"
when:
- config.tfstates['asvm'][subscription_key].subscriptions is defined
- config.tfstates['asvm'][subscription_key].subscriptions.subscription_id is not defined
### Subscription
- name: "Load variable for subscriptions"
include_vars:
name: resources
dir: "{{config_folder}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "subscriptions.asvm.yaml|subscription.asvm.yaml|tfstates.asvm.yaml"
- name: "[{{ level }} {{ subscription_key }}] - subscription"
include_tasks: "{{ level }}/ansible-subscription.yaml"
when:
- resources.subscriptions[subscription_key] is defined
- config.tfstates['asvm'][subscription_key].subscriptions.subscription_id is not defined
#### Privileged resources to deploy in the landingzone
- name: "[{{ level }} {{ subscription_key }}] - resources"
include_tasks: "{{ level }}/resources/ansible.yaml"
when:
- config.tfstates['asvm'][subscription_key].resources is defined

Просмотреть файл

@ -0,0 +1,262 @@
- name: set destination paths
set_fact:
destination_path: "{{ destination_base_path }}/{{ subscription_key }}/resources"
deployment: "resources"
- name: "Clean-up directory - subscription - {{ destination_path }}"
file:
path: "{{ destination_path }}"
state: absent
when: config.configuration_folders.asvm.cleanup_destination | default(true) | bool
- name: "Load variable for resources"
include_vars:
name: resources
dir: "{{config_folder}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "resources.asvm.yaml"
- name: "Content of resources"
debug:
msg: "{{resources}}"
- name: "[{{ level }} {{ asvm_folder }}] - resources - Creates directory"
file:
path: "{{ destination_path }}"
state: directory
#
# azuread_credentials
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - azuread_credentials"
when:
- resources.subscriptions[subscription_key].azuread_credentials is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_credentials.tfvars.j2"
#
# azuread_applications
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - azuread_applications"
when:
- resources.subscriptions[subscription_key].azuread_applications is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_applications.tfvars.j2"
#
# azuread_credential_policies
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - azuread_credential_policies"
when:
- resources.subscriptions[subscription_key].azuread_credential_policies is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_credential_policies.tfvars.j2"
#
# azuread_groups
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - azuread_groups"
when:
- resources.subscriptions[subscription_key].azuread_groups is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_groups.tfvars.j2"
#
# azuread_groups_membership
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - azuread_groups_membership"
when:
- resources.subscriptions[subscription_key].azuread_groups_membership is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_groups_membership.tfvars.j2"
#
# azuread_service_principals
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - azuread_service_principals"
when:
- resources.subscriptions[subscription_key].azuread_service_principals is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_service_principals.tfvars.j2"
#
# custom_role_definitions
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - custom_role_definitions"
when:
- resources.subscriptions[subscription_key].custom_role_definitions is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/custom_role_definitions.tfvars.j2"
#
# keyvaults
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - keyvaults"
when:
- resources.subscriptions[subscription_key].keyvaults is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/keyvaults.tfvars.j2"
#
# keyvault_access_policies
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - keyvault_access_policies"
when:
- resources.subscriptions[subscription_key].keyvault_access_policies is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/keyvault_access_policies.tfvars.j2"
#
# landingzone
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - landingzone"
when:
- deployments.deployments[subscription_key][deployment].landingzone is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/landingzone.tfvars.j2"
#
# managed_identities
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - managed_identities"
when:
- resources.subscriptions[subscription_key].managed_identities is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/managed_identities.tfvars.j2"
#
# network_security_group_definition
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - network_security_group_definition"
when:
- resources.subscriptions[subscription_key].network_security_group_definition is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/network_security_group_definition.tfvars.j2"
#
# recovery_vaults
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - recovery_vaults"
when:
- resources.subscriptions[subscription_key].recovery_vaults is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/recovery_vaults.tfvars.j2"
#
# resource_groups
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - resource_groups"
when:
- resources.subscriptions[subscription_key].resource_groups is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/resource_groups.tfvars.j2"
#
# role_mapping
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - role_mapping"
when:
- resources.subscriptions[subscription_key].role_mapping is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/role_mapping.tfvars.j2"
#
# virtual_hub_connections
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - virtual_hub_connections"
when:
- resources.subscriptions[subscription_key].virtual_hub_connections is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/virtual_hub_connections.tfvars.j2"
#
# virtual_networks
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - virtual_networks"
when:
- resources.subscriptions[subscription_key].virtual_networks is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/virtual_networks.tfvars.j2"
#
# Readme
#
- name: "[{{ level }}-{{ subscription_key }}] - resources - *.md"
when: subscription_tfstate_exists.rc == 0
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ base_templates_folder }}/{{ level }}/resources/*.md"

Просмотреть файл

@ -0,0 +1,31 @@
### Deploy base resources in {{ asvm_folder }}
```bash
rover login -t {{ config.platform_identity.tenant_name }}
unset ARM_SKIP_PROVIDER_REGISTRATION
cd /tf/caf/landingzones
git pull
git checkout {{ resources.gitops.landingzones }}
rover \
{% if config.platform_identity.azuread_identity_mode != "logged_in_user" %}
--impersonate-sp-from-keyvault-url {{ keyvaults.cred_subscription_creation_landingzones.vault_uri }} \
{% endif %}
-lz /tf/caf/landingzones/caf_solution \
-var-folder {{ destination_path }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-target_subscription {{ asvm_subscriptions_details[asvm_folder].subscription_id }} \
-tfstate {{ config.tfstates['asvm'][asvm_folder].resources.tfstate }} \
--workspace {{ config.tfstates['asvm'][asvm_folder].workspace }} \
-log-severity {{ config.gitops.rover_log_error }} \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-p ${TF_DATA_DIR}/{{ config.tfstates['asvm'][asvm_folder].resources.tfstate }}.tfplan \
-a plan
rover logout
```

Просмотреть файл

@ -0,0 +1,80 @@
- name: set destination paths
set_fact:
destination_path: "{{ destination_base_path }}/storage_containers"
deployment: "storage_containers"
- name: "Load variable for resources"
include_vars:
name: resources
dir: "{{config_folder}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "tfstates.asvm.yaml|subscriptions.asvm.yaml|subscription.asvm.yaml"
- name: "Content of resources"
debug:
msg: "{{resources}}"
- name: "[{{ level }} {{ asvm_folder }}] - storage_containers - Creates directory"
file:
path: "{{ destination_path }}"
state: directory
#
# Get storage account names
#
- name: "[{{ level }}-{{ subscription_key }}] - storage_containers - launchpad level3"
register: storage_account_level3
shell: |
az storage account list \
--subscription {{ config.caf_terraform.launchpad.subscription_id }} \
--query "[?tags.caf_tfstate=='level3' && tags.caf_environment=='{{ config.caf_terraform.launchpad.caf_environment }}'].{name:name, resource_group:resourceGroup}[0]" -o json | jq -r
- debug:
msg: "{{storage_account_level3.stdout}}"
- name: "[{{ level }}-{{ subscription_key }}] - storage_containers - launchpad level4"
register: storage_account_level4
shell: |
az storage account list \
--subscription {{ config.caf_terraform.launchpad.subscription_id }} \
--query "[?tags.caf_tfstate=='level4' && tags.caf_environment=='{{ config.caf_terraform.launchpad.caf_environment }}'].{name:name, resource_group:resourceGroup}[0]" -o json | jq -r
- debug:
msg: "{{storage_account_level4.stdout}}"
#
# landingzone
#
- name: "[{{ level }}-{{ subscription_key }}] - storage_containers - landingzone"
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/landingzone.tfvars.j2"
#
# storage_containers
#
- name: "[{{ level }}-{{ subscription_key }}] - storage_containers - storage_containers"
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ base_templates_folder }}/{{ level }}/storage_containers/storage_containers.tfvars.j2"
#
# Readme
#
- name: "[{{ level }}-{{ subscription_key }}] - storage_containers - *.md"
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ base_templates_folder }}/{{ level }}/storage_containers/*.md"

Просмотреть файл

@ -0,0 +1,28 @@
### Create storage containers for the landingzone
```bash
rover login -t {{ config.platform_identity.tenant_name }}
cd /tf/caf/landingzones
git pull
git checkout {{ resources.gitops.landingzones }}
rover \
{% if config.platform_identity.azuread_identity_mode != "logged_in_user" %}
--impersonate-sp-from-keyvault-url {{ keyvaults.cred_subscription_creation_landingzones.vault_uri }} \
{% endif %}
-lz /tf/caf/landingzones/caf_solution \
-var-folder {{ destination_path }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-target_subscription {{config.caf_terraform.launchpad.subscription_id }} \
-tfstate {{ config.tfstates.asvm[asvm_folder].subscriptions.tfstate }} \
--workspace {{ config.tfstates.asvm[asvm_folder].subscriptions.workspace | default('tfstate') }} \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-p ${TF_DATA_DIR}/{{ config.tfstates.asvm[asvm_folder].subscriptions.tfstate }}.tfplan \
-a plan
rover logout
```

Просмотреть файл

@ -0,0 +1,16 @@
storage_containers = {
{% for key in resources.subscriptions.keys() %}
{{ key }}_level3 = {
name = "{{ resources.tfstates.asvm[key].workspace }}"
storage_account = {
name = "{{storage_account_level3.stdout|from_json|json_query('name')}}"
}
}
{{ key }}_level4 = {
name = "{{ resources.tfstates.asvm[key].workspace }}"
storage_account = {
name = "{{storage_account_level4.stdout|from_json|json_query('name')}}"
}
}
{% endfor %}
}

Просмотреть файл

@ -0,0 +1,40 @@
### Generate asvm for {{ asvm_folder }}
```bash
rover login -t {{ config.platform_identity.tenant_name }}
ARM_SKIP_PROVIDER_REGISTRATION=true && rover \
{% if config.platform_identity.azuread_identity_mode != "logged_in_user" %}
--impersonate-sp-from-keyvault-url {{ keyvaults.cred_subscription_creation_landingzones.vault_uri }} \
{% endif %}
-lz /tf/caf/landingzones/caf_solution \
-var-folder {{ destination_path }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-tfstate {{ config.tfstates["asvm"][asvm_folder].subscriptions.tfstate }} \
--workspace {{ config.tfstates["asvm"][asvm_folder].workspace }} \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-p ${TF_DATA_DIR}/{{ config.tfstates["asvm"][asvm_folder].subscriptions.tfstate }}.tfplan \
-a plan
rover logout
```
Once you have executed the rover apply to create the subscription, you need to re-execute the rover ignite to generate the instructions for the next steps.
Note you need to logout and login as a caf_maintainer group member
```bash
rover login -t {{ config.platform_identity.tenant_name }}
rover ignite \
--playbook /tf/caf/starter/templates/landingzones/ansible.yaml \
-e base_templates_folder={{ base_templates_folder }} \
-e resource_template_folder={{ resource_template_folder }} \
-e config_folder={{ config_folder }} \
-e destination_base_path={{ destination_base_path }} \
-e config_folder_platform={{ config_folder_platform }}
```

Просмотреть файл

@ -0,0 +1 @@
yaml support for level coming soon.

Просмотреть файл

@ -0,0 +1,277 @@
- name: CAF Terraform - Generate Azure Subscription Vending Machine (asvm) configuration files
hosts: localhost
vars:
connectivity_virtual_wan: "{{ lookup('file', '{{ config_folder }}/connectivity_virtual_wan.yaml') | from_yaml }}"
connectivity_virtual_hub: "{{ lookup('file', '{{ config_folder }}/connectivity_virtual_hub.yaml') | from_yaml }}"
connectivity_firewall: "{{ lookup('file', '{{ config_folder }}/connectivity_firewall.yaml') | from_yaml }}"
connectivity_firewall_policies: "{{ lookup('file', '{{ config_folder }}/connectivity_firewall_policies.yaml') | from_yaml }}"
connectivity_vpn_sites: "{{ lookup('file', '{{ config_folder }}/connectivity_vpn_sites.yaml') | from_yaml }}"
connectivity_vpn_gateway_connections: "{{ lookup('file', '{{ config_folder }}/connectivity_vpn_gateway_connections.yaml') | from_yaml }}"
connectivity_express_routes: "{{ lookup('file', '{{ config_folder }}/connectivity_express_routes.yaml') | from_yaml }}"
connectivity_express_route_peerings: "{{ lookup('file', '{{ config_folder }}/connectivity_express_route_peerings.yaml') | from_yaml }}"
identity: "{{ lookup('file', '{{ config_folder }}/identity.yaml') | from_yaml }}"
management: "{{ lookup('file', '{{ config_folder }}/management.yaml') | from_yaml }}"
subscriptions: "{{ lookup('file', '{{ config_folder }}/subscriptions.yaml') | from_yaml }}"
mg: "{{ lookup('file', '{{ config_folder }}/eslz/archetype_config_overrides.caf.platform.yaml') | from_yaml }}"
mg_custom: "{{ lookup('file', '{{ config_folder }}/eslz/custom_landing_zones.caf.platform.yaml') | from_yaml }}"
mg_struture: "{{ lookup('file', '{{ config_folder }}/eslz/structure.caf.platform.yaml') | from_yaml }}"
tfstates: "{{ lookup('file', '{{ config_folder }}/tfstates.yaml') | from_yaml }}"
base_templates_folder: "{{ base_templates_folder }}"
boostrap_launchpad: boostrap_launchpad | default(false)
deploy_subscriptions: deploy_subscriptions | default(false)
tasks:
- name: "Get latest cache folder"
set_fact:
job_cache_base_path: "/home/vscode/.terraform.cache"
- name: "Creates cache directory"
file:
path: "{{ job_cache_base_path }}/launchpad"
state: directory
- name: "Load variable for platform config"
include_vars:
name: config
dir: "{{config_folder_platform | default(config_folder)}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "caf.platform.yaml|tfstates.caf.yaml|tfstates.yaml"
- name: "Content of config"
debug:
msg: "{{config}}"
#
# Level 0
#
## launchpad
- name: "[{{ level }}-{{ base_folder }}] launchpad"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
vars:
base_folder: "launchpad"
level: "level0"
subscription_key: launchpad
## credentials
- name: "[{{ level }}-{{ base_folder }}] Setup credentials"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
when:
- config.platform_identity.azuread_identity_mode == "service_principal"
- launchpad_tfstate_exists.rc == 0
vars:
base_folder: "credentials"
level: "level0"
subscription_key: launchpad_credentials
- name: "[{{ level }}-{{ base_folder }}] Clean-up directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: absent
when:
- config.platform_identity.azuread_identity_mode == "logged_in_user"
- launchpad_tfstate_exists.rc == 0
vars:
base_folder: "credentials"
level: "level0"
## billing_subscription_role_delegations
- name: "[{{ level }}-{{ base_folder }}] Configure subscription role delegations"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
when: ((config.caf_terraform.billing_subscription_role_delegations is defined) and (config.platform_identity.azuread_identity_mode == "service_principal") and (launchpad_tfstate_exists.rc == 0) and (credentials_tfstate_exists is not skipped))
vars:
base_folder: "billing_subscription_role_delegations"
level: "level0"
- name: "[{{ level }}-{{ base_folder }}] Clean-up directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: absent
when:
- level0_billing_subscription_role_delegations is skipped
vars:
base_folder: "billing_subscription_role_delegations"
level: "level0"
#
# Level 1
#
## subscriptions
- name: "{{ level }}-{{ base_folder }} | Create platform subscriptions"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
when: (config.platform_core_setup.enterprise_scale.subscription_deployment_mode == "dedicated_new" and config.platform_identity.azuread_identity_mode != "logged_in_user" and launchpad_tfstate_exists is succeeded and credentials_tfstate_exists is succeeded)
vars:
base_folder: "subscriptions"
level: "level1"
- name: "{{ level }}-{{ base_folder }} | Clean-up directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: absent
when:
- level1_subscriptions is skipped
vars:
base_folder: "subscriptions"
level: "level1"
## management
- name: "{{ level }}-{{ base_folder }} | Management services"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
when:
- (config.platform_management.enable | bool)
# - (level1_subscriptions is not skipped)
# - platform_subscriptions_details is defined
vars:
base_folder: "management"
level: "level1"
subscription_key: management
## identity
- name: "{{ level }}-{{ base_folder }} | Identity services"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
when:
# - config.platform_core_setup.enterprise_scale.subscription_deployment_mode != "single_reuse"
- launchpad_tfstate_exists is not skipped
- credentials_tfstate_exists is not skipped
- level1_subscriptions is not skipped
- platform_subscriptions_details is defined
- identity.level1 is defined
vars:
base_folder: "identity"
level: "level1"
## eslz
- name: "{{ level }}-{{ base_folder }} | Enterprise-scale services"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
when:
- (config.platform_core_setup.enterprise_scale.enable | bool)
- ( (config.platform_core_setup.enterprise_scale.enable | bool) and (level1_subscriptions is not skipped) ) or (config.platform_core_setup.enterprise_scale.subscription_deployment_mode == "single_reuse")
- (platform_subscriptions_details is defined) or (config.platform_core_setup.enterprise_scale.subscription_deployment_mode == "single_reuse")
vars:
base_folder: "eslz"
level: "level1"
#
# Level 2
#
## asvm
- name: "{{ level }}-{{ base_folder }} | Azure Subscription Vending Machine (asvm)"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
when:
- config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine
- launchpad_azuread_groups is defined
vars:
base_folder: "asvm"
level: "level2"
subscription_key: asvm
## Connectivity
- name: "{{ level }}-{{ base_folder }} | Connectivity services"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
when:
- ( (config.networking_topology.deployment_option == "virtual_wan") or (config.platform_identity.azuread_identity_mode == 'logged_in_user') )
- (platform_subscriptions_details is defined) or (config.platform_core_setup.enterprise_scale.subscription_deployment_mode == "single_reuse")
vars:
base_folder: "connectivity"
level: "level2"
folders:
- virtual_wan
## identity
- name: "{{ level }}-{{ base_folder }} | Identity services"
import_tasks: "{{ level }}/{{ base_folder }}/ansible.yaml"
when:
- config.platform_core_setup.enterprise_scale.subscription_deployment_mode != "single_reuse"
- launchpad_tfstate_exists is not skipped
- credentials_tfstate_exists is not skipped
- level1_subscriptions is not skipped
- (platform_subscriptions_details is defined) or (config.platform_core_setup.enterprise_scale.subscription_deployment_mode == "single_reuse")
vars:
base_folder: "identity"
level: "level2"
## Platform readme
- name: "[{{ base_templates_folder }}] readme"
ansible.builtin.template:
src: "{{ base_templates_folder }}/readme.md"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/readme.md"
force: yes
#
# Formatting & Linters
#
- name: Terraform Formatting
shell: |
terraform fmt -recursive {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}
# - name: Level 2 - identity
# hosts: localhost
# vars:
# config: "{{ lookup('file', '{{ config_folder }}/platform.yaml') | from_yaml }}"
# identity: "{{ lookup('file', '{{ config_folder }}/identity.yaml') | from_yaml }}"
# connectivity_virtual_wan: "{{ lookup('file', '{{ config_folder }}/connectivity_virtual_wan.yaml') | from_yaml }}"
# connectivity_virtual_hub: "{{ lookup('file', '{{ config_folder }}/connectivity_virtual_hub.yaml') | from_yaml }}"
# connectivity_firewall: "{{ lookup('file', '{{ config_folder }}/connectivity_firewall.yaml') | from_yaml }}"
# connectivity_firewall_policies: "{{ lookup('file', '{{ config_folder }}/connectivity_firewall_policies.yaml') | from_yaml }}"
# cidr: "{{ lookup('file', '{{ config_folder }}/cidr.yaml') | from_yaml }}"
# tfstates: "{{ lookup('file', '{{ config_folder }}/tfstates.yaml') | from_yaml }}"
# base_templates_folder: /tf/caf/templates/platform
# base_folder: identity
# level: level2
# folders:
# - virtual_wan
# tasks:
# - name: Creates {{ level }} directory
# file:
# path: "{{ config.configuration_folders.destination_base_path }}{{ config.configuration_folders.destination_relative_path }}/{{ level }}"
# state: directory
# - name: Creates {{ base_folder }} directory strcture
# file:
# path: "{{ config.configuration_folders.destination_base_path }}{{ config.configuration_folders.destination_relative_path }}/{{ level }}/{{ base_folder }}"
# state: directory
# - name: "{{ base_folder }} - Readme"
# ansible.builtin.template:
# src: "{{ item }}"
# dest: "{{ config.configuration_folders.destination_base_path }}{{ config.configuration_folders.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item | basename | regex_replace('.j2$', '') }}"
# force: yes
# with_fileglob:
# - "{{ level }}/{{ base_folder }}/*.md"
# - name: "{{ base_folder }} - adds"
# include_tasks: "{{ base_templates_folder }}/{{ level }}/{{ base_folder }}/platform.yaml"
# #
# # Pipelines
# #
# - name: Pipelines
# hosts: localhost
# vars:
# config: "{{ lookup('file', '{{ config_folder }}/platform.yaml') | from_yaml }}"
# connectivity: "{{ lookup('file', '{{ config_folder }}/connectivity.yaml') | from_yaml }}"
# cidr: "{{ lookup('file', '{{ config_folder }}/cidr.yaml') | from_yaml }}"
# tfstates: "{{ lookup('file', '{{ config_folder }}/tfstates.yaml') | from_yaml }}"
# base_templates_folder: /tf/caf/templates/platform
# base_folder: pipelines
# tasks:
# - import_tasks: "{{ base_folder }}/platform.yaml"
# - debug: msg="You can now proceed to the next steps and execute the deployment. Refer to the readme in {{ config.configuration_folders.destination_base_path }}{{ config.configuration_folders.destination_relative_path }}/README.md"

Просмотреть файл

@ -0,0 +1,51 @@
## Introduction
This directory contains details around the configurations which are deployed to the config. All the components are deployed in a layered approach.
### Level 0
Deployment Elements | Resources Deployed
---------------------| ------------------
bootstrap | This steps bootstrap the environment with gitops prod subscription, deploys caf subscription, and create service principals.
launchpad | This step deploys launchpad store terraform states and manage deployments.
### Level 1
Deployment Elements | Resources Deployed
----------------------------- | ------------------
Platform- Subscriptions | Deploys platform subscriptions such as management, conncetivity, and identity
management | Foundation resources to management subscription such as service health alerts, log analytics
gitops | This directory hosts the Azure DevOps configurations such as Azure DevOps projects, pipelines variable groups
Identity | This hosts the identities for the pipelines and identies are pushed to vault after created
Enterprise scale - Platform | Deploys eslz resources suych as management groups, custom roles, policies, and map that to management groups
### Level 2
Deployment Elements | Resources Deployed
------------------------------------| ------------------
Connectivity - Platform | Deploys platform connectivity resources Resource Groups, Firewalls, app gateways, Vnet, Public IPs
Connectivity - hub_connection | Deploys virtual hub connections for the virtual networks
gitops | Deploys Azure DevOps agents, aks configurations, identity etc.
# Deployment steps
Below are the steps to be followed for deployment.
## Login the Azure AD Tenant
```bash
az account clear
rover login -t <tenant_name>
```
## Prerequisites
You need a developer machine configured with the dependencies.
| Repo | Description |
|---------------------------------------------------------------------------------------------------|------------------------------------------------------------|
| [Azure Windows 10](../../../documentation/maintainer/set_azure_devops_vm.md) | Azure Windows 10 Virtual Desktop with docker engine, wsl2 and vscode |
```

Просмотреть файл

@ -0,0 +1,22 @@
- name: "[{{ level }}-{{ base_folder }}] Clean-up directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: absent
when: config.configuration_folders.platform.cleanup_destination | bool
- name: "[{{ level }}-{{ base_folder }}] Creates directory"
when: config.caf_terraform.billing_subscription_role_delegations.enable == true
register: level0_billing_subscription_role_delegations
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: directory
- name: "[{{ level }}-{{ base_folder }}] subscription role delegation"
when: config.caf_terraform.billing_subscription_role_delegations.enable == true
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ level }}/{{ base_folder }}/*.tfvars.j2"
- "{{ level }}/{{ base_folder }}/*.md"

Просмотреть файл

@ -0,0 +1,12 @@
landingzone = {
backend_type = "{{ caf_terraform.launchpad.backend_type | default("azurerm")}}"
global_settings_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
level = "{{ config.tfstates.platform.launchpad.level }}"
key = "{{ config.tfstates.platform.billing_subscription_role_delegations.lz_key_name }}"
tfstates = {
{{ config.tfstates.platform.launchpad.lz_key_name }} = {
level = "current"
tfstate = "{{ config.tfstates.platform.launchpad.tfstate }}"
}
}
}

Просмотреть файл

@ -0,0 +1,49 @@
### billing_subscription_role_delegations
Set-up the subscription delegations for platform and landingzone subscriptions
```bash
# Login to the subscription {{ config.caf_terraform.launchpad.subscription_name }} with the user {{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner }}
rover login -t {{ config.platform_identity.tenant_name }}
rover \
-lz /tf/caf/landingzones/caf_solution \
-var-folder {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/level0/billing_subscription_role_delegations \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-tfstate {{ config.tfstates.platform.billing_subscription_role_delegations.tfstate }} \
-target_subscription {{ config.caf_terraform.launchpad.subscription_id }} \
-log-severity {{ config.gitops.rover_log_error }} \
-launchpad \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-p ${TF_DATA_DIR}/{{ config.tfstates.platform.billing_subscription_role_delegations.tfstate }}.tfplan \
-a plan
rover logout
```
# Run rover ignite to generate the next level configuration files
To execute this step you need to login with on of the CAF maintainers:
{% for maintainer in config.platform_identity.caf_platform_maintainers %}
- {{ maintainer }}
{% endfor %}
```bash
rover login -t {{ config.platform_identity.tenant_name }}
rover ignite \
--playbook /tf/caf/starter/templates/platform/ansible.yaml \
-e base_templates_folder={{ base_templates_folder }} \
-e resource_template_folder={{resource_template_folder}} \
-e config_folder={{ config_folder }}
```
# Next steps
When you have successfully deployed the level0 components, you can move to the next step.
[Deploy the subscriptions](../../level1/subscriptions/readme.md)

Просмотреть файл

@ -0,0 +1,24 @@
subscription_billing_role_assignments = {
# Delegated accounts who can create subscriptions.
# Used by Gitops pipelines
subscription_creators = {
billing_account_name = "{{ config.caf_terraform.billing_subscription_role_delegations.billing_account_name }}"
enrollment_account_name = "{{ config.caf_terraform.billing_subscription_role_delegations.enrollment_account_name }}"
billing_role_definition_name = "Enrollment account subscription creator"
principals = {
azuread_service_principals = {
subscription_creation_platform = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
key = "subscription_creation_platform"
}
subscription_creation_landingzones = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
key = "subscription_creation_landingzones"
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,154 @@
- name: "[{{ level }}-{{ base_folder }}] - Set variables"
set_fact:
destination_path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
- name: "[{{ level }}-{{ base_folder }}] - Load variable for launchpad"
include_vars:
name: resources
dir: "{{config_folder}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "launchpad_credentials.yaml"
- debug:
msg: "{{resources}}"
- name: "[{{ level }}-{{ base_folder }}] Clean-up directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: absent
when: config.configuration_folders.platform.cleanup_destination | bool
- name: "[{{ level }}-{{ base_folder }}] Creates directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: directory
#
# resource_groups
#
- name: "[{{ level }}-{{ base_folder }}] - resource_groups"
when:
- resources.subscriptions[subscription_key].resource_groups is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/resource_groups.tfvars.j2"
#
# azuread_credentials
#
- name: "[{{ level }}-{{ subscription_key }}] - credentials - azuread_credentials"
when:
- resources.subscriptions[subscription_key].azuread_credentials is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_credentials.tfvars.j2"
#
# azuread_applications
#
- name: "[{{ level }}-{{ subscription_key }}] - credentials - azuread_applications"
when:
- resources.subscriptions[subscription_key].azuread_applications is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_applications.tfvars.j2"
#
# azuread_credential_policies
#
- name: "[{{ level }}-{{ subscription_key }}] - credentials - azuread_credential_policies"
when:
- resources.subscriptions[subscription_key].azuread_credential_policies is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_credential_policies.tfvars.j2"
#
# azuread_service_principals
#
- name: "[{{ level }}-{{ subscription_key }}] - credentials - azuread_service_principals"
when:
- resources.subscriptions[subscription_key].azuread_service_principals is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/azuread_service_principals.tfvars.j2"
#
# keyvaults
#
- name: "[{{ level }}-{{ subscription_key }}] - credentials - keyvaults"
when:
- resources.subscriptions[subscription_key].keyvaults is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/keyvaults.tfvars.j2"
#
# keyvault_access_policies
#
- name: "[{{ level }}-{{ subscription_key }}] - credentials - keyvault_access_policies"
when:
- resources.subscriptions[subscription_key].keyvault_access_policies is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/keyvault_access_policies.tfvars.j2"
- name: "[{{ level }}-{{ base_folder }}] generate configuration files."
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ level }}/{{ base_folder }}/*.tfvars.j2"
- name: "[{{ level }}-{{ base_folder }}] deploy."
when: boostrap_launchpad | bool
shell: |
/tf/rover/rover.sh \
-lz /tf/caf/landingzones/caf_solution \
-var-folder {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-target_subscription {{ config.caf_terraform.launchpad.subscription_id }} \
-tfstate {{ tfstates.launchpad_credentials.tfstate }} \
-launchpad \
-log-severity {{ config.gitops.rover_log_error }} \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-a apply
args:
warn: no
- debug:
msg: "{{ keyvaults.cred_subscription_creation_platform.vault_uri }}"
when: credentials_tfstate_exists.rc == 0
- name: "[{{ level }}-{{ base_folder }}] generate configuration files."
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ level }}/{{ base_folder }}/*.md"

Просмотреть файл

@ -0,0 +1,21 @@
# Store output attributes into keyvault secret
# Those values are used by the rover to connect the current remote state and
# identity the lower level
dynamic_keyvault_secrets = {
cred_ea_account_owner = { # ea account owner
account_owner_username = {
secret_name = "account-owner-username"
value = ""
}
account_owner_password = {
secret_name = "account-owner-password"
value = ""
}
tenant_id = {
secret_name = "tenant-id"
value = "{{ config.caf_terraform.launchpad.tenant_id }}" # {{ config.platform_identity.tenant_name }} Tenant
}
}
}

Просмотреть файл

@ -0,0 +1,12 @@
landingzone = {
backend_type = "{{ caf_terraform.launchpad.backend_type | default("azurerm")}}"
global_settings_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
level = "{{ config.tfstates.platform.launchpad.level }}"
key = "{{ config.tfstates.platform.launchpad_credentials.lz_key_name }}"
tfstates = {
{{ config.tfstates.platform.launchpad.lz_key_name }} = {
level = "current"
tfstate = "{{ config.tfstates.platform.launchpad.tfstate }}"
}
}
}

Просмотреть файл

@ -0,0 +1,34 @@
### Generate launchpad credentials
```bash
# For manual bootstrap:
# Login to the subscription {{ config.caf_terraform.launchpad.subscription_name }} with the user {{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner }}
rover login -t {{ config.platform_identity.tenant_name }}
rover \
{% if ((config.platform_identity.azuread_identity_mode != "logged_in_user") and (credentials_tfstate_exists.rc == 0)) %}
--impersonate-sp-from-keyvault-url {{ keyvaults.cred_identity.vault_uri }} \
{% endif %}
-lz /tf/caf/landingzones/caf_solution \
-var-folder {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-target_subscription {{ config.caf_terraform.launchpad.subscription_id }} \
-tfstate {{ config.tfstates.platform.launchpad_credentials.tfstate }} \
-launchpad \
-log-severity {{ config.gitops.rover_log_error }} \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-p ${TF_DATA_DIR}/{{ config.tfstates.platform.launchpad_credentials.tfstate }}.tfplan \
-a plan
rover logout
```
# Next steps
When you have successfully deployed the launchpad you can move to the next step.
[Deploy the billing subscription role delegation](../billing_subscription_role_delegations/readme.md)

Просмотреть файл

@ -0,0 +1,24 @@
#
# Services supported: subscriptions, storage accounts and resource groups
# Can assign roles to: AD groups, AD object ID, AD applications, Managed identities
#
role_mapping = {
built_in_role_mapping = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
resource_groups = {
sp_credentials = {
"Contributor" = {
azuread_groups = {
keys = [
"identity"
]
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
}
}
}
}
{% endif %}
}
}

Просмотреть файл

@ -0,0 +1,188 @@
- name: "[{{ level }}-{{ base_folder }}] - Set variables"
set_fact:
destination_path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
- name: "[{{ level }}-{{ base_folder }}] - Load variable for launchpad"
include_vars:
name: resources
dir: "{{config_folder}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "launchpad.yaml|level0.yaml|configuration.caf.platform.yaml"
- debug:
msg: "{{resources}}"
- name: "[{{ level }}-{{ base_folder }}] Clean-up directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: absent
when: config.configuration_folders.platform.cleanup_destination | bool
- name: "[{{ level }}-{{ base_folder }}] Creates directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: directory
#
# resource_groups
#
- name: "[{{ level }}-{{ base_folder }}] - resources - resource_groups"
when:
- resources.subscriptions[subscription_key].resource_groups is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/resource_groups.tfvars.j2"
- name: "[{{ level }}-{{ base_folder }}] launchpad"
ansible.builtin.template:
src: "{{ level }}/{{ base_folder }}/{{ item }}.tfvars.j2"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item }}.tfvars"
force: yes
loop:
- dynamic_secrets
- global_settings
- keyvaults
- landingzone
- role_mappings
- storage_accounts
- name: "[{{ level }}-{{ base_folder }}] Clean-up identity files"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item }}.tfvars"
state: absent
when: config.platform_identity.azuread_identity_mode == "logged_in_user"
loop:
- azuread_api_permissions
- azuread_applications
- azuread_group_members
- azuread_groups
- azuread_roles
- keyvault_policies
- service_principals
- name: "[{{ level }}-{{ base_folder }}] lauchpad - identity - service_principal"
ansible.builtin.template:
src: "{{ level }}/{{ base_folder }}/{{ item }}.tfvars.j2"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item }}.tfvars"
force: yes
when: config.platform_identity.azuread_identity_mode != 'logged_in_user'
loop:
- azuread_api_permissions
- azuread_applications
- azuread_group_members
- azuread_groups
- azuread_roles
- keyvault_policies
- service_principals
- name: "[{{ level }}-{{ base_folder }}] Deploy the launchpad"
when: boostrap_launchpad | bool | default(false)
shell: |
/tf/rover/rover.sh \
-lz /tf/caf/landingzones/caf_launchpad \
-var-folder {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-target_subscription {{ config.caf_terraform.launchpad.subscription_id }} \
-tfstate {{ config.tfstates.platform.launchpad.tfstate }} \
-log-severity {{ config.gitops.rover_log_error }} \
-launchpad \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-a apply
- name: "[{{ level }}-{{ base_folder }}] Get tfstate account name"
register: launchpad_storage_account
shell: |
az storage account list \
--subscription {{ config.caf_terraform.launchpad.subscription_id }} \
--query "[?tags.caf_tfstate=='{{ config.tfstates.platform.launchpad.level }}' && tags.caf_environment=='{{ config.caf_terraform.launchpad.caf_environment }}'].{name:name}[0]" -o json | jq -r .name
- debug:
msg: "{{launchpad_storage_account}}"
- name: "[{{ level }}-{{ base_folder }}] Get launchpad tfstate details"
register: launchpad_tfstate_exists
ignore_errors: true
shell: |
az storage blob download \
--name "{{ config.tfstates.platform.launchpad.tfstate }}" \
--account-name "{{ launchpad_storage_account.stdout | default('') }}" \
--container-name "{{ config.tfstates.platform.launchpad.workspace | default('tfstate') }}" \
--auth-mode "login" \
--file "~/.terraform.cache/launchpad/{{ config.tfstates.platform.launchpad.tfstate }}"
- name: "[{{ level }}-{{ base_folder }}] Get subscription_creation_landingzones details"
when:
- launchpad_tfstate_exists.rc == 0
- config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine
shell: "cat ~/.terraform.cache/launchpad/{{ config.tfstates.platform.launchpad.tfstate }}"
register: launchpad_tfstate
- name: "[{{ level }}-{{ base_folder }}] Get launchpad json data"
when:
- launchpad_tfstate_exists.rc == 0
- config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine
set_fact:
scljsondata: "{{ launchpad_tfstate.stdout | from_json }}"
- name: "[{{ level }}-{{ base_folder }}] set launchpad_azuread_groups"
when:
- launchpad_tfstate_exists.rc == 0
- config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine
set_fact:
launchpad_azuread_groups: "{{ scljsondata | json_query(path) }}"
vars:
path: 'outputs.objects.value.launchpad.azuread_groups'
- name: "[{{ level }}-{{ base_folder }}] Get credentials tfstate details"
register: credentials_tfstate_exists
ignore_errors: true
shell: |
az storage blob download \
--name "{{ config.tfstates.platform.launchpad_credentials.tfstate }}" \
--account-name "{{ launchpad_storage_account.stdout }}" \
--container-name "{{ config.tfstates.platform.launchpad.workspace | default('tfstate') }}" \
--auth-mode "login" \
--file "~/.terraform.cache/launchpad/{{ config.tfstates.platform.launchpad_credentials.tfstate }}"
- name: "[{{ level }}-{{ base_folder }}] Get launchpad_credentials details"
when: credentials_tfstate_exists.rc == 0
shell: "cat ~/.terraform.cache/launchpad/{{ config.tfstates.platform.launchpad_credentials.tfstate }}"
register: launchpad_credentials
- name: "[{{ level }}-{{ base_folder }}] Get launchpad_credentials json data"
when: credentials_tfstate_exists.rc == 0
set_fact:
credjsondata: "{{ launchpad_credentials.stdout | from_json }}"
- name: "[{{ level }}-{{ base_folder }}] set keyvaults"
when: credentials_tfstate_exists.rc == 0
set_fact:
keyvaults: "{{ credjsondata | json_query(path) }}"
vars:
path: 'outputs.objects.value.launchpad_credentials_rotation.keyvaults'
- name: "[{{ level }}-{{ base_folder }}] cleanup"
when: credentials_tfstate_exists.rc == 0
file:
path: "~/.terraform.cache/launchpad/{{ config.tfstates.platform.launchpad_credentials.tfstate }}"
state: absent
- name: "[{{ level }}-{{ base_folder }}] cleanup"
when: launchpad_tfstate_exists.rc == 0
file:
path: "~/.terraform.cache/launchpad/{{ config.tfstates.platform.launchpad.tfstate }}"
state: absent
# Update readme
- name: "[{{ level }}-{{ base_folder }}] launchpad - readme"
ansible.builtin.template:
src: "{{ level }}/{{ base_folder }}/readme.md"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/readme.md"
force: yes

Просмотреть файл

@ -0,0 +1,60 @@
azuread_api_permissions = {
level0 = {
microsoft_graph = {
resource_app_id = "00000003-0000-0000-c000-000000000000"
resource_access = {
AppRoleAssignment_ReadWrite_All = {
id = "06b708a9-e830-4db3-a914-8e69da51d44f"
type = "Role"
}
DelegatedPermissionGrant_ReadWrite_All = {
id = "8e8e4742-1d95-4f68-9d56-6ee75648c72a"
type = "Role"
}
DelegatedPermissionGrant_ReadWrite_All = {
id = "18a4783c-866b-4cc7-a460-3d5e5662c884"
type = "Role"
}
}
}
}
identity = {
active_directory_graph = {
resource_app_id = "00000002-0000-0000-c000-000000000000"
resource_access = {
Application_ReadWrite_OwnedBy = {
id = "824c81eb-e3f8-4ee6-8f6d-de7f50d565b7"
type = "Role"
}
Directory_ReadWrite_All = {
id = "78c8a3c8-a07e-4b9e-af1b-b5ccab50a175"
type = "Role"
}
}
}
microsoft_graph = {
resource_app_id = "00000003-0000-0000-c000-000000000000"
resource_access = {
AppRoleAssignment_ReadWrite_All = {
id = "06b708a9-e830-4db3-a914-8e69da51d44f"
type = "Role"
}
DelegatedPermissionGrant_ReadWrite_All = {
id = "8e8e4742-1d95-4f68-9d56-6ee75648c72a"
type = "Role"
}
GroupReadWriteAll = {
id = "62a82d76-70ea-41e2-9197-370581804d09"
type = "Role"
}
RoleManagement_ReadWrite_Directory = {
id = "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8"
type = "Role"
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
azuread_applications = {
level0 = {
application_name = "sp-caf-level0"
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
}
identity = {
application_name = "sp-caf-identity"
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
}
management = {
application_name = "sp-caf-management"
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
}
eslz = {
application_name = "sp-caf-eslz"
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
}
connectivity = {
application_name = "sp-caf-connectivity"
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
}
subscription_creation_platform = {
application_name = "sp-caf-subscription-creation-platform"
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
}
subscription_creation_landingzones = {
application_name = "sp-caf-subscription-creation-landingzones"
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
}
}

Просмотреть файл

@ -0,0 +1,34 @@
azuread_groups_membership = {
caf_platform_maintainers = {
{% if config.platform_identity.azuread_identity_mode == 'logged_in_user' %}
object_ids = {
logged_in = {
keys = ["user"]
}
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
members = {
user_principal_names = [
"{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner }}",
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user'%}
{% for user in config.platform_identity.caf_platform_maintainers %}
"{{ user }}",
{% endfor %}
{% endif %}
]
}
{% endif %}
}
caf_platform_contributors = {
members = {
user_principal_names = [
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' and config.platform_identity.caf_platform_contributors is defined %}
{% for user in config.platform_identity.caf_platform_contributors %}
"{{ user }}",
{% endfor %}
{% endif %}
]
}
}
}

Просмотреть файл

@ -0,0 +1,97 @@
azuread_groups = {
caf_platform_maintainers = {
name = "caf-platform-maintainers"
description = "High privileged group to run all CAF deployments from vscode. Can be used to bootstrap or troubleshoot deployments."
prevent_duplicate_name = true
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
}
caf_platform_contributors = {
name = "caf-platform-contributors"
description = "Can only execute terraform plans for level1 and level2. They can test platform improvements and propose PR."
prevent_duplicate_name = true
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
}
level0 = {
name = "caf-level0"
prevent_duplicate_name = true
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
members = {
azuread_service_principal_keys = ["level0"]
}
}
eslz = {
name = "caf-eslz"
prevent_duplicate_name = true
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
members = {
azuread_service_principal_keys = ["eslz"]
}
}
identity = {
name = "caf-identity"
prevent_duplicate_name = true
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
members = {
azuread_service_principal_keys = ["identity"]
}
}
management = {
name = "caf-management"
prevent_duplicate_name = true
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
members = {
azuread_service_principal_keys = ["management"]
}
}
connectivity = {
name = "caf-connectivity"
prevent_duplicate_name = true
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
members = {
azuread_service_principal_keys = ["connectivity"]
}
}
subscription_creation_platform = {
name = "caf-subscription_creation_platform"
prevent_duplicate_name = true
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
members = {
azuread_service_principal_keys = ["subscription_creation_platform"]
}
}
subscription_creation_landingzones = {
name = "caf-subscription_creation_landingzones"
prevent_duplicate_name = true
{% if config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id is defined %}
owners = ["{{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner_object_id }}"] // EA account
{% endif %}
members = {
azuread_service_principal_keys = ["subscription_creation_landingzones"]
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
#
# Available roles:
# az rest --method Get --uri https://graph.microsoft.com/v1.0/directoryRoleTemplates -o json | jq -r .value[].displayName
#
azuread_roles = {
azuread_service_principals = {
level0 = {
roles = [
"Privileged Role Administrator",
"Application Administrator",
"Groups Administrator"
]
}
identity = {
roles = [
"User Administrator",
"Application Administrator",
"Groups Administrator"
]
}
subscription_creation_landingzones = {
roles = [
"Application Administrator",
"Groups Administrator"
]
}
}
}

Просмотреть файл

@ -0,0 +1,67 @@
# Store output attributes into keyvault secret
# Those values are used by the rover to connect the current remote state and
# identity the lower level
dynamic_keyvault_secrets = {
level0 = {
subscription_id = {
output_key = "client_config"
attribute_key = "subscription_id"
secret_name = "subscription-id"
}
tenant_id = {
output_key = "client_config"
attribute_key = "tenant_id"
secret_name = "tenant-id"
}
}
level1 = {
lower_stg = {
output_key = "storage_accounts"
resource_key = "level0"
attribute_key = "name"
secret_name = "lower-storage-account-name"
}
lower_rg = {
output_key = "resource_groups"
resource_key = "level0"
attribute_key = "name"
secret_name = "lower-resource-group-name"
}
subscription_id = {
output_key = "client_config"
attribute_key = "subscription_id"
secret_name = "subscription-id"
}
tenant_id = {
output_key = "client_config"
attribute_key = "tenant_id"
secret_name = "tenant-id"
}
}
level2 = {
lower_stg = {
output_key = "storage_accounts"
resource_key = "level1"
attribute_key = "name"
secret_name = "lower-storage-account-name"
}
lower_rg = {
output_key = "resource_groups"
resource_key = "level1"
attribute_key = "name"
secret_name = "lower-resource-group-name"
}
subscription_id = {
output_key = "client_config"
attribute_key = "subscription_id"
secret_name = "subscription-id"
}
tenant_id = {
output_key = "client_config"
attribute_key = "tenant_id"
secret_name = "tenant-id"
}
}
}

Просмотреть файл

@ -0,0 +1,39 @@
# naming convention settings
# for more settings on naming convention, please refer to the provider documentation: https://github.com/aztfmod/terraform-provider-azurecaf
#
# passthrough means the default CAF naming convention is not applied and you are responsible
# of the unicity of the names you are giving. the CAF provider will clear out
passthrough = {{ config.caf_terraform.naming_convention.passthrough | string | lower }}
# adds random chars at the end of the names produced by the provider
# Do not change the following values once the launchpad deployed.
# Enable tag inheritance (can be changed)
inherit_tags = {{ config.caf_terraform.naming_convention.inherit_tags | string | lower }}
# When passthrough is set to false, define the number of random characters to add to the names
random_length = {{ config.caf_terraform.naming_convention.random_length }}
# Default region. When not set to a resource it will use that value
default_region = "{{ config.caf_terraform.launchpad.default_region_key }}"
# You can reference the regions by using region1, region2 or set your own keys
regions = {
{% for key in config.caf_terraform.launchpad.regions.keys() %}
{{ key }} = "{{ config.caf_terraform.launchpad.regions[key].name }}"
{% endfor %}
}
# Rover will adjust some tags to enable the discovery of the launchpad.
launchpad_key_names = {
keyvault = "level0"
tfstates = [
"level0",
"level1",
"level2"
]
}
# Global tags
tags = {
ApplicationOwner = "sre"
BusinessUnit = "sre"
}

Просмотреть файл

@ -0,0 +1,64 @@
keyvault_access_policies = {
# A maximum of 16 access policies per keyvault
level0 = {
sp_level0 = {
azuread_group_key = "level0"
secret_permissions = ["Set", "Get", "List", "Delete", "Purge", "Recover"]
}
identity = {
azuread_group_key = "identity"
secret_permissions = ["Get"]
}
}
# A maximum of 16 access policies per keyvault
level1 = {
sp_level0 = {
# Allow level1 devops agent to be managed from agent pool level0
azuread_group_key = "level0"
secret_permissions = ["Set", "Get", "List", "Delete", "Purge", "Recover"]
}
identity = {
azuread_group_key = "identity"
secret_permissions = ["Get"]
}
management = {
azuread_group_key = "management"
secret_permissions = ["Get"]
}
eslz = {
azuread_group_key = "eslz"
secret_permissions = ["Get"]
}
subscription_creation_platform = {
azuread_group_key = "subscription_creation_platform"
secret_permissions = ["Get"]
}
}
# A maximum of 16 access policies per keyvault
level2 = {
sp_level0 = {
azuread_group_key = "level0"
secret_permissions = ["Set", "Get", "List", "Delete", "Purge", "Recover"]
}
connectivity = {
azuread_group_key = "connectivity"
secret_permissions = ["Get"]
}
identity = {
azuread_group_key = "identity"
secret_permissions = ["Get"]
}
management = {
azuread_group_key = "management"
secret_permissions = ["Get"]
}
{% if config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine %}
subscription_creation_landingzones = {
azuread_group_key = "subscription_creation_landingzones"
secret_permissions = ["Get"]
}
{% endif %}
}
}

Просмотреть файл

@ -0,0 +1,89 @@
keyvaults = {
level0 = {
name = "{{ resources.subscriptions[subscription_key].keyvaults.level0.name }}"
resource_group_key = "{{ resources.subscriptions[subscription_key].keyvaults.level0.resource_group_key }}"
sku_name = "{{ config.platform_core_setup.sku.keyvault}}"
tags = {
tfstate = "level0"
environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
caf_tfstate = "level0"
caf_environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
}
creation_policies = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
caf_platform_maintainers = {
azuread_group_key = "caf_platform_maintainers"
secret_permissions = ["Set", "Get", "List", "Delete", "Purge", "Recover"]
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode == 'logged_in_user' %}
logged_in_user = {
# if the key is set to "logged_in_user" add the user running terraform in the keyvault policy
# More examples in /examples/keyvault
secret_permissions = ["Set", "Get", "List", "Delete", "Purge", "Recover"]
}
{% endif %}
}
}
level1 = {
name = "{{ resources.subscriptions[subscription_key].keyvaults.level1.name }}"
resource_group_key = "{{ resources.subscriptions[subscription_key].keyvaults.level1.resource_group_key }}"
sku_name = "{{ config.platform_core_setup.sku.keyvault}}"
tags = {
tfstate = "level1"
environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
caf_tfstate = "level1"
caf_environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
}
creation_policies = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
caf_platform_maintainers = {
azuread_group_key = "caf_platform_maintainers"
secret_permissions = ["Set", "Get", "List", "Delete", "Purge", "Recover"]
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode == 'logged_in_user' %}
logged_in_user = {
# if the key is set to "logged_in_user" add the user running terraform in the keyvault policy
# More examples in /examples/keyvault
secret_permissions = ["Set", "Get", "List", "Delete", "Purge", "Recover"]
}
{% endif %}
}
}
level2 = {
name = "{{ resources.subscriptions[subscription_key].keyvaults.level2.name }}"
resource_group_key = "{{ resources.subscriptions[subscription_key].keyvaults.level2.resource_group_key }}"
sku_name = "{{ config.platform_core_setup.sku.keyvault}}"
tags = {
tfstate = "level2"
environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
caf_tfstate = "level2"
caf_environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
}
creation_policies = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
caf_platform_maintainers = {
azuread_group_key = "caf_platform_maintainers"
secret_permissions = ["Set", "Get", "List", "Delete", "Purge", "Recover"]
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode == 'logged_in_user' %}
logged_in_user = {
# if the key is set to "logged_in_user" add the user running terraform in the keyvault policy
# More examples in /examples/keyvault
secret_permissions = ["Set", "Get", "List", "Delete", "Purge", "Recover"]
}
{% endif %}
}
}
}

Просмотреть файл

@ -0,0 +1,5 @@
landingzone = {
backend_type = "{{ caf_terraform.launchpad.backend_type | default("azurerm")}}"
level = "{{ config.tfstates.platform.launchpad.level }}"
key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
}

Просмотреть файл

@ -0,0 +1,79 @@
# Launchpad - {{ config.caf_terraform.launchpad.caf_environment }}
## Pre-requisites
This scenario requires the following privileges:
| Component | Privileges |
|--------------------|--------------------|
| Active Directory | None |
| Azure subscription | Subscription owner |
## Deployment
{% if config.caf_terraform.billing_subscription_role_delegations is defined %}
### Pre-requisite
Elevate your credentials to the tenant root level to have enough privileges to create the management group hierarchy.
```bash
{% if config.caf_terraform.billing_subscription_role_delegations.enable %}
# Login to the subscription {{ config.caf_terraform.launchpad.subscription_name }} with the user {{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner }}
{% else %}
# Login to the subscription {{ config.caf_terraform.launchpad.subscription_name }} with an account owner.
{% endif %}
rover login -t {{ config.platform_identity.tenant_name }}
az rest --method post --url "/providers/Microsoft.Authorization/elevateAccess?api-version=2016-07-01"
```
{% endif %}
### Launchpad
```bash
{% if config.caf_terraform.billing_subscription_role_delegations is defined %}
{% if config.caf_terraform.billing_subscription_role_delegations.enable %}
# Login to the subscription {{ config.caf_terraform.launchpad.subscription_name }} with the user {{ config.caf_terraform.billing_subscription_role_delegations.azuread_user_ea_account_owner }}
{% else %}
# Login to the subscription {{ config.caf_terraform.launchpad.subscription_name }} with an account owner.
{% endif %}
{% endif %}
rover login -t {{ config.platform_identity.tenant_name }} -s {{ config.caf_terraform.launchpad.subscription_id }}
cd /tf/caf/landingzones
git fetch origin
git checkout {{ config.gitops.caf_landingzone_branch }}
rover \
-lz /tf/caf/landingzones/caf_launchpad \
-var-folder {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-target_subscription {{ config.caf_terraform.launchpad.subscription_id }} \
-tfstate {{ config.tfstates.platform.launchpad.tfstate }} \
-log-severity {{ config.gitops.rover_log_error }} \
-launchpad \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-p ${TF_DATA_DIR}/{{ config.tfstates.platform.launchpad.tfstate }}.tfplan \
-a plan
```
## Architecture diagram
![Launchpad demo](../../../../../../documentation/img/launchpad-demo.PNG)
# Next steps
When you have successfully deployed the launchpad you can move to the next step.
{% if config.caf_terraform.billing_subscription_role_delegations is defined %}
{% if config.caf_terraform.billing_subscription_role_delegations.enable %}
[Deploy the credentials landing zone](../credentials/readme.md)
{% else %}
[Deploy the management services](../../level1/management/readme.md)
{% endif %}
{% else %}
[Deploy the management services](../../level1/management/readme.md)
{% endif %}

Просмотреть файл

@ -0,0 +1,198 @@
#
# Services supported: subscriptions, storage accounts and resource groups
# Can assign roles to: AD groups, AD object ID, AD applications, Managed identities
#
role_mapping = {
built_in_role_mapping = {
{% if config.platform_core_setup %}
management_group = {
root = {
"User Access Administrator" = {
{% if config.platform_identity.azuread_identity_mode == 'logged_in_user' %}
logged_in = {
keys = ["user"]
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
azuread_groups = {
keys = ["level0"]
}
{% endif %}
}
"Management Group Contributor" = {
{% if config.platform_identity.azuread_identity_mode == 'logged_in_user' %}
logged_in = {
keys = ["user"]
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
azuread_groups = {
keys = ["eslz", "caf_platform_maintainers"]
}
{% endif %}
}
"Owner" = {
{% if config.platform_identity.azuread_identity_mode == 'logged_in_user' %}
logged_in = {
keys = ["user"]
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
azuread_groups = {
keys = ["eslz", "caf_platform_maintainers"]
}
{% endif %}
}
}
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
subscriptions = {
logged_in_subscription = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Owner" = {
azuread_groups = {
keys = ["level0", "caf_platform_maintainers", "subscription_creation_platform"]
}
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Reader" = {
azuread_groups = {
keys = ["identity"]
}
}
{% endif %}
}
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
resource_groups = {
level0 = {
"Reader" = {
azuread_groups = {
keys = [
"identity",
"subscription_creation_platform"
]
}
}
}
level1 = {
"Reader" = {
azuread_groups = {
keys = [
"identity",
"management",
"eslz",
"subscription_creation_platform"
]
}
}
}
level2 = {
"Reader" = {
azuread_groups = {
keys = [
"identity",
"connectivity",
"management",
"subscription_creation_landingzones"
]
}
}
}
}
{% endif %}
storage_accounts = {
level0 = {
"Storage Blob Data Contributor" = {
logged_in = {
keys = ["user"]
}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
azuread_groups = {
keys = ["level0", "caf_platform_maintainers", "identity"]
}
{% endif %}
}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Storage Blob Data Reader" = {
azuread_groups = {
keys = [
"management",
"eslz",
"subscription_creation_platform"
]
}
}
{% endif %}
}
level1 = {
"Storage Blob Data Contributor" = {
logged_in = {
keys = ["user"]
}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
azuread_groups = {
keys = [
"caf_platform_maintainers",
"identity",
"management",
"eslz",
"subscription_creation_platform"
]
}
{% endif %}
}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Storage Blob Data Reader" = {
azuread_groups = {
keys = [
"connectivity",
{% if config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine %}
"level0"
{% endif %}
]
}
}
{% endif %}
}
level2 = {
"Storage Blob Data Contributor" = {
logged_in = {
keys = ["user"]
}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
azuread_groups = {
keys = [
"identity",
"connectivity",
"management",
"caf_platform_maintainers",
{% if config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine %}
"level0"
{% endif %}
]
}
}
{% endif %}
"Storage Blob Data Reader" = {
azuread_groups = {
keys = [
{% if config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine %}
"subscription_creation_landingzones"
{% endif %}
]
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
azuread_service_principals = {
# Manage the deployment of the level0
level0 = {
azuread_application = {
key = "level0"
}
}
# Manage the deployment of Enterprise Scale
eslz = {
azuread_application = {
key = "eslz"
}
}
# Manage the deployment of the connectivity services
connectivity = {
azuread_application = {
key = "connectivity"
}
}
# Manage the deployment of the shared services
management = {
azuread_application = {
key = "management"
}
}
# Manage the deployment of the identity services
identity = {
azuread_application = {
key = "identity"
}
}
# Has delegation to create platform subscriptions
subscription_creation_platform = {
azuread_application = {
key = "subscription_creation_platform"
}
}
# Has delegation to create landingzone subscriptions
subscription_creation_landingzones = {
azuread_application = {
key = "subscription_creation_landingzones"
}
}
}

Просмотреть файл

@ -0,0 +1,87 @@
storage_accounts = {
level0 = {
name = "{{ resources.subscriptions[subscription_key].storage_accounts.level0.name }}"
resource_group_key = "{{ resources.subscriptions[subscription_key].storage_accounts.level0.resource_group_key }}"
account_kind = "BlobStorage"
account_tier = "Standard"
account_replication_type = "{{ config.caf_terraform.launchpad.account_replication_type }}"
tags = {
## Those tags must never be changed after being set as they are used by the rover to locate the launchpad and the tfstates.
# Only adjust the environment value at creation time
tfstate = "level0"
environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
launchpad = "launchpad"
caf_environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
caf_launchpad = "launchpad"
caf_tfstate = "level0"
##
}
blob_properties = {
versioning_enabled = {{ config.caf_terraform.launchpad.blob_versioning_enabled | string | lower | default('true') }}
container_delete_retention_policy = {{ config.caf_terraform.launchpad.container_delete_retention_policy | default(7) }}
delete_retention_policy = {{ config.caf_terraform.launchpad.delete_retention_policy | default(7) }}
}
containers = {
{{ config.tfstates.platform.launchpad.workspace | default('tfstate') }} = {
name = "{{ config.tfstates.platform.launchpad.workspace | default('tfstate') }}"
}
}
}
level1 = {
name = "{{ resources.subscriptions[subscription_key].storage_accounts.level1.name }}"
resource_group_key = "{{ resources.subscriptions[subscription_key].storage_accounts.level1.resource_group_key }}"
account_kind = "BlobStorage"
account_tier = "Standard"
account_replication_type = "{{ config.caf_terraform.launchpad.account_replication_type }}"
tags = {
# Those tags must never be changed while set as they are used by the rover to locate the launchpad and the tfstates.
tfstate = "level1"
environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
launchpad = "launchpad"
caf_environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
caf_launchpad = "launchpad"
caf_tfstate = "level1"
}
blob_properties = {
versioning_enabled = {{ config.caf_terraform.launchpad.blob_versioning_enabled | string | lower | default('true') }}
container_delete_retention_policy = {{ config.caf_terraform.launchpad.container_delete_retention_policy | default(7) }}
delete_retention_policy = {{ config.caf_terraform.launchpad.delete_retention_policy | default(7) }}
}
containers = {
{{ config.tfstates.platform.launchpad.workspace | default('tfstate') }} = {
name = "{{ config.tfstates.platform.launchpad.workspace | default('tfstate') }}"
}
}
}
level2 = {
name = "{{ resources.subscriptions[subscription_key].storage_accounts.level2.name }}"
resource_group_key = "{{ resources.subscriptions[subscription_key].storage_accounts.level2.resource_group_key }}"
account_kind = "BlobStorage"
account_tier = "Standard"
account_replication_type = "{{ config.caf_terraform.launchpad.account_replication_type }}"
tags = {
# Those tags must never be changed while set as they are used by the rover to locate the launchpad and the tfstates.
tfstate = "level2"
environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
launchpad = "launchpad"
caf_environment = "{{ config.caf_terraform.launchpad.caf_environment }}"
caf_launchpad = "launchpad"
caf_tfstate = "level2"
}
blob_properties = {
versioning_enabled = {{ config.caf_terraform.launchpad.blob_versioning_enabled | string | lower | default('true') }}
container_delete_retention_policy = {{ config.caf_terraform.launchpad.container_delete_retention_policy | default(7) }}
delete_retention_policy = {{ config.caf_terraform.launchpad.delete_retention_policy | default(7) }}
}
containers = {
{{ config.tfstates.platform.launchpad.workspace | default('tfstate') }} = {
name = "{{ config.tfstates.platform.launchpad.workspace | default('tfstate') }}"
}
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
# Cloud Adoption Framework landing zones for Terraform - Starter template
Place here your production environment configuration files.

Просмотреть файл

@ -0,0 +1,58 @@
- name: "{{ level }}-{{ base_folder }} | Clean-up base directory"
shell: |
rm -rf "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
when:
- config.platform_core_setup.enterprise_scale.enable
- config.platform_core_setup.enterprise_scale.clean_up_destination_folder
- name: "{{ level }}-{{ base_folder }} | Creates directory structure"
shell: mkdir -p "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/lib/{{ item.path }}"
with_filetree: "{{ level }}/{{ base_folder }}/lib/{{ config.platform_core_setup.enterprise_scale.private_lib.version_to_deploy }}"
when: item.state == 'directory'
- name: "{{ level }}-{{ base_folder }} | Tfvars"
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ level }}/{{ base_folder }}/*.j2"
- "{{ level }}/{{ base_folder }}/*.md"
- name: "{{ level }}-{{ base_folder }} | Lib - archetypes - built-in"
ansible.builtin.template:
src: "{{ base_templates_folder }}/{{ level }}/eslz/lib/{{ config.platform_core_setup.enterprise_scale.private_lib.version_to_deploy }}/archetype_definitions/archetype_definition_template.json.j2"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/lib/archetype_definitions/archetype_definition_{{ mg.archetype_definitions[item].archetype_id }}.json"
force: yes
loop: "{{ mg.archetype_definitions.keys() }}"
loop_control:
loop_var: item
- name: "{{ level }}-{{ base_folder }} | Lib - archetypes - custom"
when:
- mg_custom.archetype_definitions is defined
ansible.builtin.template:
src: "{{ base_templates_folder }}/{{ level }}/eslz/lib/{{ config.platform_core_setup.enterprise_scale.private_lib.version_to_deploy }}/archetype_definitions/custom_landing_zone_template.json.j2"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/lib/archetype_definitions/archetype_definition_{{ mg_custom.archetype_definitions[item].archetype_id }}.json"
force: yes
loop: "{{ mg_custom.archetype_definitions.keys() }}"
loop_control:
loop_var: item
- name: "{{ level }}-{{ base_folder }} | Lib"
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/lib/{{ item.path }}"
force: yes
with_filetree: "{{ config_folder }}/eslz/lib"
when: item.state == 'file' and config.platform_core_setup.enterprise_scale.update_lib_folder
- name: "{{ level }}-{{ base_folder }} | overrides"
when:
- mg_custom.archetype_definitions is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ level }}/{{ base_folder }}/lib/{{ config.platform_core_setup.enterprise_scale.private_lib.version_to_deploy }}/*.tfvars.j2"

Просмотреть файл

@ -0,0 +1,28 @@
landingzone = {
backend_type = "{{ caf_terraform.launchpad.backend_type | default("azurerm")}}"
global_settings_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
level = "level1"
key = "{{ config.tfstates.platform.eslz.lz_key_name }}"
tfstates = {
{{ config.tfstates.platform.launchpad.lz_key_name }} = {
level = "lower"
tfstate = "{{ config.tfstates.platform.launchpad.tfstate }}"
}
{{ config.tfstates.platform.management.lz_key_name }} = {
level = "current"
tfstate = "{{ config.tfstates.platform.management.tfstate }}"
}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
{{ config.tfstates.platform.platform_subscriptions.lz_key_name }} = {
level = "current"
tfstate = "{{ config.tfstates.platform.platform_subscriptions.tfstate }}"
}
{% endif %}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
{{ config.tfstates.platform.identity.lz_key_name }} = {
level = "current"
tfstate = "{{ config.tfstates.platform.identity.tfstate }}"
}
{% endif %}
}
}

Просмотреть файл

@ -0,0 +1,7 @@
library_path = "../../../../{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/lib"
root_id = "{{ config.platform_core_setup.enterprise_scale.management_group_prefix }}"
root_name = "{{ config.platform_core_setup.enterprise_scale.management_group_name }}"
deploy_core_landing_zones = {{ config.platform_core_setup.enterprise_scale.deploy_core_landing_zones | string | lower }}
{% if (config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine | default(false)) and config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
reconcile_vending_subscriptions = true
{% endif %}

Просмотреть файл

@ -0,0 +1,97 @@
archetype_config_overrides = {
root = {
archetype_id = "root"
parameters = {
"Deny-Resource-Locations" = {
"listOfAllowedLocations" = {
value = [
"{{ config.caf_terraform.launchpad.regions.region1.name }}",
"{{ config.caf_terraform.launchpad.regions.region2.name }}"
]
}
}
"Deny-RSG-Locations" = {
"listOfAllowedLocations" = {
value = [
"{{ config.caf_terraform.launchpad.regions.region1.name }}",
"{{ config.caf_terraform.launchpad.regions.region2.name }}"
]
}
}
"Deploy-Resource-Diag" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_region1"
attribute_key = "id"
}
"profileName" = {
value = "eslz-diagnostic-log"
}
}
"Deploy-VM-Monitoring" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_region1"
attribute_key = "id"
}
}
"Deploy-VMSS-Monitoring" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_region1"
attribute_key = "id"
}
}
}
access_control = {}
} //root
landing-zones = {
archetype_id = "landingzone"
parameters = {}
access_control = {}
}
platform = {
archetype_id = "platform"
parameters = {}
access_control = {}
}
connectivity = {
archetype_id = "platform_connectivity"
parameters = {}
access_control = {}
}
identity = {
archetype_id = "platform_identity"
parameters = {}
access_control = {}
}
management = {
archetype_id = "platform_management"
parameters = {}
access_control = {}
}
decommissioned = {
archetype_id = "es_decommissioned"
parameters = {}
access_control = {}
}
sandboxes = {
archetype_id = "es_sandboxes"
parameters = {}
access_control = {}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default archetypes
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/archetype_definitions

Просмотреть файл

@ -0,0 +1,16 @@
{
"landingzone": {
"policy_assignments": [
],
"policy_definitions": [
],
"policy_set_definitions": [
],
"role_definitions": [
],
"archetype_config": {
"parameters": {},
"access_control": {}
}
}
}

Просмотреть файл

@ -0,0 +1,16 @@
{
"landingzone_corp": {
"policy_assignments": [
],
"policy_definitions": [
],
"policy_set_definitions": [
],
"role_definitions": [
],
"archetype_config": {
"parameters": {},
"access_control": {}
}
}
}

Просмотреть файл

@ -0,0 +1,29 @@
{
"landingzone_online": {
"policy_assignments": [
"Deploy-ASC-Defender"
],
"policy_definitions": [
],
"policy_set_definitions": [
],
"role_definitions": [
],
"archetype_config": {
"parameters": {
"Deploy-ASC-Defender": {
"pricingTierAppServices": "Free",
"pricingTierVMs": "Free",
"pricingTierSqlServers": "Standard",
"pricingTierStorageAccounts": "Standard",
"pricingTierContainerRegistry": "Free",
"pricingTierKeyVaults": "Standard",
"pricingTierKubernetesService": "Free",
"pricingTierDns": "Free",
"pricingTierArm": "Free"
}
},
"access_control": {}
}
}
}

Просмотреть файл

@ -0,0 +1,29 @@
{
"platform": {
"policy_assignments": [
"Deploy-ASC-Defender"
],
"policy_definitions": [
],
"policy_set_definitions": [
],
"role_definitions": [
],
"archetype_config": {
"parameters": {
"Deploy-ASC-Defender": {
"pricingTierAppServices": "Free",
"pricingTierVMs": "Free",
"pricingTierSqlServers": "Standard",
"pricingTierStorageAccounts": "Standard",
"pricingTierContainerRegistry": "Free",
"pricingTierKeyVaults": "Standard",
"pricingTierKubernetesService": "Free",
"pricingTierDns": "Free",
"pricingTierArm": "Free"
}
},
"access_control": {}
}
}
}

Просмотреть файл

@ -0,0 +1,16 @@
{
"platform_connectivity": {
"policy_assignments": [
],
"policy_definitions": [
],
"policy_set_definitions": [
],
"role_definitions": [
],
"archetype_config": {
"parameters": {},
"access_control": {}
}
}
}

Просмотреть файл

@ -0,0 +1,16 @@
{
"platform_identity": {
"policy_assignments": [
],
"policy_definitions": [
],
"policy_set_definitions": [
],
"role_definitions": [
],
"archetype_config": {
"parameters": {},
"access_control": {}
}
}
}

Просмотреть файл

@ -0,0 +1,16 @@
{
"platform_management": {
"policy_assignments": [
],
"policy_definitions": [
],
"policy_set_definitions": [
],
"role_definitions": [
],
"archetype_config": {
"parameters": {},
"access_control": {}
}
}
}

Просмотреть файл

@ -0,0 +1,121 @@
{
"root": {
"policy_assignments": [
"Deploy-Resource-Diag",
"Deny-Resource-Locations",
"Deny-RSG-Locations"
],
"policy_definitions": [
"Append-KV-SoftDelete",
"Deny-AA-child-resources",
"Deny-AppGW-Without-WAF",
"Deny-Private-DNS-Zones",
"Deny-PublicEndpoint-Aks",
"Deny-PublicEndpoint-CosmosDB",
"Deny-PublicEndpoint-KeyVault",
"Deny-PublicEndpoint-MariaDB",
"Deny-PublicEndpoint-MySQL",
"Deny-PublicEndpoint-PostgreSql",
"Deny-PublicEndpoint-Sql",
"Deny-PublicEndpoint-Storage",
"Deny-PublicIP",
"Deny-RDP-From-Internet",
"Deny-Subnet-Without-Nsg",
"Deny-Subnet-Without-Udr",
"Deny-VNET-Peer-Cross-Sub",
"Deny-VNet-Peering",
"Deploy-ASC-Standard",
"Deploy-Budget",
"Deploy-DDoSProtection",
"Deploy-Diagnostics-AA",
"Deploy-Diagnostics-ACI",
"Deploy-Diagnostics-ACR",
"Deploy-Diagnostics-ActivityLog",
"Deploy-Diagnostics-AKS",
"Deploy-Diagnostics-AnalysisService",
"Deploy-Diagnostics-ApiForFHIR",
"Deploy-Diagnostics-APIMgmt",
"Deploy-Diagnostics-ApplicationGateway",
"Deploy-Diagnostics-Batch",
"Deploy-Diagnostics-CDNEndpoints",
"Deploy-Diagnostics-CognitiveServices",
"Deploy-Diagnostics-CosmosDB",
"Deploy-Diagnostics-Databricks",
"Deploy-Diagnostics-DataExplorerCluster",
"Deploy-Diagnostics-DataFactory",
"Deploy-Diagnostics-DataLakeStore",
"Deploy-Diagnostics-DLAnalytics",
"Deploy-Diagnostics-EventGridSub",
"Deploy-Diagnostics-EventGridSystemTopic",
"Deploy-Diagnostics-EventGridTopic",
"Deploy-Diagnostics-EventHub",
"Deploy-Diagnostics-ExpressRoute",
"Deploy-Diagnostics-Firewall",
"Deploy-Diagnostics-FrontDoor",
"Deploy-Diagnostics-Function",
"Deploy-Diagnostics-HDInsight",
"Deploy-Diagnostics-iotHub",
"Deploy-Diagnostics-KeyVault",
"Deploy-Diagnostics-LoadBalancer",
"Deploy-Diagnostics-LogicAppsISE",
"Deploy-Diagnostics-LogicAppsWF",
"Deploy-Diagnostics-MariaDB",
"Deploy-Diagnostics-MediaService",
"Deploy-Diagnostics-MlWorkspace",
"Deploy-Diagnostics-MySQL",
"Deploy-Diagnostics-NetworkSecurityGroups",
"Deploy-Diagnostics-NIC",
"Deploy-Diagnostics-PostgreSQL",
"Deploy-Diagnostics-PowerBIEmbedded",
"Deploy-Diagnostics-PublicIP",
"Deploy-Diagnostics-RecoveryVault",
"Deploy-Diagnostics-RedisCache",
"Deploy-Diagnostics-Relay",
"Deploy-Diagnostics-SearchServices",
"Deploy-Diagnostics-ServiceBus",
"Deploy-Diagnostics-SignalR",
"Deploy-Diagnostics-SQLDBs",
"Deploy-Diagnostics-SQLElasticPools",
"Deploy-Diagnostics-SQLMI",
"Deploy-Diagnostics-StreamAnalytics",
"Deploy-Diagnostics-TimeSeriesInsights",
"Deploy-Diagnostics-TrafficManager",
"Deploy-Diagnostics-VirtualNetwork",
"Deploy-Diagnostics-VM",
"Deploy-Diagnostics-VMSS",
"Deploy-Diagnostics-VNetGW",
"Deploy-Diagnostics-WebServerFarm",
"Deploy-Diagnostics-Website",
"Deploy-Diagnostics-WVDAppGroup",
"Deploy-Diagnostics-WVDHostPools",
"Deploy-Diagnostics-WVDWorkspace",
"Deploy-DNSZoneGroup-For-Blob-PrivateEndpoint",
"Deploy-DNSZoneGroup-For-File-PrivateEndpoint",
"Deploy-DNSZoneGroup-For-KeyVault-PrivateEndpoint",
"Deploy-DNSZoneGroup-For-Queue-PrivateEndpoint",
"Deploy-DNSZoneGroup-For-Sql-PrivateEndpoint",
"Deploy-DNSZoneGroup-For-Table-PrivateEndpoint",
"Deploy-FirewallPolicy",
"Deploy-LA-Config",
"Deploy-Log-Analytics",
"Deploy-Nsg-FlowLogs-to-LA",
"Deploy-Sql-AuditingSettings",
"Deploy-Sql-SecurityAlertPolicies",
"Deploy-Sql-Tde",
"Deploy-Sql-vulnerabilityAssessments",
"Deploy-Windows-DomainJoin"
],
"policy_set_definitions": [
"Deny-PublicEndpoints",
"Deploy-Diag-LogAnalytics",
"Deploy-Sql-Security"
],
"role_definitions": [
],
"archetype_config": {
"parameters": {},
"access_control": {
}
}
}
}

Просмотреть файл

@ -0,0 +1,24 @@
custom_landing_zones = {
{{ config.eslz.root_id }}-corp = {
display_name = "Corp"
parent_management_group_id = "{{ config.eslz.root_id }}-landing-zones"
archetype_config = {
archetype_id = "landingzone_corp"
parameters = {}
access_control = {}
}
subscriptions = {}
subscription_ids = []
}
{{ config.eslz.root_id }}-online = {
display_name = "Online"
parent_management_group_id = "{{ config.eslz.root_id }}-landing-zones"
archetype_config = {
archetype_id = "landingzone_online"
parameters = {}
access_control = {}
}
subscriptions = {}
subscription_ids = []
}
}

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default policy assignments
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/policy_assignments

Просмотреть файл

@ -0,0 +1,18 @@
{
"name": "CAF-Security-Benchmark",
"type": "Microsoft.Authorization/policyAssignments",
"apiVersion": "2019-09-01",
"properties": {
"description": "The Azure Security Benchmark initiative represents the policies and controls implementing security recommendations defined in Azure Security Benchmark v2, see https://aka.ms/azsecbm. This also serves as the Azure Security Center default policy initiative. You can directly assign this initiative, or manage its policies and compliance results within Azure Security Center.",
"displayName": "Azure Security BenchMark",
"notScopes": [],
"parameters": {},
"policyDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-d0c9-4c3d-847f-89da613e70a8",
"scope": "${current_scope_resource_id}",
"enforcementMode": true
},
"location": "${default_location}",
"identity": {
"type": "None"
}
}

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default policy definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/policy_definitions

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default policy set definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/policy_set_definitions

Просмотреть файл

@ -0,0 +1,11 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default role defitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/role_definitions

Просмотреть файл

@ -0,0 +1,208 @@
archetype_config_overrides = {
root = {
archetype_id = "root"
parameters = {
"Allowed-Locations" = {
"listOfAllowedLocations" = {
values = [
{% for key in config.caf_terraform.launchpad.regions.keys() %}
"{{ config.caf_terraform.launchpad.regions[key].name }}",
{% endfor %}
]
}
}
"Deny-RSG-Locations" = {
"listOfAllowedLocations" = {
values = [
{% for key in config.caf_terraform.launchpad.regions.keys() %}
"{{ config.caf_terraform.launchpad.regions[key].name }}",
{% endfor %}
]
}
}
"Deploy-Resource-Diag" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
"profileName" = {
value = "eslz-diagnostic-log"
}
}
"Deploy-AzActivity-Log" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
{% if "VM" in config.platform_management.enable_monitoring %}
"Deploy-VM-Monitoring" = {
"logAnalytics_1" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
{% endif %}
{% if "VMSS" in config.platform_management.enable_monitoring %}
"Deploy-VMSS-Monitoring" = {
"logAnalytics_1" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
{% endif %}
{% if "Arc" in config.platform_management.enable_monitoring %}
"Deploy-WS-Arc-Monitoring" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
"Deploy-LX-Arc-Monitoring" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
{% endif %}
"Deploy-ASC-Defender" = {
"emailSecurityContact" = {
value = "{{ config.notifications.security_center_email_contact }}"
}
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
{% for parameter_key in mg.archetype_definitions.root.policy_assignments["Deploy-ASC-Defender"].keys() %}
"{{ parameter_key }}" = {
value = "{{ mg.archetype_definitions.root.policy_assignments["Deploy-ASC-Defender"][parameter_key] }}"
}
{% endfor %}
}
}
access_control = {}
} //root
landing-zones = {
archetype_id = "landingzone"
parameters = {}
access_control = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Owner" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"subscription_creation_landingzones"
]
}
}
{% endif %}
}
}
platform = {
archetype_id = "platform"
parameters = {}
access_control = {}
}
connectivity = {
archetype_id = "platform_connectivity"
parameters = {}
access_control = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Owner" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"connectivity"
]
}
}
{% if config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine %}
"[{{ config.platform_core_setup.enterprise_scale.management_group_prefix | upper }}-CONNECTIVITY] CAF-network-vhub-peering" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"subscription_creation_landingzones"
]
}
}
{% endif %}
{% endif %}
}
}
identity = {
archetype_id = "platform_identity"
parameters = {}
access_control = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Owner" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"identity"
]
}
}
{% endif %}
}
}
management = {
archetype_id = "platform_management"
parameters = {}
access_control = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Owner" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"management"
]
}
}
{% endif %}
}
}
decommissioned = {
archetype_id = "es_decommissioned"
parameters = {}
access_control = {}
}
sandboxes = {
archetype_id = "es_sandboxes"
parameters = {}
access_control = {}
}
}

Просмотреть файл

@ -0,0 +1,56 @@
archetype_config_overrides = {
{% for key, level in mg.archetype_definitions.items() %}
{{ key }} = {
archetype_id = "{{mg.archetype_definitions[key].archetype_id }}"
{% if mg.archetype_definitions[key].policy_assignments is defined %}
parameters = {
{% for pa_key, pa_value in mg.archetype_definitions[key].policy_assignments.items() %}
{% if pa_value is mapping %}
"{{ pa_key }}" = {
{% for attribute, attribute_value in pa_value.items() %}
"{{attribute}}" = {
{% if attribute_value is string %}
value = "{{ attribute_value }}"
{% elif attribute_value is boolean %}
value = {{ attribute_value | string | lower }}
{% elif attribute_value is number %}
value = {{ attribute_value }}
{% else %}
{% if attribute_value is mapping %}
{% for caf_key, caf_value in attribute_value.items() %}
{{ caf_key }} = "{{ caf_value }}"
{% endfor %}
{% else %}
values = {{ attribute_value | replace('None','[]') | replace('\'','\"') }}
{% endif %}
{% endif %}
}
{% endfor %}
}
{% endif %}
{% endfor %}
}
{% else %}
parameters = {}
{% endif %}
{% if level.archetype_config.access_control is defined %}
access_control = {
{% for level_ac_key, level_ac in level.archetype_config.access_control.items() %}
"{{level_ac_key}}" = {
{% for level_role_key, level_role in level_ac.items() %}
"{{ level_role_key }}" = {
lz_key = "{{ level_role.lz_key }}"
attribute_key = "{{ level_role.attribute_key }}"
resource_keys = {{ level_role.resource_keys | replace('None','[]') | replace('\'','\"') }}
}
{% endfor %}
}
{% endfor %}
}
{% else %}
access_control = {}
{% endif %}
}
{% endfor %}
}

Просмотреть файл

@ -0,0 +1,11 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default archetypes
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/archetype_definitions

Просмотреть файл

@ -0,0 +1,90 @@
{
"{{ mg.archetype_definitions[item].archetype_id }}": {
"policy_assignments": [
{% if mg.archetype_definitions[item].policy_assignments is defined %}
{% for key in mg.archetype_definitions[item].policy_assignments.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_assignments is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_assignments.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"policy_definitions": [
{% if mg.archetype_definitions[item].policy_definitions is defined %}
{% for key in mg.archetype_definitions[item].policy_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"policy_set_definitions": [
{% if mg.archetype_definitions[item].policy_set_definitions is defined %}
{% for key in mg.archetype_definitions[item].policy_set_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_set_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_set_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"role_definitions": [
{% if mg.archetype_definitions[item].role_definitions is defined %}
{% for key in mg.archetype_definitions[item].role_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].role_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].role_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"archetype_config": {
"parameters": {
},
"access_control": {
}
}
}
}

Просмотреть файл

@ -0,0 +1,90 @@
{
"{{ mg_custom.archetype_definitions[item].archetype_id }}": {
"policy_assignments": [
{% if mg.archetype_definitions[item].policy_assignments is defined %}
{% for key in mg.archetype_definitions[item].policy_assignments.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_assignments is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_assignments.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"policy_definitions": [
{% if mg.archetype_definitions[item].policy_definitions is defined %}
{% for key in mg.archetype_definitions[item].policy_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"policy_set_definitions": [
{% if mg.archetype_definitions[item].policy_set_definitions is defined %}
{% for key in mg.archetype_definitions[item].policy_set_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_set_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_set_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"role_definitions": [
{% if mg.archetype_definitions[item].role_definitions is defined %}
{% for key in mg.archetype_definitions[item].role_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].role_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].role_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"archetype_config": {
"parameters": {
},
"access_control": {
}
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
custom_landing_zones = {
{% for key, level in mg_custom.archetype_definitions.items() %}
{{ config.platform_core_setup.enterprise_scale.management_group_prefix }}-{{ key }} = {
display_name = "{{ mg_custom.archetype_definitions[key].display_name }}"
parent_management_group_id = "{{ config.platform_core_setup.enterprise_scale.management_group_prefix }}-{{ mg_custom.archetype_definitions[key].parent_management_group_id }}"
archetype_config = {
archetype_id = "{{mg_custom.archetype_definitions[key].archetype_id }}"
{% if mg_custom.archetype_definitions[key].policy_assignments is defined %}
parameters = {
{% for pa_key, pa_value in mg_custom.archetype_definitions[key].policy_assignments.items() %}
{% if pa_value is mapping %}
"{{ pa_key }}" = {
{% for attribute, attribute_value in pa_value.items() %}
"{{attribute}}" = {
{% if attribute_value is string %}
value = "{{ attribute_value }}"
{% elif attribute_value is boolean %}
value = {{ attribute_value | string | lower }}
{% elif attribute_value is number %}
value = {{ attribute_value }}
{% else %}
{% if attribute_value is mapping %}
{% for caf_key, caf_value in attribute_value.items() %}
{{ caf_key }} = "{{ caf_value }}"
{% endfor %}
{% else %}
values = {{ attribute_value | replace('None','[]') | replace('\'','\"') }}
{% endif %}
{% endif %}
}
{% endfor %}
}
{% endif %}
{% endfor %}
}
{% else %}
parameters = {}
{% endif %}
{% if mg_custom.archetype_definitions[key].archetype_config.access_control is defined %}
access_control = {
{% for level_ac_key, level_ac in mg_custom.archetype_definitions[key].archetype_config.access_control.items() %}
"{{level_ac_key}}" = {
{% for level_role_key, level_role in level_ac.items() %}
"{{ level_role_key }}" = {
lz_key = "{{ level_role.lz_key }}"
attribute_key = "{{ level_role.attribute_key }}"
resource_keys = {{ level_role.resource_keys | replace('None','[]') | replace('\'','\"') }}
}
{% endfor %}
}
{% endfor %}
}
{% else %}
access_control = {}
{% endif %}
}
subscriptions = {}
subscription_ids = []
}
{% endfor %}
}

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default policy assignments
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/policy_assignments

Просмотреть файл

@ -0,0 +1,18 @@
{
"name": "aks-capability",
"type": "Microsoft.Authorization/policyAssignments",
"apiVersion": "2019-09-01",
"properties": {
"description": "Restrict the capabilities to reduce the attack surface of containers in a Kubernetes cluster. This recommendation is part of CIS 5.2.8 and CIS 5.2.9 which are intended to improve the security of your Kubernetes environments. This policy is generally available for Kubernetes Service (AKS), and preview for AKS Engine and Azure Arc enabled Kubernetes. For more information, see https://aka.ms/kubepolicydoc. (labelSelector example - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#resources-that-support-set-based-requirements)",
"displayName": "Kubernetes cluster containers should only use allowed capabilities.",
"notScopes": [],
"parameters": {},
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/c26596ff-4d70-4e6a-9a30-c2506bd2f80c",
"scope": "${current_scope_resource_id}",
"enforcementMode": true
},
"location": "${default_location}",
"identity": {
"type": "None"
}
}

Просмотреть файл

@ -0,0 +1,18 @@
{
"name": "Allowed-Locations",
"type": "Microsoft.Authorization/policyAssignments",
"apiVersion": "2019-09-01",
"properties": {
"description": "Specifies the allowed locations (regions) where Resources can be deployed.",
"displayName": "Limit allowed locations for Resources",
"notScopes": [],
"parameters": {},
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c",
"scope": "${current_scope_resource_id}",
"enforcementMode": null
},
"location": "${default_location}",
"identity": {
"type": "None"
}
}

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default policy definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/policy_definitions

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default policy set definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/policy_set_definitions

Просмотреть файл

@ -0,0 +1,11 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default role defitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/role_definitions

Просмотреть файл

@ -0,0 +1,26 @@
{
"name": "48ec94a9-9a14-488d-928d-5e73f96b335c",
"type": "Microsoft.Authorization/roleDefinitions",
"apiVersion": "2018-01-01-preview",
"properties": {
"roleName": "CAF-network-vhub-peering",
"description": "Authorize vnet peerings to the vhub.",
"type": "customRole",
"permissions": [
{
"actions": [
"Microsoft.Resources/subscriptions/resourceGroups/read",
"Microsoft.Network/virtualHubs/read",
"Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/*"
],
"notActions": [
],
"dataActions": [],
"notDataActions": []
}
],
"assignableScopes": [
"${current_scope_resource_id}"
]
}
}

Просмотреть файл

@ -0,0 +1,208 @@
archetype_config_overrides = {
root = {
archetype_id = "root"
parameters = {
"Allowed-Locations" = {
"listOfAllowedLocations" = {
values = [
{% for key in config.caf_terraform.launchpad.regions.keys() %}
"{{ config.caf_terraform.launchpad.regions[key].name }}",
{% endfor %}
]
}
}
"Deny-RSG-Locations" = {
"listOfAllowedLocations" = {
values = [
{% for key in config.caf_terraform.launchpad.regions.keys() %}
"{{ config.caf_terraform.launchpad.regions[key].name }}",
{% endfor %}
]
}
}
"Deploy-Resource-Diag" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
"profileName" = {
value = "eslz-diagnostic-log"
}
}
"Deploy-AzActivity-Log" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
{% if "VM" in config.platform_management.enable_monitoring %}
"Deploy-VM-Monitoring" = {
"logAnalytics_1" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
{% endif %}
{% if "VMSS" in config.platform_management.enable_monitoring %}
"Deploy-VMSS-Monitoring" = {
"logAnalytics_1" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
{% endif %}
{% if "Arc" in config.platform_management.enable_monitoring %}
"Deploy-WS-Arc-Monitoring" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
"Deploy-LX-Arc-Monitoring" = {
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
}
{% endif %}
"Deploy-ASC-Defender" = {
"emailSecurityContact" = {
value = "{{ config.notifications.security_center_email_contact }}"
}
"logAnalytics" = {
lz_key = "{{ config.tfstates.platform.management.lz_key_name }}"
output_key = "diagnostics"
resource_type = "log_analytics"
resource_key = "central_logs_{{config.caf_terraform.launchpad.regions[config.caf_terraform.launchpad.default_region_key].slug}}"
attribute_key = "id"
}
{% for parameter_key in mg.archetype_definitions.root.policy_assignments["Deploy-ASC-Defender"].keys() %}
"{{ parameter_key }}" = {
value = "{{ mg.archetype_definitions.root.policy_assignments["Deploy-ASC-Defender"][parameter_key] }}"
}
{% endfor %}
}
}
access_control = {}
} //root
landing-zones = {
archetype_id = "landingzone"
parameters = {}
access_control = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Owner" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"subscription_creation_landingzones"
]
}
}
{% endif %}
}
}
platform = {
archetype_id = "platform"
parameters = {}
access_control = {}
}
connectivity = {
archetype_id = "platform_connectivity"
parameters = {}
access_control = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Owner" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"connectivity"
]
}
}
{% if config.platform_core_setup.enterprise_scale.enable_azure_subscription_vending_machine %}
"[{{ config.platform_core_setup.enterprise_scale.management_group_prefix | upper }}-CONNECTIVITY] CAF-network-vhub-peering" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"subscription_creation_landingzones"
]
}
}
{% endif %}
{% endif %}
}
}
identity = {
archetype_id = "platform_identity"
parameters = {}
access_control = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Owner" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"identity"
]
}
}
{% endif %}
}
}
management = {
archetype_id = "platform_management"
parameters = {}
access_control = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
"Owner" = {
"azuread_groups" = {
lz_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
attribute_key = "id"
resource_keys = [
"management"
]
}
}
{% endif %}
}
}
decommissioned = {
archetype_id = "es_decommissioned"
parameters = {}
access_control = {}
}
sandboxes = {
archetype_id = "es_sandboxes"
parameters = {}
access_control = {}
}
}

Просмотреть файл

@ -0,0 +1,56 @@
archetype_config_overrides = {
{% for key, level in mg.archetype_definitions.items() %}
{{ key }} = {
archetype_id = "{{mg.archetype_definitions[key].archetype_id }}"
{% if mg.archetype_definitions[key].policy_assignments is defined %}
parameters = {
{% for pa_key, pa_value in mg.archetype_definitions[key].policy_assignments.items() %}
{% if pa_value is mapping %}
"{{ pa_key }}" = {
{% for attribute, attribute_value in pa_value.items() %}
"{{attribute}}" = {
{% if attribute_value is string %}
value = "{{ attribute_value }}"
{% elif attribute_value is boolean %}
value = {{ attribute_value | string | lower }}
{% elif attribute_value is number %}
value = {{ attribute_value }}
{% else %}
{% if attribute_value is mapping %}
{% for caf_key, caf_value in attribute_value.items() %}
{{ caf_key }} = "{{ caf_value }}"
{% endfor %}
{% else %}
values = {{ attribute_value | replace('None','[]') | replace('\'','\"') }}
{% endif %}
{% endif %}
}
{% endfor %}
}
{% endif %}
{% endfor %}
}
{% else %}
parameters = {}
{% endif %}
{% if level.archetype_config.access_control is defined %}
access_control = {
{% for level_ac_key, level_ac in level.archetype_config.access_control.items() %}
"{{level_ac_key}}" = {
{% for level_role_key, level_role in level_ac.items() %}
"{{ level_role_key }}" = {
lz_key = "{{ level_role.lz_key }}"
attribute_key = "{{ level_role.attribute_key }}"
resource_keys = {{ level_role.resource_keys | replace('None','[]') | replace('\'','\"') }}
}
{% endfor %}
}
{% endfor %}
}
{% else %}
access_control = {}
{% endif %}
}
{% endfor %}
}

Просмотреть файл

@ -0,0 +1,11 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default archetypes
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/archetype_definitions

Просмотреть файл

@ -0,0 +1,90 @@
{
"{{ mg.archetype_definitions[item].archetype_id }}": {
"policy_assignments": [
{% if mg.archetype_definitions[item].policy_assignments is defined %}
{% for key in mg.archetype_definitions[item].policy_assignments.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_assignments is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_assignments.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"policy_definitions": [
{% if mg.archetype_definitions[item].policy_definitions is defined %}
{% for key in mg.archetype_definitions[item].policy_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"policy_set_definitions": [
{% if mg.archetype_definitions[item].policy_set_definitions is defined %}
{% for key in mg.archetype_definitions[item].policy_set_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_set_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_set_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"role_definitions": [
{% if mg.archetype_definitions[item].role_definitions is defined %}
{% for key in mg.archetype_definitions[item].role_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].role_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].role_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"archetype_config": {
"parameters": {
},
"access_control": {
}
}
}
}

Просмотреть файл

@ -0,0 +1,90 @@
{
"{{ mg_custom.archetype_definitions[item].archetype_id }}": {
"policy_assignments": [
{% if mg.archetype_definitions[item].policy_assignments is defined %}
{% for key in mg.archetype_definitions[item].policy_assignments.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_assignments is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_assignments.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"policy_definitions": [
{% if mg.archetype_definitions[item].policy_definitions is defined %}
{% for key in mg.archetype_definitions[item].policy_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"policy_set_definitions": [
{% if mg.archetype_definitions[item].policy_set_definitions is defined %}
{% for key in mg.archetype_definitions[item].policy_set_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].policy_set_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].policy_set_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"role_definitions": [
{% if mg.archetype_definitions[item].role_definitions is defined %}
{% for key in mg.archetype_definitions[item].role_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
{% if mg_custom.archetype_definitions[item].role_definitions is defined %}
{% for key in mg_custom.archetype_definitions[item].role_definitions.keys() %}
{% if loop.last %}
"{{ key }}"
{% else %}
"{{ key }}",
{% endif %}
{% endfor %}
{% endif %}
],
"archetype_config": {
"parameters": {
},
"access_control": {
}
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
custom_landing_zones = {
{% for key, level in mg_custom.archetype_definitions.items() %}
{{ config.platform_core_setup.enterprise_scale.management_group_prefix }}-{{ key }} = {
display_name = "{{ mg_custom.archetype_definitions[key].display_name }}"
parent_management_group_id = "{{ config.platform_core_setup.enterprise_scale.management_group_prefix }}-{{ mg_custom.archetype_definitions[key].parent_management_group_id }}"
archetype_config = {
archetype_id = "{{mg_custom.archetype_definitions[key].archetype_id }}"
{% if mg_custom.archetype_definitions[key].policy_assignments is defined %}
parameters = {
{% for pa_key, pa_value in mg_custom.archetype_definitions[key].policy_assignments.items() %}
{% if pa_value is mapping %}
"{{ pa_key }}" = {
{% for attribute, attribute_value in pa_value.items() %}
"{{attribute}}" = {
{% if attribute_value is string %}
value = "{{ attribute_value }}"
{% elif attribute_value is boolean %}
value = {{ attribute_value | string | lower }}
{% elif attribute_value is number %}
value = {{ attribute_value }}
{% else %}
{% if attribute_value is mapping %}
{% for caf_key, caf_value in attribute_value.items() %}
{{ caf_key }} = "{{ caf_value }}"
{% endfor %}
{% else %}
values = {{ attribute_value | replace('None','[]') | replace('\'','\"') }}
{% endif %}
{% endif %}
}
{% endfor %}
}
{% endif %}
{% endfor %}
}
{% else %}
parameters = {}
{% endif %}
{% if mg_custom.archetype_definitions[key].archetype_config.access_control is defined %}
access_control = {
{% for level_ac_key, level_ac in mg_custom.archetype_definitions[key].archetype_config.access_control.items() %}
"{{level_ac_key}}" = {
{% for level_role_key, level_role in level_ac.items() %}
"{{ level_role_key }}" = {
lz_key = "{{ level_role.lz_key }}"
attribute_key = "{{ level_role.attribute_key }}"
resource_keys = {{ level_role.resource_keys | replace('None','[]') | replace('\'','\"') }}
}
{% endfor %}
}
{% endfor %}
}
{% else %}
access_control = {}
{% endif %}
}
subscriptions = {}
subscription_ids = []
}
{% endfor %}
}

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default policy assignments
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/policy_assignments

Просмотреть файл

@ -0,0 +1,18 @@
{
"name": "aks-capability",
"type": "Microsoft.Authorization/policyAssignments",
"apiVersion": "2019-09-01",
"properties": {
"description": "Restrict the capabilities to reduce the attack surface of containers in a Kubernetes cluster. This recommendation is part of CIS 5.2.8 and CIS 5.2.9 which are intended to improve the security of your Kubernetes environments. This policy is generally available for Kubernetes Service (AKS), and preview for AKS Engine and Azure Arc enabled Kubernetes. For more information, see https://aka.ms/kubepolicydoc. (labelSelector example - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#resources-that-support-set-based-requirements)",
"displayName": "Kubernetes cluster containers should only use allowed capabilities.",
"notScopes": [],
"parameters": {},
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/c26596ff-4d70-4e6a-9a30-c2506bd2f80c",
"scope": "${current_scope_resource_id}",
"enforcementMode": true
},
"location": "${default_location}",
"identity": {
"type": "None"
}
}

Просмотреть файл

@ -0,0 +1,18 @@
{
"name": "Allowed-Locations",
"type": "Microsoft.Authorization/policyAssignments",
"apiVersion": "2019-09-01",
"properties": {
"description": "Specifies the allowed locations (regions) where Resources can be deployed.",
"displayName": "Limit allowed locations for Resources",
"notScopes": [],
"parameters": {},
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c",
"scope": "${current_scope_resource_id}",
"enforcementMode": null
},
"location": "${default_location}",
"identity": {
"type": "None"
}
}

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default policy definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/policy_definitions

Просмотреть файл

@ -0,0 +1,10 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default policy set definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/policy_set_definitions

Просмотреть файл

@ -0,0 +1,11 @@
# Public documentation of the custom landingzones
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Archetype-Definitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BExamples%5D-Deploy-Custom-Landing-Zone-Archetypes
# List of the default role defitions
https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/tree/main/modules/archetypes/lib/role_definitions

Просмотреть файл

@ -0,0 +1,26 @@
{
"name": "48ec94a9-9a14-488d-928d-5e73f96b335c",
"type": "Microsoft.Authorization/roleDefinitions",
"apiVersion": "2018-01-01-preview",
"properties": {
"roleName": "CAF-network-vhub-peering",
"description": "Authorize vnet peerings to the vhub.",
"type": "customRole",
"permissions": [
{
"actions": [
"Microsoft.Resources/subscriptions/resourceGroups/read",
"Microsoft.Network/virtualHubs/read",
"Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/*"
],
"notActions": [
],
"dataActions": [],
"notDataActions": []
}
],
"assignableScopes": [
"${current_scope_resource_id}"
]
}
}

Просмотреть файл

@ -0,0 +1,34 @@
# Enterprise scale
## Deploy Enterprise Scale
Note you need to adjust the branch to deploy Enterprise Scale to {{ config.platform_core_setup.enterprise_scale.private_lib[config.platform_core_setup.enterprise_scale.private_lib.version_to_deploy].caf_landingzone_branch }}
```bash
az account clear
# login a with a user member of the caf-platform-maintainers group
rover login -t {{ config.platform_identity.tenant_name }}
cd {{ config.configuration_folders.platform.destination_base_path }}/landingzones
git fetch origin
git checkout {{ config.platform_core_setup.enterprise_scale.private_lib[config.platform_core_setup.enterprise_scale.private_lib.version_to_deploy].caf_landingzone_branch }}
rover \
{% if keyvaults is defined and config.platform_identity.azuread_identity_mode != "logged_in_user" %}
--impersonate-sp-from-keyvault-url {{ keyvaults.cred_eslz.vault_uri }} \
{% endif %}
-lz {{ config.configuration_folders.platform.destination_base_path }}/landingzones/caf_solution/add-ons/caf_eslz \
-var-folder {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-tfstate {{ config.tfstates.platform.eslz.tfstate }} \
-log-severity ERROR \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-p ${TF_DATA_DIR}/{{ config.tfstates.platform.eslz.tfstate }}.tfplan \
-a plan
```
# Next steps
[Deploy Connectivity](../../level2/connectivity/readme.md)

Просмотреть файл

@ -0,0 +1,41 @@
subscription_id_overrides = {
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
root = []
{% else %}
root = [
"{{ config.caf_terraform.launchpad.subscription_id }}"
]
{% endif %}
decommissioned = []
sandboxes = []
landing-zones = []
platform = []
connectivity = []
management = []
identity = []
}
{% if config.platform_identity.azuread_identity_mode != 'logged_in_user' %}
subscription_id_overrides_by_keys = {
connectivity = {
connectivity = {
lz_key = "{{ config.tfstates.platform.platform_subscriptions.lz_key_name }}"
key = "connectivity"
}
}
management = {
management = {
lz_key = "{{ config.tfstates.platform.platform_subscriptions.lz_key_name }}"
key = "management"
}
}
identity = {
identity = {
lz_key = "{{ config.tfstates.platform.platform_subscriptions.lz_key_name }}"
key = "identity"
}
}
}
{% else %}
subscription_id_overrides_by_keys = {}
{% endif %}

Просмотреть файл

@ -0,0 +1,20 @@
- name: "[{{ level }}-{{ base_folder }}] Clean-up directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: absent
when: config.configuration_folders.platform.cleanup_destination | bool
- name: "[{{ level }}-{{ base_folder }}] Creates directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: directory
- name: "[{{ level }}-{{ base_folder }}] generate configuration files."
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ level }}/{{ base_folder }}/*.tfvars.j2"
- "{{ level }}/{{ base_folder }}/*.md"

Просмотреть файл

@ -0,0 +1,37 @@
azuread_groups = {
{% for key, ad_group in identity.level1.azuread_groups.items() %}
{{ key }} = {
name = "{{ ad_group.name }}"
{% if ad_group.description is defined %}
description = "{{ ad_group.description }}"
{% endif %}
{% if ad_group.members is defined %}
members = {
{% if ad_group.members.user_principal_names is defined %}
user_principal_names = {{ ad_group.members.user_principal_names | replace('None','[]') | replace('\'','\"') }}
{% endif %}
{% if ad_group.members.group_names is defined %}
group_names = {{ ad_group.members.group_names | replace('None','[]') | replace('\'','\"') }}
{% endif %}
{% if ad_group.members.object_ids is defined %}
object_ids = {{ ad_group.members.object_ids | replace('None','[]') | replace('\'','\"') }}
{% endif %}
{% if ad_group.members.group_keys is defined %}
group_keys = {{ ad_group.members.group_keys | replace('None','[]') | replace('\'','\"') }}
{% endif %}
{% if ad_group.members.service_principal_keys is defined %}
service_principal_keys = {{ ad_group.members.service_principal_keys | replace('None','[]') | replace('\'','\"') }}
{% endif %}
}
{% endif %}
{% if ad_group.owners is defined %}
owners = {
{% if ad_group.owners.user_principal_names is defined %}
user_principal_names = {{ ad_group.owners.user_principal_names | replace('None','[]') | replace('\'','\"') }}
{% endif %}
}
{% endif %}
prevent_duplicate_name = {{ ad_group.owners.prevent_duplicate_name | default(false) | string | lower }}
}
{% endfor %}
}

Просмотреть файл

@ -0,0 +1,12 @@
landingzone = {
backend_type = "{{ caf_terraform.launchpad.backend_type | default("azurerm")}}"
global_settings_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
level = "{{ config.tfstates.platform.identity.level }}"
key = "{{ config.tfstates.platform.identity.lz_key_name }}"
tfstates = {
{{ config.tfstates.platform.launchpad.lz_key_name }} = {
level = "lower"
tfstate = "{{ config.tfstates.platform.launchpad.tfstate }}"
}
}
}

Просмотреть файл

@ -0,0 +1,32 @@
# Identity
Deploy the identity services
```bash
#Note: close previous session if you logged with a different service principal using --impersonate-sp-from-keyvault-url
rover logout
# login a with a user member of the caf-maintainers group
rover login -t {{ config.platform_identity.tenant_name }}
rover \
{% if keyvaults is defined and config.platform_identity.azuread_identity_mode != "logged_in_user" %}
--impersonate-sp-from-keyvault-url {{ keyvaults.cred_identity.vault_uri }} \
{% endif %}
-lz /tf/caf/landingzones/caf_solution \
-var-folder {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-target_subscription {{ platform_subscriptions_details.identity.subscription_id }} \
-tfstate {{ config.tfstates.platform.identity.tfstate }} \
-log-severity {{ config.gitops.rover_log_error }} \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-p ${TF_DATA_DIR}/{{ config.tfstates.platform.identity.tfstate }}.tfplan \
-a plan
```
# Next steps
[Deploy Enterprise Scale](../../level1/eslz/readme.md)

Просмотреть файл

@ -0,0 +1,5 @@
resource_groups = {
mgmt = {
name = "mgmt"
}
}

Просмотреть файл

@ -0,0 +1,25 @@
monitoring = {
service_health_alerts = {
enable_service_health_alerts = true
name = "alerts"
action_group_name = "actiongrp"
shortname = "HealthAlerts"
resource_group_key = "mgmt"
email_alert_settings = {
{% for key in config.notifications.service_health_alerts.emails.keys() %}
{{ key }} = {
name = "email_alert_support1"
email_address = "{{ config.notifications.service_health_alerts.emails[key].email_address }}"
use_common_alert_schema = false
}
{% endfor %}
} #add more email alerts by repeating the block.
# webhook = {
# teams = {
# name = "servicehealth"
# service_uri = ""
# }
# }
}
}

Просмотреть файл

@ -0,0 +1,134 @@
- name: "[{{ level }}-{{ base_folder }}] Clean-up directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: absent
when:
- config.configuration_folders.platform.cleanup_destination | bool
- name: "[{{ level }}-{{ base_folder }}] Creates directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: directory
- name: "[{{ level }}-{{ base_folder }}] - Set variables"
set_fact:
destination_path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
- name: "[{{ level }}-{{ base_folder }}] - Load variables"
include_vars:
name: resources
dir: "{{config_folder}}"
depth: 1
ignore_unknown_extensions: true
files_matching: "management.yaml|configuration.caf.platform.yaml"
#
# resource_groups
#
- name: "[{{ level }}-{{ base_folder }}] - resources - resource_groups"
when:
- resources.subscriptions[subscription_key].resource_groups is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/resource_groups.tfvars.j2"
#
# automation_accounts
#
- name: "[{{ level }}-{{ base_folder }}] - resources - automation_accounts"
when:
- resources.subscriptions[subscription_key].automation_accounts is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/automation_accounts.tfvars.j2"
#
# service_health_alerts
#
- name: "[{{ level }}-{{ base_folder }}] - resources - service_health_alerts"
when:
- resources.subscriptions[subscription_key].service_health_alerts is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/servicehealth.tfvars.j2"
#
# diagnostic_log_analytics
#
- name: "[{{ level }}-{{ base_folder }}] - resources - diagnostic_log_analytics"
when:
- resources.subscriptions[subscription_key].diagnostic_log_analytics is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/diagnostic_log_analytics.tfvars.j2"
#
# diagnostic_storage_accounts
#
- name: "[{{ level }}-{{ base_folder }}] - resources - diagnostic_storage_accounts"
when:
- resources.subscriptions[subscription_key].diagnostic_storage_accounts is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/diagnostic_storage_accounts.tfvars.j2"
# diagnostics_definition
#
- name: "[{{ level }}-{{ base_folder }}] - resources - diagnostics_definition"
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/diagnostics_definition.tfvars.j2"
# diagnostics_destinations
#
- name: "[{{ level }}-{{ base_folder }}] - resources - diagnostics_destinations"
when:
- resources.subscriptions[subscription_key].diagnostics_destinations is defined
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ resource_template_folder }}/diagnostics_destinations.tfvars.j2"
#
# Readme
#
- name: "[{{ level }}-{{ base_folder }}] - resources - *.md"
# when: always
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ destination_path }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ level }}/{{ base_folder }}/*.md"
#
# Legacy calls
#
- name: "[{{ level }}-{{ base_folder }}] - generate configuration files."
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ level }}/{{ base_folder }}/*.tfvars.j2"

Просмотреть файл

@ -0,0 +1,12 @@
landingzone = {
backend_type = "{{ caf_terraform.launchpad.backend_type | default("azurerm")}}"
global_settings_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
level = "{{ config.tfstates.platform.management.level }}"
key = "{{ config.tfstates.platform.management.lz_key_name }}"
tfstates = {
{{ config.tfstates.platform.launchpad.lz_key_name }} = {
level = "lower"
tfstate = "{{ config.tfstates.platform.launchpad.tfstate }}"
}
}
}

Просмотреть файл

@ -0,0 +1,42 @@
# Management
Deploy the management services
```bash
#Note: close previous session if you logged with a different service principal using --impersonate-sp-from-keyvault-url
rover logout
# login a with a user member of the caf-maintainers group
rover login -t {{ config.platform_identity.tenant_name }}
rover \
{% if keyvaults is defined and config.platform_identity.azuread_identity_mode != "logged_in_user" %}
--impersonate-sp-from-keyvault-url {{ keyvaults.cred_management.vault_uri }} \
{% endif %}
-lz /tf/caf/landingzones/caf_solution \
-var-folder {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
{% if platform_subscriptions_details is defined %}
-target_subscription {{ platform_subscriptions_details.management.subscription_id }} \
{% else %}
-target_subscription {{ config.caf_terraform.launchpad.subscription_id }} \
{% endif %}
-tfstate {{ config.tfstates.platform.management.tfstate }} \
-log-severity {{ config.gitops.rover_log_error }} \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-p ${TF_DATA_DIR}/{{ config.tfstates.platform.management.tfstate }}.tfplan \
-a plan
```
# Next steps
When you have successfully deployed the management landing zone, you can move to the next step:
{% if config.platform_core_setup.enterprise_scale.enable %}
[Deploy Enterprise Scale](../../level1/eslz/readme.md)
{% else %}
[Deploy Connectivity](../../level2/connectivity/readme.md)
{% endif %}

Просмотреть файл

@ -0,0 +1,88 @@
- name: "[{{ level }}-{{ base_folder }}] Clean-up directory"
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: absent
when: config.configuration_folders.platform.cleanup_destination | bool
- name: "[{{ level }}-{{ base_folder }}] Creates directory"
register: level1_subscriptions
file:
path: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}"
state: directory
- name: "[{{ level }}-{{ base_folder }}] generate configuration files."
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }}/{{ item | basename | regex_replace('.j2$', '') }}"
force: yes
with_fileglob:
- "{{ level }}/{{ base_folder }}/*.tfvars.j2"
- "{{ level }}/{{ base_folder }}/*.md"
# Create the subscriptions
- name: "[{{ level }}-{{ base_folder }}] Create subscriptions."
when: deploy_subscriptions | bool
shell: |
/tf/rover/rover.sh \
--impersonate-sp-from-keyvault-url {{ keyvaults.cred_subscription_creation_platform.vault_uri }} \
-lz /tf/caf/landingzones/caf_solution \
-var-folder {{ config.configuration_folders.platform.destination_base_path }}/{{ config.configuration_folders.platform.destination_relative_path }}/{{ level }}/{{ base_folder }} \
-tfstate_subscription_id {{ config.caf_terraform.launchpad.subscription_id }} \
-target_subscription {{ config.caf_terraform.launchpad.subscription_id }} \
-tfstate {{ config.tfstates.platform.platform_subscriptions.tfstate }} \
-log-severity {{ config.gitops.rover_log_error }} \
-env {{ config.caf_terraform.launchpad.caf_environment }} \
-level {{ level }} \
-a apply
- name: "[{{ level }}-{{ base_folder }}] Get latest cache folder"
set_fact:
job_cache_base_path: "/home/vscode/.terraform.cache"
- name: "[{{ level }}-{{ base_folder }}] Get tfstate details"
register: subscription_tfstate_file_name
shell: |
az storage account list \
--subscription {{ config.caf_terraform.launchpad.subscription_id }} \
--query "[?tags.caf_tfstate=='{{ config.tfstates.platform.platform_subscriptions.level }}' && tags.caf_environment=='{{ config.caf_terraform.launchpad.caf_environment }}'].{name:name}[0]" -o json | jq -r .name
- debug:
msg: "{{ subscription_tfstate_file_name.stdout }}"
- name: "[{{ level }}-{{ base_folder }}] Download tfstate details"
register: platform_subscriptions_tfstate_exists
ignore_errors: true
shell: |
az storage blob download \
--name "{{ config.tfstates.platform.platform_subscriptions.tfstate }}" \
--account-name "{{ subscription_tfstate_file_name.stdout }}" \
--container-name "tfstate" \
--auth-mode "login" \
--file "{{ job_cache_base_path }}/{{ config.tfstates.platform.platform_subscriptions.tfstate }}"
- name: "[{{ level }}-{{ base_folder }}] Get platform_subscriptions details"
shell: "cat {{ job_cache_base_path }}/{{ config.tfstates.platform.platform_subscriptions.tfstate }}"
register: platform_subscriptions
when: platform_subscriptions_tfstate_exists.rc == 0
- name: "[{{ level }}-{{ base_folder }}] Get platform_subscriptions json data"
when: platform_subscriptions_tfstate_exists.rc == 0
set_fact:
platform_sub_jsondata: "{{ platform_subscriptions.stdout | from_json }}"
- name: "[{{ level }}-{{ base_folder }}] Get subscriptions list"
when: platform_subscriptions_tfstate_exists.rc == 0
set_fact:
platform_subscriptions_details: "{{ platform_sub_jsondata | json_query(path) }}"
vars:
path: 'outputs.objects.value.{{ config.tfstates.platform.platform_subscriptions.lz_key_name }}.subscriptions'
- name: "[{{ level }}-{{ base_folder }}] cleanup"
when: platform_subscriptions_tfstate_exists.rc == 0
file:
path: "{{ job_cache_base_path }}/{{ config.tfstates.platform.platform_subscriptions.tfstate }}"
state: absent
- debug:
msg: "Platform subscriptions - {{ platform_subscriptions_details }}"
when: platform_subscriptions_tfstate_exists.rc == 0

Просмотреть файл

@ -0,0 +1,12 @@
landingzone = {
backend_type = "azurerm"
global_settings_key = "{{ config.tfstates.platform.launchpad.lz_key_name }}"
level = "{{ config.tfstates.platform.platform_subscriptions.level }}"
key = "{{ config.tfstates.platform.platform_subscriptions.lz_key_name }}"
tfstates = {
{{ config.tfstates.platform.launchpad.lz_key_name }} = {
level = "lower"
tfstate = "{{ config.tfstates.platform.launchpad.tfstate }}"
}
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше