যখন ব্যবসা দ্রুত বৃদ্ধি পাচ্ছে, তখন একজন DevOps ইঞ্জিনিয়ার ম্যানুয়াল অ্যাক্সেস ম্যানেজমেন্ট সহ CI/CD-এর জন্য ব্যবহৃত সমস্ত সিস্টেমে স্বয়ংক্রিয় প্রক্রিয়ার চ্যালেঞ্জের মুখোমুখি হন। এই ধরনের রুটিন কাজগুলি গ্রহণযোগ্য হতে পারে যখন আপনার একটি ছোট দল থাকে, প্রতিটিতে 3টি ধাপ সহ 10টি রিলিজ এবং 10টি পরিবর্তনশীল গ্রুপ থাকে।
যাইহোক, কল্পনা করুন যে আপনার টিম শত শত লোকে বেড়েছে যাদের ম্যানুয়ালি 500টি রিলিজ, প্রতিটি 10টি পর্যায় এবং 200টি পরিবর্তনশীল গ্রুপের সাথে পরিচালনা করতে হবে। এই ক্ষেত্রে, ম্যানুয়াল অ্যাক্সেস ম্যানেজমেন্ট গ্রাহকের অনুরোধের রেজোলিউশনকে নাটকীয়ভাবে কমিয়ে দেয়।
এই পর্যায়ে, প্রতিটি ব্যবসার দক্ষ স্বয়ংক্রিয়করণের প্রয়োজন হয়।
সোশ্যাল ডিসকভারি গ্রুপে, আমরা প্রাথমিক ক্লাউড প্রযুক্তি হিসাবে Azure DevOps-এর উপর নির্ভর করি। যাইহোক, বর্তমানে, এটি স্বয়ংক্রিয় অধিকার, অনুমতি, এবং অ্যাক্সেস স্তরের কার্যকারিতার অভাব রয়েছে। ব্যবহারকারীর প্রয়োজনীয়তা অবিলম্বে সমাধান করার জন্য, আমরা কিছু কাস্টম সমাধান খুঁজে বের করার এবং স্বয়ংক্রিয়ভাবে অনুমতি প্রদান করার সিদ্ধান্ত নিয়েছি।
এই নিবন্ধে, আমরা কীভাবে Azure DevOps-এ কিছু রুটিন কাজ স্বয়ংক্রিয় করতে হয় সে সম্পর্কে কিছু ভাল হাক শেয়ার করব, যাতে ফোকাস করা যায়:
বর্তমানে, Azure অনুমতি দেওয়ার জন্য 3টি বিকল্প অফার করে: সাইটের গ্রাফিকাল ইন্টারফেস ব্যবহার করে, az DevOps CLI ব্যবহার করে এবং API অনুরোধগুলি। প্রথম বিকল্পটি সবচেয়ে সহজবোধ্য এবং দ্রুত; যাইহোক, কোম্পানির স্কেল এবং CI/CD পাইপলাইন এবং ভেরিয়েবলের সংখ্যা বাড়ার সাথে সাথে ম্যানুয়ালি অনুমতি প্রদান করা সময়সাপেক্ষ হয়ে ওঠে।
অনুমতি প্রদানের জন্য দ্বিতীয় বিকল্পটি কার্যকারিতা সীমিত; অন্য কথায়, ব্যবহারকারী একটি নির্দিষ্ট দল যা অফার করে তার বাইরে যেতে পারে না। REST API-এর মাধ্যমে তৃতীয় বিকল্পটি এই মুহূর্তে সবচেয়ে বহুমুখী। আসুন একটি নির্দিষ্ট উদাহরণ বিবেচনা করি যেখানে আমাদের একাধিক ধাপ সহ অসংখ্য রিলিজ পাইপলাইন রয়েছে:
প্রতিটি স্থাপনার পর্যায়ে একটি নির্দিষ্ট ব্যবহারকারী/গোষ্ঠী (তথাকথিত প্রি-ডিপ্লয়মেন্ট অনুমোদন ফাংশন) থেকে অনুমতি প্রয়োজন। এখানে একটি সমস্যা দেখা দেয় যখন একটি নির্দিষ্ট ব্যবহারকারী বা একটি গোষ্ঠীকে অনুমোদনকারীদের একটি প্রদত্ত তালিকা থেকে যুক্ত বা সরানোর প্রয়োজন হয়৷
কোম্পানির প্রসারিত হওয়ার সাথে সাথে, যখন পাইপলাইন এবং ধাপের সংখ্যা N গুণ বৃদ্ধি পায়, তখন রুটিন কেস একটি সমস্যা হয়ে দাঁড়ায় এবং এই সমস্যার সমাধান শুধুমাত্র API অনুরোধের মাধ্যমেই সম্ভব। আমাদের দিক থেকে বাস্তবায়নটি কীভাবে দেখায় তা এখানে:
" pool: vmImage: ubuntu-latest parameters: - name: folder_names type: string default: '\' - name: stage_names type: string default: 'Dev' - name: group_names type: string default: 'Devops' variables: - name: organization value: ORGANIZATION - name: project value: PROJECT - name: pat Value: PAT steps: - bash: | export organization="$(organization)" export project="$(project)" export pat="$(pat)" export folder_names='${{ parameters.folder_names }}' export stage_names='${{ parameters.stage_names }}' export group_names='${{ parameters.group_names }}' export pipeline_name_array=() export stage_name_array=() export group_name_array=() IFS=',' read -ra folder_name_array <<< "$folder_names" IFS=',' read -ra stage_name_array <<< "$stage_names" IFS=',' read -ra group_name_array <<< "$group_names" # Make a GET request to retrieve the release pipelines within the specified project pipeline_url="https://vsrm.dev.azure.com/$organization/$project/_apis/release/definitions?api-version=7.0" pipeline_response=$(curl -s -u ":$pat" "$pipeline_url") # Check for errors in the response if [[ "$pipeline_response" == *"error"* ]]; then echo "Error fetching release pipeline data." exit 1 fi pipeline_names=($(echo "$pipeline_response" | jq -r '.value[].name')) pipeline_ids=($(echo "$pipeline_response" | jq -r '.value[].id')) for ((i=0; i<${#pipeline_names[@]}; i++)); do pipeline_name="${pipeline_names[i]}" pipeline_id="${pipeline_ids[i]}" # Make a GET request to retrieve the release pipeline details pipeline_detail_url="https://vsrm.dev.azure.com/$organization/$project/_apis/release/definitions/$pipeline_id?api-version=7.0" pipeline_detail_response=$(curl -s -u ":$pat" "$pipeline_detail_url") # Extract the releaseDefinition.path from the pipeline details pipeline_path=$(echo "$pipeline_detail_response" | jq -r '.path') # Check if the pipeline_path matches any of the specified folder_names for folder_name in "${folder_name_array[@]}"; do if [[ "$pipeline_path" == *"$folder_name"* ]]; then # Make a GET request to retrieve all releases for the specified pipeline releases_url="https://vsrm.dev.azure.com/$organization/$project/_apis/release/releases?api-version=7.0&definitionId=$pipeline_id" releases_response=$(curl -s -u ":$pat" "$releases_url") # Extract the release names release_names=$(echo "$releases_response" | jq -r '.value[].id') release_definition_url="https://vsrm.dev.azure.com/$organization/$project/_apis/release/definitions/$pipeline_id?api-version=7.0" release_definition_response=$(curl -s -u ":$pat" "$release_definition_url") # Iterate through each group name for group_name in "${group_name_array[@]}"; do # Make a GET request to retrieve the list of groups groups_response=$(curl -s -u ":$pat" "https://vssps.dev.azure.com/$organization/_apis/graph/groups?api-version=7.1-preview.1") # Find the origin_id for the specified group name origin_id=$(echo "$groups_response" | jq -r ".value[] | select(.displayName == \"$group_name\") | .originId") if [ -z "$origin_id" ]; then echo "Group '$group_name' not found or origin_id not available for release '$release_name'." else # Iterate through each stage name for stage_name in "${stage_name_array[@]}"; do # Construct the JSON structure for the new approval new_approval='{ "rank": 1, "isAutomated": false, "isNotificationOn": false, "approver": { "id": "'"$origin_id"'" } }' # Use jq to update the JSON structure for the specified stage updated_definition=$(echo "$release_definition_response" | jq --argjson new_approval "$new_approval" '.environments |= map(if .name == "'"$stage_name"'" then .preDeployApprovals.approvals += [$new_approval] else . end)') # Make a PUT request to update the release definition for the specified stage put_response=$(curl -s -u ":$pat" -X PUT -H "Content-Type: application/json" -d "$updated_definition" "$release_definition_url") release_definition_response=$(curl -s -u ":$pat" "$release_definition_url") # Check if the update was successful if [[ "$put_response" == *"The resource could not be found."* ]]; then echo "Error updating release definition for stage '$stage_name' and group '$group_name'." else echo "Pre-deployment approval added successfully to stage '$stage_name' for group '$group_name' in '$pipeline_id'." fi done fi done fi done done displayName: 'PreDeployApprovals' "
লাইব্রেরি বিভাগের সাথে একই ধরনের সমস্যা দেখা দেয় যখন অসংখ্য পরিবর্তনশীল গোষ্ঠী তৈরি করা হয়, যেখানে একটি নির্দিষ্ট শব্দ ব্যবহার করা হয় কোনো কিছুর সাথে সংশ্লিষ্টতা বোঝাতে। আসুন একটি উদাহরণ ভেঙে দেওয়া যাক যেখানে পরিষেবা-সম্পর্কিত ভেরিয়েবলের সাথে তাদের অধিভুক্তি বোঝাতে "পরিষেবা" শব্দটি সহ একাধিক পরিবর্তনশীল গোষ্ঠী তৈরি করা হয়েছে।
এবং এমন একটি পরিস্থিতিও পরীক্ষা করুন যেখানে একটি নির্দিষ্ট গোষ্ঠীকে নির্দিষ্ট অনুমতি সহ সমস্ত ভেরিয়েবলে অ্যাক্সেসের প্রয়োজন।
এই সাধারণ ক্ষেত্রে সমাধান শুধুমাত্র REST API এর মাধ্যমে সম্ভব:
" variables: - name: organization value: ORGANIZATION - name: project value: PROJECT - name: pat value: PAT parameters: - name: searchWord type: string default: 'SERVICES' - name: UserOrGroupName type: string default: 'Devops' - name: UserRights type: string default: 'Administrator' steps: - bash: | export organization="$(organization)" export project="$(project)" export pat="$(pat)" export userOrGroupName='${{ parameters.UserOrGroupName }}' export UserRights='${{ parameters.UserRights }}' export searchWord='${{ parameters.searchWord }}' # Perform the API request and store the JSON response in a variable response=$(curl -s -u ":$pat" "https://dev.azure.com/$organization/$project/_apis/distributedtask/variablegroups?api-version=6.0") # Initialize an empty array to store matching JSON objects matching_json=() # Loop through the JSON objects and append matching objects to the array while read -r json; do if [[ $(echo "$json" | jq -r '.name' | grep "$searchWord") ]]; then matching_json+=("$json") fi done < <(echo "$response" | jq -c '.value[]') # Iterate through the matching variable groups and assign permissions for group in "${matching_json[@]}"; do # Extract the variable group ID and name variableGroupId=$(echo "$group" | jq -r '.id') variableGroupName=$(echo "$group" | jq -r '.name') # Determine the type of userOrGroupName (username or group name) if [[ $myString != *'@'* ]]; then # If userOrGroupName matches the username format, it's treated as a username roleName=$UserRights assignType="group" groupsResponse=$(curl -s -u ":$pat" "https://vssps.dev.azure.com/$organization/_apis/graph/groups?api-version=6.0-preview.1") #groupOriginId=$(curl -s -u ":$pat" "https://vssps.dev.azure.com/$organization/_apis/identities?searchFilter=$userOrGroupName&api-version=6.0" | jq -r '.value[0].originId') groupOriginId=$(echo "$groupsResponse" | jq -r ".value[] | select(.displayName == \"$userOrGroupName\") | .originId") # Get the user's originId using Azure DevOps REST API #userOriginId=$(curl -s -u ":$pat" "https://vssps.dev.azure.com/$organization/_apis/identities?searchFilter=$userOrGroupName&api-version=6.0" | jq -r '.value[0].principalName') else # Otherwise, it's treated as a group name roleName=$UserRights assignType="user" userOriginId=$(curl -s -u ":$pat" "https://vssps.dev.azure.com/$organization/_apis/identities?searchFilter=$userOrGroupName&api-version=6.0" | jq -r '.value[0].principalName') # Get the group's originId using Azure DevOps REST API #groupOriginId=$(curl -s -u ":$pat" "https://vssps.dev.azure.com/$organization/_apis/identities?searchFilter=$userOrGroupName&api-version=6.0" | jq -r '.value[0].originId') fi # Construct the API URL for assigning permissions apiUrl="https://dev.azure.com/$organization/_apis/securityroles/scopes/distributedtask.variablegroup/roleassignments/resources/19802563-1b7b-43d6-81e0-16cf29d68c0d$"$variableGroupId"?api-version=5.1-preview" # Assign permissions based on the userOrGroupName type if [ "$assignType" == "group" ]; then # Assign permissions to a group # Construct the request body with group's originId requestBody="[{ \"roleName\": \"$roleName\", \"userId\": \"$groupOriginId\" }]" else # Assign permissions to a user # Construct the request body with the user's uniqueName requestBody="[{ \"roleName\": \"$roleName\", \"uniqueName\": \"$userOrGroupName\" }]" fi # Execute the REST API call to assign permissions response=$(curl -s -u ":$pat" -X PUT -H "Content-Type: application/json" -d "$requestBody" "$apiUrl") # Check if the permissions were successfully assigned if [[ "$response" == *"error"* ]]; then echo "Error assigning permissions to $assignType '$userOrGroupName' in variable group '$variableGroupName'." else echo "Permissions assigned successfully to $assignType '$userOrGroupName' in variable group '$variableGroupName'." fi done displayName: 'variable-groups' "
এই প্রক্রিয়াগুলিকে স্বয়ংক্রিয় করার মাধ্যমে, আমরা Azure DevOps-এ অধিকার এবং অনুমতি কাঠামোর আরও বিশদ বোধগম্যতা অর্জন করেছি, প্রকল্পের নিরাপত্তা বাড়ায়।
আমাদের কাছে এখন বিস্তৃত কাজের জন্য স্থিতিশীল এবং বহুমুখী পাইপলাইন রয়েছে, ব্যবহারকারীর অনুরোধের প্রক্রিয়াকরণের সময়কে দ্রুততর করে এবং তাদের সম্পাদনের গুণমান উন্নত করে।
উপরন্তু, আমাদের সামগ্রিক সিস্টেম ডেভেলপমেন্টের গতি এবং গুণমান উন্নত হয়েছে কারণ আমরা অন্যান্য CI/CD কাজের জন্য বেশি সময় বরাদ্দ করি, রুটিন অ্যাকশন থেকে দূরে সরে যাই।
আমরা দৈনন্দিন কাজগুলিকে স্বয়ংক্রিয় করতে, বিভিন্ন বাস্তবায়ন বিকল্পগুলি অন্বেষণ এবং বিশ্লেষণ, ডিবাগ এবং পরিমার্জন করার জন্য উল্লেখযোগ্য প্রচেষ্টা করেছি। রেডিমেড পাইপলাইন এবং স্ক্রিপ্ট থাকা একটি অসাধারণ সুবিধা, কারণ তারা কয়েক সেকেন্ডের মধ্যে কাজগুলি সম্পন্ন করতে পারে।