পাঠ ১৮ · ৫৭-এর মধ্যে · মডিউল ৫
Home / Courses / Cloud Computing & DevOps / Terraform বেসিকস

Terraform বেসিকস — প্রোভাইডার, রিসোর্স, স্টেট

Terraform basics — providers, resources, state
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • Provider, resource ও state file — Terraform-এর তিনটি মূল বিল্ডিং ব্লক
  • State file কেন এত গুরুত্বপূর্ণ ও সংবেদনশীল, এবং কেন এটি প্রায়ই রিমোটলি, লকিং-সহ শেয়ার করা হয়
  • plan → apply ওয়ার্কফ্লো এবং কেন "আগে প্রিভিউ, তারপর প্রয়োগ" নিরাপত্তার জন্য গুরুত্বপূর্ণ
  • Python দিয়ে একটি toy plan/apply diffing ইঞ্জিন তৈরি করা

১ · Provider — প্ল্যাটফর্মের সাথে কথা বলার প্লাগইন

Terraform নিজে একটি জেনেরিক ইঞ্জিন — এটি নিজে থেকে জানে না কীভাবে কোনো নির্দিষ্ট ক্লাউড প্ল্যাটফর্মের সাথে কথা বলতে হয়। এই কাজটি করে ProviderProviderএকটি প্লাগইন যা Terraform-এর কোর ইঞ্জিনকে একটি নির্দিষ্ট প্ল্যাটফর্মের API-এর সাথে কীভাবে কথা বলতে হয় তা শেখায় — AWS, Azure, GCP, Kubernetes ইত্যাদির নিজস্ব provider আছে। — AWS, Azure, GCP, Kubernetes এমনকি অনেক SaaS টুলেরও নিজস্ব provider প্লাগইন আছে। এই আর্কিটেকচারের সুবিধা হলো — কোর ইঞ্জিন প্রোভাইডার-অজ্ঞেয়বাদী (provider-agnostic), শুধু নির্দিষ্ট প্লাগইনগুলো প্ল্যাটফর্ম-নির্দিষ্ট। এই কারণেই এই কোর্সের পুরো IaC মডিউল কোনো নির্দিষ্ট cloud vendor ধরে নেয় না — কনসেপ্টগুলো যেকোনো provider-এর সাথে প্রযোজ্য।

২ · Resource — একক ইনফ্রাস্ট্রাকচার অবজেক্ট

ResourceResourceএকটি একক ইনফ্রাস্ট্রাকচার অবজেক্ট যা আপনি ম্যানেজ করতে চান — যেমন একটি ভার্চুয়াল মেশিন, একটি স্টোরেজ বাকেট, একটি সিকিউরিটি গ্রুপ — কনফিগারেশনে ঘোষিত হয়। আপনার কনফিগারেশন ফাইল অনেকগুলো resource ব্লকের সমষ্টি — প্রতিটি একটি নির্দিষ্ট ইনফ্রাস্ট্রাকচার অবজেক্ট বর্ণনা করে (একটি ভিএম, একটি নেটওয়ার্ক, একটি ডেটাবেস)। এই resource-গুলো একসাথে মিলেই আপনার সম্পূর্ণ কাঙ্ক্ষিত ইনফ্রাস্ট্রাকচার তৈরি করে — L17-এ শেখা ডিক্লারেটিভ দর্শনের সরাসরি প্রয়োগ।

৩ · State file — Terraform-এর স্মৃতি

State fileState fileTerraform-এর রেকর্ড যা মনে করে বর্তমানে কী ইনফ্রাস্ট্রাকচার exist করছে — আপনার কনফিগারেশনকে বাস্তব-জগতের রিসোর্স আইডির সাথে ম্যাপ করে। হলো সেই ফাইল যা আপনার কনফিগারেশনকে বাস্তব-জগতের রিসোর্স আইডির সাথে সংযুক্ত করে — Terraform এই ফাইল ছাড়া জানতে পারবে না কোন রিসোর্স already তৈরি হয়ে গেছে এবং কোনটা এখনো তৈরি করতে হবে।

সংবেদনশীল
স্টেট ফাইলে রিসোর্সের বিস্তারিত তথ্য (কখনো কখনো সিক্রেটও) থাকে — সরাসরি Git-এ কমিট করা উচিত নয় (L40-এর সিক্রেট ম্যানেজমেন্ট নীতির সাথে সরাসরি সংযুক্ত)।
রিমোট স্টোরেজ
টিমে কাজ করার সময় স্টেট ফাইল প্রায়ই একটি শেয়ার্ড রিমোট লোকেশনে রাখা হয়, যাতে সবাই একই "সত্য উৎস" দেখে।
লকিং
দুইজন একই সময়ে apply চালালে কনফ্লিক্ট হতে পারে — লকিং মেকানিজম নিশ্চিত করে একবারে শুধু একজনই পরিবর্তন করতে পারে।

৪ · মূল ওয়ার্কফ্লো — plan তারপর apply

Terraform-এর দৈনন্দিন ওয়ার্কফ্লো তিনটি ধাপে চলে —

  • কনফিগ লেখা — কাঙ্ক্ষিত অবস্থা বর্ণনা করে ফাইল লেখা (L17-এর ডিক্লারেটিভ দর্শন)।
  • plan — কনফিগারেশনকে বর্তমান state-এর সাথে তুলনা করে কী কী পরিবর্তন হবে তার একটি প্রিভিউ দেখানো (কিছুই আসলে পরিবর্তন হয় না এই ধাপে) — কোন রিসোর্স তৈরি হবে, কোনটা পরিবর্তন হবে, কোনটা মুছে ফেলা হবে।
  • apply — plan-এ দেখানো পরিবর্তনগুলো আসলে প্রয়োগ করা এবং state file আপডেট করা।
কেন "আগে প্রিভিউ" এত গুরুত্বপূর্ণ

plan ধাপ আপনাকে ঠিক কী পরিবর্তন হতে যাচ্ছে তা দেখার সুযোগ দেয় — কোনো ভুল কনফিগারেশন যদি অপ্রত্যাশিতভাবে একটি প্রোডাকশন ডেটাবেস মুছে ফেলার চেষ্টা করে, আপনি সেটি apply করার আগেই ধরতে পারবেন। এটিই ডিক্লারেটিভ IaC-এর নিরাপত্তা মূল্য — "দেখুন, তারপর প্রয়োগ করুন", অন্ধভাবে চালানো নয়।

৫ · Python-এ plan/apply সিমুলেট করা

নিচের সিমুলেশনে desired_config ও current_state দুটো dict — যথাক্রমে আপনার কনফিগ ফাইল ও Terraform-এর state file-এর প্রতীক। plan() ফাংশন দুটো dict তুলনা করে কোন রিসোর্স add, change বা remove হবে তা বের করে — এবং এটি আগে থেকেই প্রিন্ট করে দেখায়, কোনো state আসলে পরিবর্তন করার আগে।

Python
# toy সিমুলেশন — কোনো real Terraform CLI বা cloud API কল হচ্ছে না
desired_config = {
    "web-01": {"type": "medium", "region": "dhaka-1"},
    "web-02": {"type": "small", "region": "dhaka-1"},
    "cache-01": {"type": "small", "region": "dhaka-1"},   # নতুন রিসোর্স
}

current_state = {
    "web-01": {"type": "medium", "region": "dhaka-1"},    # অপরিবর্তিত
    "web-02": {"type": "micro", "region": "dhaka-1"},      # type ভিন্ন -> change
    "db-01": {"type": "large", "region": "dhaka-1"},       # কনফিগে নেই -> remove
}

def plan(desired, current):
    """desired ও current state তুলনা করে একটি পরিবর্তনের সারাংশ ফেরত দেয়।"""
    actions = {"add": [], "change": [], "remove": [], "no-op": []}
    for name, spec in desired.items():
        if name not in current:
            actions["add"].append(name)
        elif current[name] != spec:
            actions["change"].append(name)
        else:
            actions["no-op"].append(name)
    for name in current:
        if name not in desired:
            actions["remove"].append(name)
    return actions

def apply(desired, current, plan_result):
    """plan অনুযায়ী current_state mutate করে desired-এর সাথে মিলিয়ে দেয়।"""
    for name in plan_result["remove"]:
        del current[name]
    for name in plan_result["add"] + plan_result["change"]:
        current[name] = dict(desired[name])
    return current

result = plan(desired_config, current_state)
print("=== Terraform plan (প্রিভিউ — এখনো কিছুই পরিবর্তন হয়নি) ===")
print(f"  + যোগ হবে ({len(result['add'])}):    {result['add']}")
print(f"  ~ পরিবর্তন হবে ({len(result['change'])}): {result['change']}")
print(f"  - মুছে ফেলা হবে ({len(result['remove'])}): {result['remove']}")
print(f"  = অপরিবর্তিত ({len(result['no-op'])}):  {result['no-op']}")

print("\n=== apply চালানোর পর current_state ===")
new_state = apply(desired_config, current_state, result)
for name, spec in sorted(new_state.items()):
    print(f"  {name}: {spec}")

    
লক্ষ্য করুন — plan() কল করলে current_state-এর কিছুই পরিবর্তন হয় না, শুধু একটি প্রিভিউ রিপোর্ট তৈরি হয়। শুধুমাত্র apply() কল করলেই আসল mutation ঘটে। বাস্তব Terraform-এও ঠিক এই দুই-ধাপ বিভাজন আছে — এবং একটি টিমের জন্য plan আউটপুট রিভিউ করা (প্রায়ই পুল রিকোয়েস্টে) apply করার আগে একটি স্ট্যান্ডার্ড বেস্ট প্র্যাকটিস (L21-এ বিস্তারিত)।
মূল কথা · Key takeaway

Provider Terraform-কে একটি নির্দিষ্ট প্ল্যাটফর্মের সাথে কথা বলার ক্ষমতা দেয়, resource প্রতিটি ইনফ্রাস্ট্রাকচার অবজেক্ট বর্ণনা করে, আর state file হলো Terraform-এর স্মৃতি — বাস্তবতার সাথে কনফিগের সংযোগ। plan → apply ওয়ার্কফ্লো নিশ্চিত করে আপনি সবসময় পরিবর্তন প্রয়োগ করার আগে সেটি দেখতে পান। পরের পাঠে আমরা দেখব কীভাবে এই resource-গুলোকে reusable মডিউলে বান্ডল করা যায়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ state file হারিয়ে গেলে (বা দুর্ঘটনাক্রমে মুছে গেলে) কী সমস্যা হতে পারে, এমনকি যদি সব real ইনফ্রাস্ট্রাকচার ঠিকঠাক চলতে থাকে?

Terraform আর জানবে না কোন resource-গুলো already exist করে — পরবর্তী plan/apply হয়তো সবকিছু নতুন করে তৈরি করার চেষ্টা করবে (ডুপ্লিকেট রিসোর্স, অতিরিক্ত খরচ) বা বিদ্যমান রিসোর্সকে "অচেনা" মনে করে ভুলভাবে হ্যান্ডল করবে। এই কারণেই state file-কে রিমোট, ব্যাকআপযুক্ত স্টোরেজে রাখা (ঠিক যেমন L12-এর স্ন্যাপশট প্র্যাকটিস) এবং কখনো ম্যানুয়ালি এডিট না করা এত গুরুত্বপূর্ণ।

প্র ০২ দুইজন ইঞ্জিনিয়ার একই সময়ে state file লক ছাড়াই apply চালালে কী ভুল হতে পারে?

দুজনেই একই মুহূর্তে পুরনো state পড়ে নিজ নিজ পরিবর্তনের plan তৈরি করতে পারে — তারপর দুজনেই একসাথে apply চালালে একজনের পরিবর্তন অন্যজনের লেখা state overwrite করে দিতে পারে (একটি "lost update" সমস্যা, ডেটাবেস কনকারেন্সির মতোই)। ফলাফল একটি অসামঞ্জস্যপূর্ণ state file যা বাস্তব ইনফ্রাস্ট্রাকচারের সাথে মেলে না — এই কারণেই লকিং মেকানিজম দরকার, যা নিশ্চিত করে একবারে শুধু একজনই apply চালাতে পারে।

প্র ০৩ উপরের কোড সেলে plan() কেন apply()-এর আগে আলাদা ফাংশন হিসেবে রাখা হলো, একটি সিঙ্গেল ফাংশনে merge না করে?

আলাদা রাখার ফলে আপনি প্রিভিউটি রিভিউ করতে পারেন (মানুষ পড়ে, বা CI পাইপলাইনে একটি অটোমেটেড পলিসি-চেক চালিয়ে, L21-এর মতো) — mutation ঘটার আগে। একটি সিঙ্গেল ফাংশনে merge করলে "দেখা" ও "করা" এই দুটো ভিন্ন দায়িত্ব একসাথে মিশে যেত, এবং কোনো ভুল কনফিগারেশন ধরার সুযোগ চলে যেত — ঠিক যেমন একটি ব্যাংক ট্রান্সফারে "প্রিভিউ দেখান" এবং "নিশ্চিত করুন" আলাদা ধাপ হিসেবে থাকা নিরাপত্তার জন্য গুরুত্বপূর্ণ।

অনুশীলন

  1. চিন্তা করুন: কেন state file সরাসরি Git রিপোজিটরিতে কমিট করা একটি বাজে প্র্যাকটিস হিসেবে বিবেচিত হয়, যদিও আপনার কনফিগ ফাইল (.tf) Git-এ রাখা উচিত?

    কনফিগ ফাইল বর্ণনা করে আপনি কী চান — এটি ভার্সন কন্ট্রোলের জন্য একদম উপযুক্ত (L17)। কিন্তু state file-এ প্রায়ই বাস্তব রিসোর্স আইডি, IP অ্যাড্রেস, এবং কখনো কখনো সিক্রেট থাকে — Git-এ কমিট করলে সেই সংবেদনশীল তথ্য পুরো ইতিহাসে চিরকাল থেকে যাবে (L40-এর সিক্রেট-স্ক্যানিং সমস্যার মতো), এমনকি পরে "মুছে" ফেললেও।

  2. পরীক্ষা করুন: উপরের কোড সেলে current_state-এ একটি নতুন এন্ট্রি "cache-01": {"type": "small", "region": "dhaka-1"} যোগ করে (অর্থাৎ desired_config-এর সাথে হুবহু মিলিয়ে) আবার plan() চালান — ফলাফল কী দেখায়?

    এখন cache-01 "add" তালিকা থেকে সরে "no-op" তালিকায় চলে যাবে, কারণ current_state-এ এটি already desired_config-এর সাথে হুবহু মিলে যায় — এটি দেখায় plan সবসময় বর্তমান বাস্তবতা যাচাই করে, প্রতিবার সবকিছু নতুন করে তৈরির প্রস্তাব দেয় না।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
Infrastructure as Code পরিচিতি