Skip to content

Merge pull request #76 from Azure/feature/AVM-Integration #109

Merge pull request #76 from Azure/feature/AVM-Integration

Merge pull request #76 from Azure/feature/AVM-Integration #109

name: Deploy an Oracle DB in a Data Guard configuration on Azure with Terraform and Ansible
on:
workflow_dispatch: {}
push:
branches: [ main ]
paths:
- ".github/workflows/full-dg-tf-deploy.yml"
- "terraform/bootstrap/data_guard/**"
- "terraform_units/**"
- "ansible/bootstrap/oracle/**"
pull_request:
branches: [ main ]
paths:
- ".github/workflows/full-dg-tf-deploy.yml"
- "terraform/bootstrap/data_guard/**"
- "terraform_units/**"
- "ansible/bootstrap/oracle/**"
env:
TF_LOG: ${{ vars.TF_LOG != '' && vars.TF_LOG || 'INFO' }} #"INFO"
AZ_LOCATION: ${{ vars.AZ_LOCATION != '' && vars.AZ_LOCATION || 'swedencentral' }} #"swedencentral" # can be parameterized
AZ_RG_BASENAME: ${{ vars.AZ_RG_BASENAME != '' && vars.AZ_RG_BASENAME || 'Oracle-test' }}
VM_PRIMARY_NAME: ${{ vars.VM_PRIMARY_NAME != '' && vars.VM_PRIMARY_NAME || 'vm-primary-0' }} # can be parameterized
VM_SECONDARY_NAME: ${{ vars.VM_SECONDARY_NAME != '' && vars.VM_SECONDARY_NAME || 'vm-secondary-0' }} # can be parameterized
ORCL_DB_NAME: ${{ vars.ORCL_DB_NAME != '' && vars.ORCL_DB_NAME || 'ORCL' }} #"ORCL" # can be parameterized
SOFTWARE_RG: ${{ vars.SOFTWARE_RG != '' && vars.SOFTWARE_RG || 'binaryresource' }} #"binaryresource"
USER_ASSIGNED_IDENTITY: ${{vars.USER_ASSIGNED_IDENTITY != '' && vars.USER_ASSIGNED_IDENTITY || 'oraclelza' }} #"oraclelza"
permissions:
id-token: write
contents: read
issues: write
pull-requests: write
jobs:
terraform:
name: '๐Ÿ”ง Terraform'
runs-on: ubuntu-latest
environment: test-deploy
strategy:
fail-fast: false
defaults:
run:
shell: bash
working-directory: ./terraform/bootstrap/data_guard
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout
uses: actions/checkout@v4
# Install the preferred version of Terraform CLI
- name: ๐Ÿ“ฆ Setup Terraform Cli
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.7.0
terraform_wrapper: false # keep it false to be able to read the outputs values of terraform
#Check if the SSH key is empty
- name: ๐Ÿ”Ž Validate SSH Key is not empty
run: |
if [ -z "${{ secrets.SSH_PRIVATE_KEY }}" ]
then
echo "SSH_PRIVATE_KEY is empty, you should add a SSH key to the repository secrets. Name of the secret should be SSH_PRIVATE_KEY"
exit 1
else
echo "SSH_PRIVATE_KEY is not empty"
fi
- name: ๐Ÿ—’๏ธ Create the SSH public key for VM
run: |
cat > temp_ssh_key <<EOF
${{ secrets.SSH_PRIVATE_KEY }}
EOF
chmod 400 temp_ssh_key
ssh-keygen -f temp_ssh_key -y > temp_ssh_key.pub
echo "SSH_KEY=$(cat temp_ssh_key.pub)" >> $GITHUB_ENV
echo "SSH_KEY=${{env.SSH_KEY}}"
echo "currentDate=$(date)" >> $GITHUB_ENV
echo "currentDate=${{env.currentDate}}"
# Generate random string for suffix
- name: ๐Ÿ”Ž Generate Random String for Resource Group Name
id: resourcegroup-generator
run: echo random=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 8; echo) >> $GITHUB_ENV
- run: echo ResourceGroupName=${{env.AZ_RG_BASENAME}}-${{env.random}} >> $GITHUB_ENV
- run: echo ${{env.ResourceGroupName}}
#Login to Azure
- name: ๐Ÿ”‘ Login via Azure CLI
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Get the ID of the user-assigned managed identity
run: |
usId=$(az identity show --name ${{ env.USER_ASSIGNED_IDENTITY }} --resource-group ${{ env.SOFTWARE_RG }} --query id --output tsv)
usId_modified=$(echo "$usId" | sed 's/resourcegroups/resourceGroups/g')
echo "usId=${usId_modified}" >> $GITHUB_ENV
- name: ๐Ÿ—’๏ธ Create a tfvars file for terraform
run: |
cat > terraform.tfvars <<EOF
location = "${{env.AZ_LOCATION}}"
resourcegroup_name = "${{env.ResourceGroupName}}"
resourcegroup_tags = {
"environment" = "dev"
"project" = "oracle"
"created" = "${{env.currentDate}}"
"GITHUB_ACTOR" = "${{ github.actor }}"
"GITHUB_ACTION_REPOSITORY" = "${{ github.repository }}"
"GITHUB_ACTION_REF" = "${{ github.ref }}"
"GITHUB_RUN_ID" = "${{ github.run_id }}"
"GITHUB_WORKFLOW" = "${{ github.workflow }}"
}
vm_user_assigned_identity_id = "${{env.usId}}"
is_resource_lock_enabled = false
#is_diagnostic_settings_enabled = true
#diagnostic_target = "Log_Analytics_Workspace"
ssh_key = "${{env.SSH_KEY}}"
EOF
# Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
- name: ๐Ÿ“ฆ Terraform Init
id: init
env:
RESOURCE_GROUP: ${{ env.ResourceGroupName }}
run: terraform init
# Run a terraform validate
- name: ๐Ÿ”Ž Terraform Validate
id: validate
if: github.ref != 'refs/heads/main'
continue-on-error: true
run: terraform validate -no-color
# Run a terraform plan for pull requests only and add a comment
- name: ๐Ÿ’ป Terraform Plan
id: plan
continue-on-error: true
run: terraform plan -no-color
- name: "Add a comment to pull requests with plan resutls"
id: comment
uses: actions/github-script@v6
if: (github.event_name == 'pull_request' || github.event_name == 'push')
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `
#### Terraform Initialization โš™๏ธ\`${{ steps.init.outcome }}\`
#### Terraform Validation ๐Ÿค–\`${{ steps.validate.outcome }}\`
#### Terraform Plan ๐Ÿ“–\`${{ steps.plan.outcome }}\`
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Workflow: \`${{ github.workflow }}\`*`;
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
- name: "โ—ป๏ธ Terraform Plan Status โ‰๏ธ"
if: steps.plan.outcome == 'failure'
run: exit 1
# On push to main, build or change infrastructure according to Terraform configuration files
# On push to main, build or change infrastructure according to Terraform configuration files
- name: ๐Ÿ’ป Terraform Apply
# if: github.ref == 'refs/heads/main' && github.event_name == 'push' # Remove when I am ready
run: terraform apply -auto-approve
- name: ๐Ÿ”ƒ Terraform Refresh State
run: terraform apply -refresh-only -auto-approve
# - run: echo "vm_public_ip_address=$(terraform output vm_public_ip_address)" >> $GITHUB_ENV
# - run: echo ${{env.vm_public_ip_address}}
outputs:
ResourceGroupName: ${{ env.ResourceGroupName }}
# VM_Public_IP_Address: ${{ env.vm_public_ip_address }}
##############################################################################################################################################
# SSH Keys and parameter preparation for Ansible #
##############################################################################################################################################
ssh-keys:
needs: terraform
name: '๐Ÿงฎ Prepare and run Ansible Playbook'
runs-on: ubuntu-latest
environment: test-deploy
defaults:
run:
shell: bash
working-directory: ansible/bootstrap/oracle
steps:
- name: ๐Ÿ›’ Checkout
uses: actions/checkout@v4
- name: ๐Ÿ”Ž Check IP
run: echo "currentRunnerIP=$(curl https://api.ipify.org)" >> $GITHUB_ENV
- run: echo "currentRunnerIP=${{env.currentRunnerIP}}"
- name: 'Install SSH Key'
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.SSH_PRIVATE_KEY }}
name: 'github_actions_id_rsa'
known_hosts: "sometin"
# Login to Azure CLI
- name: ๐Ÿ”‘ Login via Azure CLI
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# Get Subscription ID
- name: ๐Ÿ”Ž Get Subscription ID
id: subid
run: echo "SubscriptionID=$(az account show --query id -o tsv)" >> $GITHUB_ENV
- run: echo ${{env.SubscriptionID}}
- name: Get the ID of the user-assigned managed identity
run: |
usId=$(az identity show --name ${{ env.USER_ASSIGNED_IDENTITY }} --resource-group ${{ env.SOFTWARE_RG }} --query id --output tsv)
usId_modified=$(echo "$usId" | sed 's/resourcegroups/resourceGroups/g')
echo "usId=${usId_modified}" >> $GITHUB_ENV
- name: โŒ› Calculate endTimeUTC for JIT request
run: echo "endTimeUtc=$(date -d '+2 hour' '+%FT%T')" >> $GITHUB_ENV
- run: echo "endTimeUtc=${{env.endTimeUtc}}"
- run: echo "ResourceGroupName=${{needs.terraform.outputs.ResourceGroupName}}" >> $GITHUB_ENV
- run: echo "ResourceGroupName=${{env.ResourceGroupName}}"
- name: ๐Ÿ”‘ Login via Az Module
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
enable-AzPSSession: true
- name: โœ… Enable JIT on VM
uses: azure/powershell@v1
with:
inlineScript: |
$JitPolicyVm1 = (@{id="/subscriptions/${{env.SubscriptionID}}/resourceGroups/${{env.ResourceGroupName}}/providers/Microsoft.Compute/virtualMachines/${{env.VM_PRIMARY_NAME}}"; ports=(@{number=22;endTimeUtc="${{env.endTimeUtc}}";allowedSourceAddressPrefix=@("${{env.currentRunnerIP}}")})})
$JitPolicyVm2 = (@{id="/subscriptions/${{env.SubscriptionID}}/resourceGroups/${{env.ResourceGroupName}}/providers/Microsoft.Compute/virtualMachines/${{env.VM_SECONDARY_NAME}}"; ports=(@{number=22;endTimeUtc="${{env.endTimeUtc}}";allowedSourceAddressPrefix=@("${{env.currentRunnerIP}}")})})
$JitPolicyArr1=@($JitPolicyVm1)
$JitPolicyArr2=@($JitPolicyVm2)
Start-AzJitNetworkAccessPolicy -ResourceId "/subscriptions/${{env.SubscriptionID}}/resourceGroups/${{env.ResourceGroupName}}/providers/Microsoft.Security/locations/${{env.AZ_LOCATION}}/jitNetworkAccessPolicies/JIT-SSH-Policy-primary" -VirtualMachine $JitPolicyArr1
Start-AzJitNetworkAccessPolicy -ResourceId "/subscriptions/${{env.SubscriptionID}}/resourceGroups/${{env.ResourceGroupName}}/providers/Microsoft.Security/locations/${{env.AZ_LOCATION}}/jitNetworkAccessPolicies/JIT-SSH-Policy-secondary" -VirtualMachine $JitPolicyArr2
azPSVersion: "latest"
- name: ๐Ÿ”Ž Get VM Public IP Addresses
run: |
echo "VM_Primary_IP_Address=$(az vm list-ip-addresses --resource-group ${{env.ResourceGroupName}} --name ${{env.VM_PRIMARY_NAME}} --query [0].virtualMachine.network.publicIpAddresses[0].ipAddress -o tsv)" >> $GITHUB_ENV
echo "VM_Secondary_IP_Address=$(az vm list-ip-addresses --resource-group ${{env.ResourceGroupName}} --name ${{env.VM_SECONDARY_NAME}} --query [0].virtualMachine.network.publicIpAddresses[0].ipAddress -o tsv)" >> $GITHUB_ENV
- run: echo ${{env.VM_Primary_IP_Address}}
- run: echo ${{env.VM_Secondary_IP_Address}}
- name: ๐Ÿงฎ Get Known Hosts parameter using ssh-keyscan
run: |
ssh-keyscan -T 300 -H ${{env.VM_Primary_IP_Address}} >> /home/runner/.ssh/known_hosts
ssh-keyscan -T 300 -H ${{env.VM_Secondary_IP_Address}} >> /home/runner/.ssh/known_hosts
- name: ๐Ÿ—’๏ธ Create the inventory file
run: |
cat > inventory <<EOF
[ora-x1]
${{env.VM_PRIMARY_NAME}} ansible_host=${{env.VM_Primary_IP_Address}} ansible_ssh_private_key_file=/home/runner/.ssh/github_actions_id_rsa ansible_user=oracle
[ora-x2]
${{env.VM_SECONDARY_NAME}} ansible_host=${{env.VM_Secondary_IP_Address}} ansible_ssh_private_key_file=/home/runner/.ssh/github_actions_id_rsa ansible_user=oracle
EOF
- name: ๐Ÿ—’๏ธ Show the inventory file
run: cat inventory
##############################################################################################################################################
# Ansible #
##############################################################################################################################################
- name: '๐Ÿ—’๏ธ๐Ÿ’ฟ๐Ÿ”ง Invoke ansible playbook ๐Ÿ˜ด๐Ÿ˜ด๐Ÿ˜ด'
run: ansible-playbook playbook_dg.yml -i inventory --extra-vars "data_guard=yes vm_user_assigned_identity_id=${{env.usId}}"
- name: Run Ansible playbook to test Oracle database state
run: ansible-playbook testplaybook_dg.yml -i inventory > ansible_output.txt
- name: Evaluate test output
run: |
lines=(
"INSTANCE_NAME\t STATUS DATABASE_STATUS"
"${{env.ORCL_DB_NAME}}\t\t OPEN\t ACTIVE"
)
found_all=true
for line in "${lines[@]}"; do
if ! grep -qF "$line" "ansible_output.txt"; then
found_all=false
break
fi
done
if $found_all; then
echo "All lines found in the output."
# Perform further actions based on the output
else
echo "Not all lines found in the output. Showing ansible output:"
cat ansible_output.txt
exit 1 # Exit with a non-zero code to indicate failure
fi